diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index f79d1bd692..c9f07f12c3 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -61,6 +61,9 @@ jobs: - name: Validate architecture vs prompt includes (repo, non-sample trees) run: pdd checkup --validate-arch-includes + - name: Architecture completeness gate (module <-> architecture.json bijection) + run: pytest tests/test_architecture_completeness.py -q --no-header + - name: Run unit tests run: > pytest tests/ @@ -76,6 +79,9 @@ jobs: --deselect=tests/test_setup_tool.py::test_create_api_env_script_with_special_characters_zsh --deselect=tests/test_setup_tool.py::test_create_api_env_script_with_common_problematic_characters + - name: Check .pdd/meta fingerprints are current + run: python scripts/stamp_fingerprints.py --check + public-cli-regression: if: github.event_name != 'pull_request' || github.event.pull_request.draft != true name: Public CLI Regression diff --git a/.gitignore b/.gitignore index 003e63fe71..7cffca5287 100644 --- a/.gitignore +++ b/.gitignore @@ -243,34 +243,13 @@ yarn.lock .pdd/* !.pdd/ !.pdd/meta/ -.pdd/meta/*.json -!.pdd/meta/agentic_bug_orchestrator_python.json -!.pdd/meta/agentic_bug_python.json -!.pdd/meta/agentic_checkup_python.json -!.pdd/meta/agentic_checkup_orchestrator_python.json -# Agentic helper fingerprints are regenerated by auto-heal and intentionally -# remain ignored unless a module has an explicit reason to pin metadata. -!.pdd/meta/agentic_e2e_fix_orchestrator_python.json -!.pdd/meta/checkup_review_loop_python.json -!.pdd/meta/commands_checkup_python.json -!.pdd/meta/commands_prompt_python.json -!.pdd/meta/construct_paths_python.json -!.pdd/meta/prompt_lint_python.json -!.pdd/meta/core_cloud_python.json -!.pdd/meta/fix_code_loop_python.json -!.pdd/meta/fix_error_loop_python.json -!.pdd/meta/get_comment_python.json -!.pdd/meta/get_test_command_python.json -!.pdd/meta/git_update_python.json -!.pdd/meta/llm_invoke_python.json -!.pdd/meta/preprocess_python.json -!.pdd/meta/summarize_directory_python.json -!.pdd/meta/sync_determine_operation_python.json -!.pdd/meta/sync_tui_python.json -# Design-refresh (PR #1561) dev units: track their fingerprints so the auto-heal -# drift detector sees committed metadata for the modules this PR changed. -!.pdd/meta/cli_theme_python.json -!.pdd/meta/sync_animation_python.json +# Track ALL dev-unit fingerprints (global-sync issue A). The committed +# .pdd/meta/*.json set is the oracle `pdd sync` trusts for every decision, so it +# must be complete and current, not a hand-maintained allowlist. Regenerate the +# whole set with scripts/stamp_fingerprints.py (never hand-edit; see +# CONTRIBUTING.md). Only transient files stay ignored: atomic-write temp files +# below, and sync logs via the global `*.log` rule above. +.pdd/meta/*.tmp .pdd/meta/*_run.json # Issue #1006 follow-up: these two modules' tests pass quickly but # `pdd sync` runs >20 minutes on them in CI, hitting the auto-heal diff --git a/.pdd/meta/_keyring_timeout_python.json b/.pdd/meta/_keyring_timeout_python.json new file mode 100644 index 0000000000..3ec5e0cdb7 --- /dev/null +++ b/.pdd/meta/_keyring_timeout_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.919599+00:00", + "command": "fix", + "prompt_hash": "febb0f9fd9f1e5f011acb73b44124f44ab3d401d53dc317912518f0798a075ad", + "code_hash": "edf79dca88f4b80350a607c8978cbc74ed9b195eaa1e052172409d15529db1e1", + "example_hash": "41f83d5ef117caba86b788f69b519f7e88f3d7dc2a641a017d74a7cee9be6ed1", + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_architecture_orchestrator_python.json b/.pdd/meta/agentic_architecture_orchestrator_python.json index 492082f7d9..f388d14cc0 100644 --- a/.pdd/meta/agentic_architecture_orchestrator_python.json +++ b/.pdd/meta/agentic_architecture_orchestrator_python.json @@ -1,17 +1,20 @@ { - "pdd_version": "0.0.230", - "timestamp": "2026-05-07T22:56:39.108172+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.924654+00:00", "command": "fix", - "prompt_hash": "f00e7085128ae23be67d1969542748cee0b0035552a1bf9ee3601a7e4497a3d1", - "code_hash": "a782f86dda46bdb22d9dc90a48049428b07e38f791dc55c1ac10b47e78156eab", + "prompt_hash": "d54fc246924930ac8e4dd5332bb00618220b4526eda5bd5267634430da6c0698", + "code_hash": "72bdf8417b0f27eb676bdf858032368dc5325e076f665e8de46e080315d4dc37", "example_hash": "aad78130a96907c5d1bd197a7bca53d6d1bdcf5775776860993f9e5c68635867", "test_hash": "30049fa3b4706221c111095cbbb9992f3335bd9ec6d51d6068bcd0cc2f9cf9f5", "test_files": { "test_agentic_architecture_orchestrator.py": "30049fa3b4706221c111095cbbb9992f3335bd9ec6d51d6068bcd0cc2f9cf9f5" }, "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", - "context/python_preamble.prompt": "57a3e51f529024ec0cb9658cd6ac61a7c8051ba0c8e887b31cf00b2e78a07d83", - "context/render_mermaid_example.py": "193a57bc4191595f252881c016ba7168c27c27c01ff192128d98cf76ce5f842b" + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/render_mermaid_example.py": "193a57bc4191595f252881c016ba7168c27c27c01ff192128d98cf76ce5f842b", + "pdd/architecture_registry.py": "5722225846b9cc8189b410a78e5714b4f2916d5748271d27f6f738692429c309", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56" } -} +} \ No newline at end of file diff --git a/.pdd/meta/agentic_architecture_python.json b/.pdd/meta/agentic_architecture_python.json new file mode 100644 index 0000000000..37454f6dfc --- /dev/null +++ b/.pdd/meta/agentic_architecture_python.json @@ -0,0 +1,14 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.926032+00:00", + "command": "fix", + "prompt_hash": "2c9c70c851e1af7c0af70a9c9ce960f96a6c3bcce7edadef570457bed96987d4", + "code_hash": "09dd7b12d48fc7c0c0888157a5b366c42a077cd21b880f477ec763bca06edf28", + "example_hash": "f82ea3f16297c0cd0f7993549054bc9213953ebe03144aa5abc7bd74ba707e13", + "test_hash": "c15c58d2200e68f2db1c31c387d5f7c992e89623e037f47cf37042a5a43484de", + "test_files": { + "test_agentic_architecture.py": "c15c58d2200e68f2db1c31c387d5f7c992e89623e037f47cf37042a5a43484de", + "test_agentic_architecture_orchestrator.py": "30049fa3b4706221c111095cbbb9992f3335bd9ec6d51d6068bcd0cc2f9cf9f5" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/agentic_bug_orchestrator_python.json b/.pdd/meta/agentic_bug_orchestrator_python.json index a28ea032b6..4830e85164 100644 --- a/.pdd/meta/agentic_bug_orchestrator_python.json +++ b/.pdd/meta/agentic_bug_orchestrator_python.json @@ -1,20 +1,20 @@ { - "pdd_version": "0.0.236", - "timestamp": "2026-05-26T00:00:00.000000+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.930583+00:00", "command": "fix", - "prompt_hash": "d33d79202c9e395e9a2a43a67741d6578ca002d07dec20cdeec77f9af0849fd1", - "code_hash": "03f458276287612267b7a96b5cb2042df81fb6a15d57f531634094f5e921aef9", + "prompt_hash": "074d1786614ee59558833746bede5e9236a9ceba635c5d8eb477880ea17eb9fd", + "code_hash": "f04052d57db4963fbdab55cc1076d11f72e5e7a50c8c40a15e8d8266a6cd189c", "example_hash": "23b545fc0f892e14ee9660902d3f7e04893f0b793246c1ea9d559656fd39ffe6", - "test_hash": "781fe2b15c71d3098e06d8845e8e03255d547093f65cb81a7d0b769d6c1a14e7", + "test_hash": "b0b0448434f1a87c1beb56ed4411462091b96096f3d662eca706b336ac43d3bf", "test_files": { - "test_agentic_bug_orchestrator.py": "781fe2b15c71d3098e06d8845e8e03255d547093f65cb81a7d0b769d6c1a14e7", + "test_agentic_bug_orchestrator.py": "b0b0448434f1a87c1beb56ed4411462091b96096f3d662eca706b336ac43d3bf", "test_agentic_bug_orchestrator_1.py": "8085e2041355e53aef1709eeba3591fa0f7c512a637038b8c48d633e4cd07aa2", - "test_agentic_bug_orchestrator_step_comments.py": "693e1c1b571a1daee1013fb48bc8390f263a6544c163e3f55bcb60dafa1bb679" + "test_agentic_bug_orchestrator_step_comments.py": "d8b948bbe7f2ee02202cf54c28812a2e6eb85f398b0c50bd0b8e9d9387c3a5c8" }, "include_deps": { "context/agentic_bug_example.py": "96e35df4e8425a9173ff6a30281892c2bd8b81361cff72b732e488172ddf52f5", - "context/agentic_common_example.py": "113b5829cae3a9e38c362b74d5115ba09cab06c7be5831527d9829f97f7b3b7d", + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" } -} +} \ No newline at end of file diff --git a/.pdd/meta/agentic_bug_python.json b/.pdd/meta/agentic_bug_python.json index 06e2e27fba..af784122d4 100644 --- a/.pdd/meta/agentic_bug_python.json +++ b/.pdd/meta/agentic_bug_python.json @@ -1,21 +1,22 @@ { - "pdd_version": "0.0.228", - "timestamp": "2026-05-26T00:00:00.000000+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.932667+00:00", "command": "regenerate-public", "prompt_hash": "e0bd2e856ac450d59c9b284919708dd8771379b5a868f237a36174bec4d77efe", - "code_hash": "cd8cbb5f532bb89bdee0487bdd541240d0bb050c78d541135e251c94addb52f6", - "example_hash": null, + "code_hash": "ff878e298b3e1eb0639f99e68c88bb3507074208773f94440d413099fe896dc1", + "example_hash": "96e35df4e8425a9173ff6a30281892c2bd8b81361cff72b732e488172ddf52f5", "test_hash": "d9a31f3d603ee36158bbb2341c9ab43ba71f1762cef29027940b35404abba585", "test_files": { "test_agentic_bug.py": "d9a31f3d603ee36158bbb2341c9ab43ba71f1762cef29027940b35404abba585", - "test_agentic_bug_orchestrator.py": "781fe2b15c71d3098e06d8845e8e03255d547093f65cb81a7d0b769d6c1a14e7", + "test_agentic_bug_orchestrator.py": "b0b0448434f1a87c1beb56ed4411462091b96096f3d662eca706b336ac43d3bf", "test_agentic_bug_orchestrator_1.py": "8085e2041355e53aef1709eeba3591fa0f7c512a637038b8c48d633e4cd07aa2", + "test_agentic_bug_orchestrator_step_comments.py": "d8b948bbe7f2ee02202cf54c28812a2e6eb85f398b0c50bd0b8e9d9387c3a5c8", "test_agentic_bug_step10_prompt.py": "cca4e817639dabd0afd5f805eb894b78decc1858472d114debf885ffacbdf3d2", - "test_agentic_bug_step11_prompt.py": "8b41000be193c0024d999dd6cf1bd9f2f4615692e1c6bc52d0c46a83b560427b", - "test_agentic_bug_step7_prompt.py": "60c139b52632113013e2e1ac5c77f13257d0d370fc2be61d2420522bf44c830a" + "test_agentic_bug_step11_prompt.py": "fffd3447622a9df074b2a310da27fc466d964813b692210daaa5aaf6c6d59f59", + "test_agentic_bug_step7_prompt.py": "376be9629d5928d1dd5b3245572af3c3e8799d66d59e7c933ab08c0405b64faf" }, "include_deps": { "context/agentic_bug_orchestrator_example.py": "23b545fc0f892e14ee9660902d3f7e04893f0b793246c1ea9d559656fd39ffe6", "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" } -} +} \ No newline at end of file diff --git a/.pdd/meta/agentic_change_orchestrator_python.json b/.pdd/meta/agentic_change_orchestrator_python.json index c1a7ee6dbd..0aaa0e8a61 100644 --- a/.pdd/meta/agentic_change_orchestrator_python.json +++ b/.pdd/meta/agentic_change_orchestrator_python.json @@ -1,21 +1,22 @@ { - "pdd_version": "0.0.233", - "timestamp": "2026-05-26T00:00:00.000000+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.940185+00:00", "command": "fix", - "prompt_hash": "147aebab37557437b25ade6f4a78b72c77350aed19d5f1d6d900feeaf4eadca4", - "code_hash": "19d8f4174475e05699edd0de801f238b3df6edc799b1ee44c5367cbad978adce", + "prompt_hash": "c4a5d86036e7020281184875081fe00099168f15de45a3f6fe050b689193499f", + "code_hash": "0eb021e1a02844b18205300373b4f45033faffa90c2eb681754403d26b864ad0", "example_hash": "fceaa9e9326bc78d10c74e519d756a09235b9606aba7b6e513fd3e2ebc56b4a2", - "test_hash": "10833f31e7de88490aa42d807f609f8481bb567c55aea5172b91429e398a91c4", + "test_hash": "2143164ebab405ee47b64dc45ce6c52eb025a54340233bb749b7333aa40cfbdd", "test_files": { - "test_agentic_change_orchestrator.py": "10833f31e7de88490aa42d807f609f8481bb567c55aea5172b91429e398a91c4" + "test_agentic_change_orchestrator.py": "2143164ebab405ee47b64dc45ce6c52eb025a54340233bb749b7333aa40cfbdd" }, "include_deps": { - "context/construct_paths_example.py": "9fb88745a2a0e2834f9da30c2128a122fef4d5a74212ceacc5f9c3d9ddb9a8ca", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", "context/get_extension_example.py": "def9c34c1416259a0a031a996b96aeff5e73a7012adff6af1cc557e74ba97cc8", "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", - "pdd/agentic_common.py": "82e30c361de4d7b9a62928fe21d8f55df96691c461c73750b30595e3a04446f2", - "pdd/architecture_sync.py": "fb55dc87aed5eca7eb62918291a247f4f3cc6b67555cdc8c02e22ce90a980d70", - "pdd/preprocess.py": "f95eedec5bcb4ca505d8682d4a38ea5d4b3eb07ac1d52a38ebffa9f89941a92e", - "pdd/sync_order.py": "118e49a22b0be2ae3017fdfeb1e05715a878efb89fab7750868be2388f2eaad9" + "pdd/agentic_common.py": "7b9b57eac64bac2b4fd18d4eea3d2feb3c7d99ff43f0ca1961a0bfd22a9576da", + "pdd/architecture_sync.py": "0e0ff74978f49d629f92365723ebbea0e7c70e6d5234ccfdafb1c8e09c110c11", + "pdd/pre_checkup_gate.py": "9ff0bbaa18c82cf4da836126861c40936a4555b8ff972f4bceee370492f37ace", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56", + "pdd/sync_order.py": "6cdaed3b56d9cc12f38ee5ac93b8623899372ea2d177c4f131ec258edb94d0dd" } -} +} \ No newline at end of file diff --git a/.pdd/meta/agentic_change_python.json b/.pdd/meta/agentic_change_python.json new file mode 100644 index 0000000000..cdf23592cf --- /dev/null +++ b/.pdd/meta/agentic_change_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.942045+00:00", + "command": "fix", + "prompt_hash": "629db7a1468e83aa831209e199f3058e30d83a572304487b9fcc89c6abb5bbdb", + "code_hash": "3ef811c674502fe2038225a183bb25edd372c86f52ac2308261448d5127f92b0", + "example_hash": "a8308b2af708c804d626a4a96012c5a67f5bc6469e33857cafc07627213828d7", + "test_hash": "12f8826c496f290c152545546209de1f78787aad054e2b45b16443dfb8d80b04", + "test_files": { + "test_agentic_change.py": "12f8826c496f290c152545546209de1f78787aad054e2b45b16443dfb8d80b04", + "test_agentic_change_orchestrator.py": "2143164ebab405ee47b64dc45ce6c52eb025a54340233bb749b7333aa40cfbdd" + }, + "include_deps": { + "context/agentic_change_orchestrator_example.py": "fceaa9e9326bc78d10c74e519d756a09235b9606aba7b6e513fd3e2ebc56b4a2", + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_checkup_orchestrator_python.json b/.pdd/meta/agentic_checkup_orchestrator_python.json index 2322ad2d07..040bf5ad96 100644 --- a/.pdd/meta/agentic_checkup_orchestrator_python.json +++ b/.pdd/meta/agentic_checkup_orchestrator_python.json @@ -1,18 +1,18 @@ { - "pdd_version": "0.0.273", - "timestamp": "2026-06-13T18:03:20.500524+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.945988+00:00", "command": "example", - "prompt_hash": "74563384c8358e0cfb994bd2b5e37a93a478a64c3e83a7cd1a63fa44246258d5", - "code_hash": "2a29489d565b0ebf5ef1d353f0070e806f9f990cd1eedc1c547abb983ddf93c0", - "example_hash": "9290bba1f27a21c2a8603065864fd059de6fde22151d6f0062e813a48b91061a", - "test_hash": "40ab1539859560e07acc3b4f5ac70ddeaa30e37333a04658c43c40593b18d55c", + "prompt_hash": "f6ce089869d82405a523e00b735d95f9a6b36d26cdbf84858df60ec56d5ff234", + "code_hash": "743a47b8a779974079f0ae3bb77090e47b3bd87606ac73a58cbf71e8b645d52e", + "example_hash": "295a9ae294e22224d3c447f13c462854bfe4e526cebc89bcbba417ea514c7ef2", + "test_hash": "7bfdf5931599356de13b4129f62ad5ed7ae928e10ae391c88a2e16f62ebf8d5c", "test_files": { - "test_agentic_checkup_orchestrator.py": "40ab1539859560e07acc3b4f5ac70ddeaa30e37333a04658c43c40593b18d55c" + "test_agentic_checkup_orchestrator.py": "7bfdf5931599356de13b4129f62ad5ed7ae928e10ae391c88a2e16f62ebf8d5c" }, "include_deps": { "context/agentic_bug_orchestrator_example.py": "23b545fc0f892e14ee9660902d3f7e04893f0b793246c1ea9d559656fd39ffe6", - "context/agentic_common_example.py": "f9cdb30a932973d341b795faf510056b95573610af5ac48e3849cf42c205998e", - "context/agentic_e2e_fix_orchestrator_example.py": "182e33cbf3aa78dc177caead0bf7f9e827cc17f0f9ba7cc32c0dd027063bde64", + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/agentic_e2e_fix_orchestrator_example.py": "180cc2c4316b531586040d61fc5f82f1f605d5c5736fd23bad63a889ac0b7d1e", "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", "context/preprocess_example.py": "e7082a4a9ef830048ff32ed774a3e74da1cf4662a22115e6bd5543e1b9cdbf43" diff --git a/.pdd/meta/agentic_checkup_python.json b/.pdd/meta/agentic_checkup_python.json index c5d85cd57e..275bdd8dfc 100644 --- a/.pdd/meta/agentic_checkup_python.json +++ b/.pdd/meta/agentic_checkup_python.json @@ -1,22 +1,23 @@ { - "pdd_version": "0.0.288.dev0", - "timestamp": "2026-06-26T22:58:40.678699+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.949003+00:00", "command": "test", - "prompt_hash": "86d6766af5e844962a3160de65f69177aab4146060461afeb674ad489ab71e26", - "code_hash": "30e37254c3d5721d0b7a43c023a677b670219b072fd0fa7a64990ce0c13fd175", + "prompt_hash": "4b264564e6cdff492415aff67a3da2362933b7d802a1e1470d31d973d7d7566a", + "code_hash": "d5b79fc009c5f935fd7fe70b2b2517f4f1f5c2c02a1231bf1d87ddbc4baba042", "example_hash": "57c5e8ae852946da79151ab49e44400f2c093bf6f8add0bf25a01c9872a13101", "test_hash": "4d7a9c10ba2f9925f7b567f885b6dd67dd48aa3e8757e347ddad9e6c7b6f3e02", "test_files": { "test_agentic_checkup.py": "4d7a9c10ba2f9925f7b567f885b6dd67dd48aa3e8757e347ddad9e6c7b6f3e02", - "test_agentic_checkup_orchestrator.py": "e37ccd4a0ffaa25a52f1374c570bb4fa9b4a69bd95a9a766f4e60d97a4dfb5fe" + "test_agentic_checkup_orchestrator.py": "7bfdf5931599356de13b4129f62ad5ed7ae928e10ae391c88a2e16f62ebf8d5c", + "test_agentic_checkup_step5_pass_evidence.py": "a9d5ac08e1cbf1326ab352cffdfbb3bc84c280327b70413ef6f982860bf89e44" }, "include_deps": { "context/agentic_change_example.py": "a8308b2af708c804d626a4a96012c5a67f5bc6469e33857cafc07627213828d7", "context/agentic_checkup_orchestrator_example.py": "295a9ae294e22224d3c447f13c462854bfe4e526cebc89bcbba417ea514c7ef2", "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", - "context/agentic_sync_example.py": "9bcc2f199a1463e8c424d8210ea6d5f34ad6fb5fcb1022aa879a9ecba0f8d5d6", + "context/agentic_sync_example.py": "aa9683f1a26841f3cae854e72cce38636f0859d8dd2f7672fa14e412afdef884", "context/checkup_review_loop_example.py": "b392eaa48f9bd84660d4a2aaec04c5237a7791059c6ffd64288b34eadce686bb", "context/ci_validation_example.py": "06b910b216f68af58862e76712ccf82bae1bdf87f3fa95b043408fb079f45ce0", "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" } -} +} \ No newline at end of file diff --git a/.pdd/meta/agentic_common_python.json b/.pdd/meta/agentic_common_python.json new file mode 100644 index 0000000000..b192eab6d6 --- /dev/null +++ b/.pdd/meta/agentic_common_python.json @@ -0,0 +1,22 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.954971+00:00", + "command": "fix", + "prompt_hash": "300127d8bb7c0a9c48aecad8b02a9b9e1bead782d778e5e70f97b6f827eab308", + "code_hash": "7b9b57eac64bac2b4fd18d4eea3d2feb3c7d99ff43f0ca1961a0bfd22a9576da", + "example_hash": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "test_hash": "9007b43d4423a3d90be314d02afbd04b6a95537d3a19c19bfeef5a0a6c8232c0", + "test_files": { + "test_agentic_common.py": "9007b43d4423a3d90be314d02afbd04b6a95537d3a19c19bfeef5a0a6c8232c0", + "test_agentic_common_issue_813_anthropic_api_key_oauth_shadow.py": "4207732c6fb0e88b5be1fe632acd087f0b992e1ac7498dff036a0b9b9ea72138", + "test_agentic_common_worktree.py": "16fb52387d635f1bbccbac20d08ef5b5d878021b70fc1ad79e5d36664df2b9a9" + }, + "include_deps": { + "context/_keyring_timeout_example.py": "41f83d5ef117caba86b788f69b519f7e88f3d7dc2a641a017d74a7cee9be6ed1", + "context/api_key_scanner_example.py": "4b9f880a30445f25edffc394857ae0c218097726c41ab996a03fcbc4350b5482", + "context/auth_service_example.py": "4b6d2c8c9e13d1edc0d9805ee0c07657494cbf31dffe2bb0029b72975674a8cf", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/agentic_common.py": "7b9b57eac64bac2b4fd18d4eea3d2feb3c7d99ff43f0ca1961a0bfd22a9576da", + "pdd/llm_invoke.py": "2c423dbe12dd22d1acfca6c658bfef36eaf3b016626ae4a2845576500bfcb54b" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_common_worktree_python.json b/.pdd/meta/agentic_common_worktree_python.json new file mode 100644 index 0000000000..3d9aec431c --- /dev/null +++ b/.pdd/meta/agentic_common_worktree_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.956231+00:00", + "command": "fix", + "prompt_hash": "97ac2a6c3d3d0eff020a5508dddfe4c8e58530594d49372922a605fbcdeb9541", + "code_hash": "9d54b21c1801d1f708340917eeb265435d968698392e501c2d8d214da6f4ee3f", + "example_hash": null, + "test_hash": "16fb52387d635f1bbccbac20d08ef5b5d878021b70fc1ad79e5d36664df2b9a9", + "test_files": { + "test_agentic_common_worktree.py": "16fb52387d635f1bbccbac20d08ef5b5d878021b70fc1ad79e5d36664df2b9a9" + }, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_crash_python.json b/.pdd/meta/agentic_crash_python.json new file mode 100644 index 0000000000..7972bd7e61 --- /dev/null +++ b/.pdd/meta/agentic_crash_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.958442+00:00", + "command": "fix", + "prompt_hash": "54aa28a454f27094d61f5ab79d3e52e379ffc0d3e240df1d7746e220846d6757", + "code_hash": "78b5f9651c10d3ce474ff62b32f8f8953135847858fd6873c0824356068285c4", + "example_hash": "4a79b152db522063ccf099eca0419b419a656f168839d197f85781ae4fe1b5f6", + "test_hash": "413b22619a7acea9126f270cd4b99da1cf27108bd1d56a92e32cd58594c4b0c8", + "test_files": { + "test_agentic_crash.py": "413b22619a7acea9126f270cd4b99da1cf27108bd1d56a92e32cd58594c4b0c8" + }, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/get_run_command_example.py": "4d187e844adb255c2738274556f4828728d0d73d84ebf8bda885d51d4be9414f", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "prompts/agentic_crash_explore_LLM.prompt": "1a63eddbf271c5976841de535dcc518db58a5b4b5a61b7a6c08472458ad9ed3c" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_e2e_fix_orchestrator_python.json b/.pdd/meta/agentic_e2e_fix_orchestrator_python.json index 3714c38a3a..e0240219e2 100644 --- a/.pdd/meta/agentic_e2e_fix_orchestrator_python.json +++ b/.pdd/meta/agentic_e2e_fix_orchestrator_python.json @@ -1,19 +1,19 @@ { - "pdd_version": "0.0.257", - "timestamp": "2026-06-02T05:35:45.135425+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.962337+00:00", "command": "test", - "prompt_hash": "3db920baa54e4b2a67fee0dc24de08e26087b7f60ce9687b2fd7261b14f1f621", - "code_hash": "605779bd8be274f89d045972413cd2ce394cf4e6c808e24bfd7f4674fb10b17d", - "example_hash": "8cc5f11b0430113ad52c918cdbabafe254cf01667eb66e1ac8837f5cea965024", - "test_hash": "f15b0ccd990d7ab178700b28211c4e46f29f991500272c73583149309572607c", + "prompt_hash": "52d67e9a9cb487be694437418a6663657780aeb10c5196218f2c3bcf11bf1b09", + "code_hash": "ad8df3444101a4ac3297db032f33b875c581c91fc615bc0f602a89b53113920e", + "example_hash": "180cc2c4316b531586040d61fc5f82f1f605d5c5736fd23bad63a889ac0b7d1e", + "test_hash": "b481eb0460268575f6816a13711049ca5df749871185df6759ece7d7bd5f6fcd", "test_files": { - "test_agentic_e2e_fix_orchestrator.py": "f15b0ccd990d7ab178700b28211c4e46f29f991500272c73583149309572607c", + "test_agentic_e2e_fix_orchestrator.py": "b481eb0460268575f6816a13711049ca5df749871185df6759ece7d7bd5f6fcd", "test_agentic_e2e_fix_orchestrator_resume_prompt.py": "8e29ea7eb6330f75fa53b2699663f417b43cb063b8ac5613f34cbf0a6ceaa917" }, "include_deps": { "context/agentic_bug_orchestrator_example.py": "23b545fc0f892e14ee9660902d3f7e04893f0b793246c1ea9d559656fd39ffe6", - "context/agentic_common_example.py": "113b5829cae3a9e38c362b74d5115ba09cab06c7be5831527d9829f97f7b3b7d", - "context/agentic_e2e_fix_orchestrator_example.py": "8cc5f11b0430113ad52c918cdbabafe254cf01667eb66e1ac8837f5cea965024", + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/agentic_e2e_fix_orchestrator_example.py": "180cc2c4316b531586040d61fc5f82f1f605d5c5736fd23bad63a889ac0b7d1e", "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", "context/agentic_test_orchestrator_example.py": "473901f6f62844c81a047fa69387c4116d91a522c62d5c6ad30188e2e99dfaa5", "context/change/16/fix_error_loop.py": "01872cfcc6c3f5095551acd109e06b35ece96c15ca75dcf40ac395c3ed867e40", diff --git a/.pdd/meta/agentic_e2e_fix_python.json b/.pdd/meta/agentic_e2e_fix_python.json new file mode 100644 index 0000000000..be2e4de716 --- /dev/null +++ b/.pdd/meta/agentic_e2e_fix_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.964147+00:00", + "command": "fix", + "prompt_hash": "fc0a1a8955b1b6c6848985a7a037eb7595bc0134357d599cd34c85cf41f885be", + "code_hash": "ab0b08e59e59b52431e7f4ba6a2b39f6267c7a43d0d45bf3573596c96649a57c", + "example_hash": "79fd30a0fa6d8a5b9e23b34f7328ba3f95089a0ebb83e8244dae79ea68cff33a", + "test_hash": "5e5507d9fc6035164d78e1dcf3ccf983694c4057185d5644ff47f7743e052c3b", + "test_files": { + "test_agentic_e2e_fix.py": "5e5507d9fc6035164d78e1dcf3ccf983694c4057185d5644ff47f7743e052c3b", + "test_agentic_e2e_fix_orchestrator.py": "b481eb0460268575f6816a13711049ca5df749871185df6759ece7d7bd5f6fcd", + "test_agentic_e2e_fix_orchestrator_resume_prompt.py": "8e29ea7eb6330f75fa53b2699663f417b43cb063b8ac5613f34cbf0a6ceaa917", + "test_agentic_e2e_fix_step10_prompt.py": "dd9b37268f192ce23b07dc9af157e4fcb1554c457adff67b719a00c89a8c7e63" + }, + "include_deps": { + "context/agentic_e2e_fix_orchestrator_example.py": "180cc2c4316b531586040d61fc5f82f1f605d5c5736fd23bad63a889ac0b7d1e", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_fix_python.json b/.pdd/meta/agentic_fix_python.json new file mode 100644 index 0000000000..cfc51a91b3 --- /dev/null +++ b/.pdd/meta/agentic_fix_python.json @@ -0,0 +1,20 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.966064+00:00", + "command": "fix", + "prompt_hash": "2aa547410ad5be7b47e1e69dcbfad7abb844b621a432730f24886a1e5014f350", + "code_hash": "10fad4819034f9b2553c2fc584d2e977f490b1de43533219545a71ae19904179", + "example_hash": "407a024fcba9402e00b401477725ae72b1de718ab59bea29b69509a0b15f621f", + "test_hash": "635df7c7b087344d3c138b16af299aa878a0cb9d958d7ac22a91915fa64735ba", + "test_files": { + "test_agentic_fix.py": "635df7c7b087344d3c138b16af299aa878a0cb9d958d7ac22a91915fa64735ba" + }, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", + "context/get_language_example.py": "8b09af25b96fa49e2dcf5ff307bbeb0bc83e84b64618971d393397772a2fafab", + "context/get_run_command_example.py": "4d187e844adb255c2738274556f4828728d0d73d84ebf8bda885d51d4be9414f", + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_langtest_python.json b/.pdd/meta/agentic_langtest_python.json new file mode 100644 index 0000000000..f335c634fc --- /dev/null +++ b/.pdd/meta/agentic_langtest_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.966985+00:00", + "command": "fix", + "prompt_hash": "e63708a4640607ed61932a11b5c2f5c15a46e00fc1ad2113b9622414c4950f28", + "code_hash": "7cb0be38d526d0a630500185427a9f76666d2b79385f7333a7608eb1fb33cfc9", + "example_hash": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", + "test_hash": "9a745d3b5cb6721be2993e2b49bb25a860d269de30168dc6d5850097937de1c2", + "test_files": { + "test_agentic_langtest.py": "9a745d3b5cb6721be2993e2b49bb25a860d269de30168dc6d5850097937de1c2" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_multishot_python.json b/.pdd/meta/agentic_multishot_python.json index 9445b3f464..9252215b5d 100644 --- a/.pdd/meta/agentic_multishot_python.json +++ b/.pdd/meta/agentic_multishot_python.json @@ -1,8 +1,8 @@ { - "pdd_version": "0.0.274", - "timestamp": "2026-06-16T01:03:00.882482+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.968865+00:00", "command": "test", - "prompt_hash": "235a4c06a65d4cc3e27794db81c846aa988e056bf1896a559c3fbf9645f84fbb", + "prompt_hash": "0628511c4ecc1d928041296b13623b351b51e9596bd46485162b9b6a966b0073", "code_hash": "ecd0bfb52beb8172399b6da05f17d4ea308b22c4a5b10d5a32a98829c6e48edc", "example_hash": "7708a23b090f5cde8ace2e358fd35f4efcfcb3b96910d8e86aa0f6ab09447167", "test_hash": "8e1a14a52353590059265c6e59540c64b68ff650fb605fc78e831d6b3bac6d1d", @@ -11,7 +11,7 @@ }, "include_deps": { "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", - "pdd/agentic_common.py": "922f9c3b140c10e04d8ddbacf50f1399df7fda0a5e6dbb99f6e0af0bb131d1dd", + "pdd/agentic_common.py": "7b9b57eac64bac2b4fd18d4eea3d2feb3c7d99ff43f0ca1961a0bfd22a9576da", "pdd/failure_classification.py": "0675857d23f52e447f15ace22ec37c2f003a61d3c810254c8547129c29d697ff" } } \ No newline at end of file diff --git a/.pdd/meta/agentic_split_orchestrator_python.json b/.pdd/meta/agentic_split_orchestrator_python.json index 703edbae6c..3a2983a537 100644 --- a/.pdd/meta/agentic_split_orchestrator_python.json +++ b/.pdd/meta/agentic_split_orchestrator_python.json @@ -1,16 +1,22 @@ { - "pdd_version": "0.0.234", - "timestamp": "2026-05-12T02:53:56.081699+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.972774+00:00", "command": "fix", - "prompt_hash": "a9590c82b2b9cbbc9c39e64580662b9e1d1b779225cdbb0e798fd81d92257b6d", - "code_hash": "daaa0487f9e9538f2536f8e3ed061efb0068b83cda8bcae9aa993a0d1955baf4", + "prompt_hash": "e942762fcacc211d4f279733aa9a37f836c102b7bed4143cc5e6aa9f6d1236ae", + "code_hash": "6e36d867ae932aa29f1aff078182179b6b0d3ddb86a99ddc5e3e23deaa110549", "example_hash": "3f521ffaf006c3ab7fad663edc7d4bcc89f4bafe81ba43e760e9fe99ef7ea46c", "test_hash": "be3d9b082577fc7fac8bae292c109ab2d9df7b376f775825168c69941c10bbb7", "test_files": { "test_agentic_split_orchestrator.py": "be3d9b082577fc7fac8bae292c109ab2d9df7b376f775825168c69941c10bbb7" }, "include_deps": { + "context/agentic_test_orchestrator_example.py": "473901f6f62844c81a047fa69387c4116d91a522c62d5c6ad30188e2e99dfaa5", "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", - "context/python_preamble.prompt": "57a3e51f529024ec0cb9658cd6ac61a7c8051ba0c8e887b31cf00b2e78a07d83" + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/agentic_common.py": "7b9b57eac64bac2b4fd18d4eea3d2feb3c7d99ff43f0ca1961a0bfd22a9576da", + "pdd/agentic_common_worktree.py": "9d54b21c1801d1f708340917eeb265435d968698392e501c2d8d214da6f4ee3f", + "pdd/architecture_sync.py": "0e0ff74978f49d629f92365723ebbea0e7c70e6d5234ccfdafb1c8e09c110c11", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56", + "pdd/split_validation.py": "e51a9512fc4cedbe362f25da3c97c23a89057901bff10c6b04eeefe77b79dafe" } } \ No newline at end of file diff --git a/.pdd/meta/agentic_split_python.json b/.pdd/meta/agentic_split_python.json new file mode 100644 index 0000000000..ea26705fb2 --- /dev/null +++ b/.pdd/meta/agentic_split_python.json @@ -0,0 +1,22 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.974817+00:00", + "command": "fix", + "prompt_hash": "216d6d2604fc6c2374b755be22a60994e34a73be331114b13ac7d8c5a5abc87e", + "code_hash": "044ff030cd6896b3a20507b6797033c2784fc2c17826024a304cea446d255038", + "example_hash": "25283e3976254717458001e7bcd058ae7d62af3c867a78e6206a9135091dadf4", + "test_hash": "8f3f1396daa31100274cd8a0c232b3bece8c2d46a1c0bc996f978fbeff739fa3", + "test_files": { + "test_agentic_split.py": "8f3f1396daa31100274cd8a0c232b3bece8c2d46a1c0bc996f978fbeff739fa3", + "test_agentic_split_orchestrator.py": "be3d9b082577fc7fac8bae292c109ab2d9df7b376f775825168c69941c10bbb7", + "test_agentic_split_real.py": "d68ec06d83da47934743bdb963d4eee4c7a46ee6ec5abedcee6c12b4bf022c15", + "test_agentic_split_v2.py": "a609bc2db408f669da9f3429b2389c244e53ca5c53da834a032ca2e2e9820ff5" + }, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/agentic_split_orchestrator_example.py": "3f521ffaf006c3ab7fad663edc7d4bcc89f4bafe81ba43e760e9fe99ef7ea46c", + "context/get_language_example.py": "8b09af25b96fa49e2dcf5ff307bbeb0bc83e84b64618971d393397772a2fafab", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/agentic_common_worktree.py": "9d54b21c1801d1f708340917eeb265435d968698392e501c2d8d214da6f4ee3f" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_sync_python.json b/.pdd/meta/agentic_sync_python.json new file mode 100644 index 0000000000..737918a977 --- /dev/null +++ b/.pdd/meta/agentic_sync_python.json @@ -0,0 +1,25 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.979289+00:00", + "command": "fix", + "prompt_hash": "b46925c0018d0b91841bee732c5da57c6d1d71b8b848d57543005120a6885f08", + "code_hash": "b98bddf2b04560eb402d856aa6dd9a7077e731123edfb3bf9cbd80064681d879", + "example_hash": "aa9683f1a26841f3cae854e72cce38636f0859d8dd2f7672fa14e412afdef884", + "test_hash": "5365a19305e0fc78b8fb0f21b6b7a16c4ed405bd75e32c3e6ddcd1e814b6a15b", + "test_files": { + "test_agentic_sync.py": "5365a19305e0fc78b8fb0f21b6b7a16c4ed405bd75e32c3e6ddcd1e814b6a15b", + "test_agentic_sync_fallback_realgit.py": "cad09f7c0d18a9983a106fbb688db018ff52a2ba46e5cbd3dfd86389ab1b32de", + "test_agentic_sync_mocked_e2e.py": "a8adc991a2796b06a4f8ef59c531eab632350784ca7961d1282d4fa8424b38fe", + "test_agentic_sync_nearest_config.py": "afbed1e4021978e9b7480f4bb80bdba737c4a0adffb7768b3191b14bbce6ca19", + "test_agentic_sync_runner.py": "daf57f3bb02e467053caf2fa5ac5b8d11cdad4db74016ee521d6e6f64c283621" + }, + "include_deps": { + "context/agentic_change_example.py": "a8308b2af708c804d626a4a96012c5a67f5bc6469e33857cafc07627213828d7", + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/agentic_sync_runner_example.py": "fcb36209abb5ab882ad551754fb574d62509c968f8c564e18163be7f2b7ef35a", + "context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0", + "context/auto_deps_main_example.py": "9b9c36c18d39494b6e31b46f6e4758c25135b6fa3d1672fbb7365844f7d2de04", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/sync_order_example.py": "b4eb1e167e7604c7dbac703bdcba7287188fa9d2d5ef6d490216d003cdc542fa" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_sync_runner_python.json b/.pdd/meta/agentic_sync_runner_python.json index 9a568d6680..5e5409a218 100644 --- a/.pdd/meta/agentic_sync_runner_python.json +++ b/.pdd/meta/agentic_sync_runner_python.json @@ -1,21 +1,21 @@ { - "pdd_version": "0.0.275.dev3", - "timestamp": "2026-06-16T01:31:18.161464+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.981826+00:00", "command": "fix", - "prompt_hash": "0784d6ff7f749b74e6938bfdd945a1cf97aed961ebea4bf3849fe512bf7163f9", - "code_hash": "587d51aaed8470de39bbefbb0882f481f269431cca8b50e45b22e90dbd2eb695", + "prompt_hash": "1039b82c0ca610342a21046297c0798f86a4f99217ebe2725b147a7f3cca2401", + "code_hash": "572f865d1bdbddf4827d5f308eb72030c3ed085c4f570284cbda8c961d01af2d", "example_hash": "fcb36209abb5ab882ad551754fb574d62509c968f8c564e18163be7f2b7ef35a", - "test_hash": "781deb083113a3c96b990501f24cdf41e0742190be5cfbe85553639c25e62e22", + "test_hash": "daf57f3bb02e467053caf2fa5ac5b8d11cdad4db74016ee521d6e6f64c283621", "test_files": { - "test_agentic_sync_runner.py": "781deb083113a3c96b990501f24cdf41e0742190be5cfbe85553639c25e62e22" + "test_agentic_sync_runner.py": "daf57f3bb02e467053caf2fa5ac5b8d11cdad4db74016ee521d6e6f64c283621" }, "include_deps": { "context/agentic_change_example.py": "a8308b2af708c804d626a4a96012c5a67f5bc6469e33857cafc07627213828d7", - "context/agentic_common_example.py": "f9cdb30a932973d341b795faf510056b95573610af5ac48e3849cf42c205998e", + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", "context/agentic_test_orchestrator_example.py": "473901f6f62844c81a047fa69387c4116d91a522c62d5c6ad30188e2e99dfaa5", "context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0", "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", "context/sync_order_example.py": "b4eb1e167e7604c7dbac703bdcba7287188fa9d2d5ef6d490216d003cdc542fa" } -} +} \ No newline at end of file diff --git a/.pdd/meta/agentic_test_generate_python.json b/.pdd/meta/agentic_test_generate_python.json new file mode 100644 index 0000000000..d5d24c0aa7 --- /dev/null +++ b/.pdd/meta/agentic_test_generate_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.983157+00:00", + "command": "fix", + "prompt_hash": "6d77ecd278bedaf7504434656e855bcb25accb0fb843d901ed3be899ad2c158e", + "code_hash": "5c34b242014d6cc56a1e9504bc649032ecea24fc09c50754afad2d421053020e", + "example_hash": "5b021f33624a41accf5463ea151ccfaab9f9f268ca95fb21c94e3f0dda9f4661", + "test_hash": "29a887349d31f0a1233ae1375be56dd4530bf8d9d1ca8c38917091f105fc1024", + "test_files": { + "test_agentic_test_generate.py": "29a887349d31f0a1233ae1375be56dd4530bf8d9d1ca8c38917091f105fc1024" + }, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_test_orchestrator_python.json b/.pdd/meta/agentic_test_orchestrator_python.json new file mode 100644 index 0000000000..958eb969de --- /dev/null +++ b/.pdd/meta/agentic_test_orchestrator_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.984635+00:00", + "command": "fix", + "prompt_hash": "eff9420f13230d7a96a6ff94c36ab5b49cfe47d9cbc47a1cb1532a566aaa0239", + "code_hash": "38435707d35f64e685a3789ff8752768ffcfffed927af11b9dd90ba08ab93ff9", + "example_hash": "473901f6f62844c81a047fa69387c4116d91a522c62d5c6ad30188e2e99dfaa5", + "test_hash": "b352fb8c22880f5c95e74f3b2b7a89251fd13e6563ff2ec550f8e4f5e34807dd", + "test_files": { + "test_agentic_test_orchestrator.py": "b352fb8c22880f5c95e74f3b2b7a89251fd13e6563ff2ec550f8e4f5e34807dd" + }, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/pytest_output_example.py": "6f0cd68e3c4440081267c716651caec4fbdd7d9c4516f967517f02ae2a853920" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_test_python.json b/.pdd/meta/agentic_test_python.json new file mode 100644 index 0000000000..308851d2cd --- /dev/null +++ b/.pdd/meta/agentic_test_python.json @@ -0,0 +1,20 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.986351+00:00", + "command": "fix", + "prompt_hash": "ddab846232bad2745161f117a908bb88f9050878fe61f944e7e18dece79dad61", + "code_hash": "e9834cc73b9d125bc46354b262a9b17067150f044055edda5529adb695a0b372", + "example_hash": "1d8b490a2b0ff53a8b786c6c95faeecc4efd0becf837b35273508bae19fac3ab", + "test_hash": "03163284790ecd0c014d9df0311a75a03243f4355430dcaf8080bfaaa6b321d4", + "test_files": { + "test_agentic_test.py": "03163284790ecd0c014d9df0311a75a03243f4355430dcaf8080bfaaa6b321d4", + "test_agentic_test_generate.py": "29a887349d31f0a1233ae1375be56dd4530bf8d9d1ca8c38917091f105fc1024", + "test_agentic_test_orchestrator.py": "b352fb8c22880f5c95e74f3b2b7a89251fd13e6563ff2ec550f8e4f5e34807dd" + }, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/agentic_test_orchestrator_example.py": "473901f6f62844c81a047fa69387c4116d91a522c62d5c6ad30188e2e99dfaa5", + "context/cmd_test_main_example.py": "0774984ee29f70407429f1eff4a1b76287715b7ddd6adf2bc149e874c9b844bb", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_update_python.json b/.pdd/meta/agentic_update_python.json new file mode 100644 index 0000000000..1315976722 --- /dev/null +++ b/.pdd/meta/agentic_update_python.json @@ -0,0 +1,20 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.989161+00:00", + "command": "fix", + "prompt_hash": "3f8ff49dcf8a62526143f7d4c06c0780cc0180351c1da693eeb28e3b37d3aa0c", + "code_hash": "f0bd8afbbb4cbbc8dcde8116061a09fdd7a888daa540b48eb46b360153a5c6a4", + "example_hash": "967254cd1c636ac5ba09afa0446312ffc1f28d3041ed453a4c6588c931f722c4", + "test_hash": "45d00c80b59d0037dbb148b706fe758a789fa9a5fce1129a675cee1b4dfc99bd", + "test_files": { + "test_agentic_update.py": "45d00c80b59d0037dbb148b706fe758a789fa9a5fce1129a675cee1b4dfc99bd" + }, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/agentic_common.py": "7b9b57eac64bac2b4fd18d4eea3d2feb3c7d99ff43f0ca1961a0bfd22a9576da", + "pdd/sync_order.py": "6cdaed3b56d9cc12f38ee5ac93b8623899372ea2d177c4f131ec258edb94d0dd", + "prompts/agentic_update_LLM.prompt": "0fe3ff3c55d39cd68d3f4d1105e156a608c8668a27c3d81efea5289b2430ba17" + } +} \ No newline at end of file diff --git a/.pdd/meta/agentic_verify_python.json b/.pdd/meta/agentic_verify_python.json new file mode 100644 index 0000000000..3345477ca1 --- /dev/null +++ b/.pdd/meta/agentic_verify_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.990637+00:00", + "command": "fix", + "prompt_hash": "9c95d066438cd9e009e36875214172c588a7db46d1805dddbc74018b930a179f", + "code_hash": "ede66dcf689550aaebe8991e6ba90eaa2502315789de90faaa507c8944cb5060", + "example_hash": "1a64161baccfc035ffbd73bf99cc1888d28d8d714a49deb01a75918e3b9732f8", + "test_hash": "e16430d5639d9871b7ae2ac231fc8db00a51fbd7dcfb9d5b8f39a4dc66f4c145", + "test_files": { + "test_agentic_verify.py": "e16430d5639d9871b7ae2ac231fc8db00a51fbd7dcfb9d5b8f39a4dc66f4c145" + }, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "prompts/agentic_verify_explore_LLM.prompt": "c02f53aa9b003f4acd9f2cff09cccf166993e0ffe21a012caa0fdf52ac8d105c" + } +} \ No newline at end of file diff --git a/.pdd/meta/api_contract_slicer_python.json b/.pdd/meta/api_contract_slicer_python.json new file mode 100644 index 0000000000..5998edab9d --- /dev/null +++ b/.pdd/meta/api_contract_slicer_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.991694+00:00", + "command": "fix", + "prompt_hash": "79768843f184f50c60dd2caf94ba173f115b04a4f6fc50eca601de9008c710d5", + "code_hash": "0170b39f8ac42c0ff86ea426c1f98449158a5f3321474422a252397283c77f3e", + "example_hash": "9eec29df45d8ce83496ddb65c5954d475e5eebf33c94914e1a716b124b6ba3ad", + "test_hash": "d8deb0dc7223949fbd47486d05adc7764e17d42a5b6e52a3e9de5daaed28f637", + "test_files": { + "test_api_contract_slicer.py": "d8deb0dc7223949fbd47486d05adc7764e17d42a5b6e52a3e9de5daaed28f637" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/api_key_scanner_python.json b/.pdd/meta/api_key_scanner_python.json new file mode 100644 index 0000000000..ca2d768e01 --- /dev/null +++ b/.pdd/meta/api_key_scanner_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.992596+00:00", + "command": "fix", + "prompt_hash": "4d93f1716d60a12ac6a6730482c96f1a7242ecdc57b9d3c28dafada9c137c4b9", + "code_hash": "45cc7a5ac182630ed7a04980cff5430658cc79989904264cf1556c04311a0610", + "example_hash": "4b9f880a30445f25edffc394857ae0c218097726c41ab996a03fcbc4350b5482", + "test_hash": "28e01c935c44ef9838aae8b9e88d013f74ae6edd4a8f64a999e620bf7896b6dc", + "test_files": { + "test_api_key_scanner.py": "28e01c935c44ef9838aae8b9e88d013f74ae6edd4a8f64a999e620bf7896b6dc" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/architecture_include_validation_python.json b/.pdd/meta/architecture_include_validation_python.json new file mode 100644 index 0000000000..32e84cb1da --- /dev/null +++ b/.pdd/meta/architecture_include_validation_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.994038+00:00", + "command": "fix", + "prompt_hash": "bba83e01cec00a1a3c1fb7a9f174705bd169cb7c42cff69ac9a83d611a53e957", + "code_hash": "b412fed88e36c21751565c0c94a9dfe314c3a280d04ab0a2e82f1ea17ca81c08", + "example_hash": null, + "test_hash": "2b53d831392eefeb2d7cc83c38e683c96b107e9c11ef9ff880e5ec8c5976bc43", + "test_files": { + "test_architecture_include_validation.py": "2b53d831392eefeb2d7cc83c38e683c96b107e9c11ef9ff880e5ec8c5976bc43" + }, + "include_deps": { + "context/architecture_registry_example.py": "5eff404eddcfb9d898df972d703013b7124d352e5fe898a1977d7986ef17ed1e", + "context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0" + } +} \ No newline at end of file diff --git a/.pdd/meta/architecture_sync_python.json b/.pdd/meta/architecture_sync_python.json index 1bdd459444..d69cea15f7 100644 --- a/.pdd/meta/architecture_sync_python.json +++ b/.pdd/meta/architecture_sync_python.json @@ -1,10 +1,10 @@ { - "pdd_version": "0.0.268", - "timestamp": "2026-06-09T23:29:39.505348+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.995592+00:00", "command": "example", "prompt_hash": "2fb502ebb1466361da7c064975239bc9e9bbf2e80bb3b2da95e3027862f2dd99", "code_hash": "0e0ff74978f49d629f92365723ebbea0e7c70e6d5234ccfdafb1c8e09c110c11", - "example_hash": "6e1f65de35fcf1dbd0def81710da6f378495b37187eb01cd01ef43f662d9f1ba", + "example_hash": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0", "test_hash": "55cd4d0fc35712f39a23dc54f2dea33011e608861616a389a877436e90f48342", "test_files": { "test_architecture_sync.py": "55cd4d0fc35712f39a23dc54f2dea33011e608861616a389a877436e90f48342" diff --git a/.pdd/meta/auth_service_python.json b/.pdd/meta/auth_service_python.json new file mode 100644 index 0000000000..bad2c5a560 --- /dev/null +++ b/.pdd/meta/auth_service_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.996941+00:00", + "command": "fix", + "prompt_hash": "f78505eb6667a42e4995a591d706334faf15fa7f164b659877bb5c7f343d2df0", + "code_hash": "a4b689e00ad7106bb775f1b365d67b3d5106f5e4c7021d7ac751468652d7693c", + "example_hash": "4b6d2c8c9e13d1edc0d9805ee0c07657494cbf31dffe2bb0029b72975674a8cf", + "test_hash": "82838601cf50ad78807070aa07b5433bbd964c49102ba3215a696e8d7683522c", + "test_files": { + "test_auth_service.py": "82838601cf50ad78807070aa07b5433bbd964c49102ba3215a696e8d7683522c" + }, + "include_deps": { + "context/_keyring_timeout_example.py": "41f83d5ef117caba86b788f69b519f7e88f3d7dc2a641a017d74a7cee9be6ed1", + "context/get_jwt_token_example.py": "76a87c8257cb1f4cfdc2edff6225c752432afb3a5e352fcb016d08d8eae16e01", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/auto_deps_architecture_python.json b/.pdd/meta/auto_deps_architecture_python.json new file mode 100644 index 0000000000..314de32bad --- /dev/null +++ b/.pdd/meta/auto_deps_architecture_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:17.998691+00:00", + "command": "fix", + "prompt_hash": "2e31837c35515ab3922301a394228ab699115c424a9b3a0a42bb26e673544893", + "code_hash": "d20e3525f49abb1c4d4d923ed619e9f0b26cb29cbca12df2f22479b8d3926a44", + "example_hash": null, + "test_hash": "9d72534ca9044063c7c75a4ab8f5addac9f49e0e27248a055541e15aec8b4cd7", + "test_files": { + "test_auto_deps_architecture.py": "9d72534ca9044063c7c75a4ab8f5addac9f49e0e27248a055541e15aec8b4cd7" + }, + "include_deps": { + "context/architecture_registry_example.py": "5eff404eddcfb9d898df972d703013b7124d352e5fe898a1977d7986ef17ed1e", + "context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0", + "context/sync_order_example.py": "b4eb1e167e7604c7dbac703bdcba7287188fa9d2d5ef6d490216d003cdc542fa" + } +} \ No newline at end of file diff --git a/.pdd/meta/auto_deps_main_python.json b/.pdd/meta/auto_deps_main_python.json new file mode 100644 index 0000000000..ee16ef01f9 --- /dev/null +++ b/.pdd/meta/auto_deps_main_python.json @@ -0,0 +1,23 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.442689+00:00", + "command": "fix", + "prompt_hash": "891b3052d91d751f0a835ef56cb2cc56d7746d602f35c713327f18ade51b7cd2", + "code_hash": "b5df06f550c4dc0151af20f911eb19552d494cfb9853a7f35bd67c096a40581a", + "example_hash": "9b9c36c18d39494b6e31b46f6e4758c25135b6fa3d1672fbb7365844f7d2de04", + "test_hash": "315cb31e6727a0410054111f294d19e1897d7551b30f79536bdc4b1655750a20", + "test_files": { + "test_auto_deps_main.py": "315cb31e6727a0410054111f294d19e1897d7551b30f79536bdc4b1655750a20" + }, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/insert_includes_example.py": "c304bf4656f75fa26192c92b3eb0422a903d8a1bc82dda3158dc9f184b337873", + "context/operation_log_example.py": "2660def7012d9b79bad3db33cf399ac603cc1c35dff0b064cbdd2638a013de1d", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/auto_deps_architecture.py": "d20e3525f49abb1c4d4d923ed619e9f0b26cb29cbca12df2f22479b8d3926a44", + "pdd/operation_log.py": "d6712703f752c2045cd11a5c144b67454bb80700b83bd4e4612e55d2107b6072", + "pdd/validate_prompt_includes.py": "fa97b72a58534e255768634f928f4cfdd2130a7a4d8b296da3eefdf77d47855b" + } +} \ No newline at end of file diff --git a/.pdd/meta/auto_include_python.json b/.pdd/meta/auto_include_python.json new file mode 100644 index 0000000000..1f92e3cf6a --- /dev/null +++ b/.pdd/meta/auto_include_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.006120+00:00", + "command": "fix", + "prompt_hash": "47d0a02c54c54e2b9835a96321fe76ae283abc9467c20af1e5363312b960e27c", + "code_hash": "5bcd43f2318cb317a8fe7cbbc510a8bd020ddf8b2b6cfa5dafbf128b37586b8a", + "example_hash": "0c278ed716298149777bc7082827eb8aa6d80dac264c66b6cded6871232f2df7", + "test_hash": "96b40dc287e8ae63ceb00676026e6f89254d83fd27e3955315bfaa6c5080b689", + "test_files": { + "test_auto_include.py": "96b40dc287e8ae63ceb00676026e6f89254d83fd27e3955315bfaa6c5080b689" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/summarize_directory_example.py": "99f9b729f3b148891d471e79e5a29e67e39c30aad8e9922ced2982ca02b158ea", + "context/embed_retrieve_example.py": "17920a5c72ada21b31f6df15d9ad156d67ebaf8f141c3839b068d9379360cd84", + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f" + } +} \ No newline at end of file diff --git a/.pdd/meta/auto_update_python.json b/.pdd/meta/auto_update_python.json new file mode 100644 index 0000000000..44a348e27c --- /dev/null +++ b/.pdd/meta/auto_update_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.007226+00:00", + "command": "fix", + "prompt_hash": "072c1e4211367cc131c7aef9b844c8590e3a0642afafda817bfbf19179227e80", + "code_hash": "0945c48a916c2783701c5aac8da7e2f033c4a2c0939f1cb96dab30bdb39eadba", + "example_hash": "4fc8ab0da96c0e8abe793d195dbc64b6b9771631be818eecde059769e5914fc9", + "test_hash": "5b414e3024d1db83dcc7def5e91579e8dcf2f7b414b3173b8dfb053364d2afdf", + "test_files": { + "test_auto_update.py": "5b414e3024d1db83dcc7def5e91579e8dcf2f7b414b3173b8dfb053364d2afdf" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/bug_main_python.json b/.pdd/meta/bug_main_python.json new file mode 100644 index 0000000000..3fff02f47d --- /dev/null +++ b/.pdd/meta/bug_main_python.json @@ -0,0 +1,21 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.009404+00:00", + "command": "fix", + "prompt_hash": "9eb6e4dcae71fb61e683ddfe07362e0e6668ffa18eca3160585781e6e4201df2", + "code_hash": "add508e807f11ea3bf90b1a1038307c2f491fdc7cbf338029e097b987aff3ae2", + "example_hash": "662ecde39d05a5345533c4ae74c6b1031f016916ec2ff4764d34a26f7ec26172", + "test_hash": "12d1162a4485fd58f8c122251c4929884b3f6ec4a60943c5f1fa6315698f01e1", + "test_files": { + "test_bug_main.py": "12d1162a4485fd58f8c122251c4929884b3f6ec4a60943c5f1fa6315698f01e1" + }, + "include_deps": { + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/agentic_bug_example.py": "96e35df4e8425a9173ff6a30281892c2bd8b81361cff72b732e488172ddf52f5", + "context/bug_to_unit_test_example.py": "ab1d665923937f6599f78c0cac82f5d7c04a7afa092e7767d67e417e067a8817", + "context/cloud_function_call.py": "62ac7724ce2e3eddbba2b743bd7aa5790dbed34eae01e77e0252c4cc6358c95e", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/get_jwt_token_example.py": "76a87c8257cb1f4cfdc2edff6225c752432afb3a5e352fcb016d08d8eae16e01" + } +} \ No newline at end of file diff --git a/.pdd/meta/bug_to_unit_test_python.json b/.pdd/meta/bug_to_unit_test_python.json new file mode 100644 index 0000000000..754bb2cb4b --- /dev/null +++ b/.pdd/meta/bug_to_unit_test_python.json @@ -0,0 +1,20 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.011272+00:00", + "command": "fix", + "prompt_hash": "f0c0cb57741620d320c1e7248e44e033b6c929187186268bd636538ea3f2efb3", + "code_hash": "06842a372290a414035bcf89c011ff457e111d14a253c2a4dc4a6516b9040add", + "example_hash": "ab1d665923937f6599f78c0cac82f5d7c04a7afa092e7767d67e417e067a8817", + "test_hash": "6a131f5326feadafe8520656f49bb2eba8bc07cf9dd4453149dcb02646e82aef", + "test_files": { + "test_bug_to_unit_test.py": "6a131f5326feadafe8520656f49bb2eba8bc07cf9dd4453149dcb02646e82aef" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/unfinished_prompt_example.py": "5d4d74064e5b845391f035fb8631903ec2f39a54a90713f9c13dc24d2eb73c4f", + "context/continue_generation_example.py": "29c33a967bcdf4fa0037e1c7b0ec699c00027606dbe9d93fa4d1434f9c16c19e", + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/postprocess_example.py": "b98362e6ffb84ae7826dad27d0854e30706f3993266314439859986b32ecd0bd" + } +} \ No newline at end of file diff --git a/.pdd/meta/change_main_python.json b/.pdd/meta/change_main_python.json new file mode 100644 index 0000000000..212a2b840a --- /dev/null +++ b/.pdd/meta/change_main_python.json @@ -0,0 +1,21 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.013925+00:00", + "command": "fix", + "prompt_hash": "e8db62d56db28f59310080c7bd87fce607be32fb8d92f03b562b7ef73d52a376", + "code_hash": "a350dfbafdcee8275e86760417f194ea3154b17417a32137a49f36f2e3e182de", + "example_hash": "990b2dfc2b9ef2f1871cb4fe1b7a01d5d6f4f4f54f9a6a95fc3661c679802d42", + "test_hash": "5a1fd84f2fd4f14538fab3571575552c12089e687e8d24080fa2934d52f0d890", + "test_files": { + "test_change_main.py": "5a1fd84f2fd4f14538fab3571575552c12089e687e8d24080fa2934d52f0d890" + }, + "include_deps": { + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/change_example.py": "cc5f850c8e41a8c5b82c94b6667975d1811dff1f51888bb8caf4ff69815f8352", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/get_extension_example.py": "def9c34c1416259a0a031a996b96aeff5e73a7012adff6af1cc557e74ba97cc8", + "context/process_csv_change_example.py": "c7cf31cd55d5312b5398d3d518eddd33354fa184d0e92c6cf926a3c71d018227", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/validate_prompt_includes.py": "fa97b72a58534e255768634f928f4cfdd2130a7a4d8b296da3eefdf77d47855b" + } +} \ No newline at end of file diff --git a/.pdd/meta/change_python.json b/.pdd/meta/change_python.json new file mode 100644 index 0000000000..4d259444cc --- /dev/null +++ b/.pdd/meta/change_python.json @@ -0,0 +1,22 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.016420+00:00", + "command": "fix", + "prompt_hash": "78e1d54b350c8096a9652d858a7f41406b224756dc55a4894c1d97df37f14698", + "code_hash": "d66d22f9ef9e6a80574d91b115bf72aac534d238dd8a8085d5744e1a8c1562a3", + "example_hash": "cc5f850c8e41a8c5b82c94b6667975d1811dff1f51888bb8caf4ff69815f8352", + "test_hash": "1bab7571601fbb32b1d86fd1100129c94cf40a09b9d26b3890249d76d398a74c", + "test_files": { + "test_change.py": "1bab7571601fbb32b1d86fd1100129c94cf40a09b9d26b3890249d76d398a74c", + "test_change_call_site_and_retry.py": "2c668ab36b5e09dd57231484932b3221d2d8bd1e4fa70836eefe2f3d5551d7db", + "test_change_main.py": "5a1fd84f2fd4f14538fab3571575552c12089e687e8d24080fa2934d52f0d890" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/change/12/initial_postprocess.py": "845d5ada84654630518eda1f06e372bd2af87a230077ce8078efc543b9286714", + "context/change/14/initial_change.py": "fec3fba35a4710e02375bdb017bcf33882c2a3b690733fc9d5c6021c1602785c", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "pdd/llm_invoke.py": "2c423dbe12dd22d1acfca6c658bfef36eaf3b016626ae4a2845576500bfcb54b", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56" + } +} \ No newline at end of file diff --git a/.pdd/meta/checkup_agent_python.json b/.pdd/meta/checkup_agent_python.json new file mode 100644 index 0000000000..e7492f037b --- /dev/null +++ b/.pdd/meta/checkup_agent_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.017136+00:00", + "command": "fix", + "prompt_hash": "653a638d7dbede994e83a15482b572c27f5d5a27df3e8f20ad244544a5a9318e", + "code_hash": "d091af7573d71153ee8106191cd8e732bd098cfcfb50f40e8bdbb4acb41ec41d", + "example_hash": null, + "test_hash": "9ee77b78b481814461321d77954839bfbbf1190be0677a15b4fb5d2ce9da7914", + "test_files": { + "test_checkup_agent.py": "9ee77b78b481814461321d77954839bfbbf1190be0677a15b4fb5d2ce9da7914" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/checkup_file_selection_python.json b/.pdd/meta/checkup_file_selection_python.json new file mode 100644 index 0000000000..031ac9c120 --- /dev/null +++ b/.pdd/meta/checkup_file_selection_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.017749+00:00", + "command": "fix", + "prompt_hash": "f49ed34a0a7be2d0539d9b5b1cbd1f8aec20537a4f5a4b2d97d514ebbdfe0f51", + "code_hash": "2139db4e6952575d8a094096f37687c5918619cf5a5733bd7d47d27717be9ab2", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/checkup_gates_python.json b/.pdd/meta/checkup_gates_python.json new file mode 100644 index 0000000000..d4e547f78c --- /dev/null +++ b/.pdd/meta/checkup_gates_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.020123+00:00", + "command": "fix", + "prompt_hash": "40c44f6befb609de46b5441e473bdedf9192cf820b13639ed278252a542c79ab", + "code_hash": "b6086954a93e0495661e6ca39d96b3079b928d13308bf0fcd6a16b1e0b5f511c", + "example_hash": "fb271f80bd2c0b2e1aebecec9c99431d4319d0d3121167a8cb8fb65154f7f284", + "test_hash": "a0e5bba5c85bbda53c497a838b7154a5629c6f82d7cfd71035aa15706b5b6b28", + "test_files": { + "test_checkup_gates.py": "a0e5bba5c85bbda53c497a838b7154a5629c6f82d7cfd71035aa15706b5b6b28" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/checkup_interactive_main_python.json b/.pdd/meta/checkup_interactive_main_python.json new file mode 100644 index 0000000000..8ec267734d --- /dev/null +++ b/.pdd/meta/checkup_interactive_main_python.json @@ -0,0 +1,11 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.020860+00:00", + "command": "fix", + "prompt_hash": "5abaf9a3e2e5ff24c024ff03f07ccc9c7686a13cd389f0e98870a78fd97cb1cb", + "code_hash": "82ef9d91f959a52ba334a15c89b9fa94afa164d6b69eae0f0c029579163035f2", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/checkup_interactive_session_python.json b/.pdd/meta/checkup_interactive_session_python.json new file mode 100644 index 0000000000..192ecabc41 --- /dev/null +++ b/.pdd/meta/checkup_interactive_session_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.021963+00:00", + "command": "fix", + "prompt_hash": "56f9f2ddfd309e93c49b22a6b319618f4e9254f433bceb6ff9e10aaa1c4951f6", + "code_hash": "9a06207e7c3a1c8c53f38c9029cf9fb24baf3a8f8be6170eee420aadf604bde7", + "example_hash": "711e6ebd025da577a9f5e94f6d347deeb5636c7a61051547b8daade00b40edae", + "test_hash": "83db77cb2a2bdc260952041dc31c6524c2904eef1aa62e02dc371a948058213e", + "test_files": { + "test_checkup_interactive_session.py": "83db77cb2a2bdc260952041dc31c6524c2904eef1aa62e02dc371a948058213e", + "test_checkup_interactive_session_spike.py": "324c842cc728489e21cd5e26a2c2e7feede37e18a7a7fb9cdb2b03f996de36c4" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/checkup_planner_python.json b/.pdd/meta/checkup_planner_python.json new file mode 100644 index 0000000000..1c762b4b41 --- /dev/null +++ b/.pdd/meta/checkup_planner_python.json @@ -0,0 +1,11 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.022373+00:00", + "command": "fix", + "prompt_hash": "6d3d2dc712729d789b6293c33c411fb8cff975dcb17ebbd38ea7138b876ce241", + "code_hash": "2ab02a1c2c971b16b700c6ecf1062eafcaa053aec8c8cd43c9864a6cad5dbd1b", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/checkup_prompt_apply_python.json b/.pdd/meta/checkup_prompt_apply_python.json new file mode 100644 index 0000000000..f606f02ffe --- /dev/null +++ b/.pdd/meta/checkup_prompt_apply_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.022852+00:00", + "command": "fix", + "prompt_hash": "6da60ee60615d16570497d688e47ef4d6d35ced3f2b033c5969755f1d8923118", + "code_hash": "d72f795e03a2eb9b60023ee3f2e23975d10a61c7deb0daeb319030e5a1f92206", + "example_hash": null, + "test_hash": "c699ccf181590954e7155624838dd6d0f39cd81f958f18cd7e11276046e9282d", + "test_files": { + "test_checkup_prompt_apply.py": "c699ccf181590954e7155624838dd6d0f39cd81f958f18cd7e11276046e9282d" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/checkup_prompt_main_python.json b/.pdd/meta/checkup_prompt_main_python.json new file mode 100644 index 0000000000..bb91077d3b --- /dev/null +++ b/.pdd/meta/checkup_prompt_main_python.json @@ -0,0 +1,24 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-09T22:54:16.280957+00:00", + "command": "fix", + "prompt_hash": "e7423a67b6bfc98b14bd442622701daf3a9d875eafa9912a70d6b167fbf3f81a", + "code_hash": "538367ba6b7fa74d9f86ba6d1235219598f7a124c58cf00ad0abf6cf027526dc", + "example_hash": "04845b3c70203df3ce51d3c3a1aaac61bcb0089d2af3f7d92af73b7f923ab32c", + "test_hash": "81f5c4befae10e8f4f1078f2e772db1646dd43c1bd24c541e8687a4659e760af", + "test_files": { + "test_checkup_prompt_main.py": "81f5c4befae10e8f4f1078f2e772db1646dd43c1bd24c541e8687a4659e760af" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/construct_paths.py": "7b42a002e4ecc27be4d517cbc07f1fc9425e795d4918714f382d2ed5ddd00eab", + "pdd/context_snapshot_policy.py": "a292f02584ff67ef491d97d914d8282db4e6694aed97e140480064851953760e", + "pdd/contract_check.py": "5c168f2c45b6754acce9b7fd50730c1832053df160b5de88f064061333b84f1a", + "pdd/coverage_contracts.py": "dc209452c683697866e133c5673a2caba1828c9e4836e012f277e0250a643c0c", + "pdd/evidence_store.py": "eccc499c6f076b52e4b481a07904b9390120e52c49cb2479adbdf5eb433df14c", + "pdd/gate_main.py": "8281d282d9ad62dc01c92c4f520ea0be70e5fba50281d18fd4111445ebfd613a", + "pdd/prompt_lint.py": "df85f363082925c714e23be8d7b2e941d62a8542a3370a2581d16780cef1474e", + "pdd/source_set_model.py": "72465c576d85e606d557e602ea78c8dc58646455a429a8dfa4e7b66da22210fa", + "pdd/user_story_tests.py": "48a1597e9b7cd409ab62e9ef6b005f5fdb29699207c24f640b149f7e79adf79b" + } +} \ No newline at end of file diff --git a/.pdd/meta/checkup_report_python.json b/.pdd/meta/checkup_report_python.json new file mode 100644 index 0000000000..c5390979dd --- /dev/null +++ b/.pdd/meta/checkup_report_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.028884+00:00", + "command": "fix", + "prompt_hash": "2b1ffbc310805ac63bcd051f10ce03159534752ea729a6b11e91bf8e27400188", + "code_hash": "1dbd1ce70b5bad244814044cda35b08e31f3830ade98aa7abadf55913d156fc8", + "example_hash": null, + "test_hash": "cfd2b272dc93e2d58657a6e07aec68cc14f8883ed1354900cea3545cc4eab203", + "test_files": { + "test_checkup_report.py": "cfd2b272dc93e2d58657a6e07aec68cc14f8883ed1354900cea3545cc4eab203" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/checkup_review_loop_python.json b/.pdd/meta/checkup_review_loop_python.json index 1710fae580..a2b154c930 100644 --- a/.pdd/meta/checkup_review_loop_python.json +++ b/.pdd/meta/checkup_review_loop_python.json @@ -1,13 +1,13 @@ { - "pdd_version": "0.0.288.dev0", - "timestamp": "2026-06-26T23:09:14.954776+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.033050+00:00", "command": "fix", - "prompt_hash": "05eafdbdedc8b12e80c5ac70d860902292ba364ff2dcf5da19959e9862ace5ef", - "code_hash": "a49524b05e78c8969518f44a654f9f90e869225fe94f7ad5d7ed3c675c1eead7", + "prompt_hash": "a4ec8ed0458adb258d495ea6444d014d4898e004814ee8447359816658d26be5", + "code_hash": "8addb7e16d4f1b4e36ce8214748c8fdae9db53a09802d29a872b046d687bf2d1", "example_hash": "b392eaa48f9bd84660d4a2aaec04c5237a7791059c6ffd64288b34eadce686bb", - "test_hash": "e388afeff3508ef3e87be8261fe8400d7cdec20eabcb4b31717e755019bc41fa", + "test_hash": "6b357b123dd5b1a72c4231def4042c8a7cb748c3fbb84680a4159e6256511730", "test_files": { - "test_checkup_review_loop.py": "e388afeff3508ef3e87be8261fe8400d7cdec20eabcb4b31717e755019bc41fa" + "test_checkup_review_loop.py": "6b357b123dd5b1a72c4231def4042c8a7cb748c3fbb84680a4159e6256511730" }, "include_deps": { "context/agentic_change_example.py": "a8308b2af708c804d626a4a96012c5a67f5bc6469e33857cafc07627213828d7", @@ -16,4 +16,4 @@ "context/agentic_e2e_fix_orchestrator_example.py": "180cc2c4316b531586040d61fc5f82f1f605d5c5736fd23bad63a889ac0b7d1e", "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" } -} +} \ No newline at end of file diff --git a/.pdd/meta/checkup_simplify_claude_python.json b/.pdd/meta/checkup_simplify_claude_python.json new file mode 100644 index 0000000000..3ccd2186d2 --- /dev/null +++ b/.pdd/meta/checkup_simplify_claude_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.033767+00:00", + "command": "fix", + "prompt_hash": "d3587f96ae7eee752af14011c3ecceb94fb0c20691132fe5ef7aaf82caa8e083", + "code_hash": "0dd6846d6b5e9ec63ac71fa6474eb84f06bc2dbb5f168e9c01eeeb2eee7f1c7a", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/checkup_simplify_engines_python.json b/.pdd/meta/checkup_simplify_engines_python.json new file mode 100644 index 0000000000..6fd7340bf2 --- /dev/null +++ b/.pdd/meta/checkup_simplify_engines_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.034500+00:00", + "command": "fix", + "prompt_hash": "0eaef948b5e725a8dbc1a14b1240b52946cbb9893401d0a6b001004c93267086", + "code_hash": "9009191475a97901ad9b4eac39a682f55b4789297022d9c3752aba4126ea3230", + "example_hash": null, + "test_hash": "e1ae900756abdba9805b2e50002811d22e48afb3ebe3fea3b60b7cd003487984", + "test_files": { + "test_checkup_simplify_engines.py": "e1ae900756abdba9805b2e50002811d22e48afb3ebe3fea3b60b7cd003487984" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/checkup_simplify_python.json b/.pdd/meta/checkup_simplify_python.json new file mode 100644 index 0000000000..92c48b51fe --- /dev/null +++ b/.pdd/meta/checkup_simplify_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:22:51.413274+00:00", + "command": "fix", + "prompt_hash": "3a0b375fbd9294ef119fed542d772ad16485ab579fe15fd55619e363d08e10a2", + "code_hash": "6ec8b1694b0ec8c9955261fff159b1a86397d54f24a0f95ea5243ac9cbd6df7f", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/checkup_tools_python.json b/.pdd/meta/checkup_tools_python.json new file mode 100644 index 0000000000..f01c2d5581 --- /dev/null +++ b/.pdd/meta/checkup_tools_python.json @@ -0,0 +1,11 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.036008+00:00", + "command": "fix", + "prompt_hash": "df087676e5581e2e05a7d6daa036b5e1113cccd5c837a1b4a3508b890e1da118", + "code_hash": "d11c11dc9670827619d71214c4d7d3b6535c13460a67d8290af83c34f050e757", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/ci_detect_changed_modules_python.json b/.pdd/meta/ci_detect_changed_modules_python.json new file mode 100644 index 0000000000..37e4a16ff5 --- /dev/null +++ b/.pdd/meta/ci_detect_changed_modules_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.037608+00:00", + "command": "fix", + "prompt_hash": "ef30764861a3080d2fb093ca747f86a3f46bba733a0cdc6a5634efc1b36a73a2", + "code_hash": "0d77f7c40e1498558218ea0d552f09bc9d02e128be4dca789f6b0f13dfc76007", + "example_hash": "bb23030662fb2337011f8e8db75b455be5d1b2f66cda50685d35745a31f362b2", + "test_hash": "592936e3f208b79f1b4410e9766a7ba16971c11eea7f9b26e1221550972b37f4", + "test_files": { + "test_ci_detect_changed_modules.py": "592936e3f208b79f1b4410e9766a7ba16971c11eea7f9b26e1221550972b37f4" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/ci_drift_heal_python.json b/.pdd/meta/ci_drift_heal_python.json index c40747ccc7..e66bbfc4e7 100644 --- a/.pdd/meta/ci_drift_heal_python.json +++ b/.pdd/meta/ci_drift_heal_python.json @@ -1,6 +1,6 @@ { - "pdd_version": "0.0.296.dev0", - "timestamp": "2026-07-09T11:32:55.747881+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.041012+00:00", "command": "fix", "prompt_hash": "09fe741d2541f30d7ece2447ba91fa8cf91bb4c9ef937810abe6e622649b68df", "code_hash": "4866e9be8fc9513c4d7a5cd03c5eff4c7ebb3a639024c91682322d77f462b94a", diff --git a/.pdd/meta/ci_validation_python.json b/.pdd/meta/ci_validation_python.json new file mode 100644 index 0000000000..fc7051d998 --- /dev/null +++ b/.pdd/meta/ci_validation_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.042730+00:00", + "command": "fix", + "prompt_hash": "6627ace940bcffc3b9cd754c83014635f2949a11c32f6b133e6f209ddc908d3b", + "code_hash": "01935b7b312fcbc3a1812290f6d69fe5d65ddf3530f546c8da729bb8d6a3aeae", + "example_hash": "06b910b216f68af58862e76712ccf82bae1bdf87f3fa95b043408fb079f45ce0", + "test_hash": "c68f729059f3bf0409b5e7f797b2fcf61fc463502e07d8c4db79b10be6a7fb7f", + "test_files": { + "test_ci_validation.py": "c68f729059f3bf0409b5e7f797b2fcf61fc463502e07d8c4db79b10be6a7fb7f" + }, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/ci_validation_example.py": "06b910b216f68af58862e76712ccf82bae1bdf87f3fa95b043408fb079f45ce0", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/cli_branding_python.json b/.pdd/meta/cli_branding_python.json new file mode 100644 index 0000000000..3531875455 --- /dev/null +++ b/.pdd/meta/cli_branding_python.json @@ -0,0 +1,11 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.043245+00:00", + "command": "fix", + "prompt_hash": "2b1c46c67c15235c43a7aacbaba313a0a98dc21383d3b91507173eabfd588fba", + "code_hash": "fc9056e722b00d532aafc3c051eab4d6ba3f5cb7a3aaa4492194202cc8000a24", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/cli_detector_python.json b/.pdd/meta/cli_detector_python.json new file mode 100644 index 0000000000..3bb0536351 --- /dev/null +++ b/.pdd/meta/cli_detector_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.044131+00:00", + "command": "fix", + "prompt_hash": "4bcecc483a368af1d6dc9d9f0b12e608e760b179c5b03c2c4d866a06cbdbe59b", + "code_hash": "78b4e788b206d2902296351faecfcc8e2ced44b9dbc696837b4c7bd45f1fce7a", + "example_hash": "db64a53121864c633ba1586f1af491ae7cc617c10cf4c477a7e6d262f676cd23", + "test_hash": "6b92fed161723fc46aa7aef74efa1af0506c8da8f8085e87cd20cef9297f2724", + "test_files": { + "test_cli_detector.py": "6b92fed161723fc46aa7aef74efa1af0506c8da8f8085e87cd20cef9297f2724" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/cli_status_python.json b/.pdd/meta/cli_status_python.json new file mode 100644 index 0000000000..19bc9b3584 --- /dev/null +++ b/.pdd/meta/cli_status_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.044926+00:00", + "command": "fix", + "prompt_hash": "121ff116fa307d8034c8f8826f0e1382fd6467fe4eb9d5ef480030901d114e57", + "code_hash": "9aacc600635c5a2e6f5d9b2ef028ba701b3d6605b23e497aabc0382a6d8ef1d5", + "example_hash": null, + "test_hash": "968db7dd3edc563c9f7b43ea83dab5030150bcb29e74f60c5ba84e066ee29069", + "test_files": { + "test_cli_status.py": "968db7dd3edc563c9f7b43ea83dab5030150bcb29e74f60c5ba84e066ee29069" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/cli_theme_python.json b/.pdd/meta/cli_theme_python.json index 0adcc03a55..9366676f5b 100644 --- a/.pdd/meta/cli_theme_python.json +++ b/.pdd/meta/cli_theme_python.json @@ -1,6 +1,6 @@ { - "pdd_version": "0.0.278.dev0", - "timestamp": "2026-06-19T04:14:17.493767+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.045793+00:00", "command": "fix", "prompt_hash": "92ea1f8c1b4039771ee67668a380f44dffc6d976751dd1ec78a498f2c55defb5", "code_hash": "00d434d8798de28ba50dfc195655f57e9ac671cf178c6d77a473dd18e5cd1b06", diff --git a/.pdd/meta/cmd_test_main_python.json b/.pdd/meta/cmd_test_main_python.json new file mode 100644 index 0000000000..5ced2e7628 --- /dev/null +++ b/.pdd/meta/cmd_test_main_python.json @@ -0,0 +1,22 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.495671+00:00", + "command": "fix", + "prompt_hash": "bcdd092301e52973cf761b409159fdc035a12de4b74caa554ce2010600d56ddc", + "code_hash": "cc809a1c54c8f4aa2ac4dd9e391d89df4b4e1be2dacc42f0a71306b4a666cf3d", + "example_hash": "0774984ee29f70407429f1eff4a1b76287715b7ddd6adf2bc149e874c9b844bb", + "test_hash": "d940dca46c808464348228dba68d1f88a884ff9ea0d827ce9c4e8ef27e78d643", + "test_files": { + "test_cmd_test_main.py": "d940dca46c808464348228dba68d1f88a884ff9ea0d827ce9c4e8ef27e78d643" + }, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/cloud_function_call.py": "62ac7724ce2e3eddbba2b743bd7aa5790dbed34eae01e77e0252c4cc6358c95e", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/generate_test_example.py": "f73533a9cf63f2e2f4818ee923a8500e612d155e230017dbdb35aec945e41998", + "context/get_jwt_token_example.py": "76a87c8257cb1f4cfdc2edff6225c752432afb3a5e352fcb016d08d8eae16e01", + "context/increase_tests_example.py": "f53be429e9a9e6a523c770ed6d0d4498e2201b22d5a8e4ef28ac64e8ff172c91" + } +} \ No newline at end of file diff --git a/.pdd/meta/code_generator_main_python.json b/.pdd/meta/code_generator_main_python.json index 0f74e423fd..2e18db5405 100644 --- a/.pdd/meta/code_generator_main_python.json +++ b/.pdd/meta/code_generator_main_python.json @@ -1,29 +1,29 @@ { - "pdd_version": "0.0.268", - "timestamp": "2026-06-09T23:30:32.073262+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.055341+00:00", "command": "example", - "prompt_hash": "9c646810b3e55fa6ba5c0bb1d8210e459446ffa6826c565d7012ea6bd55fb6d5", - "code_hash": "1293a25723fdcb7870711b4759248fe4681d5107cf2106feff61df57cb56ee96", - "example_hash": "25bb0d4b5fe4f31d0d4d3795d24e8e9bb2cd346c74e4fc916d0a9ba8590141be", - "test_hash": "3cc05a05384f982254fdb38d8a156fc7507a1dfec200fc91705d01fbbfad134d", + "prompt_hash": "b0e5511b3cfab37280bfa33b4def679b78664af37da564d77d56a324e0466670", + "code_hash": "e83cc9bd8890d032fcf63001b83feb766d632ab08d00e20110e489e0386962b4", + "example_hash": "4b66f24a1e2ac4195255738566b0cccdac0971523a245d3a8b78ad45d9e1a872", + "test_hash": "5770dba6d2f38bfb7d8051e866bf622b43401b073ac8d97202fe25b4e805d231", "test_files": { - "test_code_generator_main.py": "3cc05a05384f982254fdb38d8a156fc7507a1dfec200fc91705d01fbbfad134d" + "test_code_generator_main.py": "5770dba6d2f38bfb7d8051e866bf622b43401b073ac8d97202fe25b4e805d231" }, "include_deps": { "context/__init__example.py": "84208a445d03a336e465709ec0687eb969c8d865e6739bf8b79f2c8a8aad351a", "context/_keyring_timeout_example.py": "41f83d5ef117caba86b788f69b519f7e88f3d7dc2a641a017d74a7cee9be6ed1", "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", "context/api_key_scanner_example.py": "4b9f880a30445f25edffc394857ae0c218097726c41ab996a03fcbc4350b5482", - "context/architecture_sync_example.py": "6e1f65de35fcf1dbd0def81710da6f378495b37187eb01cd01ef43f662d9f1ba", + "context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0", "context/auth_service_example.py": "4b6d2c8c9e13d1edc0d9805ee0c07657494cbf31dffe2bb0029b72975674a8cf", "context/auto_include_example.py": "0c278ed716298149777bc7082827eb8aa6d80dac264c66b6cded6871232f2df7", "pdd/code_generator.py": "33235364b431edbb0fb1c4d8eb03fa21a3b776e07ea585530c19212da20c486d", - "pdd/code_generator_main.py": "1293a25723fdcb7870711b4759248fe4681d5107cf2106feff61df57cb56ee96", - "pdd/construct_paths.py": "832d478849bfe83c12d4a473c270902d0af0be577ae3276b07ef8a48c148d35f", + "pdd/code_generator_main.py": "e83cc9bd8890d032fcf63001b83feb766d632ab08d00e20110e489e0386962b4", + "pdd/construct_paths.py": "7b42a002e4ecc27be4d517cbc07f1fc9425e795d4918714f382d2ed5ddd00eab", "pdd/core/cloud.py": "0487c0b989996af144df3af1c818a6eeafe52fd209710e5a54eb43990c89ae97", - "pdd/incremental_code_generator.py": "266a4bed6ea41519a1b1c3c33057cb33cffecd2a3075ee0c2f790798bfff4834", - "pdd/preprocess.py": "a6c86dd348d0028a0638114338013e4633fb66fa51f756bc30458f4dce833703", + "pdd/incremental_code_generator.py": "8dd5dd7f3016b8fae86056484fdc49b824405cdcd6911e8b8657d3e043b3b06d", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56", "pdd/python_env_detector.py": "cbe4044a83cd88a683f36d6ecfca245fef6a03ea2abc7f5c407636fccc051f35", - "prompts/context_snapshot_python.prompt": "d8e129b55b27814d16e7566f07dfdaae0cc6950352f351fd78cfeeab125a7cbf" + "prompts/context_snapshot_python.prompt": "05f46fa9adba7ed3c9c6088d150ad821c723e9d0a3efed9db2fe8a461386da9d" } } \ No newline at end of file diff --git a/.pdd/meta/code_generator_python.json b/.pdd/meta/code_generator_python.json new file mode 100644 index 0000000000..ddc54ba66b --- /dev/null +++ b/.pdd/meta/code_generator_python.json @@ -0,0 +1,14 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.056771+00:00", + "command": "fix", + "prompt_hash": "2db9dd103925fef2b56138d3fa0056d86d30d1026e60ea17477d892d6df1357e", + "code_hash": "33235364b431edbb0fb1c4d8eb03fa21a3b776e07ea585530c19212da20c486d", + "example_hash": "4e66ed9866b05a2de791376d4881ae5d1d52ac4a6ea99176dce8febdf9a14355", + "test_hash": "48919cac185306dfdcb2d37f86596c9858b1f6515a16b36dadd1491448f34566", + "test_files": { + "test_code_generator.py": "48919cac185306dfdcb2d37f86596c9858b1f6515a16b36dadd1491448f34566", + "test_code_generator_main.py": "5770dba6d2f38bfb7d8051e866bf622b43401b073ac8d97202fe25b4e805d231" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/codex_subscription_python.json b/.pdd/meta/codex_subscription_python.json new file mode 100644 index 0000000000..fd94c8463d --- /dev/null +++ b/.pdd/meta/codex_subscription_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.057675+00:00", + "command": "fix", + "prompt_hash": "17eedbc09a256171e39540fdd62097d30a4d66fba0fb20843b994315e2d5fc3c", + "code_hash": "4a3ff334aa58b5f8e004f2eff9a67a041ed0a4d374a6646090239d8b6411df1a", + "example_hash": null, + "test_hash": "e26c73fc92e9cba43f3e225860f20e593f398118fe17db33c03a10bd9b3ff849", + "test_files": { + "test_codex_subscription.py": "e26c73fc92e9cba43f3e225860f20e593f398118fe17db33c03a10bd9b3ff849" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/commands_analysis_python.json b/.pdd/meta/commands_analysis_python.json new file mode 100644 index 0000000000..e21b746221 --- /dev/null +++ b/.pdd/meta/commands_analysis_python.json @@ -0,0 +1,22 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.060540+00:00", + "command": "fix", + "prompt_hash": "f618f0ee1b3dfb720406f22d8c6dacf83a0e99942a0e7432a6d3d29526f0c4b7", + "code_hash": "a553780c2267d05af75ea447ea01e94519ab7b5a27e940abec6183797e7fb1ba", + "example_hash": "01ff82910c50ec8f8cfd22f8e3b7c011f286a4b9f90e7dd5b2eb685c1f339e56", + "test_hash": "0e747c6b4b27cad8f2b651663bc4ae01dacdb5f01558c0996ec39a6af7d19fa4", + "test_files": { + "test_analysis.py": "0e747c6b4b27cad8f2b651663bc4ae01dacdb5f01558c0996ec39a6af7d19fa4" + }, + "include_deps": { + "context/agentic_bug_example.py": "96e35df4e8425a9173ff6a30281892c2bd8b81361cff72b732e488172ddf52f5", + "context/bug_main_example.py": "662ecde39d05a5345533c4ae74c6b1031f016916ec2ff4764d34a26f7ec26172", + "context/conflicts_main_example.py": "36e9f73fd888af26dc73f35ee765715e99e4325881bc0ffdc5d70106eda7e239", + "context/crash_main_example.py": "5c9b909107f74f773ccf7013d556a5812643d2a3b086e79fe07da7ea7877458c", + "context/detect_change_main_example.py": "ddc494709d127398004f53de58bede388692b2839a97b26668c55575369d9edd", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/trace_main_example.py": "c1f43ebf55c05af801640f5f8331ee7a674aef9e3f383b0e5ffee7abc7246fe1", + "context/track_cost_example.py": "bd60efaa7d6274b0e4d1743ac05785461af65ec785b254730288d08774f72ed6" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_auth_python.json b/.pdd/meta/commands_auth_python.json new file mode 100644 index 0000000000..f37e8fa013 --- /dev/null +++ b/.pdd/meta/commands_auth_python.json @@ -0,0 +1,20 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.062313+00:00", + "command": "fix", + "prompt_hash": "9c6188da46f58639009a26f6848c90e714785bb0b89d8bb86ed44454209318a9", + "code_hash": "c33a045aa16c202eacc6c195bdef5d88c24ac7df2e36a9d7f3172de7963b768d", + "example_hash": "d1e77e2ab5a5b6f3c400a8a68a73bb633f6d45e39dd4a90306dcc18c729d9000", + "test_hash": "258c7340e7bcf624d67f5b5f25907cf55e328215c7f4aae9edd156d5f4066af7", + "test_files": { + "test_auth.py": "258c7340e7bcf624d67f5b5f25907cf55e328215c7f4aae9edd156d5f4066af7" + }, + "include_deps": { + "context/_keyring_timeout_example.py": "41f83d5ef117caba86b788f69b519f7e88f3d7dc2a641a017d74a7cee9be6ed1", + "context/auth_service_example.py": "4b6d2c8c9e13d1edc0d9805ee0c07657494cbf31dffe2bb0029b72975674a8cf", + "context/commands/__init___example.py": "cc6e0de7d94b1dddda35d9c8ecb800710263d928191f1d4cb2400bae36c31cb4", + "context/core/cloud_example.py": "8a14ae9e69dbccfbbfffd9bde1ea11192334a10aaf9d5b5e6dba5ac23ba0dd90", + "context/get_jwt_token_example.py": "76a87c8257cb1f4cfdc2edff6225c752432afb3a5e352fcb016d08d8eae16e01", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_checkup_python.json b/.pdd/meta/commands_checkup_python.json index ad99414b05..a0f19d01c4 100644 --- a/.pdd/meta/commands_checkup_python.json +++ b/.pdd/meta/commands_checkup_python.json @@ -1,8 +1,8 @@ { - "pdd_version": "0.0.288.dev0", - "timestamp": "2026-06-26T22:58:40.738135+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.064419+00:00", "command": "test", - "prompt_hash": "f26cc513b81c467f58c2bbf0b79d1186904a48d7d801faa69302ac59eba64f39", + "prompt_hash": "d42ff466ad8a19df7c2101a92e4b38737707e87ef7b59a64e77133a2c848a0b4", "code_hash": "0b7936aabeb40dd4eb0bde84d3693b8df3b5f423b3514b740b4fa4140a64ee6e", "example_hash": "847008502198ff360190831172d5d6cc6de6e5e76d3782d2b53823297a5b1349", "test_hash": "790c9e6532de75e9b2b4b1912a61c4fdcea229568cd40572245f06a1985be628", @@ -15,8 +15,8 @@ "include_deps": { "context/agentic_checkup_orchestrator_example.py": "295a9ae294e22224d3c447f13c462854bfe4e526cebc89bcbba417ea514c7ef2", "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", - "context/agentic_sync_example.py": "9bcc2f199a1463e8c424d8210ea6d5f34ad6fb5fcb1022aa879a9ecba0f8d5d6", + "context/agentic_sync_example.py": "aa9683f1a26841f3cae854e72cce38636f0859d8dd2f7672fa14e412afdef884", "context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0", "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" } -} +} \ No newline at end of file diff --git a/.pdd/meta/commands_checkup_simplify_python.json b/.pdd/meta/commands_checkup_simplify_python.json new file mode 100644 index 0000000000..06a50f4e4b --- /dev/null +++ b/.pdd/meta/commands_checkup_simplify_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:22:51.446610+00:00", + "command": "fix", + "prompt_hash": "879ba667d716fcf666ee1df507348749953d0dfba4e099fa0a9bac016945e101", + "code_hash": "8f469665e09459f6405617c84214475a15f6b2353842f9b477c2089b437bf0a0", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_connect_python.json b/.pdd/meta/commands_connect_python.json new file mode 100644 index 0000000000..74c5acef96 --- /dev/null +++ b/.pdd/meta/commands_connect_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.067162+00:00", + "command": "fix", + "prompt_hash": "a881214405113719a1009a1bd4ec2c340fbce3fa764c7d50a5e6965a7b5ef3b2", + "code_hash": "f3a2c8d66a2f2555188a61a4828a5114bf1b99e6c2ebf19f84aef2b7da39cc5e", + "example_hash": "d2bcda4ae1e9de22d1b657c28045a0b122dc1ef22a77e4d670f41229779a6083", + "test_hash": "fad9177f084c4ce574ce11a468f55206bcd45dc7602601775f800bc39272252e", + "test_files": { + "test_connect.py": "fad9177f084c4ce574ce11a468f55206bcd45dc7602601775f800bc39272252e" + }, + "include_deps": { + "context/commands/__init___example.py": "cc6e0de7d94b1dddda35d9c8ecb800710263d928191f1d4cb2400bae36c31cb4", + "context/commands/misc_example.py": "7d7985d21dd7b962257856cb92d13e26c016bc09ce14aaac4c68baaead97dc4b", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/remote_session_example.py": "ebcf3626cc73386164ac48e804e2e7973cb9783892ac88cda49779cf158389c5", + "context/server/app_example.py": "554bb0307f2f7a0dc79ef86f8a4fd42e5017225d0c56379ad99d2939f3c1087a" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_context_python.json b/.pdd/meta/commands_context_python.json new file mode 100644 index 0000000000..e6855456e5 --- /dev/null +++ b/.pdd/meta/commands_context_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.068432+00:00", + "command": "fix", + "prompt_hash": "e7351a988d749a910cd9dca718bb35d3a037a4a8fc75dd4211fb0e72f9b953cd", + "code_hash": "3b67e44225298a9a76412cde3b8a933dad9e5a74be731453edf2e527bec5ee63", + "example_hash": null, + "test_hash": "9f907c4708d249f15104b0352694359b0e901ff0c8de5bfd32dfa658a62d90d6", + "test_files": { + "test_context.py": "9f907c4708d249f15104b0352694359b0e901ff0c8de5bfd32dfa658a62d90d6" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "prompts/commands/replay_python.prompt": "58cdc1775f5b2777fa766261fd6bb46385c1bd079630046fe4b67abee0c88778" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_contracts_python.json b/.pdd/meta/commands_contracts_python.json new file mode 100644 index 0000000000..cbc8fc846f --- /dev/null +++ b/.pdd/meta/commands_contracts_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.069152+00:00", + "command": "fix", + "prompt_hash": "ad58fc1a29f77c72d231a233db025de60a25c6509b030d198cdd77bed1e7d8cb", + "code_hash": "cf070bb6670f4051aacf864306095707c581c6a48e57876d4d41d546fec5981c", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_firecrawl_python.json b/.pdd/meta/commands_firecrawl_python.json new file mode 100644 index 0000000000..4252b8c4ad --- /dev/null +++ b/.pdd/meta/commands_firecrawl_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.069903+00:00", + "command": "fix", + "prompt_hash": "e94170a1ab45cc700c6420a694f00624b4372d76a11746b6476162958e9de03a", + "code_hash": "ef4b2875252e86550cbe6c87e07a3662edff352f87c3affdbf0393146833be7f", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_fix_python.json b/.pdd/meta/commands_fix_python.json new file mode 100644 index 0000000000..58f5982e5d --- /dev/null +++ b/.pdd/meta/commands_fix_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.071436+00:00", + "command": "fix", + "prompt_hash": "71d34c70ff508a726367285baaf296c9032b316bf81c5cbc97d6b900553d718a", + "code_hash": "52a8eaa4c37566690f374fe5f596cf1d63762c8e68c1133985042d8571fd6543", + "example_hash": "4b2db39fec015e750d59894877e7dc630152aff56fe262dee275f03ba2c6f97d", + "test_hash": "837ea1b2e8d5c1019893b1461f97a00511a3d018e19b4faf443e91d4e9589eda", + "test_files": { + "test_fix.py": "837ea1b2e8d5c1019893b1461f97a00511a3d018e19b4faf443e91d4e9589eda" + }, + "include_deps": { + "context/agentic_e2e_fix_example.py": "79fd30a0fa6d8a5b9e23b34f7328ba3f95089a0ebb83e8244dae79ea68cff33a", + "context/fix_main_example.py": "29f88d8bfcc2256ebebe6efcb611f9487eb5ca6d8781286a6dd98689f92bca7a", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/track_cost_example.py": "bd60efaa7d6274b0e4d1743ac05785461af65ec785b254730288d08774f72ed6" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_gate_python.json b/.pdd/meta/commands_gate_python.json new file mode 100644 index 0000000000..34fbf8aa0f --- /dev/null +++ b/.pdd/meta/commands_gate_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.072151+00:00", + "command": "fix", + "prompt_hash": "55c2e3207298e63d0362345861747fe6711b18fd5f9f4acb9d6d73c821840542", + "code_hash": "c75e65038098b8cbf57526c341028047c24f2813fcea2b3daeffe76bd1b57832", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_generate_python.json b/.pdd/meta/commands_generate_python.json new file mode 100644 index 0000000000..3c0adaa206 --- /dev/null +++ b/.pdd/meta/commands_generate_python.json @@ -0,0 +1,24 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-09T22:54:16.334575+00:00", + "command": "fix", + "prompt_hash": "f538c52d5e7a071976cae837c14d570f8aeccf2ca9383c4fed48271dd21f3c4d", + "code_hash": "7751e368d656f940760d38cbe617011ae5f9dffcaed0ac3fd85ec1fd6b1567b5", + "example_hash": "ecfaafca740d69b46d569368cb5a289410cf6fde5ef45c880bde396c797052c2", + "test_hash": "f7603f270d2fbcd0a7623ddbd6bb63e1e3f33ae51868d163b9a3a5e3f58483a2", + "test_files": { + "test_generate.py": "f7603f270d2fbcd0a7623ddbd6bb63e1e3f33ae51868d163b9a3a5e3f58483a2" + }, + "include_deps": { + "context/agentic_architecture_example.py": "f82ea3f16297c0cd0f7993549054bc9213953ebe03144aa5abc7bd74ba707e13", + "context/agentic_test_example.py": "1d8b490a2b0ff53a8b786c6c95faeecc4efd0becf837b35273508bae19fac3ab", + "context/auth_service_example.py": "4b6d2c8c9e13d1edc0d9805ee0c07657494cbf31dffe2bb0029b72975674a8cf", + "context/cmd_test_main_example.py": "0774984ee29f70407429f1eff4a1b76287715b7ddd6adf2bc149e874c9b844bb", + "context/code_generator_main_example.py": "4b66f24a1e2ac4195255738566b0cccdac0971523a245d3a8b78ad45d9e1a872", + "context/context_generator_main_example.py": "77c3de786f31392540ec609d4b3942e98e996ae14c4014f5018cf2b05a560df4", + "context/operation_log_example.py": "2660def7012d9b79bad3db33cf399ac603cc1c35dff0b064cbdd2638a013de1d", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/track_cost_example.py": "bd60efaa7d6274b0e4d1743ac05785461af65ec785b254730288d08774f72ed6", + "pdd/user_story_tests.py": "48a1597e9b7cd409ab62e9ef6b005f5fdb29699207c24f640b149f7e79adf79b" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_maintenance_python.json b/.pdd/meta/commands_maintenance_python.json new file mode 100644 index 0000000000..814d97572c --- /dev/null +++ b/.pdd/meta/commands_maintenance_python.json @@ -0,0 +1,23 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.529788+00:00", + "command": "fix", + "prompt_hash": "2a12b3738fd483e3af54be825ffad3fc76c224df0af7bf582dbd48161a9a94c3", + "code_hash": "4fa2499fe14940b0bd6858ca665870236c8be4342b7d44514d920381616b980a", + "example_hash": "89252d2bb50c54846397d3ec319aae9ce69434d2af7a6d746dd7d85a317216e6", + "test_hash": "8ec4ee78179bdd2988cb4e3ee74224a80234e3b84c01fe01059f493cdff06b8a", + "test_files": { + "test_maintenance.py": "8ec4ee78179bdd2988cb4e3ee74224a80234e3b84c01fe01059f493cdff06b8a" + }, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/__init__example.py": "84208a445d03a336e465709ec0687eb969c8d865e6739bf8b79f2c8a8aad351a", + "context/agentic_sync_example.py": "aa9683f1a26841f3cae854e72cce38636f0859d8dd2f7672fa14e412afdef884", + "context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0", + "context/auto_deps_main_example.py": "9b9c36c18d39494b6e31b46f6e4758c25135b6fa3d1672fbb7365844f7d2de04", + "context/change/15/initial_cli.py": "b607c3e67702ad3bc5e61cc40ce85571e4ff09015de96016c8d7d56eb78b8c74", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/sync_main_example.py": "9a7961a4044a54c46ce6c43c8adbbb83da46f233d9c6a9eaa28302440c0052c0", + "context/track_cost_example.py": "bd60efaa7d6274b0e4d1743ac05785461af65ec785b254730288d08774f72ed6" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_misc_python.json b/.pdd/meta/commands_misc_python.json new file mode 100644 index 0000000000..5f07cecc92 --- /dev/null +++ b/.pdd/meta/commands_misc_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.532875+00:00", + "command": "fix", + "prompt_hash": "1bf6621e4f05b8af703d8e23a3bc9aec758b5bfe6bb75cb3c20501136687f60a", + "code_hash": "e49fff5cb806fc88e2ba5e54f445f2b2f855d6902946319c47a264ab6f13885c", + "example_hash": "7d7985d21dd7b962257856cb92d13e26c016bc09ce14aaac4c68baaead97dc4b", + "test_hash": null, + "test_files": {}, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/preprocess_main_example.py": "dc4cf0483361ef94467d8936ceef30b54b62e61d658c916663407a8f142ec851", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_modify_python.json b/.pdd/meta/commands_modify_python.json index 554d70abc8..8426eaa617 100644 --- a/.pdd/meta/commands_modify_python.json +++ b/.pdd/meta/commands_modify_python.json @@ -1,13 +1,13 @@ { - "pdd_version": "0.0.233", - "timestamp": "2026-05-26T00:00:00.000000+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.082584+00:00", "command": "fix", - "prompt_hash": "fe1b5bb35af67e03327d83b877d8eff14be1ee1ab5379bf3190ddaf2d49001cc", - "code_hash": "35c29832636deb8dd6be2f637b3ccdc6e965e044b6554fb1befca967790b0ca1", + "prompt_hash": "bfe68bce6a639f9ebde548e3e0812a7632082c6988f0441bc1f51d2466741419", + "code_hash": "666d20da91b1037b24aa9a5c9f10684417ac31ad9c5c765c7b59d50916db7a99", "example_hash": "84edd899e3202fe13dc2e9437505a85552ec870b325e5628727d7202371d8134", - "test_hash": "8915daf33703cfcd6e2561795faf0b400534c24a49d2ef5e971edd51efde3106", + "test_hash": "86242f8ba65ee0bd4c5224b7c39f243c851a66a8812d4747d543e77fda043707", "test_files": { - "tests/commands/test_modify.py": "8915daf33703cfcd6e2561795faf0b400534c24a49d2ef5e971edd51efde3106" + "test_modify.py": "86242f8ba65ee0bd4c5224b7c39f243c851a66a8812d4747d543e77fda043707" }, "include_deps": { "context/agentic_change_example.py": "a8308b2af708c804d626a4a96012c5a67f5bc6469e33857cafc07627213828d7", @@ -19,6 +19,6 @@ "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", "context/split_main_example.py": "ced192fcbfbfda15b35bd2dad5bbf4b4888acb091084cf58326832d224a77661", "context/track_cost_example.py": "bd60efaa7d6274b0e4d1743ac05785461af65ec785b254730288d08774f72ed6", - "context/update_main_example.py": "b318834c648f25e6141d81f3ecac1d9da592a6bb4740500983f2b07b8a10d902" + "context/update_main_example.py": "9fe01fd19196c2604c91d101e49db849e0e1c9fabafeb2ad831a9c91b2f26416" } -} +} \ No newline at end of file diff --git a/.pdd/meta/commands_prompt_python.json b/.pdd/meta/commands_prompt_python.json index 2b322ebb99..6b00c9ab1a 100644 --- a/.pdd/meta/commands_prompt_python.json +++ b/.pdd/meta/commands_prompt_python.json @@ -1,6 +1,6 @@ { - "pdd_version": "0.0.251.dev112", - "timestamp": "2026-05-27T04:45:33.151707+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.083281+00:00", "command": "test", "prompt_hash": "49253a7519e0deac37df0994c88519e0e4868e2c5420afd0fcf4eb89426e6583", "code_hash": "8c1e3e7d8dfaab47ee8c309c5fa7d3662b183807f5116dfd8e3d03aeba2adc6c", diff --git a/.pdd/meta/commands_replay_python.json b/.pdd/meta/commands_replay_python.json new file mode 100644 index 0000000000..ce975357f7 --- /dev/null +++ b/.pdd/meta/commands_replay_python.json @@ -0,0 +1,14 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.083969+00:00", + "command": "fix", + "prompt_hash": "ac7765224d914a62041e86ed93be1e13fcab04bc1530083032596057e96f07ed", + "code_hash": "6bb573b739993026c3aea2b3e47decd12420c33eb9932d7e7098058e600ff5b3", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "prompts/context_snapshot_python.prompt": "05f46fa9adba7ed3c9c6088d150ad821c723e9d0a3efed9db2fe8a461386da9d" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_report_python.json b/.pdd/meta/commands_report_python.json new file mode 100644 index 0000000000..2fb11faf32 --- /dev/null +++ b/.pdd/meta/commands_report_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.084973+00:00", + "command": "fix", + "prompt_hash": "388398722a80945d1338eee36a96481e02911ec6d5e63e15a546a7d1dbe047b3", + "code_hash": "c07ff974784d7f8f08b464e4326f6e22bb1250945411531dc3860cd7609f5c35", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/core/dump_example.py": "0416a935ac490b958ceedfc6e68c7fbd7e383966374ba13ccf7ebfeaf8ad0803", + "context/core/errors_example.py": "b50dbf08aec028bd57a8fe74762bf92aee9a1fe953fb3d7db50cc686c72bfee3", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_sessions_python.json b/.pdd/meta/commands_sessions_python.json new file mode 100644 index 0000000000..17954361d4 --- /dev/null +++ b/.pdd/meta/commands_sessions_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.086233+00:00", + "command": "fix", + "prompt_hash": "cdfcedf57ee76527c398fbda65c5ee8050c523b3dcafb7c8ec8d0d65ea452422", + "code_hash": "8b05a311e2ad2ef499eb6c97b4f68308f659f87976770181eb0b946a41012266", + "example_hash": "d235c7a2fad154663ce3016baa1babd45a1b5fa5041ba130024dbe88ab791a28", + "test_hash": "38af3c6838c75f99f3a8082116b8a2f0e771ba915be87fcf9bb377b5f57e8b76", + "test_files": { + "test_sessions.py": "38af3c6838c75f99f3a8082116b8a2f0e771ba915be87fcf9bb377b5f57e8b76" + }, + "include_deps": { + "context/core/cloud_example.py": "8a14ae9e69dbccfbbfffd9bde1ea11192334a10aaf9d5b5e6dba5ac23ba0dd90", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/remote_session_example.py": "ebcf3626cc73386164ac48e804e2e7973cb9783892ac88cda49779cf158389c5" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_story_python.json b/.pdd/meta/commands_story_python.json new file mode 100644 index 0000000000..9106266fab --- /dev/null +++ b/.pdd/meta/commands_story_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-09T22:54:16.355128+00:00", + "command": "fix", + "prompt_hash": "75668f6249cff70e5bb9d31517a60591a18264e5a347eaf938085f35a595e4f6", + "code_hash": "afd7ee5fde8aacd3691e321de48fa0d7dfb077413f7685393fa10adb5ad824fa", + "example_hash": null, + "test_hash": "71d27dbe130a088e83033618c5d1bfb98baf8d2841e20d55925e8b0390db548e", + "test_files": { + "test_story.py": "71d27dbe130a088e83033618c5d1bfb98baf8d2841e20d55925e8b0390db548e" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/user_story_tests.py": "48a1597e9b7cd409ab62e9ef6b005f5fdb29699207c24f640b149f7e79adf79b" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_templates_python.json b/.pdd/meta/commands_templates_python.json new file mode 100644 index 0000000000..d7b5f77e1e --- /dev/null +++ b/.pdd/meta/commands_templates_python.json @@ -0,0 +1,14 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.545362+00:00", + "command": "fix", + "prompt_hash": "1e639b0cb31c602f7d32c0aeba64ba166a8945b7eea4e6218c3ab585fdeaf284", + "code_hash": "6cb72959c02bcb9df6c2661ed339d61a782f4466980c0f899371af2113818dd0", + "example_hash": "017d7a9cbdec60dd67cee662f3b21a68638137cd086fb335ef92889e1d91b09a", + "test_hash": null, + "test_files": {}, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_utility_python.json b/.pdd/meta/commands_utility_python.json new file mode 100644 index 0000000000..9e19ec2b16 --- /dev/null +++ b/.pdd/meta/commands_utility_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.549852+00:00", + "command": "fix", + "prompt_hash": "e49fdb165f324559f702cf8cc9d6b2fb5f1ca98bccf16a2fdc5ad594a91b5f8a", + "code_hash": "9d0f70da4a91baf636335b69168fff3806b48d8fa6bc6bf0ca8c910bf1f11559", + "example_hash": "fb7389c0ec837b8b6ee481e2eda04800f565fe247643f5bb9922dd1dfdab984a", + "test_hash": "077383807291f4ba2f3b070b264441a322e6429ef6d3c2ac64d0b88694602c31", + "test_files": { + "test_utility.py": "077383807291f4ba2f3b070b264441a322e6429ef6d3c2ac64d0b88694602c31" + }, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/fix_verification_main_example.py": "6490f398aca6df47b1a95e46a4ecfc0aabe299b5022de5c31ebc436e1bc3e459", + "context/install_completion_example.py": "999be17c0ce476845d7048a1a909ad425e60c298dbc7c74f51b596ef134a8aec", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/track_cost_example.py": "bd60efaa7d6274b0e4d1743ac05785461af65ec785b254730288d08774f72ed6" + } +} \ No newline at end of file diff --git a/.pdd/meta/commands_which_python.json b/.pdd/meta/commands_which_python.json new file mode 100644 index 0000000000..fb0850af57 --- /dev/null +++ b/.pdd/meta/commands_which_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.092184+00:00", + "command": "fix", + "prompt_hash": "2f3eac13978f6cbf66396d352071f00a3a670e81a6ca48206fa784aff9ae1882", + "code_hash": "c36548d914d06d2c2b56c55b607249eaadb7c162cf1b1284d0377c79790214fa", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/__init__example.py": "84208a445d03a336e465709ec0687eb969c8d865e6739bf8b79f2c8a8aad351a", + "context/auto_deps_main_example.py": "9b9c36c18d39494b6e31b46f6e4758c25135b6fa3d1672fbb7365844f7d2de04", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/comment_line_python.json b/.pdd/meta/comment_line_python.json new file mode 100644 index 0000000000..a9a741d744 --- /dev/null +++ b/.pdd/meta/comment_line_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.093101+00:00", + "command": "fix", + "prompt_hash": "f5012401ed39f35053deac030ea7bf5444c31b88a7d1380807895ecea0e0e6c4", + "code_hash": "b17da17f86c6d5f20b8bfaef23d324930099d888ad80a916ee73a2c3c69028f6", + "example_hash": "debc424d539b65aef1267888bb2eb77cf4de16745407bb5df5a18b8e2f231095", + "test_hash": "1c7f412de99137ecf6240c7d03eb7453334864768ef60360704890b46ee0bd83", + "test_files": { + "test_comment_line.py": "1c7f412de99137ecf6240c7d03eb7453334864768ef60360704890b46ee0bd83" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/compressed_sync_context_python.json b/.pdd/meta/compressed_sync_context_python.json new file mode 100644 index 0000000000..f4acfd20ed --- /dev/null +++ b/.pdd/meta/compressed_sync_context_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.095306+00:00", + "command": "fix", + "prompt_hash": "c167d3127c74eab08bfd5a5293c8264e0582e777722fc47f0608b78bec5707ee", + "code_hash": "661fde0fcc42ce39c1004e47a774a8fd799b789065519cb5688949c35fed4859", + "example_hash": null, + "test_hash": "5fd1feba54991f20e5e95264a7bcc9055cdb541f8c7df5a2f7d90b22b936af2f", + "test_files": { + "test_compressed_sync_context.py": "5fd1feba54991f20e5e95264a7bcc9055cdb541f8c7df5a2f7d90b22b936af2f" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/context_snapshot.py": "6aa1ea0d41f73c40b02b72603f6193d4b6e51fdefb57d62f501311365a9ab1b8", + "pdd/contract_ir.py": "0d3392ec8990ac0ff6ddb4a00fee3c6719395f046a59a263ade7d8676e3648e9", + "pdd/operation_log.py": "d6712703f752c2045cd11a5c144b67454bb80700b83bd4e4612e55d2107b6072", + "pdd/test_context_packer.py": "27c7d14747d38df8edf24c8ef2ce1b7ca21d8f2eea38aec02ba06042ee144c0b" + } +} \ No newline at end of file diff --git a/.pdd/meta/config_resolution_python.json b/.pdd/meta/config_resolution_python.json new file mode 100644 index 0000000000..115bd1b128 --- /dev/null +++ b/.pdd/meta/config_resolution_python.json @@ -0,0 +1,14 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.096322+00:00", + "command": "fix", + "prompt_hash": "f623b13d873302a7d13d94fe5d8556aa818f138dca71b4b8633dbce77bbbe030", + "code_hash": "d50eb62241f36eed780d793a4631444f6f62e2551cff831e5c133f5dddc6a291", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/__init__.py": "52c9a7d1a0608830d7f112b533c0bc2990435813d6d7c8dac921530e862bf5dd" + } +} \ No newline at end of file diff --git a/.pdd/meta/conflicts_in_prompts_python.json b/.pdd/meta/conflicts_in_prompts_python.json new file mode 100644 index 0000000000..45aaa630ad --- /dev/null +++ b/.pdd/meta/conflicts_in_prompts_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.097474+00:00", + "command": "fix", + "prompt_hash": "06501ee2b4c3a052873db04a0f3c8b629de39d655f665459591961d1e613fabc", + "code_hash": "c0bd91a9575d399f8d95a428c1f96c227c4a85985ba3efc36e89c3b6d2e44c01", + "example_hash": "d286f38ca6e43216d62197230f6c53ec665734db782d965e5448f8e9f8bbdfb2", + "test_hash": "21832d31b23e3e2898a4d64828d515f6f3196c8a48832570af4d7717a0e631c7", + "test_files": { + "test_conflicts_in_prompts.py": "21832d31b23e3e2898a4d64828d515f6f3196c8a48832570af4d7717a0e631c7" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f" + } +} \ No newline at end of file diff --git a/.pdd/meta/conflicts_main_python.json b/.pdd/meta/conflicts_main_python.json new file mode 100644 index 0000000000..f6edc2b3cc --- /dev/null +++ b/.pdd/meta/conflicts_main_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.098854+00:00", + "command": "fix", + "prompt_hash": "7a2bb8b334031ae46ded9dd01e1c0fa4cf4c68fc863dd33a347d36d0f95ea662", + "code_hash": "63eb4e64da42e51be42ce4d691e6f4928753f99efd017359944a81ad114fc1be", + "example_hash": "36e9f73fd888af26dc73f35ee765715e99e4325881bc0ffdc5d70106eda7e239", + "test_hash": "aad1db3e48e41d608905390ad1cfa8bd109ffa1b0d65cb75d8863ac4afe5751d", + "test_files": { + "test_conflicts_main.py": "aad1db3e48e41d608905390ad1cfa8bd109ffa1b0d65cb75d8863ac4afe5751d" + }, + "include_deps": { + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/conflicts_in_prompts_example.py": "d286f38ca6e43216d62197230f6c53ec665734db782d965e5448f8e9f8bbdfb2", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/construct_paths_python.json b/.pdd/meta/construct_paths_python.json index 950822c8d3..5ae37883e4 100644 --- a/.pdd/meta/construct_paths_python.json +++ b/.pdd/meta/construct_paths_python.json @@ -1,16 +1,16 @@ { - "pdd_version": "0.0.278.dev0", - "timestamp": "2026-06-19T04:14:17.583096+00:00", + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.562835+00:00", "command": "fix", - "prompt_hash": "2b7c13628eda07779cc96080af940fb135118e626453a902f370b1864484d447", - "code_hash": "556b154328d8400f46209c9c8af61e7162675becfb49a4bf55d42f045972243c", + "prompt_hash": "ed06c69349c1f30998fc211c7092cd97d780ff400e21615432cc28120de61972", + "code_hash": "7b42a002e4ecc27be4d517cbc07f1fc9425e795d4918714f382d2ed5ddd00eab", "example_hash": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", - "test_hash": "a6a100f304748466b50be8cade7bfbad2dec3b81c9c285f9bf2213072a5ea5a1", + "test_hash": "2144db1645d973f2df5f8867c515943c5a09e674b34569c016055e576fdf6418", "test_files": { - "test_construct_paths.py": "a6a100f304748466b50be8cade7bfbad2dec3b81c9c285f9bf2213072a5ea5a1" + "test_construct_paths.py": "2144db1645d973f2df5f8867c515943c5a09e674b34569c016055e576fdf6418" }, "include_deps": { - "README.md": "80504ec987045c156641c781764ebbd33af20da3e8854307158fe1110fe1845b", + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", "context/generate_output_paths_example.py": "0f8a6384ebfe9dc3ea448a4df7b4f5ebf9aa9b714f67a4322437638700a4307f", "context/get_extension_example.py": "def9c34c1416259a0a031a996b96aeff5e73a7012adff6af1cc557e74ba97cc8", "context/get_language_example.py": "8b09af25b96fa49e2dcf5ff307bbeb0bc83e84b64618971d393397772a2fafab", diff --git a/.pdd/meta/content_selector_python.json b/.pdd/meta/content_selector_python.json new file mode 100644 index 0000000000..b5c8e7078d --- /dev/null +++ b/.pdd/meta/content_selector_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.103003+00:00", + "command": "fix", + "prompt_hash": "584ea795707777378846cdcc413bbf1754ee3638d141878a4095c41a791b583b", + "code_hash": "c18d5f9ce0903169445ee17d2e408b88ad91a931e710e783f7c9c7cc3f2ec4af", + "example_hash": "1b71d5d614f81c6543cc3a437ed8fcaa810372b06dc9bf23bde3cd09d9d715e6", + "test_hash": "acfbcbea92e5b943890af0983083d9dca84c7b4c6dd8d0980ec303815f45dbc6", + "test_files": { + "test_content_selector.py": "acfbcbea92e5b943890af0983083d9dca84c7b4c6dd8d0980ec303815f45dbc6" + }, + "include_deps": { + "context/core/errors_example.py": "b50dbf08aec028bd57a8fe74762bf92aee9a1fe953fb3d7db50cc686c72bfee3", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/pytest_slicer.py": "c02a5488fe6f7248ed387636d5824cb2f1876544d3991e43ef253648a36e49a2" + } +} \ No newline at end of file diff --git a/.pdd/meta/context_audit_python.json b/.pdd/meta/context_audit_python.json index 93bade2994..e7bc0d515f 100644 --- a/.pdd/meta/context_audit_python.json +++ b/.pdd/meta/context_audit_python.json @@ -1,6 +1,6 @@ { - "pdd_version": "0.0.270.dev0", - "timestamp": "2026-06-10T23:02:30.507619+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.104389+00:00", "command": "fix", "prompt_hash": "8ddab8adfedeb2b501ae5df3b1989d446c13a72f64ea55668f62b75c605eff6d", "code_hash": "e9a2c33294a9045ed7eb93c4a4c53ae27added9e2b408619d7c5204386893b45", @@ -12,4 +12,4 @@ "include_deps": { "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" } -} +} \ No newline at end of file diff --git a/.pdd/meta/context_generator_main_python.json b/.pdd/meta/context_generator_main_python.json new file mode 100644 index 0000000000..aeaa4746dd --- /dev/null +++ b/.pdd/meta/context_generator_main_python.json @@ -0,0 +1,23 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.107456+00:00", + "command": "fix", + "prompt_hash": "25d5bc7ab8ea0cc23287232060cb5d9d13b5523dcd5c2facaa84ac60d7d8fee1", + "code_hash": "ea5831e4bafe49e056cc2d7e18be7b632f8de5752004bf08e66ebc4c82688c14", + "example_hash": "77c3de786f31392540ec609d4b3942e98e996ae14c4014f5018cf2b05a560df4", + "test_hash": "96a066f4b0dfdc29fb7f28b51edfb0621e61de3167ddd9972fbc41eda92f4d9c", + "test_files": { + "test_context_generator_main.py": "96a066f4b0dfdc29fb7f28b51edfb0621e61de3167ddd9972fbc41eda92f4d9c" + }, + "include_deps": { + "context/auth_service_example.py": "4b6d2c8c9e13d1edc0d9805ee0c07657494cbf31dffe2bb0029b72975674a8cf", + "context/change/15/README.md": "1fc1b1dc45e3f0178c7dc607c7b2ac723fd990d2277e384defd8956221183abe", + "context/change/15/initial_cli.py": "b607c3e67702ad3bc5e61cc40ce85571e4ff09015de96016c8d7d56eb78b8c74", + "context/change/19/context_generator.py": "046c50404d58d06586c6acb481acbd2e20042de7155b5f7eaffab5ebebceda0a", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/context_generator_example.py": "277b54ee71ffc836d9db9ad4d4fc0b3fee365ce6f5691222acad11b58461923b", + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56" + } +} \ No newline at end of file diff --git a/.pdd/meta/context_generator_python.json b/.pdd/meta/context_generator_python.json new file mode 100644 index 0000000000..c479693208 --- /dev/null +++ b/.pdd/meta/context_generator_python.json @@ -0,0 +1,22 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.109357+00:00", + "command": "fix", + "prompt_hash": "3417f94071a8113de24dbc9a3bbb393e85bd04a1b6fedd555ef0a1f39e57f164", + "code_hash": "f972e2e2ce7444b7d3d186ab4540b8cad4f66dd0d97d37df00c58f32a9e3e369", + "example_hash": "277b54ee71ffc836d9db9ad4d4fc0b3fee365ce6f5691222acad11b58461923b", + "test_hash": "a9f7878a97090fd2021fd0f39213f0d1fb8fc8f3207df7e985e2fa4caa91ac76", + "test_files": { + "test_context_generator.py": "a9f7878a97090fd2021fd0f39213f0d1fb8fc8f3207df7e985e2fa4caa91ac76", + "test_context_generator_main.py": "96a066f4b0dfdc29fb7f28b51edfb0621e61de3167ddd9972fbc41eda92f4d9c" + }, + "include_deps": { + "context/continue_generation_example.py": "29c33a967bcdf4fa0037e1c7b0ec699c00027606dbe9d93fa4d1434f9c16c19e", + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/postprocess_example.py": "b98362e6ffb84ae7826dad27d0854e30706f3993266314439859986b32ecd0bd", + "context/preprocess_example.py": "e7082a4a9ef830048ff32ed774a3e74da1cf4662a22115e6bd5543e1b9cdbf43", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/unfinished_prompt_example.py": "5d4d74064e5b845391f035fb8631903ec2f39a54a90713f9c13dc24d2eb73c4f" + } +} \ No newline at end of file diff --git a/.pdd/meta/context_snapshot_python.json b/.pdd/meta/context_snapshot_python.json index b3f00193bd..6acecdf596 100644 --- a/.pdd/meta/context_snapshot_python.json +++ b/.pdd/meta/context_snapshot_python.json @@ -1,12 +1,13 @@ { - "pdd_version": "0.0.266", - "timestamp": "2026-06-09T01:03:49.414629+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.111622+00:00", "command": "test", "prompt_hash": "d0da42dec926cca9da0b6ac499a2764830e30016e9a24f5749677a90fb976b3f", "code_hash": "6aa1ea0d41f73c40b02b72603f6193d4b6e51fdefb57d62f501311365a9ab1b8", "example_hash": "91a9137de063da9671886f64be60bf572b100e5e4f2ca380a4e49d34fb5b988e", "test_hash": "dfc29bc3df0886ab730f2c37cd5e222e09a2ab3552afaa55fc045b0ee5743b36", "test_files": { + "test_context_snapshot.py": "dfc29bc3df0886ab730f2c37cd5e222e09a2ab3552afaa55fc045b0ee5743b36", "test_context_snapshot_policy.py": "123557c02253b3cf88a5f17daddf0ca71b41e7cada231054152afb9530f9a014", "test_context_snapshot_replay.py": "6e492182f15054d5abd48807d984be1ce2d671687d458175a8be6c382a498fc4" }, diff --git a/.pdd/meta/continue_generation_python.json b/.pdd/meta/continue_generation_python.json new file mode 100644 index 0000000000..86957e3975 --- /dev/null +++ b/.pdd/meta/continue_generation_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.113609+00:00", + "command": "fix", + "prompt_hash": "d8542e8a2dc3de4d0ceef1e16793504494e6827f58ded38b4944e6139d0b5a3c", + "code_hash": "900b9e3385dbb947e4358357a58176cb9bc7a81e36bf8ac7cbada9ac6cf67e07", + "example_hash": "29c33a967bcdf4fa0037e1c7b0ec699c00027606dbe9d93fa4d1434f9c16c19e", + "test_hash": "e286fcdc2b4cd6240666d230f2816de7c45f328de66d6802203ca65d2f4598e5", + "test_files": { + "test_continue_generation.py": "e286fcdc2b4cd6240666d230f2816de7c45f328de66d6802203ca65d2f4598e5" + }, + "include_deps": { + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/unfinished_prompt_example.py": "5d4d74064e5b845391f035fb8631903ec2f39a54a90713f9c13dc24d2eb73c4f", + "context/change/17/continue_generation.py": "6d48786ab0ae7ac29cb5852dfca0de6af5363657967f82e99d009ae0a042d97a", + "context/change/18/code_generator.py": "c2df2d5c6852d547b62ac13ba50e6cd63cfce7e86431ee0e73c678660f9ce9e7" + } +} \ No newline at end of file diff --git a/.pdd/meta/contract_check_python.json b/.pdd/meta/contract_check_python.json new file mode 100644 index 0000000000..2de4e1831f --- /dev/null +++ b/.pdd/meta/contract_check_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.114407+00:00", + "command": "fix", + "prompt_hash": "31835ff8ff5462b9781926cf3b31b7c48ffccf56c338241ee55621fc2bfe9781", + "code_hash": "5c168f2c45b6754acce9b7fd50730c1832053df160b5de88f064061333b84f1a", + "example_hash": null, + "test_hash": "53e93d8558d08f62bef9c3ee67aa45d7c0e0a3db142f94766e5afe0e65993f71", + "test_files": { + "test_contract_check.py": "53e93d8558d08f62bef9c3ee67aa45d7c0e0a3db142f94766e5afe0e65993f71" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/contract_ir_python.json b/.pdd/meta/contract_ir_python.json new file mode 100644 index 0000000000..ac1d8d351c --- /dev/null +++ b/.pdd/meta/contract_ir_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.114985+00:00", + "command": "fix", + "prompt_hash": "92eb68b7fdc9a1b3fdb9c4f85ddc7e91d2aa9706c93da9915bd0b246dc792e24", + "code_hash": "0d3392ec8990ac0ff6ddb4a00fee3c6719395f046a59a263ade7d8676e3648e9", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/core_cli_python.json b/.pdd/meta/core_cli_python.json new file mode 100644 index 0000000000..8144fb48a7 --- /dev/null +++ b/.pdd/meta/core_cli_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.116539+00:00", + "command": "fix", + "prompt_hash": "172c853810b5b5005c10461da3014d38eea67f70fef2c3a6312eeac96eae8553", + "code_hash": "fbdda9757fb6a4fd4be14ac670d4d79a3d2fdcc145ba1623fa63f85c1f6d7afa", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/auto_update_example.py": "4fc8ab0da96c0e8abe793d195dbc64b6b9771631be818eecde059769e5914fc9", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/track_cost_example.py": "bd60efaa7d6274b0e4d1743ac05785461af65ec785b254730288d08774f72ed6" + } +} \ No newline at end of file diff --git a/.pdd/meta/core_cloud_python.json b/.pdd/meta/core_cloud_python.json index b95c1a6f18..6758682e3a 100644 --- a/.pdd/meta/core_cloud_python.json +++ b/.pdd/meta/core_cloud_python.json @@ -1,17 +1,19 @@ { - "pdd_version": "0.0.228", - "timestamp": "2026-05-06T03:28:21.404249+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.118154+00:00", "command": "regenerate-public", - "prompt_hash": "49fbb650d8825497c6ae5df53529bc7e1dc7b20d109c8e20f8016ec139c0f91e", - "code_hash": "ba9a9df53edd5b1b17f855992d9144187b5fc76231af8b9a9ac1c6fe66511b73", + "prompt_hash": "6055e3d124f3f4d5af83ec67ece60ee2653e0522f01e215d2b3c6f68dabceba8", + "code_hash": "0487c0b989996af144df3af1c818a6eeafe52fd209710e5a54eb43990c89ae97", "example_hash": "8a14ae9e69dbccfbbfffd9bde1ea11192334a10aaf9d5b5e6dba5ac23ba0dd90", - "test_hash": "4a745fb082f574b002076290ba6efc63d6e6da2e9cb32ed26608eac084acea2a", + "test_hash": "cc7c747045ad3b0e491b00ca993e66ba2c5d1144b8f7b03052df70851fc366d5", "test_files": { - "test_cloud.py": "4a745fb082f574b002076290ba6efc63d6e6da2e9cb32ed26608eac084acea2a" + "test_cloud.py": "cc7c747045ad3b0e491b00ca993e66ba2c5d1144b8f7b03052df70851fc366d5" }, "include_deps": { "context/_keyring_timeout_example.py": "41f83d5ef117caba86b788f69b519f7e88f3d7dc2a641a017d74a7cee9be6ed1", + "context/auth_service_example.py": "4b6d2c8c9e13d1edc0d9805ee0c07657494cbf31dffe2bb0029b72975674a8cf", + "context/core/errors_example.py": "b50dbf08aec028bd57a8fe74762bf92aee9a1fe953fb3d7db50cc686c72bfee3", "context/core/utils_example.py": "ecda9cd9c5910246e0f857eacacac6ec381eb531695e5a56670a9dd8c02c0ef1", - "context/python_preamble.prompt": "57a3e51f529024ec0cb9658cd6ac61a7c8051ba0c8e887b31cf00b2e78a07d83" + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" } } \ No newline at end of file diff --git a/.pdd/meta/core_dump_python.json b/.pdd/meta/core_dump_python.json new file mode 100644 index 0000000000..3b134826a9 --- /dev/null +++ b/.pdd/meta/core_dump_python.json @@ -0,0 +1,14 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.584508+00:00", + "command": "fix", + "prompt_hash": "376be364fe8bbd3891431153b43d76268f643ca91d9f4dfa0d0a4a5efad5fd7b", + "code_hash": "658eb3a08930160339f1e5c73b9093b38ae4289191b4b63c7e92115b33728882", + "example_hash": "0416a935ac490b958ceedfc6e68c7fbd7e383966374ba13ccf7ebfeaf8ad0803", + "test_hash": null, + "test_files": {}, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/core_duplicate_cli_guard_python.json b/.pdd/meta/core_duplicate_cli_guard_python.json new file mode 100644 index 0000000000..3a07864659 --- /dev/null +++ b/.pdd/meta/core_duplicate_cli_guard_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.121318+00:00", + "command": "fix", + "prompt_hash": "f041d7a7d8825a0f61eba4f523cfe32cd71089dfeb4db7831b1b8e99280b7451", + "code_hash": "26ff24c97640c5818897f00cc5648708ffc719d6d7f113d24551662b9d724141", + "example_hash": "947ec40a9a778e302a6d217b16ce7e65df82cda777c3361915cc635ba106de83", + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/__init__example.py": "84208a445d03a336e465709ec0687eb969c8d865e6739bf8b79f2c8a8aad351a", + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", + "context/agentic_test_orchestrator_example.py": "473901f6f62844c81a047fa69387c4116d91a522c62d5c6ad30188e2e99dfaa5", + "context/architecture_registry_example.py": "5eff404eddcfb9d898df972d703013b7124d352e5fe898a1977d7986ef17ed1e", + "context/auth_service_example.py": "4b6d2c8c9e13d1edc0d9805ee0c07657494cbf31dffe2bb0029b72975674a8cf", + "pdd/architecture_registry.py": "5722225846b9cc8189b410a78e5714b4f2916d5748271d27f6f738692429c309" + } +} \ No newline at end of file diff --git a/.pdd/meta/core_errors_python.json b/.pdd/meta/core_errors_python.json new file mode 100644 index 0000000000..12987e25ec --- /dev/null +++ b/.pdd/meta/core_errors_python.json @@ -0,0 +1,14 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.589387+00:00", + "command": "fix", + "prompt_hash": "7a1e0ee32d7d77201cde7e56df13d7a18701a8fc52d6eb7cc7507458a40010e0", + "code_hash": "8d6ab075c9667038bcd5c043ff888f3f531b179d6c1337fe806591a2ee4e708a", + "example_hash": "b50dbf08aec028bd57a8fe74762bf92aee9a1fe953fb3d7db50cc686c72bfee3", + "test_hash": null, + "test_files": {}, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/core_remote_session_python.json b/.pdd/meta/core_remote_session_python.json new file mode 100644 index 0000000000..4c8d6b3d7d --- /dev/null +++ b/.pdd/meta/core_remote_session_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:22:51.513760+00:00", + "command": "fix", + "prompt_hash": "cbfe9e8449a09296501889ea92a9a2cae7b5743592ad2632b139e5423c1bab77", + "code_hash": "578e98f63ccacd49af41b7d17a31460a2eedffd8648e9fcaefc54fc17d0d3ffb", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/core_utils_python.json b/.pdd/meta/core_utils_python.json new file mode 100644 index 0000000000..64a9539727 --- /dev/null +++ b/.pdd/meta/core_utils_python.json @@ -0,0 +1,14 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.592763+00:00", + "command": "fix", + "prompt_hash": "bc14dac8215a3279591bde6eb02b3725f409ecace594f7a1b85c49003be23000", + "code_hash": "4386b3c9efd08ba0510aa80150b579f203fe9dbc32e047712599a34f55537191", + "example_hash": "ecda9cd9c5910246e0f857eacacac6ec381eb531695e5a56670a9dd8c02c0ef1", + "test_hash": null, + "test_files": {}, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/coverage_contracts_python.json b/.pdd/meta/coverage_contracts_python.json new file mode 100644 index 0000000000..fd2d993d15 --- /dev/null +++ b/.pdd/meta/coverage_contracts_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-09T22:54:16.404053+00:00", + "command": "fix", + "prompt_hash": "044980f2b8fe2bd1c0a3c294322b4e8767d9f12f8bb24410ba6e4ed818465dd8", + "code_hash": "dc209452c683697866e133c5673a2caba1828c9e4836e012f277e0250a643c0c", + "example_hash": null, + "test_hash": "b0229cfee890abcf8c9289183022c1e6409ed63df113dfebbaa1f0fffac161ac", + "test_files": { + "test_coverage_contracts.py": "b0229cfee890abcf8c9289183022c1e6409ed63df113dfebbaa1f0fffac161ac" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/crash_main_python.json b/.pdd/meta/crash_main_python.json new file mode 100644 index 0000000000..636feb37b5 --- /dev/null +++ b/.pdd/meta/crash_main_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.127519+00:00", + "command": "fix", + "prompt_hash": "a2b233c48783a94be5b4e746a8267b114a0198702ed0f69e0357fc9b8164508d", + "code_hash": "bab5a25767957060eded0b59efc275da7f76acc173a23ec943c08a36c4eb2764", + "example_hash": "5c9b909107f74f773ccf7013d556a5812643d2a3b086e79fe07da7ea7877458c", + "test_hash": "aaf36bd8c512e712a3f276438fad35b2a2427564a2f1b85f46ab9f17e47f05c1", + "test_files": { + "test_crash_main.py": "aaf36bd8c512e712a3f276438fad35b2a2427564a2f1b85f46ab9f17e47f05c1" + }, + "include_deps": { + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/fix_code_loop_example.py": "c512f0f317474550a9ed74b5211ae0eab62c3f04115db14af67a88033aacfa87", + "context/fix_code_module_errors_example.py": "f35244d6e920711044fee92ade984f3e820a0fbfe766fd3d49718cbcfe793e60", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/detect_change_main_python.json b/.pdd/meta/detect_change_main_python.json new file mode 100644 index 0000000000..f071e53f22 --- /dev/null +++ b/.pdd/meta/detect_change_main_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.600240+00:00", + "command": "fix", + "prompt_hash": "89e195d121090da7aa59c3c52f1fe544799383feed0aab78542248c88f2c7aa6", + "code_hash": "2ef7a1859b4195c37e441c7ec79c00cfdff81f68af80dc277a4ee88b586d923a", + "example_hash": "ddc494709d127398004f53de58bede388692b2839a97b26668c55575369d9edd", + "test_hash": "59bf5094d1970406c4a1d4da3666532ad64bb71b8a15063172bd18626196a0e6", + "test_files": { + "test_detect_change_main.py": "59bf5094d1970406c4a1d4da3666532ad64bb71b8a15063172bd18626196a0e6" + }, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/detect_change_example.py": "44d0c9172db01bd645d48a11a7afa527a7e509f5aac911fa41ed091d3f982096" + } +} \ No newline at end of file diff --git a/.pdd/meta/detect_change_python.json b/.pdd/meta/detect_change_python.json new file mode 100644 index 0000000000..1df4ff88b8 --- /dev/null +++ b/.pdd/meta/detect_change_python.json @@ -0,0 +1,22 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.132053+00:00", + "command": "fix", + "prompt_hash": "74aafd447bc746f4ea46fbd2bce19b9824803689714266c58f6724e31225ae61", + "code_hash": "46a3bf79582e6f88dc13f23183c0a8a871cbcfc4fd7e6ef98fccb49e90ca908c", + "example_hash": "44d0c9172db01bd645d48a11a7afa527a7e509f5aac911fa41ed091d3f982096", + "test_hash": "df46c280fdddde6da3943f8b058dabb79f5d22889f3a338f672b961919f49455", + "test_files": { + "test_detect_change.py": "df46c280fdddde6da3943f8b058dabb79f5d22889f3a338f672b961919f49455", + "test_detect_change_main.py": "59bf5094d1970406c4a1d4da3666532ad64bb71b8a15063172bd18626196a0e6" + }, + "include_deps": { + "context/continue_generation_example.py": "29c33a967bcdf4fa0037e1c7b0ec699c00027606dbe9d93fa4d1434f9c16c19e", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/unfinished_prompt_example.py": "5d4d74064e5b845391f035fb8631903ec2f39a54a90713f9c13dc24d2eb73c4f", + "pdd/llm_invoke.py": "2c423dbe12dd22d1acfca6c658bfef36eaf3b016626ae4a2845576500bfcb54b", + "pdd/postprocess.py": "628880a68346c3c1bd8326b4d1b495452578937ed97b27234ad56ba332c4c2ed", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56" + } +} \ No newline at end of file diff --git a/.pdd/meta/durable_sync_runner_python.json b/.pdd/meta/durable_sync_runner_python.json index cc5f8fafc2..55a5ed19ad 100644 --- a/.pdd/meta/durable_sync_runner_python.json +++ b/.pdd/meta/durable_sync_runner_python.json @@ -1,18 +1,18 @@ { - "pdd_version": "0.0.275.dev3", - "timestamp": "2026-06-16T02:09:40.279393+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.133357+00:00", "command": "fix", - "prompt_hash": "607af0a8fd816de522eed6823ffa1865e52fd97893ea7a1d2c62381f3aaa1681", - "code_hash": "b95c3f7c268f7e91650068af248a25ad89ac9c133673c550cace9e22c4079265", + "prompt_hash": "33674ac16f38437372bccd6d7b6d6b1c720528a4f6f4c358cfb78c34e2441ede", + "code_hash": "9bf0265a38b57f44c5c5f9962d55d6f5e17792b6217cf5749d65c2e123985392", "example_hash": null, - "test_hash": "088d55daf37c57130e44c01d4e13fd2f259e2a83dba229f693aa2653abcd15dd", + "test_hash": "e3daa0813fc26b59eb50dc3f74ea395684a4f171c1342417fb68a190fdbb407e", "test_files": { - "test_durable_sync_runner.py": "088d55daf37c57130e44c01d4e13fd2f259e2a83dba229f693aa2653abcd15dd" + "test_durable_sync_runner.py": "e3daa0813fc26b59eb50dc3f74ea395684a4f171c1342417fb68a190fdbb407e" }, "include_deps": { - "context/agentic_sync_example.py": "9bcc2f199a1463e8c424d8210ea6d5f34ad6fb5fcb1022aa879a9ecba0f8d5d6", + "context/agentic_sync_example.py": "aa9683f1a26841f3cae854e72cce38636f0859d8dd2f7672fa14e412afdef884", "context/agentic_sync_runner_example.py": "fcb36209abb5ab882ad551754fb574d62509c968f8c564e18163be7f2b7ef35a", "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", "context/sync_order_example.py": "b4eb1e167e7604c7dbac703bdcba7287188fa9d2d5ef6d490216d003cdc542fa" } -} +} \ No newline at end of file diff --git a/.pdd/meta/edit_file_python.json b/.pdd/meta/edit_file_python.json new file mode 100644 index 0000000000..08a0d769af --- /dev/null +++ b/.pdd/meta/edit_file_python.json @@ -0,0 +1,11 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.134040+00:00", + "command": "fix", + "prompt_hash": "7617636b78e26e2ff2ab2840a087435689a14dda5e8a5e6304ff75af2c7a6364", + "code_hash": "f85859f8a18a6243db9edd331620e79e8b0f2e1ddb6ca99fb5e9f4228b38fbdd", + "example_hash": "272d1967123c63800bb6afff073cf409edd71e598bcd904fa9852e0a482f87e3", + "test_hash": null, + "test_files": {}, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/embed_retrieve_python.json b/.pdd/meta/embed_retrieve_python.json new file mode 100644 index 0000000000..6de93ef1a4 --- /dev/null +++ b/.pdd/meta/embed_retrieve_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.134972+00:00", + "command": "fix", + "prompt_hash": "555494744ac9cac860b021a4f122449a6a67be1a9388f23a7ac8ca47f0f47603", + "code_hash": "86834b149d6112a05bd2b98f38878c4d967d0667e9722ed7ce43f5217f98729e", + "example_hash": "17920a5c72ada21b31f6df15d9ad156d67ebaf8f141c3839b068d9379360cd84", + "test_hash": "601a45693322f01348ab0958c18a6627bb880e8f0da87acc9ca5d4fbe1984497", + "test_files": { + "test_embed_retrieve.py": "601a45693322f01348ab0958c18a6627bb880e8f0da87acc9ca5d4fbe1984497" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/evidence_manifest_python.json b/.pdd/meta/evidence_manifest_python.json new file mode 100644 index 0000000000..784a1819de --- /dev/null +++ b/.pdd/meta/evidence_manifest_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.136133+00:00", + "command": "fix", + "prompt_hash": "272555690e7df823d17bb5729d7f2ae871b27af13aa4109c533e3c9dbd289792", + "code_hash": "8f468855a9a9f997b73147b37cf937d1594dacf147c7e2ec34565099a9f1e40b", + "example_hash": null, + "test_hash": "ead52731d4af649d05635fbfab7ff9e7007cb1d2f761aa556e51f84f3f425944", + "test_files": { + "test_evidence_manifest.py": "ead52731d4af649d05635fbfab7ff9e7007cb1d2f761aa556e51f84f3f425944" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/evidence_store_python.json b/.pdd/meta/evidence_store_python.json new file mode 100644 index 0000000000..206c505f52 --- /dev/null +++ b/.pdd/meta/evidence_store_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.136720+00:00", + "command": "fix", + "prompt_hash": "5083989f11139a52428b6b58b8f6d9126d4aa75afd54a0af03d3a676ec73dc69", + "code_hash": "eccc499c6f076b52e4b481a07904b9390120e52c49cb2479adbdf5eb433df14c", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/extracts_prune_python.json b/.pdd/meta/extracts_prune_python.json new file mode 100644 index 0000000000..8d203c94d4 --- /dev/null +++ b/.pdd/meta/extracts_prune_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.138380+00:00", + "command": "fix", + "prompt_hash": "1c1743a61d7690b35115be3fbabbaa5bc55e1268b74efa7b0af5ce782f29c571", + "code_hash": "0bc2ebd73806ce23453c8b97a763891a8c045c3d9214edd0a719b8fc328c0a06", + "example_hash": "b8a894f24d715f42be14b9977b6c091e75401e3f46f0722703fa63b61b26a7ee", + "test_hash": "9be47ef7359ee64d009cc03612f1e196a35d535a815e29012193eacad5aa52df", + "test_files": { + "test_extracts_prune.py": "9be47ef7359ee64d009cc03612f1e196a35d535a815e29012193eacad5aa52df" + }, + "include_deps": { + "context/__init__example.py": "84208a445d03a336e465709ec0687eb969c8d865e6739bf8b79f2c8a8aad351a", + "context/include_query_extractor_example.py": "83c8b945de75bd49b258f4feabbdda3d9c08a06ad9afca3e90df7534f68a34aa", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/failure_classification_python.json b/.pdd/meta/failure_classification_python.json index 4d38774775..a8d1ccf670 100644 --- a/.pdd/meta/failure_classification_python.json +++ b/.pdd/meta/failure_classification_python.json @@ -1,6 +1,6 @@ { - "pdd_version": "0.0.274", - "timestamp": "2026-06-16T01:06:08.613980+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.139182+00:00", "command": "fix", "prompt_hash": "f4a1ca1b51cb57f7c8513109c6f17e181045166075daf11d973d31cc495af816", "code_hash": "0675857d23f52e447f15ace22ec37c2f003a61d3c810254c8547129c29d697ff", diff --git a/.pdd/meta/find_section_python.json b/.pdd/meta/find_section_python.json new file mode 100644 index 0000000000..bbe5e23fe0 --- /dev/null +++ b/.pdd/meta/find_section_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.139985+00:00", + "command": "fix", + "prompt_hash": "30b22c834c4a659be69878bef0d3cea6f7cf75c93e6dc9f3e585a7a4cbb564b5", + "code_hash": "973fc53d8e0a0c24401a52f5a565598aeb5436fec4e0ab0314afb2983580fc47", + "example_hash": "681043403aa02b9eb0d265a95560fce565507d0656d1693f79650f8eb5f0683a", + "test_hash": "916c5c0598cf1687a3d83aa6abacea5c53efe278df211d834e2f731097b979e7", + "test_files": { + "test_find_section.py": "916c5c0598cf1687a3d83aa6abacea5c53efe278df211d834e2f731097b979e7" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/firecrawl_cache_python.json b/.pdd/meta/firecrawl_cache_python.json new file mode 100644 index 0000000000..84f9140c46 --- /dev/null +++ b/.pdd/meta/firecrawl_cache_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.140944+00:00", + "command": "fix", + "prompt_hash": "1b8c25255fc1387765e986b0775379102e71c71c7e0b90076c65822992162894", + "code_hash": "4b5ffcf7246451bf1c60f86594dda44e4eeeb89778a56e0a400ba35dd17e1677", + "example_hash": null, + "test_hash": "3bfaeaed94c1f594f11291f7cdf837e0d6abc96c97b85e3d62dee88dc439f5cb", + "test_files": { + "test_firecrawl_cache.py": "3bfaeaed94c1f594f11291f7cdf837e0d6abc96c97b85e3d62dee88dc439f5cb" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/fix_code_loop_python.json b/.pdd/meta/fix_code_loop_python.json index 9bac79604f..c6e5b074ca 100644 --- a/.pdd/meta/fix_code_loop_python.json +++ b/.pdd/meta/fix_code_loop_python.json @@ -1,10 +1,10 @@ { - "pdd_version": "0.0.228", - "timestamp": "2026-05-06T03:28:21.485952+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.142901+00:00", "command": "regenerate-public", - "prompt_hash": "947f0f0d054800b620cc2174bf96a8658fa4687190e9e4e50f23e39d1f91c1d0", + "prompt_hash": "6a32dcc711f1e24de6b23d4a498bd00b1d41ab058d4fd64dfc3b76ea42d8dc94", "code_hash": "3bff0fb82111513ed5aa32c33bd80578c7178e871f818be1b01635b7a2b185b1", - "example_hash": null, + "example_hash": "c512f0f317474550a9ed74b5211ae0eab62c3f04115db14af67a88033aacfa87", "test_hash": "bc1f4f81af211623bcb2539cc4761f7e5ccb6926aebf86cc6aefa3b152557400", "test_files": { "test_fix_code_loop.py": "bc1f4f81af211623bcb2539cc4761f7e5ccb6926aebf86cc6aefa3b152557400" @@ -14,6 +14,6 @@ "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", "context/fix_code_module_errors_example.py": "f35244d6e920711044fee92ade984f3e820a0fbfe766fd3d49718cbcfe793e60", "context/get_language_example.py": "8b09af25b96fa49e2dcf5ff307bbeb0bc83e84b64618971d393397772a2fafab", - "context/python_preamble.prompt": "57a3e51f529024ec0cb9658cd6ac61a7c8051ba0c8e887b31cf00b2e78a07d83" + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" } } \ No newline at end of file diff --git a/.pdd/meta/fix_code_module_errors_python.json b/.pdd/meta/fix_code_module_errors_python.json new file mode 100644 index 0000000000..c33e1fd28e --- /dev/null +++ b/.pdd/meta/fix_code_module_errors_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.144040+00:00", + "command": "fix", + "prompt_hash": "67f31fdf9d727abafe85503ac9aacc3043e1373bd87893ee97d91bbe7fcf3807", + "code_hash": "cdb5b1681cc0629f6bd28c19be5378e9a005280d606723a366a5fabc2133ed89", + "example_hash": "f35244d6e920711044fee92ade984f3e820a0fbfe766fd3d49718cbcfe793e60", + "test_hash": "6af8d3af08f9de9717e0c1f2abd2c0c0013da80fb22b9bcf8198b5ee0aae4ee0", + "test_files": { + "test_fix_code_module_errors.py": "6af8d3af08f9de9717e0c1f2abd2c0c0013da80fb22b9bcf8198b5ee0aae4ee0" + }, + "include_deps": { + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/fix_error_loop_python.json b/.pdd/meta/fix_error_loop_python.json index 1cd791ee0e..acf9b88363 100644 --- a/.pdd/meta/fix_error_loop_python.json +++ b/.pdd/meta/fix_error_loop_python.json @@ -1,11 +1,28 @@ { - "pdd_version": "0.0.257", - "timestamp": "2026-06-02T05:32:50.786435+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.147757+00:00", "command": "example", - "prompt_hash": null, - "code_hash": null, - "example_hash": null, - "test_hash": null, - "test_files": null, - "include_deps": null + "prompt_hash": "ae052bd412593cd6bf368c6aa753852f19b7698162713431ca0e054e0b7d9c3f", + "code_hash": "8454d9d818cbc14e80f15b8d79fd790279bcdfb31bbb68b84a0611b99396c0c6", + "example_hash": "a86435e82357bc671ac6dadee6df22c163d27a30cbdba102e9cbe3bc3121fc43", + "test_hash": "6190aeff914edf5b7929874866480d44a2805956a2917afaa1fd14975c5606ae", + "test_files": { + "test_fix_error_loop.py": "6190aeff914edf5b7929874866480d44a2805956a2917afaa1fd14975c5606ae", + "test_fix_error_loop_failure_aware.py": "07cc0a1fa6055d0e27b0a01dd73883e215cb7e5c329bbe870cf97c13e3d40e1b", + "test_fix_error_loop_prompt_contract.py": "57f50dae4378256814c37cc875032e829288f9fb34687aa53b68dea39175cbca", + "test_fix_error_loop_sync_e2e.py": "a838021d92b7b0e3d2042957c82542aa8aabbb2ddc9c67d1f601817bfc397e9e" + }, + "include_deps": { + "context/agentic_fix_example.py": "407a024fcba9402e00b401477725ae72b1de718ab59bea29b69509a0b15f621f", + "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", + "context/core/cloud_example.py": "8a14ae9e69dbccfbbfffd9bde1ea11192334a10aaf9d5b5e6dba5ac23ba0dd90", + "context/fix_errors_from_unit_tests_example.py": "df6c93b3ae585af13827cf3edb398e7c0da75648b34ff0477044b659df85a117", + "context/get_language_example.py": "8b09af25b96fa49e2dcf5ff307bbeb0bc83e84b64618971d393397772a2fafab", + "context/pytest_example.py": "f13c02d911d386ac5bab8e3d4aae64a768e4a7e960803c80fcac14e9ba5975cb", + "context/python_env_detector_example.py": "65619b3e8bad0509f1ccf54d81b5c3760f423b6fc1925c932fc7968da78a172e", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/failure_classification.py": "0675857d23f52e447f15ace22ec37c2f003a61d3c810254c8547129c29d697ff", + "pdd/fix_focus.py": "e341e6bda871ddc5ba92cf05e9d554d49bc5e4afebf5b4f1482971edf557063d", + "pdd/test_context_packer.py": "27c7d14747d38df8edf24c8ef2ce1b7ca21d8f2eea38aec02ba06042ee144c0b" + } } \ No newline at end of file diff --git a/.pdd/meta/fix_errors_from_unit_tests_python.json b/.pdd/meta/fix_errors_from_unit_tests_python.json new file mode 100644 index 0000000000..e734ccd066 --- /dev/null +++ b/.pdd/meta/fix_errors_from_unit_tests_python.json @@ -0,0 +1,21 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.150911+00:00", + "command": "fix", + "prompt_hash": "5dd9132474b83ac80334bfeefdfc55478d506da9d95acd54ddbbca97c00995a6", + "code_hash": "7ad19b3ca4b61406c1da5dcd86783a5b1aa183f9ecea231979675de6f3924404", + "example_hash": "df6c93b3ae585af13827cf3edb398e7c0da75648b34ff0477044b659df85a117", + "test_hash": "cbf129e3ab3a9f6731b197ec4641264a0cd68238123fa626dfd55b87fd20ca09", + "test_files": { + "test_fix_errors_from_unit_tests.py": "cbf129e3ab3a9f6731b197ec4641264a0cd68238123fa626dfd55b87fd20ca09" + }, + "include_deps": { + "context/change/10/initial_fix_errors_from_unit_tests.py": "7a3eb969bcaa76dc85d59c4013a41a7da5bb7472daf97df2b2cad0475642d2c7", + "context/change/16/fix_error_loop.py": "01872cfcc6c3f5095551acd109e06b35ece96c15ca75dcf40ac395c3ed867e40", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/edit_file.py": "f85859f8a18a6243db9edd331620e79e8b0f2e1ddb6ca99fb5e9f4228b38fbdd", + "pdd/llm_invoke.py": "2c423dbe12dd22d1acfca6c658bfef36eaf3b016626ae4a2845576500bfcb54b", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56" + } +} \ No newline at end of file diff --git a/.pdd/meta/fix_focus_python.json b/.pdd/meta/fix_focus_python.json new file mode 100644 index 0000000000..668a8af8ce --- /dev/null +++ b/.pdd/meta/fix_focus_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.152694+00:00", + "command": "fix", + "prompt_hash": "5495f47b8ca32f768f25b4864a4a6bd59daae7ac21544c4ae75363e0ca6d2ee4", + "code_hash": "e341e6bda871ddc5ba92cf05e9d554d49bc5e4afebf5b4f1482971edf557063d", + "example_hash": null, + "test_hash": "3c43a5554ef2810db0466cc388f4d6a144399005613422787997777e99ea4270", + "test_files": { + "test_fix_focus.py": "3c43a5554ef2810db0466cc388f4d6a144399005613422787997777e99ea4270" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/prompts/llm_invoke_python.prompt": "88face96e298219fba7448186eb71f1586a676888a827a04d326882df8e4f41e" + } +} \ No newline at end of file diff --git a/.pdd/meta/fix_main_python.json b/.pdd/meta/fix_main_python.json new file mode 100644 index 0000000000..f61eb865f1 --- /dev/null +++ b/.pdd/meta/fix_main_python.json @@ -0,0 +1,23 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.155692+00:00", + "command": "fix", + "prompt_hash": "aa3e9b550afbcdb9e95e2c92fddf04111709b422649cb3266e728fa8b5a38652", + "code_hash": "3f67d418500e2ec32d016c3ec50bce459e8fa05e75711b46d2a38e683347a60f", + "example_hash": "29f88d8bfcc2256ebebe6efcb611f9487eb5ca6d8781286a6dd98689f92bca7a", + "test_hash": "6a19d93c9375efa0972129a4642f5e84663dc939822bffe1543cfeda87741789", + "test_files": { + "test_fix_main.py": "6a19d93c9375efa0972129a4642f5e84663dc939822bffe1543cfeda87741789", + "test_fix_main_issue_232.py": "f33bb063df3cb570b5b46e3361cf3339be65de7fdabe249d8ba0f15893ba2207" + }, + "include_deps": { + "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", + "context/auth_service_example.py": "4b6d2c8c9e13d1edc0d9805ee0c07657494cbf31dffe2bb0029b72975674a8cf", + "context/change/10/initial_fix_errors_from_unit_tests.py": "7a3eb969bcaa76dc85d59c4013a41a7da5bb7472daf97df2b2cad0475642d2c7", + "context/change/15/initial_cli.py": "b607c3e67702ad3bc5e61cc40ce85571e4ff09015de96016c8d7d56eb78b8c74", + "context/change/16/fix_error_loop.py": "01872cfcc6c3f5095551acd109e06b35ece96c15ca75dcf40ac395c3ed867e40", + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/fix_focus.py": "e341e6bda871ddc5ba92cf05e9d554d49bc5e4afebf5b4f1482971edf557063d" + } +} \ No newline at end of file diff --git a/.pdd/meta/fix_verification_errors_loop_python.json b/.pdd/meta/fix_verification_errors_loop_python.json new file mode 100644 index 0000000000..16b3189713 --- /dev/null +++ b/.pdd/meta/fix_verification_errors_loop_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.158000+00:00", + "command": "fix", + "prompt_hash": "3afa1fd7d8e92ad9a2d6a5f20468ade73a9ea077adb8a01fb2dba1583cde6086", + "code_hash": "ea1649f2cafd6e7eb7db3f19489d7ac0bcc96137b9b73291b6720ba4fb38341f", + "example_hash": "0212e6010a7c163c476c3df095f6ca99414829f73df34c60651afae83633d307", + "test_hash": "ce41af1918e9b7c68ebfeb8d478eb2931fa3f8b809b05443952c8ce4f376010c", + "test_files": { + "test_fix_verification_errors_loop.py": "ce41af1918e9b7c68ebfeb8d478eb2931fa3f8b809b05443952c8ce4f376010c" + }, + "include_deps": { + "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", + "context/agentic_verify_example.py": "1a64161baccfc035ffbd73bf99cc1888d28d8d714a49deb01a75918e3b9732f8", + "context/fix_verification_errors_example.py": "087d89544db864fe4c8025124ce2fd3fdf9f621c988bd415ba9a0ba942d2f497", + "context/get_language_example.py": "8b09af25b96fa49e2dcf5ff307bbeb0bc83e84b64618971d393397772a2fafab", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/fix_verification_errors_python.json b/.pdd/meta/fix_verification_errors_python.json new file mode 100644 index 0000000000..8e1b54e2d7 --- /dev/null +++ b/.pdd/meta/fix_verification_errors_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.159427+00:00", + "command": "fix", + "prompt_hash": "65f5c3f6032927203ab5722359e8b1c0ab326708dde9483408fc1c77fd85a683", + "code_hash": "5abede2b80b05cb859c83d41618c1e5bebddabfecddf7501042a9f09526fcefa", + "example_hash": "087d89544db864fe4c8025124ce2fd3fdf9f621c988bd415ba9a0ba942d2f497", + "test_hash": "3af44c3e8d2eb7e3635f935450832a6ae733dc5577c7e7d2fc1dcfc1eb673d45", + "test_files": { + "test_fix_verification_errors.py": "3af44c3e8d2eb7e3635f935450832a6ae733dc5577c7e7d2fc1dcfc1eb673d45", + "test_fix_verification_errors_loop.py": "ce41af1918e9b7c68ebfeb8d478eb2931fa3f8b809b05443952c8ce4f376010c" + }, + "include_deps": { + "context/edit_file_example.py": "272d1967123c63800bb6afff073cf409edd71e598bcd904fa9852e0a482f87e3", + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/fix_verification_main_python.json b/.pdd/meta/fix_verification_main_python.json new file mode 100644 index 0000000000..2a7bbe26fa --- /dev/null +++ b/.pdd/meta/fix_verification_main_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.161842+00:00", + "command": "fix", + "prompt_hash": "d6577292faee7ad7513ead07332c24f2a836d0b1bb8793064004d7549e240d41", + "code_hash": "42b18d666938c41e9ee58b08cfec349de715da44ace4f5e2987bc9d725f4a089", + "example_hash": "6490f398aca6df47b1a95e46a4ecfc0aabe299b5022de5c31ebc436e1bc3e459", + "test_hash": "e67998c377e5f50222458fa6da9eec72a9873f4b5181af641963b45632ded190", + "test_files": { + "test_fix_verification_main.py": "e67998c377e5f50222458fa6da9eec72a9873f4b5181af641963b45632ded190" + }, + "include_deps": { + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/fix_verification_errors_example.py": "087d89544db864fe4c8025124ce2fd3fdf9f621c988bd415ba9a0ba942d2f497", + "context/fix_verification_errors_loop_example.py": "0212e6010a7c163c476c3df095f6ca99414829f73df34c60651afae83633d307", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/gate_policy_python.json b/.pdd/meta/gate_policy_python.json new file mode 100644 index 0000000000..5c981414ee --- /dev/null +++ b/.pdd/meta/gate_policy_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.162628+00:00", + "command": "fix", + "prompt_hash": "d489b706b3b826e8e18bba5d945a97aaa8abca83728afffcd91bd9a51b83da40", + "code_hash": "110ad068b2e5f1d4c0aa0e8ace3c8da8acb05d48f3116b6229459ed258d69667", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/generate_model_catalog_python.json b/.pdd/meta/generate_model_catalog_python.json new file mode 100644 index 0000000000..64e9ec9079 --- /dev/null +++ b/.pdd/meta/generate_model_catalog_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.164053+00:00", + "command": "fix", + "prompt_hash": "1e0ffc1fb8e8172bb396b8050c67bfbf750e28bd4191ffb63f7d664d0530827e", + "code_hash": "2c3ede9cb5b9ab319af0b7c950309a1fc0cb620a0555cef6b427b004fce1a353", + "example_hash": "44157c26beba999e90db7480ce87f26060f25cbf6cdf06643f5af9d5f8001acc", + "test_hash": "4232fde1a3d6080889f39d71389beeab6143f83732b1b510eb6a1d916eef11b9", + "test_files": { + "test_generate_model_catalog.py": "4232fde1a3d6080889f39d71389beeab6143f83732b1b510eb6a1d916eef11b9" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/generate_output_paths_python.json b/.pdd/meta/generate_output_paths_python.json new file mode 100644 index 0000000000..7dcd20362d --- /dev/null +++ b/.pdd/meta/generate_output_paths_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.639873+00:00", + "command": "fix", + "prompt_hash": "d0b48ec878fad98d3e05c5e3df796cd6b319146c996e7d57ceaa75a8fcabf92b", + "code_hash": "13890fe1983e3554b58f67404ce3848b3e9e329c03cf661446ad9ef62a71b43e", + "example_hash": "0f8a6384ebfe9dc3ea448a4df7b4f5ebf9aa9b714f67a4322437638700a4307f", + "test_hash": "c0ed9c8db22f119f3849ee3e00d8bac35b8d67fc78dfcccc5d6fefc78d1f081a", + "test_files": { + "test_generate_output_paths.py": "c0ed9c8db22f119f3849ee3e00d8bac35b8d67fc78dfcccc5d6fefc78d1f081a", + "test_generate_output_paths_regression.py": "3763968e8dc0e661e6516520f9282776ae81f82d5207633926ccee6fd60d50ba" + }, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/auto_deps_main_example.py": "9b9c36c18d39494b6e31b46f6e4758c25135b6fa3d1672fbb7365844f7d2de04", + "context/change/15/initial_cli.py": "b607c3e67702ad3bc5e61cc40ce85571e4ff09015de96016c8d7d56eb78b8c74" + } +} \ No newline at end of file diff --git a/.pdd/meta/generate_test_python.json b/.pdd/meta/generate_test_python.json new file mode 100644 index 0000000000..0922f06913 --- /dev/null +++ b/.pdd/meta/generate_test_python.json @@ -0,0 +1,26 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.169002+00:00", + "command": "fix", + "prompt_hash": "c77e5b4722100e72acb3addac1f9f2cd4e87cecc314f9ff4e984314b3d21eff6", + "code_hash": "c34164ec5abded5fbc0e600f3ad007fc6175a29d0662dded3d57edbae9e3fb17", + "example_hash": "f73533a9cf63f2e2f4818ee923a8500e612d155e230017dbdb35aec945e41998", + "test_hash": "f5f7fcc72e1cb4e7af0434ecd2dc423a27cdc71c769dd74b122582517fd66722", + "test_files": { + "test_generate_test.py": "f5f7fcc72e1cb4e7af0434ecd2dc423a27cdc71c769dd74b122582517fd66722", + "test_generate_test_llm_preprocess.py": "bd9e1c8d5c9e68cd788f0e073a8064c3957de7d427e13453fa6ce7aff2853130" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/bug_to_unit_test_example.py": "ab1d665923937f6599f78c0cac82f5d7c04a7afa092e7767d67e417e067a8817", + "context/change/12/initial_postprocess.py": "845d5ada84654630518eda1f06e372bd2af87a230077ce8078efc543b9286714", + "context/change/17/continue_generation.py": "6d48786ab0ae7ac29cb5852dfca0de6af5363657967f82e99d009ae0a042d97a", + "context/change/18/code_generator.py": "c2df2d5c6852d547b62ac13ba50e6cd63cfce7e86431ee0e73c678660f9ce9e7", + "context/continue_generation_example.py": "29c33a967bcdf4fa0037e1c7b0ec699c00027606dbe9d93fa4d1434f9c16c19e", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/unfinished_prompt_example.py": "5d4d74064e5b845391f035fb8631903ec2f39a54a90713f9c13dc24d2eb73c4f", + "pdd/llm_invoke.py": "2c423dbe12dd22d1acfca6c658bfef36eaf3b016626ae4a2845576500bfcb54b", + "pdd/postprocess.py": "628880a68346c3c1bd8326b4d1b495452578937ed97b27234ad56ba332c4c2ed", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56" + } +} \ No newline at end of file diff --git a/.pdd/meta/generation_completion_python.json b/.pdd/meta/generation_completion_python.json new file mode 100644 index 0000000000..d38d42db90 --- /dev/null +++ b/.pdd/meta/generation_completion_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.169791+00:00", + "command": "fix", + "prompt_hash": "dde7e344d8f08e298359f9ef72e7b38c0badc58c47d5d1162d51a60601808fcf", + "code_hash": "8313db548a81def2265688d325e532fd1a44ac1f45c1cf65f534b15887b012b9", + "example_hash": "84f4a9f8ef36a750f6c15b4be89673d4ef6c9af5c81972173cac90f1f9876ae4", + "test_hash": "1d77ed3aa9c0d54a4d367098e24a765fb456122c9e1a681594290a00a64e912c", + "test_files": { + "test_generation_completion.py": "1d77ed3aa9c0d54a4d367098e24a765fb456122c9e1a681594290a00a64e912c" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/get_comment_python.json b/.pdd/meta/get_comment_python.json index bfc420c74b..1862691444 100644 --- a/.pdd/meta/get_comment_python.json +++ b/.pdd/meta/get_comment_python.json @@ -1,10 +1,10 @@ { - "pdd_version": "0.0.228", - "timestamp": "2026-05-06T03:28:21.545972+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.170844+00:00", "command": "regenerate-public", "prompt_hash": "8ed03ff72edaea977592d8c37140fddb51fe19866589dfd9d2b5d0fcdbde5420", "code_hash": "53e70fe3e4f7ba637f7c754d95a751e068754eb06d265b1e95636349c118a883", - "example_hash": null, + "example_hash": "3293b4631db7c783d87745abf36a79996d5c182b74e8bee885a2e522550db851", "test_hash": "8bdc2cf2b963f5277960f1703ea4d90780923bfb2e37aec38322324dd3870151", "test_files": { "test_get_comment.py": "8bdc2cf2b963f5277960f1703ea4d90780923bfb2e37aec38322324dd3870151" diff --git a/.pdd/meta/get_extension_python.json b/.pdd/meta/get_extension_python.json new file mode 100644 index 0000000000..c4a9e9b0d8 --- /dev/null +++ b/.pdd/meta/get_extension_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.172112+00:00", + "command": "fix", + "prompt_hash": "8cc4693dd0372bc8971b7e4902618a975fca012d410d403cb36b174ee2dea8c1", + "code_hash": "4f9ab4599944c8faf14fb060f83a0b6745d1d096b357de34b21c3b34a84480d7", + "example_hash": "def9c34c1416259a0a031a996b96aeff5e73a7012adff6af1cc557e74ba97cc8", + "test_hash": "80bd7a246b278b7b59bf0847c4ac9e28e688ce9deb1b467b1261865c3f0613e7", + "test_files": { + "test_get_extension.py": "80bd7a246b278b7b59bf0847c4ac9e28e688ce9deb1b467b1261865c3f0613e7" + }, + "include_deps": { + "context/path_resolution_example.py": "15ba84fd11c61af8fa12dcb19d63d42f0cb09b747542bf20e69650b8ff4ac137", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/get_jwt_token_python.json b/.pdd/meta/get_jwt_token_python.json new file mode 100644 index 0000000000..e4fa1f748d --- /dev/null +++ b/.pdd/meta/get_jwt_token_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.173421+00:00", + "command": "fix", + "prompt_hash": "7a311e6892810f02d38a2482ee1f42cf0f5f575a394f0de2518baf4a3c45c304", + "code_hash": "93f690a01f9945f8c7434b48c52e3548764275b99c933e8952c7bf5f0a3b4316", + "example_hash": "76a87c8257cb1f4cfdc2edff6225c752432afb3a5e352fcb016d08d8eae16e01", + "test_hash": "2a3e12d31e50900171f486e4e0aafb35784a923ca240a1b6b61d4ffddc8127c0", + "test_files": { + "test_get_jwt_token.py": "2a3e12d31e50900171f486e4e0aafb35784a923ca240a1b6b61d4ffddc8127c0" + }, + "include_deps": { + "context/_keyring_timeout_example.py": "41f83d5ef117caba86b788f69b519f7e88f3d7dc2a641a017d74a7cee9be6ed1", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/get_language_python.json b/.pdd/meta/get_language_python.json new file mode 100644 index 0000000000..0db0358392 --- /dev/null +++ b/.pdd/meta/get_language_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.174260+00:00", + "command": "fix", + "prompt_hash": "342a9c09594784c2199ab87d52dd03e6d8b27a220c219d121b08b28efaf8c444", + "code_hash": "65004add40c93365c1cbd705a6ef24d5bda6f7016c3a67749b25e61122c50ce7", + "example_hash": "8b09af25b96fa49e2dcf5ff307bbeb0bc83e84b64618971d393397772a2fafab", + "test_hash": "4c358e4be326c279add6c33d30c83250b7936ad26d31eda0d895715c27fadd55", + "test_files": { + "test_get_language.py": "4c358e4be326c279add6c33d30c83250b7936ad26d31eda0d895715c27fadd55" + }, + "include_deps": { + "context/path_resolution_example.py": "15ba84fd11c61af8fa12dcb19d63d42f0cb09b747542bf20e69650b8ff4ac137" + } +} \ No newline at end of file diff --git a/.pdd/meta/get_lint_commands_python.json b/.pdd/meta/get_lint_commands_python.json new file mode 100644 index 0000000000..38dd314e8d --- /dev/null +++ b/.pdd/meta/get_lint_commands_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.175472+00:00", + "command": "fix", + "prompt_hash": "099f72e088fc18213c08338f23a33750ac07da246ee38b2449fe7875c46dc1ce", + "code_hash": "3f48c9f1d209d09754f53f07c6196288ae8ff0e239e8020f4b370ddf36285f45", + "example_hash": null, + "test_hash": "68fc0f98f587c85efd0f3da10f6af45c82d0eef8d0830fcf91cde89af27f24ec", + "test_files": { + "test_get_lint_commands.py": "68fc0f98f587c85efd0f3da10f6af45c82d0eef8d0830fcf91cde89af27f24ec" + }, + "include_deps": { + "context/get_test_command_example.py": "33b16476536f8c9424d3222ffc4a5cd5ecee6ad0589b2c66a4f3c7f1e265106c", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/get_run_command_python.json b/.pdd/meta/get_run_command_python.json new file mode 100644 index 0000000000..307ccffb5a --- /dev/null +++ b/.pdd/meta/get_run_command_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.176379+00:00", + "command": "fix", + "prompt_hash": "8ca93563bceca5fb9462c5cee01eb969f1c56d6bd162206a10ff0d953fd6cb78", + "code_hash": "5eec778a234abc4ace4166d71a68f6e49f1b81491962867de81f61be882a5319", + "example_hash": "4d187e844adb255c2738274556f4828728d0d73d84ebf8bda885d51d4be9414f", + "test_hash": "14eec1ffc3bac502fa314c8b9a3dcd63e99066fc1583db9c76c0c2bddb03020c", + "test_files": { + "test_get_run_command.py": "14eec1ffc3bac502fa314c8b9a3dcd63e99066fc1583db9c76c0c2bddb03020c" + }, + "include_deps": { + "context/path_resolution_example.py": "15ba84fd11c61af8fa12dcb19d63d42f0cb09b747542bf20e69650b8ff4ac137", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/get_test_command_python.json b/.pdd/meta/get_test_command_python.json index 3bab6eeb9f..a0cd00661c 100644 --- a/.pdd/meta/get_test_command_python.json +++ b/.pdd/meta/get_test_command_python.json @@ -1,15 +1,16 @@ { - "pdd_version": "0.0.228", - "timestamp": "2026-05-06T03:28:21.593685+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.177735+00:00", "command": "regenerate-public", - "prompt_hash": "a8e7effb43b26e42006d3d2febfc38e0da7c1d53221ac4068d89b6d0c08ecfb5", + "prompt_hash": "ba7e479e5f8a3344d1e5f1694701fb6158e7ee8193b26576a5b459334e8bc683", "code_hash": "a1e871896a289049c73df4071ec48fc47792ff6cab05332816ba702cd3bc1cb6", - "example_hash": null, - "test_hash": "5d759cb5c4491420c4ee01d6973f6d0493d02cfe1cdb0bfaf8cb540a868bc365", + "example_hash": "33b16476536f8c9424d3222ffc4a5cd5ecee6ad0589b2c66a4f3c7f1e265106c", + "test_hash": "116bb2f5a7c5ae9e0715801f27749c4263554168051066635a0f66e7c95409bb", "test_files": { - "test_get_test_command.py": "5d759cb5c4491420c4ee01d6973f6d0493d02cfe1cdb0bfaf8cb540a868bc365" + "test_get_test_command.py": "116bb2f5a7c5ae9e0715801f27749c4263554168051066635a0f66e7c95409bb" }, "include_deps": { + "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", "pdd/agentic_langtest.py": "7cb0be38d526d0a630500185427a9f76666d2b79385f7333a7608eb1fb33cfc9", "pdd/data/language_format.csv": "db3542c10a4c5c2a4a9bbe02558b4bd02fbb357fb243e33de4831d337484373e", "pdd/get_language.py": "65004add40c93365c1cbd705a6ef24d5bda6f7016c3a67749b25e61122c50ce7" diff --git a/.pdd/meta/git_porcelain_python.json b/.pdd/meta/git_porcelain_python.json new file mode 100644 index 0000000000..2a3a09396f --- /dev/null +++ b/.pdd/meta/git_porcelain_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.178530+00:00", + "command": "fix", + "prompt_hash": "2c95a26de29ea34f2c2f0386acc109026889e316b12e7573fcdfce342907459a", + "code_hash": "b10bbce7f1bb04079472bf76c36a4aef208bdf497a8922af224a69cb16717814", + "example_hash": null, + "test_hash": "0b07fd3d4859a21b88df03f4145dca9026de9f6bc83915340ae03b3518c31d0c", + "test_files": { + "test_git_porcelain.py": "0b07fd3d4859a21b88df03f4145dca9026de9f6bc83915340ae03b3518c31d0c" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/git_update_python.json b/.pdd/meta/git_update_python.json index 9db3f4fb85..4a7b1c1744 100644 --- a/.pdd/meta/git_update_python.json +++ b/.pdd/meta/git_update_python.json @@ -1,17 +1,17 @@ { - "pdd_version": "0.0.228", - "timestamp": "2026-05-06T03:28:21.639062+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.179677+00:00", "command": "regenerate-public", - "prompt_hash": "e72e5e77e584584087eab387348fde20d1e8bbbb84a9be9dfac78a5f027141e2", + "prompt_hash": "0aca4d865e12f462ee930a445fbd8d376bde2efa0ba9a5a6b57877d80f91f714", "code_hash": "ca810925f80b85596c0f1f84b4a7c052ff19a8530d24ed6989730467df2a3f17", - "example_hash": null, + "example_hash": "811304cee2cb890879ee68a816aed49dff2e7b50cfdca3125e16e0ae99819574", "test_hash": "5522f599ed1b3734d7e8097160813671f9ed10e7224fec8f46799a8389ff6ba8", "test_files": { "test_git_update.py": "5522f599ed1b3734d7e8097160813671f9ed10e7224fec8f46799a8389ff6ba8" }, "include_deps": { - "context/agentic_update_example.py": "dafed5c5980cce43f16f6828ef45f76e3fdd4ee8a41d21e53cea846b2b6dca33", - "context/python_preamble.prompt": "57a3e51f529024ec0cb9658cd6ac61a7c8051ba0c8e887b31cf00b2e78a07d83", + "context/agentic_update_example.py": "967254cd1c636ac5ba09afa0446312ffc1f28d3041ed453a4c6588c931f722c4", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", "context/update_prompt_example.py": "56b9d8c7aa823c2996a3dc5741a47844e223bfd088366d74e48b6a099b5a11c4" } } \ No newline at end of file diff --git a/.pdd/meta/grounding_policy_python.json b/.pdd/meta/grounding_policy_python.json new file mode 100644 index 0000000000..5a4c49661d --- /dev/null +++ b/.pdd/meta/grounding_policy_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.180459+00:00", + "command": "fix", + "prompt_hash": "e9ff88e848dcdcd473657d892478bacf4ab76c09f39378cb6a3d3a43578db91d", + "code_hash": "76dfda96180a84f8efaa3836ed4a5b4fccaf16f69d12c72006d4ec89529e8cca", + "example_hash": null, + "test_hash": "43315e1f8058649cda8d9b018d793dadbaecb14b8758d23014502596204562af", + "test_files": { + "test_grounding_policy.py": "43315e1f8058649cda8d9b018d793dadbaecb14b8758d23014502596204562af" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/include_query_extractor_python.json b/.pdd/meta/include_query_extractor_python.json new file mode 100644 index 0000000000..c0db17703b --- /dev/null +++ b/.pdd/meta/include_query_extractor_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.183164+00:00", + "command": "fix", + "prompt_hash": "369d47b3113a0645885716e99e00eb6474834b38a91e30fa30b7625d53afcf0b", + "code_hash": "fa95489a0b7b8f3ad6e887844fe14f38350e56b3d2fc2e4add08318334bcd191", + "example_hash": "83c8b945de75bd49b258f4feabbdda3d9c08a06ad9afca3e90df7534f68a34aa", + "test_hash": "732d96c26df09674d07bce08f5e797c375025f0e9f2d9151d977f7d221dbc1dc", + "test_files": { + "test_include_query_extractor.py": "732d96c26df09674d07bce08f5e797c375025f0e9f2d9151d977f7d221dbc1dc" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/llm_invoke.py": "2c423dbe12dd22d1acfca6c658bfef36eaf3b016626ae4a2845576500bfcb54b", + "pdd/load_prompt_template.py": "903bb43e90d778fa04ba42cd8e329ac42e1b419bb372268f5955f26de43cce47", + "pdd/path_resolution.py": "7bcc2c4d994a9540b65f9dbd426787709c7ad012191e9ee2da20bb3db21faffb", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56" + } +} \ No newline at end of file diff --git a/.pdd/meta/increase_tests_python.json b/.pdd/meta/increase_tests_python.json new file mode 100644 index 0000000000..a9a5848c07 --- /dev/null +++ b/.pdd/meta/increase_tests_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.184627+00:00", + "command": "fix", + "prompt_hash": "ae3160a949d20da81e12d62a0129c1b8e3b0d7b2073c97536d25cfd258a94abe", + "code_hash": "ebc70cf5dd42a5a2cb9b6212169270dfdc5b463b1fc31c12d3ac80c11a140879", + "example_hash": "f53be429e9a9e6a523c770ed6d0d4498e2201b22d5a8e4ef28ac64e8ff172c91", + "test_hash": "a87d41f02d445cc713056b4c16a1aa4a1e3ca599db7b127d14ed84ee744d7374", + "test_files": { + "test_increase_tests.py": "a87d41f02d445cc713056b4c16a1aa4a1e3ca599db7b127d14ed84ee744d7374" + }, + "include_deps": { + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/postprocess_example.py": "b98362e6ffb84ae7826dad27d0854e30706f3993266314439859986b32ecd0bd", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/incremental_code_generator_python.json b/.pdd/meta/incremental_code_generator_python.json new file mode 100644 index 0000000000..317890560d --- /dev/null +++ b/.pdd/meta/incremental_code_generator_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.186035+00:00", + "command": "fix", + "prompt_hash": "e2bfbfc77a1dee1e4b393a6f9b377de31f07b6a0ab2693153455366467180b1b", + "code_hash": "8dd5dd7f3016b8fae86056484fdc49b824405cdcd6911e8b8657d3e043b3b06d", + "example_hash": "f6cd628ddc2c10853a15c449b32451a1290d2457942d642989678720bc0cea82", + "test_hash": "38b440d47fa99aa72101a9ed7d81d8371c714e60c2b65101de2644aac9a83a05", + "test_files": { + "test_incremental_code_generator.py": "38b440d47fa99aa72101a9ed7d81d8371c714e60c2b65101de2644aac9a83a05" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/postprocess_example.py": "b98362e6ffb84ae7826dad27d0854e30706f3993266314439859986b32ecd0bd", + "context/preprocess_example.py": "e7082a4a9ef830048ff32ed774a3e74da1cf4662a22115e6bd5543e1b9cdbf43" + } +} \ No newline at end of file diff --git a/.pdd/meta/incremental_prd_architecture_python.json b/.pdd/meta/incremental_prd_architecture_python.json new file mode 100644 index 0000000000..c02fbcf633 --- /dev/null +++ b/.pdd/meta/incremental_prd_architecture_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.188317+00:00", + "command": "fix", + "prompt_hash": "1d30710eab8191e5a651513484774f60603091ccc97fa9518ec2aef35b1d5a49", + "code_hash": "96f56cca2ab0ce86ec389e77057421d9d972d9fff9b937d60fcca92aac6adf9b", + "example_hash": null, + "test_hash": "f916a659d90399dbeb54e93d89fc21664bb1a6cc7ca6e24046ec24a0e32e4e1b", + "test_files": { + "test_incremental_prd_architecture.py": "f916a659d90399dbeb54e93d89fc21664bb1a6cc7ca6e24046ec24a0e32e4e1b", + "test_incremental_prd_architecture_real.py": "5ee10fda3e99b8c8ab3e7e51b83ee93bac711ac3a7655c7eefb833ad4404e2ef" + }, + "include_deps": { + "context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0", + "context/change_example.py": "cc5f850c8e41a8c5b82c94b6667975d1811dff1f51888bb8caf4ff69815f8352", + "context/detect_change_example.py": "44d0c9172db01bd645d48a11a7afa527a7e509f5aac911fa41ed091d3f982096", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/insert_includes_python.json b/.pdd/meta/insert_includes_python.json new file mode 100644 index 0000000000..ea27c1cce6 --- /dev/null +++ b/.pdd/meta/insert_includes_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.190357+00:00", + "command": "fix", + "prompt_hash": "c91a10b91e1df2f4fa1d8cce5fb3e3a98b7632497153c9f181a20c77df00a4c7", + "code_hash": "6aaac12e5b791d63a1223d711a89b49e1d7eba4eb6dbf60c2273c608c6c73307", + "example_hash": "c304bf4656f75fa26192c92b3eb0422a903d8a1bc82dda3158dc9f184b337873", + "test_hash": "23acdecc0cf99af1b1fe67fb9115304e40f4e80b5d9bb94eaaadbbddcc1acaca", + "test_files": { + "test_insert_includes.py": "23acdecc0cf99af1b1fe67fb9115304e40f4e80b5d9bb94eaaadbbddcc1acaca" + }, + "include_deps": { + "context/auto_include_example.py": "0c278ed716298149777bc7082827eb8aa6d80dac264c66b6cded6871232f2df7", + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/preprocess_example.py": "e7082a4a9ef830048ff32ed774a3e74da1cf4662a22115e6bd5543e1b9cdbf43", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/install_completion_python.json b/.pdd/meta/install_completion_python.json new file mode 100644 index 0000000000..ebe7a56be7 --- /dev/null +++ b/.pdd/meta/install_completion_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.191632+00:00", + "command": "fix", + "prompt_hash": "195b5686fa2baa0e4c63549f75ebc9e7bb7eaeeec3b9f32a9043c3fc202c506e", + "code_hash": "6cb309b8c3810efb049c3014a6088f7ac3511a163f22f06fcfc66f99b4f33f8a", + "example_hash": "999be17c0ce476845d7048a1a909ad425e60c298dbc7c74f51b596ef134a8aec", + "test_hash": "56947546867a8e09632bef8249b84743ec2f578890db958c7dcb7e151f48719d", + "test_files": { + "test_install_completion.py": "56947546867a8e09632bef8249b84743ec2f578890db958c7dcb7e151f48719d" + }, + "include_deps": { + "context/click_example.py": "07653c30b63c213102cf00f7c3380a5005330014e00a26342921cd85d20273c1", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/llm_invoke_python.json b/.pdd/meta/llm_invoke_python.json index 5dc5650fae..44a2c5a33b 100644 --- a/.pdd/meta/llm_invoke_python.json +++ b/.pdd/meta/llm_invoke_python.json @@ -1,18 +1,19 @@ { - "pdd_version": "0.0.263.dev0", - "timestamp": "2026-06-04T20:30:11.770314+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.195957+00:00", "command": "test", - "prompt_hash": "43285448cc8f00ad6509e964509d9ec768914e02eef40f1a958bae69a60823a0", - "code_hash": "127c8cf41cdba1a5ec3865330cfba5ebbb9a582f65c19479a57fe732e33f3f2f", + "prompt_hash": "41404dff8babe6a1ff78b29f7ada4a173fcba6d8b3e0f6248c949716992d1dd2", + "code_hash": "2c423dbe12dd22d1acfca6c658bfef36eaf3b016626ae4a2845576500bfcb54b", "example_hash": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", - "test_hash": "1979352422b5ea80ee7f2ea14a8a2c20ab880d02fd1d2365d8dd54050d2f5f18", + "test_hash": "4aaae7e8dc472e0fb280e9d773dce17ae806ee959c232badc1fb922011df1fb9", "test_files": { - "test_llm_invoke.py": "1979352422b5ea80ee7f2ea14a8a2c20ab880d02fd1d2365d8dd54050d2f5f18", + "test_llm_invoke.py": "4aaae7e8dc472e0fb280e9d773dce17ae806ee959c232badc1fb922011df1fb9", "test_llm_invoke_csv_model_registration.py": "1583b5e076fe5227a11f3d1079034ab4a21bc6131a3433f37bd68e35a99ba49e", "test_llm_invoke_grounding.py": "e219903b03ea56b821cfaaf18b8f279bdddf9d59729daf86a3456bee7bbe2ecb", "test_llm_invoke_integration.py": "2eb4bd2565761a4148762c6fb73c887cb6e12ff962742e05f5c2d4d62b47aaf9", "test_llm_invoke_nested_schema.py": "c983a19874abacc3e0ea9d6ca2ec87495960d2dd96d4e93be4039ed1bc995b9b", "test_llm_invoke_retry_cost.py": "bfdfe7b814b78813f86b8bc6d081831daf531187feff66358260f6282925958c", + "test_llm_invoke_task_routing.py": "b3bb259d14c832001e09ec865b27bfb471d572a7f43290975447cbfa863b93b7", "test_llm_invoke_vertex_retry.py": "eafe91ba9376dc7e2da36faf3cd2de3cef50f70cf1c4fc5655e373deb7f50c26" }, "include_deps": { @@ -20,6 +21,6 @@ "context/path_resolution_example.py": "15ba84fd11c61af8fa12dcb19d63d42f0cb09b747542bf20e69650b8ff4ac137", "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", "pdd/core/cloud.py": "0487c0b989996af144df3af1c818a6eeafe52fd209710e5a54eb43990c89ae97", - "pdd/server/token_counter.py": "f784679da58bbabb9eb9b17c86f13e1ec5d98f2f2bd09407b4e1b044f83ca8a1" + "pdd/server/token_counter.py": "847ac13e3804c451aca64652877940c06b72c2fc33edc00232f1d4aa16c957e8" } } \ No newline at end of file diff --git a/.pdd/meta/load_prompt_template_python.json b/.pdd/meta/load_prompt_template_python.json new file mode 100644 index 0000000000..569236788b --- /dev/null +++ b/.pdd/meta/load_prompt_template_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.196872+00:00", + "command": "fix", + "prompt_hash": "7621531d40ff40acbc0088160d6e48b959b88afca9dd461258c56a57ec1b42a2", + "code_hash": "903bb43e90d778fa04ba42cd8e329ac42e1b419bb372268f5955f26de43cce47", + "example_hash": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "test_hash": "2b9f85a3571946d1bda46d572c14f81add70efd1feff32ec6fb1ac42ea3a3052", + "test_files": { + "test_load_prompt_template.py": "2b9f85a3571946d1bda46d572c14f81add70efd1feff32ec6fb1ac42ea3a3052" + }, + "include_deps": { + "context/path_resolution_example.py": "15ba84fd11c61af8fa12dcb19d63d42f0cb09b747542bf20e69650b8ff4ac137" + } +} \ No newline at end of file diff --git a/.pdd/meta/logo_animation_python.json b/.pdd/meta/logo_animation_python.json new file mode 100644 index 0000000000..517f2feba1 --- /dev/null +++ b/.pdd/meta/logo_animation_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.197911+00:00", + "command": "fix", + "prompt_hash": "f0724c74377388bfb72c6b9fb4877b65990a9cf88e7a1fa5fbdabdf3c3860a36", + "code_hash": "9fa1c95b3b85cded9cb00016dbecdbc5f8ef585611238848770541b4704e923e", + "example_hash": "f32a5cc330235c3b61fb16ac62d3ae7531c7c6dbc11af077fc79054736f0944d", + "test_hash": "063bdd4453f663f540e2d978187498e8a431096e53451aa4fd80a038167fdcf4", + "test_files": { + "test_logo_animation.py": "063bdd4453f663f540e2d978187498e8a431096e53451aa4fd80a038167fdcf4" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/metadata_sync_python.json b/.pdd/meta/metadata_sync_python.json index bba992e110..67fd00fdd2 100644 --- a/.pdd/meta/metadata_sync_python.json +++ b/.pdd/meta/metadata_sync_python.json @@ -1,19 +1,19 @@ { - "pdd_version": "0.0.237", - "timestamp": "2026-05-16T20:50:00.397700+00:00", + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.682670+00:00", "command": "fix", - "prompt_hash": "18e8e073748217aa9c224bea7c7e8425ea36de28f769b30684cd21033018bfab", - "code_hash": "739ace489ae1bf00a9aa696332dabce993b934047228fe0d10a1ef9145130fb2", + "prompt_hash": "8c833103e615b0ffefad1d36b1d22c8a6d9837f2d7f66aaec3e227b0d12cc328", + "code_hash": "3afaa5a409e500b3f91aab33dacee33fae47447fba80a1702d65a33761261782", "example_hash": "704453b3386724eef225b61c48f2fe922e1a973ac211c494e1d955dd44bcca11", - "test_hash": "a1bf7b5a8ce6c671aee77fca08edd29eea86e93cce2d02871d0bd25b31591dc3", + "test_hash": "82af9e0bff7550d1f9bf46f4c6e7f4792143565e4e228d1507976f6b9fbac5f6", "test_files": { - "test_metadata_sync.py": "a1bf7b5a8ce6c671aee77fca08edd29eea86e93cce2d02871d0bd25b31591dc3" + "test_metadata_sync.py": "82af9e0bff7550d1f9bf46f4c6e7f4792143565e4e228d1507976f6b9fbac5f6" }, "include_deps": { "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", - "pdd/architecture_registry.py": "f822c4ef9d66da1dc6e6104ad3ca99d822c277423e71bb807966e4fc80f779ed", - "pdd/architecture_sync.py": "89329e32dd4d13e7b095f1b96843e383936bbc5f329e50daf006dbb12af0d54b", - "pdd/operation_log.py": "78b05aadd7656dbabb7cdd113fa84d39d32c64e4cb4322aa2ac2570d390d94ef", - "pdd/sync_determine_operation.py": "dff5e2b0628122c49305106d255e91d57446d7c768d4ae78fa4a09a619383e23" + "pdd/architecture_registry.py": "5722225846b9cc8189b410a78e5714b4f2916d5748271d27f6f738692429c309", + "pdd/architecture_sync.py": "0e0ff74978f49d629f92365723ebbea0e7c70e6d5234ccfdafb1c8e09c110c11", + "pdd/operation_log.py": "d6712703f752c2045cd11a5c144b67454bb80700b83bd4e4612e55d2107b6072", + "pdd/sync_determine_operation.py": "ef8e41ab61742b9d8eb8fd2b1ee1d4de415223e0eb56734b70dee0bdb30f4b30" } -} +} \ No newline at end of file diff --git a/.pdd/meta/model_tester_python.json b/.pdd/meta/model_tester_python.json new file mode 100644 index 0000000000..6d9bad086a --- /dev/null +++ b/.pdd/meta/model_tester_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.201502+00:00", + "command": "fix", + "prompt_hash": "fd82b8c71a0ba03c664946897f00c969643ee2fe846a001a02c560c9d2003327", + "code_hash": "786ac21e50a29087e035af7fbbfeac05e3fc2930ec3d4916ca73774623da5384", + "example_hash": "9d33f093cdac4c100bf671e891a68c7f83e2e09e74cd65f6b53cb567d77d3cd0", + "test_hash": "2b2bbc0e90e0df80cd92d307f7f9e35572a815e4d56a92220f5fd138c5241c8a", + "test_files": { + "test_model_tester.py": "2b2bbc0e90e0df80cd92d307f7f9e35572a815e4d56a92220f5fd138c5241c8a" + }, + "include_deps": { + "context/provider_manager_example.py": "cc2bbe6cefff4ad24af4365b0c4bc2c99e672f825d12edaca7307de9c04ecfb8" + } +} \ No newline at end of file diff --git a/.pdd/meta/one_session_sync_python.json b/.pdd/meta/one_session_sync_python.json new file mode 100644 index 0000000000..15c6e02970 --- /dev/null +++ b/.pdd/meta/one_session_sync_python.json @@ -0,0 +1,20 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.204261+00:00", + "command": "fix", + "prompt_hash": "d63a4299e9dbdaf43a74f14160a021b7e31be6403eb041fe8383b429ca6ffe11", + "code_hash": "37541cc02ed9eae748f2270beb39c5a66bf098bac87bf757a3e00e382d41fcf2", + "example_hash": "f2260e5e7b558e0cf1b0cb26acb7c95072c5ffd17d19231a2455979e7d8b9556", + "test_hash": "9d58025a7360f7fb7bc846db6b2014cc6a8cdea6e64a69c4952f3a723efa4a72", + "test_files": { + "test_one_session_sync.py": "9d58025a7360f7fb7bc846db6b2014cc6a8cdea6e64a69c4952f3a723efa4a72" + }, + "include_deps": { + "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", + "context/agentic_sync_runner_example.py": "fcb36209abb5ab882ad551754fb574d62509c968f8c564e18163be7f2b7ef35a", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/agentic_common.py": "7b9b57eac64bac2b4fd18d4eea3d2feb3c7d99ff43f0ca1961a0bfd22a9576da", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56" + } +} \ No newline at end of file diff --git a/.pdd/meta/operation_log_python.json b/.pdd/meta/operation_log_python.json new file mode 100644 index 0000000000..8d297c9ab7 --- /dev/null +++ b/.pdd/meta/operation_log_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.205644+00:00", + "command": "fix", + "prompt_hash": "d09eedd7f6b781864544d62c7f513b760a3e418b3cd29efa738a4b0ddedc52ed", + "code_hash": "d6712703f752c2045cd11a5c144b67454bb80700b83bd4e4612e55d2107b6072", + "example_hash": "2660def7012d9b79bad3db33cf399ac603cc1c35dff0b064cbdd2638a013de1d", + "test_hash": "0ec46ea27197930ae19f0f5486d34fb7f2e3800d6b2670edd1f464e72f0ae27c", + "test_files": { + "test_operation_log.py": "0ec46ea27197930ae19f0f5486d34fb7f2e3800d6b2670edd1f464e72f0ae27c", + "test_operation_logging_e2e.py": "1fc74b2e4db145dfe21150f751d4b71a81ab0aaf1cc72e44bbe8b037f671556e" + }, + "include_deps": { + "context/sync_determine_operation_example.py": "225d20ea0210bbf52650a9f424879ff495e574dd2411eb4b86592a6d06cf59a5" + } +} \ No newline at end of file diff --git a/.pdd/meta/path_resolution_python.json b/.pdd/meta/path_resolution_python.json new file mode 100644 index 0000000000..797ba7f5cb --- /dev/null +++ b/.pdd/meta/path_resolution_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.206369+00:00", + "command": "fix", + "prompt_hash": "17ed2dc0cc1541e5267c988065f5ca4c6e077193b2f2b7420ed57acfa0c0e710", + "code_hash": "7bcc2c4d994a9540b65f9dbd426787709c7ad012191e9ee2da20bb3db21faffb", + "example_hash": "15ba84fd11c61af8fa12dcb19d63d42f0cb09b747542bf20e69650b8ff4ac137", + "test_hash": "f731aa81caf48b2ee79d16e9c93860a17c0597305d877b853473a903f521f716", + "test_files": { + "test_path_resolution.py": "f731aa81caf48b2ee79d16e9c93860a17c0597305d877b853473a903f521f716" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/pddrc_initializer_python.json b/.pdd/meta/pddrc_initializer_python.json new file mode 100644 index 0000000000..dee7085e84 --- /dev/null +++ b/.pdd/meta/pddrc_initializer_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.207392+00:00", + "command": "fix", + "prompt_hash": "9399d4347fee000bf46bb4a9414f384bbcd158a8e44f6ba35dc5ef4560c0c6a9", + "code_hash": "6112573fcff622386dc139f43d2dc30d99e68a3f362784f059e771648bbb928b", + "example_hash": "3ba502fa9e2a2f438aaa890f0b654a98e5b8665253e10446acafda90f7eedad9", + "test_hash": "fd553d3478328b50bb6d6639165478da1f43ba8b4c2e901cf6de52bf7a3fd317", + "test_files": { + "test_pddrc_initializer.py": "fd553d3478328b50bb6d6639165478da1f43ba8b4c2e901cf6de52bf7a3fd317" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/pin_example_hack_python.json b/.pdd/meta/pin_example_hack_python.json index 8f159bc131..f00d867476 100644 --- a/.pdd/meta/pin_example_hack_python.json +++ b/.pdd/meta/pin_example_hack_python.json @@ -1,8 +1,8 @@ { - "pdd_version": "0.0.234", - "timestamp": "2026-05-12T02:53:56.323298+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.209843+00:00", "command": "fix", - "prompt_hash": "77e8c17f1eb7ed4e1347312a702129bcfd8af95a985ee1ccd718291f83c93fae", + "prompt_hash": "677655b3eb5ef57bbe1f4216ce0ceb5abe7ba3ee2f5a94388c2f5bdfcb21383b", "code_hash": "c90df80316bc8339fcf9d4690ac29e4dc8ae3391e25c26c82e496b87dcd4a788", "example_hash": "34e285dedea6ad0d3f3a5539401cc7c0bf5b2181d954915963fd66cd86e54e3c", "test_hash": "7fbffec72f9941a52b4ba7efd6468330dd2a3235dc53d0bb81168ea6eb6c3a72", @@ -12,13 +12,13 @@ "include_deps": { "context/auto_deps_main_example.py": "9b9c36c18d39494b6e31b46f6e4758c25135b6fa3d1672fbb7365844f7d2de04", "context/cmd_test_main_example.py": "0774984ee29f70407429f1eff4a1b76287715b7ddd6adf2bc149e874c9b844bb", - "context/code_generator_main_example.py": "679590f0913600886d99469d8ece23fbbfe6bea2e626b0d5d2ab5fdcdc079b80", + "context/code_generator_main_example.py": "4b66f24a1e2ac4195255738566b0cccdac0971523a245d3a8b78ad45d9e1a872", "context/context_generator_main_example.py": "77c3de786f31392540ec609d4b3942e98e996ae14c4014f5018cf2b05a560df4", "context/crash_main_example.py": "5c9b909107f74f773ccf7013d556a5812643d2a3b086e79fe07da7ea7877458c", "context/fix_main_example.py": "29f88d8bfcc2256ebebe6efcb611f9487eb5ca6d8781286a6dd98689f92bca7a", "context/fix_verification_main_example.py": "6490f398aca6df47b1a95e46a4ecfc0aabe299b5022de5c31ebc436e1bc3e459", "context/get_test_command_example.py": "33b16476536f8c9424d3222ffc4a5cd5ecee6ad0589b2c66a4f3c7f1e265106c", - "context/sync_determine_operation_example.py": "94a333b37d9875bcc9b39e22d4d00de1b196ad2f7e0fa997e6f638e153dbdfad", - "context/update_main_example.py": "b318834c648f25e6141d81f3ecac1d9da592a6bb4740500983f2b07b8a10d902" + "context/sync_determine_operation_example.py": "225d20ea0210bbf52650a9f424879ff495e574dd2411eb4b86592a6d06cf59a5", + "context/update_main_example.py": "9fe01fd19196c2604c91d101e49db849e0e1c9fabafeb2ad831a9c91b2f26416" } } \ No newline at end of file diff --git a/.pdd/meta/postprocess_0_python.json b/.pdd/meta/postprocess_0_python.json new file mode 100644 index 0000000000..c7a9661493 --- /dev/null +++ b/.pdd/meta/postprocess_0_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.211001+00:00", + "command": "fix", + "prompt_hash": "dfb5fe2454d0270eff594b45258a05d3f55d7a0c56f4fafec6d2b6a09556a628", + "code_hash": "396d7b1b20852d812b0b25a1dad2f8b32ba1a37ab6c857f6c327a443804b74f3", + "example_hash": "2ff3f65a972fbd46519fefbb1467687c4d9f2b269e14e3810eb1a8427b1b396e", + "test_hash": "4feb11f470c15bc1edf5987e72bbbc1f7de76a9c5c9d64eafce93cd77a289c9d", + "test_files": { + "test_postprocess_0.py": "4feb11f470c15bc1edf5987e72bbbc1f7de76a9c5c9d64eafce93cd77a289c9d" + }, + "include_deps": { + "context/comment_line_example.py": "debc424d539b65aef1267888bb2eb77cf4de16745407bb5df5a18b8e2f231095", + "context/find_section_example.py": "681043403aa02b9eb0d265a95560fce565507d0656d1693f79650f8eb5f0683a", + "context/get_comment_example.py": "3293b4631db7c783d87745abf36a79996d5c182b74e8bee885a2e522550db851" + } +} \ No newline at end of file diff --git a/.pdd/meta/postprocess_python.json b/.pdd/meta/postprocess_python.json new file mode 100644 index 0000000000..e2490443f6 --- /dev/null +++ b/.pdd/meta/postprocess_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.212001+00:00", + "command": "fix", + "prompt_hash": "3df8ad9a21611349ce5cbb63192882f07d450736a968439868d049fcd6a10817", + "code_hash": "628880a68346c3c1bd8326b4d1b495452578937ed97b27234ad56ba332c4c2ed", + "example_hash": "b98362e6ffb84ae7826dad27d0854e30706f3993266314439859986b32ecd0bd", + "test_hash": "5b297188f029a9b15aaaaba54045508e3962190b406dc96503a417d965899912", + "test_files": { + "test_postprocess.py": "5b297188f029a9b15aaaaba54045508e3962190b406dc96503a417d965899912", + "test_postprocess_0.py": "4feb11f470c15bc1edf5987e72bbbc1f7de76a9c5c9d64eafce93cd77a289c9d" + }, + "include_deps": { + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/pre_checkup_gate_python.json b/.pdd/meta/pre_checkup_gate_python.json new file mode 100644 index 0000000000..d103668dcb --- /dev/null +++ b/.pdd/meta/pre_checkup_gate_python.json @@ -0,0 +1,21 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.702526+00:00", + "command": "fix", + "prompt_hash": "30924f51857c9aae17ade802b113868f992d850e7a04f6898b7620572c7cb32f", + "code_hash": "9ff0bbaa18c82cf4da836126861c40936a4555b8ff972f4bceee370492f37ace", + "example_hash": "82585fac9350c3436528870c0b1d7e64228e6903cc4b5d0a62a35edecb266906", + "test_hash": "d22520612cce8bf31712ff999b5f5f45153ddcd63a4044eb82c220d4a8021f62", + "test_files": { + "test_pre_checkup_gate.py": "d22520612cce8bf31712ff999b5f5f45153ddcd63a4044eb82c220d4a8021f62" + }, + "include_deps": { + "context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0", + "context/ci_drift_heal_example.py": "550e750fb041fb73dc92462d4f266bc999102ddd8e4efd3b40dbab0bb1d0d879", + "context/metadata_sync_example.py": "704453b3386724eef225b61c48f2fe922e1a973ac211c494e1d955dd44bcca11", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/checkup_gates.py": "b6086954a93e0495661e6ca39d96b3079b928d13308bf0fcd6a16b1e0b5f511c", + "pdd/operation_log.py": "d6712703f752c2045cd11a5c144b67454bb80700b83bd4e4612e55d2107b6072", + "pdd/sync_determine_operation.py": "ef8e41ab61742b9d8eb8fd2b1ee1d4de415223e0eb56734b70dee0bdb30f4b30" + } +} \ No newline at end of file diff --git a/.pdd/meta/preprocess_main_python.json b/.pdd/meta/preprocess_main_python.json new file mode 100644 index 0000000000..5cb1eb2f13 --- /dev/null +++ b/.pdd/meta/preprocess_main_python.json @@ -0,0 +1,22 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.707446+00:00", + "command": "fix", + "prompt_hash": "0a339cd291b75846ab9ba8d63328b7bdef0a4b24764b5bc277f2e2282f466504", + "code_hash": "a069c3a73be88d3e3e93d40fd66e482c69bfb56ab627d862da31585612ac203b", + "example_hash": "dc4cf0483361ef94467d8936ceef30b54b62e61d658c916663407a8f142ec851", + "test_hash": "0fa3887c7a4e2c3f051ce719117b8f07444da419b35363b9dff4a2b5ae0f17e9", + "test_files": { + "test_preprocess_main.py": "0fa3887c7a4e2c3f051ce719117b8f07444da419b35363b9dff4a2b5ae0f17e9", + "test_preprocess_main_pdd_tags.py": "abddfe41c460aa7dcd4186028134deb4eef0a85419a7f6f59869a274c25f7ee8" + }, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/preprocess_example.py": "e7082a4a9ef830048ff32ed774a3e74da1cf4662a22115e6bd5543e1b9cdbf43", + "context/xml_tagger_example.py": "3b4606a0ac11cb728a5aa657d457312c83f2d710f53f4ac94d35675f7e911f60" + } +} \ No newline at end of file diff --git a/.pdd/meta/preprocess_python.json b/.pdd/meta/preprocess_python.json index c42350e3c5..6a801062c4 100644 --- a/.pdd/meta/preprocess_python.json +++ b/.pdd/meta/preprocess_python.json @@ -1,9 +1,9 @@ { - "pdd_version": "0.0.270.dev0", - "timestamp": "2026-06-10T22:02:58.234695+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.218871+00:00", "command": "fix", - "prompt_hash": "5ae17e5a4d901ef324d56e2c7c221863cf1ad68b6081b1e7b8d0225df3f9015c", - "code_hash": "edb9bc460d68b854c6b832ddd1c3ce42cc60e71526dade63aea1ae6a132d2259", + "prompt_hash": "029c02ce24a5e0329cd238fc79050d04b8e9eb5ecf030d0c23def59078c941f2", + "code_hash": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56", "example_hash": "e7082a4a9ef830048ff32ed774a3e74da1cf4662a22115e6bd5543e1b9cdbf43", "test_hash": "ec9a69c157443c17d8cdece1e5102252f31b470d9107efea66d89cfa977d9d13", "test_files": { @@ -16,7 +16,7 @@ "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", "context/auto_include_example.py": "0c278ed716298149777bc7082827eb8aa6d80dac264c66b6cded6871232f2df7", "context/path_resolution_example.py": "15ba84fd11c61af8fa12dcb19d63d42f0cb09b747542bf20e69650b8ff4ac137", - "pdd/content_selector.py": "0e4fe56b42ea98d8cbf3c59f0f25849adbad5068db2f5835e5b2e7c4c21e46d2", - "pdd/include_query_extractor.py": "c2bea7ea18bb8d743394485938240089f980ca3df94b3c62f29c606ff76da453" + "pdd/content_selector.py": "c18d5f9ce0903169445ee17d2e408b88ad91a931e710e783f7c9c7cc3f2ec4af", + "pdd/include_query_extractor.py": "fa95489a0b7b8f3ad6e887844fe14f38350e56b3d2fc2e4add08318334bcd191" } -} +} \ No newline at end of file diff --git a/.pdd/meta/process_csv_change_python.json b/.pdd/meta/process_csv_change_python.json new file mode 100644 index 0000000000..ea0aa16a73 --- /dev/null +++ b/.pdd/meta/process_csv_change_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.220066+00:00", + "command": "fix", + "prompt_hash": "51fda08db3c4bb80bab1047527095d28167707c6e0599c6e349064de59d604f5", + "code_hash": "0827d5c0585403e3c5f7c75c2fc07f5e9d5f635faf9159cc4b4182565308cd55", + "example_hash": "c7cf31cd55d5312b5398d3d518eddd33354fa184d0e92c6cf926a3c71d018227", + "test_hash": "9dda43c68ce5e530860175e3fc21128f9288364ffdbe0b387570185f1d144d10", + "test_files": { + "test_process_csv_change.py": "9dda43c68ce5e530860175e3fc21128f9288364ffdbe0b387570185f1d144d10" + }, + "include_deps": { + "context/get_extension_example.py": "def9c34c1416259a0a031a996b96aeff5e73a7012adff6af1cc557e74ba97cc8", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/prompt_lint_python.json b/.pdd/meta/prompt_lint_python.json index 303d1c7302..164b1bbf73 100644 --- a/.pdd/meta/prompt_lint_python.json +++ b/.pdd/meta/prompt_lint_python.json @@ -1,6 +1,6 @@ { - "pdd_version": "0.0.251.dev112", - "timestamp": "2026-05-27T04:45:33.058394+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.220887+00:00", "command": "test", "prompt_hash": "ad547c4d37d4860acab5d4dfac46e9d4caa9182547683f64a5b5ee5caf2b9907", "code_hash": "df85f363082925c714e23be8d7b2e941d62a8542a3370a2581d16780cef1474e", diff --git a/.pdd/meta/prompt_repair_python.json b/.pdd/meta/prompt_repair_python.json new file mode 100644 index 0000000000..b6d23badff --- /dev/null +++ b/.pdd/meta/prompt_repair_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.222373+00:00", + "command": "fix", + "prompt_hash": "7534fda4f31eaa0a65b13f01ad13ff704e3976d4a6ce7463320a9b51e293728b", + "code_hash": "3bb7949b5d27a078b93f2ec872b9af53b1265dcd2740780b128b527a1cf3346d", + "example_hash": null, + "test_hash": "f5cf48e8ee5aea58108f422cc6127e21b9736b79f5dcef3d1464eb557bceb72f", + "test_files": { + "test_prompt_repair.py": "f5cf48e8ee5aea58108f422cc6127e21b9736b79f5dcef3d1464eb557bceb72f" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/change.py": "d66d22f9ef9e6a80574d91b115bf72aac534d238dd8a8085d5744e1a8c1562a3", + "pdd/checkup_prompt_main.py": "538367ba6b7fa74d9f86ba6d1235219598f7a124c58cf00ad0abf6cf027526dc", + "pdd/prompt_lint.py": "df85f363082925c714e23be8d7b2e941d62a8542a3370a2581d16780cef1474e", + "pdd/server/token_counter.py": "847ac13e3804c451aca64652877940c06b72c2fc33edc00232f1d4aa16c957e8" + } +} \ No newline at end of file diff --git a/.pdd/meta/prompt_tester_python.json b/.pdd/meta/prompt_tester_python.json new file mode 100644 index 0000000000..2d76f3d2e7 --- /dev/null +++ b/.pdd/meta/prompt_tester_python.json @@ -0,0 +1,14 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.223079+00:00", + "command": "fix", + "prompt_hash": "db8cacbb67c5c5e4cd986133e3a0b53d93944dae1488f15cf9deb317bcc6a3af", + "code_hash": "77fb98a82eee4a2fd8d5f95091c1a1676b270507431503dd35b90c538a279025", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec" + } +} \ No newline at end of file diff --git a/.pdd/meta/provider_manager_python.json b/.pdd/meta/provider_manager_python.json index f51164f22e..22e5d955aa 100644 --- a/.pdd/meta/provider_manager_python.json +++ b/.pdd/meta/provider_manager_python.json @@ -1,13 +1,13 @@ { - "pdd_version": "0.0.263.dev0", - "timestamp": "2026-06-04T20:30:11.998649+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.224536+00:00", "command": "test", - "prompt_hash": "11c78955a7665810d69fc06f4d706e7c8f48e2ed980141effb747aed9d02289f", - "code_hash": "130d9e1813d8fa98baccd63ef6bc01177722a2c39d383f881e37ffede71ed594", + "prompt_hash": "47b3f7a1cdecac48fa42942e640be85b68a40908bbb2c4cacab726cedc20c689", + "code_hash": "4003ea5149d472ca4c0b04ee456c43619ca3286b89e450aaf28ca1342bef62fc", "example_hash": "cc2bbe6cefff4ad24af4365b0c4bc2c99e672f825d12edaca7307de9c04ecfb8", - "test_hash": "6e297ca99057e4895a24e0e0a5a9469d646eff5a7ebe712d1885223fc52cd1ef", + "test_hash": "dbc8d81a2b1e01dd446fb45911e5081d65c57b1c9ee9a1ffb35a49c0a982cd38", "test_files": { - "test_provider_manager.py": "6e297ca99057e4895a24e0e0a5a9469d646eff5a7ebe712d1885223fc52cd1ef" + "test_provider_manager.py": "dbc8d81a2b1e01dd446fb45911e5081d65c57b1c9ee9a1ffb35a49c0a982cd38" }, "include_deps": {} } \ No newline at end of file diff --git a/.pdd/meta/pytest_output_python.json b/.pdd/meta/pytest_output_python.json new file mode 100644 index 0000000000..b86ee1b394 --- /dev/null +++ b/.pdd/meta/pytest_output_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.225517+00:00", + "command": "fix", + "prompt_hash": "2a42925af61c91af5d4c42843b6afe93f68fb8a35b5cee77a8a290f73c269315", + "code_hash": "652a27dd1e08769611abe3cb6a83f372c450279d6bf1b676741b9b7186665975", + "example_hash": "6f0cd68e3c4440081267c716651caec4fbdd7d9c4516f967517f02ae2a853920", + "test_hash": "5845761aa11bc7d4601a0e1d1b756a3c1977ae57a7d99e63b15fa70097eeab0c", + "test_files": { + "test_pytest_output.py": "5845761aa11bc7d4601a0e1d1b756a3c1977ae57a7d99e63b15fa70097eeab0c" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/pytest_slicer_python.json b/.pdd/meta/pytest_slicer_python.json new file mode 100644 index 0000000000..2ea1bc72f7 --- /dev/null +++ b/.pdd/meta/pytest_slicer_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.226389+00:00", + "command": "fix", + "prompt_hash": "2eab3431bb3a44fa472f1f28d490d5841b4c298069b73081f47b4a4336a66fa0", + "code_hash": "c02a5488fe6f7248ed387636d5824cb2f1876544d3991e43ef253648a36e49a2", + "example_hash": "c6b81f2c94831b454b37bfd7080b36f4d97a35b26083a4836276437d8f58e7e6", + "test_hash": "cb1ee4708a4471cbc2696b6f2f4a590493fa0c217c02bd1108ae18fde889b382", + "test_files": { + "test_pytest_slicer.py": "cb1ee4708a4471cbc2696b6f2f4a590493fa0c217c02bd1108ae18fde889b382" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/python_env_detector_python.json b/.pdd/meta/python_env_detector_python.json new file mode 100644 index 0000000000..1842844506 --- /dev/null +++ b/.pdd/meta/python_env_detector_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.226954+00:00", + "command": "fix", + "prompt_hash": "87662b4bec6070ff94de5f45f5fa821b98bd352514b4a5bd1c204961048799ab", + "code_hash": "cbe4044a83cd88a683f36d6ecfca245fef6a03ea2abc7f5c407636fccc051f35", + "example_hash": "65619b3e8bad0509f1ccf54d81b5c3760f423b6fc1925c932fc7968da78a172e", + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/reasoning_python.json b/.pdd/meta/reasoning_python.json new file mode 100644 index 0000000000..8fb849f4e1 --- /dev/null +++ b/.pdd/meta/reasoning_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.227679+00:00", + "command": "fix", + "prompt_hash": "8c85242e5ca00f29a3f6d2dc47702a15a317dd6dfa303609f70e9555cfdf4722", + "code_hash": "b75e2beac078d86bcbee0ceb2a44cd19b4fab7a456890eead6510cd2823efa39", + "example_hash": null, + "test_hash": "43dde127047a91029a799f07efbec37256f60ec3a7b0f81b701c5d5fc17eac04", + "test_files": { + "test_reasoning.py": "43dde127047a91029a799f07efbec37256f60ec3a7b0f81b701c5d5fc17eac04" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/remote_session_python.json b/.pdd/meta/remote_session_python.json new file mode 100644 index 0000000000..35f4681a3b --- /dev/null +++ b/.pdd/meta/remote_session_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:22:51.624312+00:00", + "command": "fix", + "prompt_hash": "9a49cada503bed5d892ea7b68570c6357b6ee220a2f5a41ba1e37204f61e6760", + "code_hash": "163292e0200246767f685e9478c8ab4531c06ba471be4ceb9a6ee8065b1cfc12", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/auth_service_example.py": "4b6d2c8c9e13d1edc0d9805ee0c07657494cbf31dffe2bb0029b72975674a8cf", + "context/cloud_example.py": "92418beb7a1e7f25682bf2abce5b19013b95e7eed95256c930b83c1dd6dbfb0a", + "context/get_jwt_token_example.py": "76a87c8257cb1f4cfdc2edff6225c752432afb3a5e352fcb016d08d8eae16e01", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/render_mermaid_python.json b/.pdd/meta/render_mermaid_python.json new file mode 100644 index 0000000000..703cdd19ac --- /dev/null +++ b/.pdd/meta/render_mermaid_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.230205+00:00", + "command": "fix", + "prompt_hash": "42ea0f2c0870cd0542cdc8804b0ab3814e6e6e811382f31fbc7f8e692413bf0b", + "code_hash": "54ab32f4a65722fd22a49efde8529c413812ee30240b52f68515b94a8032ad57", + "example_hash": "193a57bc4191595f252881c016ba7168c27c27c01ff192128d98cf76ce5f842b", + "test_hash": "2bec83d7c4771c2c1833eb63ff31d3de1d1d3adf9d24fec0026179f56391c643", + "test_files": { + "test_render_mermaid.py": "2bec83d7c4771c2c1833eb63ff31d3de1d1d3adf9d24fec0026179f56391c643" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/resolved_sync_unit_python.json b/.pdd/meta/resolved_sync_unit_python.json new file mode 100644 index 0000000000..7d89d5a4cf --- /dev/null +++ b/.pdd/meta/resolved_sync_unit_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.230947+00:00", + "command": "fix", + "prompt_hash": "22b41b5041d1747474bb7d1410d0a72393fbd104ac169097e8d8d4f4ea22d9b3", + "code_hash": "2ef910bdcf6f4aece8ec9440c554d3775e7ca848ee8e5b8150e4d08960fe347b", + "example_hash": null, + "test_hash": "183be24862b44bb809a0f2b234f8c912bca2447620807d353d31cd535975eb7e", + "test_files": { + "test_resolved_sync_unit.py": "183be24862b44bb809a0f2b234f8c912bca2447620807d353d31cd535975eb7e" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/routing_policy_python.json b/.pdd/meta/routing_policy_python.json new file mode 100644 index 0000000000..6db5b5b6a3 --- /dev/null +++ b/.pdd/meta/routing_policy_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.232389+00:00", + "command": "fix", + "prompt_hash": "98fd165a7e3e936568815cff0b127c2504af6be42780b2e9c56a49bea3fd257d", + "code_hash": "3337699ac936552fa0e6d143fc70f6a0183edbbcbc73b1177922f3d8207dab56", + "example_hash": null, + "test_hash": "57fc855ee1f1b1bd6519e8d90495935cbe4b6cbfdc36e40040c2ce222df35532", + "test_files": { + "test_routing_policy.py": "57fc855ee1f1b1bd6519e8d90495935cbe4b6cbfdc36e40040c2ce222df35532" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/data/deepswe_manifest.json": "8d1f4d658bc01cd579c0d367e4906244d9e143f54033dd694fa403ef7ed13eea", + "pdd/gate_policy.py": "110ad068b2e5f1d4c0aa0e8ace3c8da8acb05d48f3116b6229459ed258d69667", + "pdd/reasoning.py": "b75e2beac078d86bcbee0ceb2a44cd19b4fab7a456890eead6510cd2823efa39" + } +} \ No newline at end of file diff --git a/.pdd/meta/run_generated_python.json b/.pdd/meta/run_generated_python.json new file mode 100644 index 0000000000..b504ce9c3c --- /dev/null +++ b/.pdd/meta/run_generated_python.json @@ -0,0 +1,11 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.232748+00:00", + "command": "fix", + "prompt_hash": "96731fed482942f0acaaf72323a8b588499f8beafab4c6fa739236b1bd941221", + "code_hash": "96731fed482942f0acaaf72323a8b588499f8beafab4c6fa739236b1bd941221", + "example_hash": null, + "test_hash": null, + "test_files": {}, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/server_app_python.json b/.pdd/meta/server_app_python.json new file mode 100644 index 0000000000..b37594da30 --- /dev/null +++ b/.pdd/meta/server_app_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.234599+00:00", + "command": "fix", + "prompt_hash": "4c5bfabf92ab16c46596507eb86161fc85295e660f90b3120a1e19b95f9e8059", + "code_hash": "28394e19c9174f44cda9cf37b93229d74012bd2e61c49e0d03e665796be94279", + "example_hash": "554bb0307f2f7a0dc79ef86f8a4fd42e5017225d0c56379ad99d2939f3c1087a", + "test_hash": "3e5be61ccaf4a5ba709fc6429e029972e25548a7859b28c5712042856484806f", + "test_files": { + "test_app.py": "3e5be61ccaf4a5ba709fc6429e029972e25548a7859b28c5712042856484806f" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/server/jobs_example.py": "7694bd486d012fd9d52543c5f79a289f61dac3ed0f8c94c2ea9fd25ed6333f12", + "context/server/models_example.py": "223a832718e72ca6640728e4bd700da58edb89667931085d7d949774d8fbc228", + "context/server/security_example.py": "dc4f3dc06455ba6180f4536aa67ce4e9a5efd52e0e9cd7702c3f2ec6ef96aac9" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_click_executor_python.json b/.pdd/meta/server_click_executor_python.json new file mode 100644 index 0000000000..c8c2eb22f6 --- /dev/null +++ b/.pdd/meta/server_click_executor_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.235785+00:00", + "command": "fix", + "prompt_hash": "d82aceff435ce2a46ef2f4297f06393f559254168774ef122d905417a8b3ed0d", + "code_hash": "b8e1e120d050f0545f2c3979c94390b07431a30af391bfd30bc052770f11edda", + "example_hash": "a61ee766f5b09bfa0de58533125b85d8855186f17f3109101887177a87d8d5c9", + "test_hash": "b07719b4afb26baeff40c15d2e69ecd7b2d7575f4871ab07ea99afc6d64dc640", + "test_files": { + "test_click_executor.py": "b07719b4afb26baeff40c15d2e69ecd7b2d7575f4871ab07ea99afc6d64dc640" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_executor_python.json b/.pdd/meta/server_executor_python.json new file mode 100644 index 0000000000..69d7a269e8 --- /dev/null +++ b/.pdd/meta/server_executor_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.237383+00:00", + "command": "fix", + "prompt_hash": "77f3e901f05b202431961276e304ea0327345a69924dff2caf8ab5322ee9e95f", + "code_hash": "7b4662572a02d650c81af625290ecbd7e1823bb3d4247326d726f9ba95fc511c", + "example_hash": "fb5d8d40aeeb2bf401dc6a84a43bee6936ee1e734b2b00bd08e7fbc44219a9cf", + "test_hash": "455266ddce3a40b351c4d1e56cb2b9a0afc23cac6a70bbae84f92f0ecf715684", + "test_files": { + "test_executor.py": "455266ddce3a40b351c4d1e56cb2b9a0afc23cac6a70bbae84f92f0ecf715684" + }, + "include_deps": { + "context/click_executor_example.py": "322d322bcbe2672e9adb1d52744018f38ae6ae3209711441816f923727760b8e", + "context/core/cli_example.py": "bbc8ef9798628f6ed733c17b3863585b3724cd08c7e09ace0049c13fc157abb2", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_jobs_python.json b/.pdd/meta/server_jobs_python.json index cfe8bd735a..fbe86bbbb4 100644 --- a/.pdd/meta/server_jobs_python.json +++ b/.pdd/meta/server_jobs_python.json @@ -1,8 +1,8 @@ { - "pdd_version": "0.0.231", - "timestamp": "2026-05-08T21:23:44.571476+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.238661+00:00", "command": "fix", - "prompt_hash": "0e9e0e584f70825743a2ff11ea0d73cdc43df6030ef9707b385fb23475348f61", + "prompt_hash": "c79d067d505a37c8d69ffb22ca520baa6ab385be61cd489b9e63627393585dd1", "code_hash": "975aa1aaab1500f7ad8e0dfe667842fb43a5b44e045ebd4e2a7cd177aa0af3e7", "example_hash": "7694bd486d012fd9d52543c5f79a289f61dac3ed0f8c94c2ea9fd25ed6333f12", "test_hash": "0027ff985ee28e4f4cc9823f793f737c3b1b6e9cb7a526a58f2d7fa6f42945e5", @@ -10,7 +10,7 @@ "test_jobs.py": "0027ff985ee28e4f4cc9823f793f737c3b1b6e9cb7a526a58f2d7fa6f42945e5" }, "include_deps": { - "context/python_preamble.prompt": "57a3e51f529024ec0cb9658cd6ac61a7c8051ba0c8e887b31cf00b2e78a07d83", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", "context/server/jobs_example.py": "7694bd486d012fd9d52543c5f79a289f61dac3ed0f8c94c2ea9fd25ed6333f12", "context/server/models_example.py": "223a832718e72ca6640728e4bd700da58edb89667931085d7d949774d8fbc228" } diff --git a/.pdd/meta/server_models_python.json b/.pdd/meta/server_models_python.json new file mode 100644 index 0000000000..c95c534025 --- /dev/null +++ b/.pdd/meta/server_models_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.240414+00:00", + "command": "fix", + "prompt_hash": "afa81609237d8c779745c0e4d660247233d9f7626443976f6efa300e80e6744e", + "code_hash": "a17b2f09033b2fe7063f484a2efb59184f7cb2fba822ee25605a93bbe5905941", + "example_hash": "223a832718e72ca6640728e4bd700da58edb89667931085d7d949774d8fbc228", + "test_hash": "df4d2f5cebc4b26915c7120e12f4168d71741c59eda7dd29202c141c9aabac8d", + "test_files": { + "test_models.py": "df4d2f5cebc4b26915c7120e12f4168d71741c59eda7dd29202c141c9aabac8d" + }, + "include_deps": { + "context/fastapi_example.py": "4b3d64175b8f46b48a4387e9cf98ed4024fd74a57413bd2f8a55c5d9c3dd0108", + "context/job_manager_example.py": "e421698fdf16c5dfebf97eb3f2cb7210878efa1869804f871100489e35102543", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/websocket_example.py": "6f50399fd41177e47da3f170a1cc84380f8f266036e8f510f7807a916265ab0f" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_routes_architecture_python.json b/.pdd/meta/server_routes_architecture_python.json new file mode 100644 index 0000000000..7ca941fd5f --- /dev/null +++ b/.pdd/meta/server_routes_architecture_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.242130+00:00", + "command": "fix", + "prompt_hash": "a768e3fc89c1c6f5aa261492547c841a9239893662727f682404d6b749752d37", + "code_hash": "67df73835b6282fde848c0ab558f3fa18c108b2b27b20aec8cf23f2f1a291446", + "example_hash": "6adddc3703c575ef7eaa12c58df0067e438c2fe328e5037fb69856595e862bf8", + "test_hash": "46e1daefed9b370ac235b49d9ec762bf27f945d37d31176dceb196606ed2e3fd", + "test_files": { + "test_architecture.py": "46e1daefed9b370ac235b49d9ec762bf27f945d37d31176dceb196606ed2e3fd" + }, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0", + "context/fastapi_example.py": "4b3d64175b8f46b48a4387e9cf98ed4024fd74a57413bd2f8a55c5d9c3dd0108", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_routes_auth_python.json b/.pdd/meta/server_routes_auth_python.json new file mode 100644 index 0000000000..b92d08e628 --- /dev/null +++ b/.pdd/meta/server_routes_auth_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.243453+00:00", + "command": "fix", + "prompt_hash": "c5ed12c3a857e38d8431cafd7a817c18642693b62bc746681efd7be9c5c0460f", + "code_hash": "0964f8ca62a4b95d114f8938c5afd5cf5ec480a350b7bd442fa4fc62ef200eb3", + "example_hash": "8d6c5e1e858f07440184c0ae1b7f98ffa53a57233031513fa6566131404ceab5", + "test_hash": "701217ca61602e22b3dfe34efce69d732857292b5ee21057b65a474627e682d3", + "test_files": { + "test_auth.py": "701217ca61602e22b3dfe34efce69d732857292b5ee21057b65a474627e682d3" + }, + "include_deps": { + "context/auth_service_example.py": "4b6d2c8c9e13d1edc0d9805ee0c07657494cbf31dffe2bb0029b72975674a8cf", + "context/get_jwt_token_example.py": "76a87c8257cb1f4cfdc2edff6225c752432afb3a5e352fcb016d08d8eae16e01", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_routes_commands_python.json b/.pdd/meta/server_routes_commands_python.json new file mode 100644 index 0000000000..efc9ed807f --- /dev/null +++ b/.pdd/meta/server_routes_commands_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.245758+00:00", + "command": "fix", + "prompt_hash": "924811be56ddaf51635685a42a7a4019dd92d77486051d5e1e800bcdcf268b08", + "code_hash": "aaa1af05971354a74e545756b347fe1f718875bd71d12c4bd51962ad084e0fe4", + "example_hash": "2aee960b3f00528d1da6cd878806b82a9ea703827d66a8618d1899d6ea9076ea", + "test_hash": "a60d4df0560867a7cbab7edb3526fc4f5da7fb81cfa5ada9c925a029f7155f2c", + "test_files": { + "test_commands.py": "a60d4df0560867a7cbab7edb3526fc4f5da7fb81cfa5ada9c925a029f7155f2c" + }, + "include_deps": { + "context/cli_example.py": "b2205688a0e74867c4e108ef97770861d2ecf21419d9f3e10991acbeb8c4235a", + "context/fastapi_example.py": "4b3d64175b8f46b48a4387e9cf98ed4024fd74a57413bd2f8a55c5d9c3dd0108", + "context/job_manager_example.py": "e421698fdf16c5dfebf97eb3f2cb7210878efa1869804f871100489e35102543", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/server/models_example.py": "223a832718e72ca6640728e4bd700da58edb89667931085d7d949774d8fbc228" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_routes_config_python.json b/.pdd/meta/server_routes_config_python.json new file mode 100644 index 0000000000..10f043e3d1 --- /dev/null +++ b/.pdd/meta/server_routes_config_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.246990+00:00", + "command": "fix", + "prompt_hash": "7ba89c5ce719f92cc6c9e281f1c94bf2594dd4f0e0229ae9cf75431e2787b11c", + "code_hash": "efe80f308a6a2acdb6a207288f746d39f0947da597c44c358595ff7b70e587d9", + "example_hash": "58f2b27cdf85200a52fbe76324d02431dc62f339afd3896de37c0fe759f55cde", + "test_hash": "d56946a2922b543bd87bdf3f124e24b648a374676463f7e695aa6ef1b994290f", + "test_files": { + "test_config.py": "d56946a2922b543bd87bdf3f124e24b648a374676463f7e695aa6ef1b994290f" + }, + "include_deps": { + "context/cloud_example.py": "92418beb7a1e7f25682bf2abce5b19013b95e7eed95256c930b83c1dd6dbfb0a", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/server/routes/config_example.py": "58f2b27cdf85200a52fbe76324d02431dc62f339afd3896de37c0fe759f55cde" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_routes_extracts_python.json b/.pdd/meta/server_routes_extracts_python.json new file mode 100644 index 0000000000..640990b72e --- /dev/null +++ b/.pdd/meta/server_routes_extracts_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.248482+00:00", + "command": "fix", + "prompt_hash": "3ee950acac8a66d9aa2331c30049a7f91935f73b3a9753534e89d98e133a6adc", + "code_hash": "1308d4b7c79d5560e31d25c60820405c1e5f3ef2560eb03674f0fb4e9cf03794", + "example_hash": "41070c16e3eca0acdf3b0877a08143df0d48b3fbd06db022fb7e1e883574688e", + "test_hash": "74e98e152be97fcc0658530f189051ba2d1e74386bf0cb358cdfbb355c5bba69", + "test_files": { + "test_extracts.py": "74e98e152be97fcc0658530f189051ba2d1e74386bf0cb358cdfbb355c5bba69" + }, + "include_deps": { + "context/extracts_prune_example.py": "b8a894f24d715f42be14b9977b6c091e75401e3f46f0722703fa63b61b26a7ee", + "context/fastapi_example.py": "4b3d64175b8f46b48a4387e9cf98ed4024fd74a57413bd2f8a55c5d9c3dd0108", + "context/include_query_extractor_example.py": "83c8b945de75bd49b258f4feabbdda3d9c08a06ad9afca3e90df7534f68a34aa", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_routes_files_python.json b/.pdd/meta/server_routes_files_python.json new file mode 100644 index 0000000000..7077399a05 --- /dev/null +++ b/.pdd/meta/server_routes_files_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.249927+00:00", + "command": "fix", + "prompt_hash": "33d6cabb10340b8fae1ab7b0ebe3439b0aff13aacffb9d1f1f837446a92be95c", + "code_hash": "966d3704d46b38d3448cad3b9f3b748b61e8b572c4281f6cc063da105e56d290", + "example_hash": "9e5d8e77e1d0238394c331a872fcb31e4597100fc9427d8bba7ee3550df4323d", + "test_hash": "e4b391cc756c8e86adeb7a9a43bb60a735b2063c24c88792243afe8b91595a16", + "test_files": { + "test_files.py": "e4b391cc756c8e86adeb7a9a43bb60a735b2063c24c88792243afe8b91595a16" + }, + "include_deps": { + "context/fastapi_example.py": "4b3d64175b8f46b48a4387e9cf98ed4024fd74a57413bd2f8a55c5d9c3dd0108", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/server/models_example.py": "223a832718e72ca6640728e4bd700da58edb89667931085d7d949774d8fbc228", + "context/server/security_example.py": "dc4f3dc06455ba6180f4536aa67ce4e9a5efd52e0e9cd7702c3f2ec6ef96aac9" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_routes_prompts_python.json b/.pdd/meta/server_routes_prompts_python.json new file mode 100644 index 0000000000..d9622d098a --- /dev/null +++ b/.pdd/meta/server_routes_prompts_python.json @@ -0,0 +1,22 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.252037+00:00", + "command": "fix", + "prompt_hash": "5a129d5e7110839ed03cde6337526e04fd63eec16ab5e6385e999f28ab589f4b", + "code_hash": "b49c7a2072243f6d1b3bbe762e1f023d890fbbdccf4c57f882df3de04f7a971f", + "example_hash": "0c143874f91ca038a84580bd829fccf6adbe8bedfd33517089cc2bee6b7a04e1", + "test_hash": "7096ca56eb33b8d62a18e3327eac88fdaa702b7b205e22a8a946273c840fcd66", + "test_files": { + "test_prompts.py": "7096ca56eb33b8d62a18e3327eac88fdaa702b7b205e22a8a946273c840fcd66" + }, + "include_deps": { + "context/code_generator_main_example.py": "4b66f24a1e2ac4195255738566b0cccdac0971523a245d3a8b78ad45d9e1a872", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/fastapi_example.py": "4b3d64175b8f46b48a4387e9cf98ed4024fd74a57413bd2f8a55c5d9c3dd0108", + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/server/security_example.py": "dc4f3dc06455ba6180f4536aa67ce4e9a5efd52e0e9cd7702c3f2ec6ef96aac9", + "context/sync_determine_operation_example.py": "225d20ea0210bbf52650a9f424879ff495e574dd2411eb4b86592a6d06cf59a5" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_routes_websocket_python.json b/.pdd/meta/server_routes_websocket_python.json new file mode 100644 index 0000000000..fb9f682e17 --- /dev/null +++ b/.pdd/meta/server_routes_websocket_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.253309+00:00", + "command": "fix", + "prompt_hash": "95bf0f052dbb0161c69f356bae8d9cc273dce51db1729e520f87fa48fa40c583", + "code_hash": "961fbae5ff5944b4be4104be65320c8bfb3bda62bff4db6eeaf4df8b1da1039a", + "example_hash": "a4c2313449f3174039edf4ab1f2030247b4da0eb31e6411b1008516be95eb3cf", + "test_hash": "d6167bdd4e173c9a3f00555361206d57130d280419edc1f051cbb8608acb8405", + "test_files": { + "test_websocket.py": "d6167bdd4e173c9a3f00555361206d57130d280419edc1f051cbb8608acb8405" + }, + "include_deps": { + "context/job_manager_example.py": "e421698fdf16c5dfebf97eb3f2cb7210878efa1869804f871100489e35102543", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/server/models_example.py": "223a832718e72ca6640728e4bd700da58edb89667931085d7d949774d8fbc228" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_security_python.json b/.pdd/meta/server_security_python.json new file mode 100644 index 0000000000..bd50ae45a3 --- /dev/null +++ b/.pdd/meta/server_security_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.254217+00:00", + "command": "fix", + "prompt_hash": "e54788c440ea949be62bfd1ce223d9fdb605884254981500234d3d2967e6d2c6", + "code_hash": "1eeb395579f49c24ce7a6b695438e74f738af2dac604384bf8e2105c3d6cf4e7", + "example_hash": "dc4f3dc06455ba6180f4536aa67ce4e9a5efd52e0e9cd7702c3f2ec6ef96aac9", + "test_hash": "db19cd096ac28c9be920565d84cb9c4a8b9ccc4b8eee2331d92477316483be83", + "test_files": { + "test_security.py": "db19cd096ac28c9be920565d84cb9c4a8b9ccc4b8eee2331d92477316483be83" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_terminal_spawner_python.json b/.pdd/meta/server_terminal_spawner_python.json new file mode 100644 index 0000000000..cf37d382de --- /dev/null +++ b/.pdd/meta/server_terminal_spawner_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.255677+00:00", + "command": "fix", + "prompt_hash": "c6238db322a2907172a42f470c7a345b8e4703692c1240e522c3e4c8d553a07b", + "code_hash": "efffd485569bb2f2e673b408c607a02a9b3325f46d2db8af2f4f8f8439f49fb5", + "example_hash": "de89761e9228d058064a52ed3bf4d84f2ceba1991e5b8e0a45a4af8f0c477e19", + "test_hash": "4cea9f3187218738c94ec6f09ae1c131d7fdef426dd4da6c495231e7e3163e42", + "test_files": { + "test_terminal_spawner.py": "4cea9f3187218738c94ec6f09ae1c131d7fdef426dd4da6c495231e7e3163e42" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/server_token_counter_python.json b/.pdd/meta/server_token_counter_python.json new file mode 100644 index 0000000000..a298eb5d0a --- /dev/null +++ b/.pdd/meta/server_token_counter_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.256309+00:00", + "command": "fix", + "prompt_hash": "af89232b742fe3ce556d33ac961e0a0342711e50c8305242b78d5406d3aee4db", + "code_hash": "847ac13e3804c451aca64652877940c06b72c2fc33edc00232f1d4aa16c957e8", + "example_hash": "75a40b397db75482e743fc7ec0ec26ae09650746111abb48a941e8d58214c9a5", + "test_hash": "f661f43b95b469139c1c46b8e0091a7fdf81a993ff73ab5331f8c13c8f6a1984", + "test_files": { + "test_token_counter.py": "f661f43b95b469139c1c46b8e0091a7fdf81a993ff73ab5331f8c13c8f6a1984" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/setup_tool_python.json b/.pdd/meta/setup_tool_python.json new file mode 100644 index 0000000000..19d31d3cf5 --- /dev/null +++ b/.pdd/meta/setup_tool_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.258619+00:00", + "command": "fix", + "prompt_hash": "ce35b42282c1f64f3c38653035d643e945e8b88a7ee291955b6620fb3e065176", + "code_hash": "8d2c78cd67bea21d2637669586eaa49608de0b0d6a5e1d36d6ddb9554e968baa", + "example_hash": "be376935a57f642e65ac1e938c2dec8e7b8dd66348164eaac648fcf4864e3b59", + "test_hash": "7447aae2c91e9706330b183766fe95ebe5d33e24b9f8f235dc9fe8d9e1b6b405", + "test_files": { + "test_setup_tool.py": "7447aae2c91e9706330b183766fe95ebe5d33e24b9f8f235dc9fe8d9e1b6b405" + }, + "include_deps": { + "context/cli_detector_example.py": "db64a53121864c633ba1586f1af491ae7cc617c10cf4c477a7e6d262f676cd23", + "context/model_tester_example.py": "9d33f093cdac4c100bf671e891a68c7f83e2e09e74cd65f6b53cb567d77d3cd0", + "context/pddrc_initializer_example.py": "3ba502fa9e2a2f438aaa890f0b654a98e5b8665253e10446acafda90f7eedad9", + "context/provider_manager_example.py": "cc2bbe6cefff4ad24af4365b0c4bc2c99e672f825d12edaca7307de9c04ecfb8" + } +} \ No newline at end of file diff --git a/.pdd/meta/split_main_python.json b/.pdd/meta/split_main_python.json new file mode 100644 index 0000000000..e3255722eb --- /dev/null +++ b/.pdd/meta/split_main_python.json @@ -0,0 +1,20 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.763585+00:00", + "command": "fix", + "prompt_hash": "f75671b4aae840f964ea24e517984b5debf6765a00cfb82ada6747be9aef2d65", + "code_hash": "381837b939069c6fef6c115caaefaa34c3103722de6ca95037ced13941edae75", + "example_hash": "ced192fcbfbfda15b35bd2dad5bbf4b4888acb091084cf58326832d224a77661", + "test_hash": "fbc89f592fccf464f1ea1f31f912861a00bd8789f18b2f2d310901e59d969887", + "test_files": { + "test_split_main.py": "fbc89f592fccf464f1ea1f31f912861a00bd8789f18b2f2d310901e59d969887" + }, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/split_example.py": "c36a6c3e5ddac660ef95d72dabc89f5453a3ebcd47f5f82ddc5fec77806154d9", + "pdd/validate_prompt_includes.py": "fa97b72a58534e255768634f928f4cfdd2130a7a4d8b296da3eefdf77d47855b" + } +} \ No newline at end of file diff --git a/.pdd/meta/split_python.json b/.pdd/meta/split_python.json new file mode 100644 index 0000000000..ad574f8a46 --- /dev/null +++ b/.pdd/meta/split_python.json @@ -0,0 +1,22 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.263057+00:00", + "command": "fix", + "prompt_hash": "b3d22d3a857a401b3e28f39864bef2a8bad98b59f0e2046731c878eae8a5b1a1", + "code_hash": "45894f5de8eb4f172b375f50858f89476bc39ab25bd1c6f0cf45dbb29f884b1e", + "example_hash": "c36a6c3e5ddac660ef95d72dabc89f5453a3ebcd47f5f82ddc5fec77806154d9", + "test_hash": "c0689f1b51cdf02e68d03f22a21828301701ab59c92fbf1ec9bad382ae8461c2", + "test_files": { + "test_split.py": "c0689f1b51cdf02e68d03f22a21828301701ab59c92fbf1ec9bad382ae8461c2", + "test_split_main.py": "fbc89f592fccf464f1ea1f31f912861a00bd8789f18b2f2d310901e59d969887", + "test_split_seam_resolution.py": "bce4215346ba2c4c22da9cd22313889df1229c6df8f66ffa1f561fa031f1233f", + "test_split_validation.py": "38159d3460e22b00859f413a841dbc515563bbeaf6c8e1c5b8671f9ba5a6ec98" + }, + "include_deps": { + "context/change/13/initial_split.py": "61247194419b17b8cd986efdbbbb6782a34e89b3cfe6e895ce24419490b33578", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/llm_invoke.py": "2c423dbe12dd22d1acfca6c658bfef36eaf3b016626ae4a2845576500bfcb54b", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56" + } +} \ No newline at end of file diff --git a/.pdd/meta/split_validation_python.json b/.pdd/meta/split_validation_python.json new file mode 100644 index 0000000000..4976998ab4 --- /dev/null +++ b/.pdd/meta/split_validation_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.264250+00:00", + "command": "fix", + "prompt_hash": "ac0c920040e138d26877842ef7e159726d5e1ed06d4811ef183449fac331eed5", + "code_hash": "e51a9512fc4cedbe362f25da3c97c23a89057901bff10c6b04eeefe77b79dafe", + "example_hash": "007c34d78c7605bb0ddfc3cb2df14095e387159b3ea746056e34cb304547a358", + "test_hash": "38159d3460e22b00859f413a841dbc515563bbeaf6c8e1c5b8671f9ba5a6ec98", + "test_files": { + "test_split_validation.py": "38159d3460e22b00859f413a841dbc515563bbeaf6c8e1c5b8671f9ba5a6ec98" + }, + "include_deps": { + "context/get_test_command_example.py": "33b16476536f8c9424d3222ffc4a5cd5ecee6ad0589b2c66a4f3c7f1e265106c", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/prompts/get_lint_commands_python.prompt": "d8c47a583973f8fd15353b01a4744b6ac7d31949beff51af7c917c9aac7736b7" + } +} \ No newline at end of file diff --git a/.pdd/meta/story_coverage_python.json b/.pdd/meta/story_coverage_python.json new file mode 100644 index 0000000000..de3d6aa2e6 --- /dev/null +++ b/.pdd/meta/story_coverage_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-09T22:54:16.565310+00:00", + "command": "fix", + "prompt_hash": "f87f5e1535e47a460b80bef1e5452899886ec2d6c4bdb1a153230e7bdc9d6a90", + "code_hash": "e7d0bd9054769ede102f721e343dc9da377961a228bd0dfbf92aaced93960a0b", + "example_hash": "8dc2e6fea81ea2bab743ab5e22295e41eb277bc75cd553fb35278020dc4eef02", + "test_hash": "08c59062b5dcd4f23a08edceca64d5e9f3e87135298efda8be8fd4c3f61ae3ac", + "test_files": { + "test_story_coverage.py": "08c59062b5dcd4f23a08edceca64d5e9f3e87135298efda8be8fd4c3f61ae3ac" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/story_regression_gate_python.json b/.pdd/meta/story_regression_gate_python.json new file mode 100644 index 0000000000..25e51ab784 --- /dev/null +++ b/.pdd/meta/story_regression_gate_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-09T22:54:16.567924+00:00", + "command": "fix", + "prompt_hash": "b7bda11ea95f0eeaf4d1f21f4d2eb3fa2c06fff9485dba4b4f4655c28b8df09c", + "code_hash": "bd4a4ff48aee34786e450ab17c113e74bb68c4f42221d9a17d6cfd5023ea82fc", + "example_hash": "b1afd246420e7fa92c0650c8739646b1a6d68a8cecb664881899a276774cf8c4", + "test_hash": "514ae8c1ebf5ccef73caa0aa521b49e2995e10c33ed3e6cc3723bea0576ce7e8", + "test_files": { + "test_story_regression_gate.py": "514ae8c1ebf5ccef73caa0aa521b49e2995e10c33ed3e6cc3723bea0576ce7e8" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/story_regression_python.json b/.pdd/meta/story_regression_python.json new file mode 100644 index 0000000000..9a3fcfec0b --- /dev/null +++ b/.pdd/meta/story_regression_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-09T22:54:16.570565+00:00", + "command": "fix", + "prompt_hash": "8103e5b2755bf50a484a5f5cf420f2bb0e593c1f0d56d750573c99db81a85f77", + "code_hash": "1d921a6c4c8aefb97e4b5e5ce563c45273e14d8ca243ac147275d12d7b4aa94d", + "example_hash": "90aa347c0d78ae8e7e951cf390836da3778d7d9c70d7bd9407b7b1a1764c66f1", + "test_hash": "ea383b00fd2d08c7a0298f533d0d28b7d83bb1f0f5484da54c60514dcde8f045", + "test_files": { + "test_story_regression.py": "ea383b00fd2d08c7a0298f533d0d28b7d83bb1f0f5484da54c60514dcde8f045", + "test_story_regression_gate.py": "514ae8c1ebf5ccef73caa0aa521b49e2995e10c33ed3e6cc3723bea0576ce7e8" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/user_story_tests_example.py": "53214544b7af564ee41f178b9cbabc5162c9c6648638366f609a2ec5a46398d3" + } +} \ No newline at end of file diff --git a/.pdd/meta/story_test_generator_python.json b/.pdd/meta/story_test_generator_python.json new file mode 100644 index 0000000000..8dbd99664c --- /dev/null +++ b/.pdd/meta/story_test_generator_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.268366+00:00", + "command": "fix", + "prompt_hash": "08ebc4332fc8aace6e07544ef82bba07e1a1a9eb00ee78086633d510a7c70e35", + "code_hash": "8189426246b8bc0c1cc5563b93fbca74cdd5786875f06725c7d0ad78f177a119", + "example_hash": null, + "test_hash": "6c019ddd2abcbd71e06527b5e8fc129b4511b991baf1511d70d96ff84eb73eff", + "test_files": { + "test_story_test_generator.py": "6c019ddd2abcbd71e06527b5e8fc129b4511b991baf1511d70d96ff84eb73eff" + }, + "include_deps": {} +} \ No newline at end of file diff --git a/.pdd/meta/summarize_directory_python.json b/.pdd/meta/summarize_directory_python.json index ce819c39ea..fe6c333185 100644 --- a/.pdd/meta/summarize_directory_python.json +++ b/.pdd/meta/summarize_directory_python.json @@ -1,8 +1,8 @@ { - "pdd_version": "0.0.246.dev34", - "timestamp": "2026-05-21T02:39:13.009516+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.270468+00:00", "command": "update", - "prompt_hash": "d4bb457d5fbf85648e7c2222affd99928729947aa2a46936b7ae2c855bf34d80", + "prompt_hash": "693ec6464aea87ceb2babb81f138ebb328f0bc40eebfc7b8f82344033a81ea84", "code_hash": "e715f8c9f90dd9cb6b656ef83df4a09388edd97451e0706e964fe713eecf6aaf", "example_hash": "99f9b729f3b148891d471e79e5a29e67e39c30aad8e9922ced2982ca02b158ea", "test_hash": "049ed6ddc2cd8bd3915eeb79f0b87524eef136157f684cfd7453d35d0f799425", @@ -10,8 +10,8 @@ "test_summarize_directory.py": "049ed6ddc2cd8bd3915eeb79f0b87524eef136157f684cfd7453d35d0f799425" }, "include_deps": { - "context/llm_invoke_example.py": "48c5aece0ddd153f95ec8a53802d2173d4da12a920b398c5feebb307b9958417", + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" } -} +} \ No newline at end of file diff --git a/.pdd/meta/sync_animation_python.json b/.pdd/meta/sync_animation_python.json index c741954a75..5efec8de61 100644 --- a/.pdd/meta/sync_animation_python.json +++ b/.pdd/meta/sync_animation_python.json @@ -1,8 +1,8 @@ { - "pdd_version": "0.0.278.dev0", - "timestamp": "2026-06-19T04:14:17.628703+00:00", + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.780928+00:00", "command": "fix", - "prompt_hash": "d27f3b08a706dd32a91e67c7c4623ca47728f1b2138a8533ec25d67793568021", + "prompt_hash": "f5073703052cded4e199a33c4c1cb6ce37c6e2bcc9b87cd5c4bafb4d9dbdee39", "code_hash": "b9261a68d14049db76c89179f98ee1c45a073cc9a656607d447612e23f68e285", "example_hash": "5b6a8af940fe44241889a279c1a8d3c978fe0c6ec3afc648510d8e33ffa7b18c", "test_hash": "f76041e4a4e313d3005d5961e76c3b08bfc26549e498d341cefe31560c328ebd", @@ -13,7 +13,7 @@ "test_sync_animation_phases.py": "fa098d0dc0e6bb3f1f45863c1d01cf1a798fdcbaa3a402025aa241a39faef76c" }, "include_deps": { - "README.md": "80504ec987045c156641c781764ebbd33af20da3e8854307158fe1110fe1845b", + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", "context/logo_animation_example.py": "f32a5cc330235c3b61fb16ac62d3ae7531c7c6dbc11af077fc79054736f0944d", "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" } diff --git a/.pdd/meta/sync_determine_operation_python.json b/.pdd/meta/sync_determine_operation_python.json index 7445a7bb1d..9e150aaded 100644 --- a/.pdd/meta/sync_determine_operation_python.json +++ b/.pdd/meta/sync_determine_operation_python.json @@ -1,13 +1,13 @@ { - "pdd_version": "0.0.296.dev0", - "timestamp": "2026-07-09T09:54:54.515165+00:00", + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.787539+00:00", "command": "fix", - "prompt_hash": "67066038dc835de0304bdf437277088994f8293b858343af8a8c1ba00b7c86a2", - "code_hash": "e616ac746674a9f1e8be84bf3093d5e1f45ff0175bc3767b758f2d17b2c54132", + "prompt_hash": "8800e2a4bdc8a41cc66b6c1300d7d3907cd738766ff20311ea8d6f239aeecbcc", + "code_hash": "ef8e41ab61742b9d8eb8fd2b1ee1d4de415223e0eb56734b70dee0bdb30f4b30", "example_hash": "225d20ea0210bbf52650a9f424879ff495e574dd2411eb4b86592a6d06cf59a5", - "test_hash": "0e896d21fdf4a2974a3fc0ae3332308e2f2d3d30f4fdb13d39b075c3fb8ec04d", + "test_hash": "5ebd9f9a9c18a30e44193619a944ffbd4dc427274a79d98ee4eeaa1497d6da06", "test_files": { - "test_sync_determine_operation.py": "0e896d21fdf4a2974a3fc0ae3332308e2f2d3d30f4fdb13d39b075c3fb8ec04d" + "test_sync_determine_operation.py": "5ebd9f9a9c18a30e44193619a944ffbd4dc427274a79d98ee4eeaa1497d6da06" }, "include_deps": { "context/agentic_langtest_example.py": "dbc5c87dc1b75d3299dbfb67b17df1838f9ba2c9bdb457097df0832cea112c3f", diff --git a/.pdd/meta/sync_main_python.json b/.pdd/meta/sync_main_python.json new file mode 100644 index 0000000000..6800314c50 --- /dev/null +++ b/.pdd/meta/sync_main_python.json @@ -0,0 +1,24 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.795086+00:00", + "command": "fix", + "prompt_hash": "09a082d96379107d0e0076310eadd4d617681dbcf139bde90c39705c1c521ae0", + "code_hash": "cca0497f63c1f43f3e8c9359f6a6fa7d0e8b3e9a5082aa911294907b7515b602", + "example_hash": "9a7961a4044a54c46ce6c43c8adbbb83da46f233d9c6a9eaa28302440c0052c0", + "test_hash": "30f5651c0868812dacc7141629dc7ba11afb6a4417e695e98c2f74c1dfa4d1d1", + "test_files": { + "test_sync_main.py": "30f5651c0868812dacc7141629dc7ba11afb6a4417e695e98c2f74c1dfa4d1d1" + }, + "include_deps": { + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", + "context/auth_service_example.py": "4b6d2c8c9e13d1edc0d9805ee0c07657494cbf31dffe2bb0029b72975674a8cf", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/sync_main_example.py": "9a7961a4044a54c46ce6c43c8adbbb83da46f233d9c6a9eaa28302440c0052c0", + "context/sync_orchestration_example.py": "cc1313a948946026d418c85a07e5bf0eedeb5915c44d630440e489979755e1f7", + "pdd/agentic_sync_runner.py": "572f865d1bdbddf4827d5f308eb72030c3ed085c4f570284cbda8c961d01af2d", + "pdd/code_generator_main.py": "e83cc9bd8890d032fcf63001b83feb766d632ab08d00e20110e489e0386962b4", + "pdd/compressed_sync_context.py": "661fde0fcc42ce39c1004e47a774a8fd799b789065519cb5688949c35fed4859" + } +} \ No newline at end of file diff --git a/.pdd/meta/sync_orchestration_python.json b/.pdd/meta/sync_orchestration_python.json index 5b52c7dd61..f47a1b6a58 100644 --- a/.pdd/meta/sync_orchestration_python.json +++ b/.pdd/meta/sync_orchestration_python.json @@ -1,31 +1,32 @@ { - "pdd_version": "0.0.234", - "timestamp": "2026-05-12T02:53:56.362536+00:00", + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-10T02:49:03.806824+00:00", "command": "fix", - "prompt_hash": "b9b900ce1b0cf9e50b73c4aad750e2231f632beda3c286c0469f21c0dc3a8475", - "code_hash": "7ef19b3e7eafd7abd0f54c9de74b7c4d8be5559d84b2673f1c60cb9313d4842e", + "prompt_hash": "33db18386d74d066127d81053e0f4c0f73284584c77e29524a968a8d23b209f2", + "code_hash": "72a9e22929b3538e8c63915bf12568998d5ae1226d84b4518c6f4e460f06a2b6", "example_hash": "cc1313a948946026d418c85a07e5bf0eedeb5915c44d630440e489979755e1f7", - "test_hash": "3fce96bc02b851cc3e2d00001b0de3a2436f687c65a53d5e423212ac5c6d2517", + "test_hash": "807987d709b08ea7064a727bfa536ffcd10441ec1742f4743e9a4c25b8f8a63f", "test_files": { - "test_sync_orchestration.py": "3fce96bc02b851cc3e2d00001b0de3a2436f687c65a53d5e423212ac5c6d2517" + "test_sync_orchestration.py": "807987d709b08ea7064a727bfa536ffcd10441ec1742f4743e9a4c25b8f8a63f" }, "include_deps": { - "docs/whitepaper.md": "25b87d4af53ba35e7fa80b12f6bf09d12e16b8b952341df94cf8b47160886adf", - "README.md": "b572bb7bf64f3d9473845a45b8c942fc66b527bc516c960059bc538e574ae4bc", + "docs/whitepaper.md": "3347db03fa35376024b59d8546efdf0858655746490561b88b710fa6b85017cc", + "README.md": "20fa5b831d67be4650513b50015390462ef972943077b5c63faebaf91906d1f2", "context/auto_deps_main_example.py": "9b9c36c18d39494b6e31b46f6e4758c25135b6fa3d1672fbb7365844f7d2de04", "context/cmd_test_main_example.py": "0774984ee29f70407429f1eff4a1b76287715b7ddd6adf2bc149e874c9b844bb", - "context/code_generator_main_example.py": "679590f0913600886d99469d8ece23fbbfe6bea2e626b0d5d2ab5fdcdc079b80", + "context/code_generator_main_example.py": "4b66f24a1e2ac4195255738566b0cccdac0971523a245d3a8b78ad45d9e1a872", "context/context_generator_main_example.py": "77c3de786f31392540ec609d4b3942e98e996ae14c4014f5018cf2b05a560df4", "context/crash_main_example.py": "5c9b909107f74f773ccf7013d556a5812643d2a3b086e79fe07da7ea7877458c", "context/fix_main_example.py": "29f88d8bfcc2256ebebe6efcb611f9487eb5ca6d8781286a6dd98689f92bca7a", "context/fix_verification_main_example.py": "6490f398aca6df47b1a95e46a4ecfc0aabe299b5022de5c31ebc436e1bc3e459", "context/get_extension_example.py": "def9c34c1416259a0a031a996b96aeff5e73a7012adff6af1cc557e74ba97cc8", - "context/get_run_command_example.py": "4d187e844adb255c2738274556f4828728d0d73d84ebf8bda885d51d4be9414f", "context/get_test_command_example.py": "33b16476536f8c9424d3222ffc4a5cd5ecee6ad0589b2c66a4f3c7f1e265106c", - "context/operation_log_example.py": "6cd7962ab255d6080aeb97897175250f987ce936cf7333e7601f10834405a940", - "context/pytest_output_example.py": "6f0cd68e3c4440081267c716651caec4fbdd7d9c4516f967517f02ae2a853920", + "context/operation_log_example.py": "2660def7012d9b79bad3db33cf399ac603cc1c35dff0b064cbdd2638a013de1d", "context/python_env_detector_example.py": "65619b3e8bad0509f1ccf54d81b5c3760f423b6fc1925c932fc7968da78a172e", - "context/sync_determine_operation_example.py": "94a333b37d9875bcc9b39e22d4d00de1b196ad2f7e0fa997e6f638e153dbdfad", - "context/update_main_example.py": "b318834c648f25e6141d81f3ecac1d9da592a6bb4740500983f2b07b8a10d902" + "context/sync_determine_operation_example.py": "225d20ea0210bbf52650a9f424879ff495e574dd2411eb4b86592a6d06cf59a5", + "context/update_main_example.py": "9fe01fd19196c2604c91d101e49db849e0e1c9fabafeb2ad831a9c91b2f26416", + "pdd/compressed_sync_context.py": "661fde0fcc42ce39c1004e47a774a8fd799b789065519cb5688949c35fed4859", + "pdd/get_run_command.py": "5eec778a234abc4ace4166d71a68f6e49f1b81491962867de81f61be882a5319", + "pdd/pytest_output.py": "652a27dd1e08769611abe3cb6a83f372c450279d6bf1b676741b9b7186665975" } } \ No newline at end of file diff --git a/.pdd/meta/sync_order_python.json b/.pdd/meta/sync_order_python.json new file mode 100644 index 0000000000..cc4c626617 --- /dev/null +++ b/.pdd/meta/sync_order_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.290763+00:00", + "command": "fix", + "prompt_hash": "427b20071b0f873b8a9db5550f040fbea6ba89a49ae58f421ceccfc545679616", + "code_hash": "6cdaed3b56d9cc12f38ee5ac93b8623899372ea2d177c4f131ec258edb94d0dd", + "example_hash": "b4eb1e167e7604c7dbac703bdcba7287188fa9d2d5ef6d490216d003cdc542fa", + "test_hash": "196505cf8e1fa198c07c118479a6847bc2b921efb8f01be98651e01f302bbf85", + "test_files": { + "test_sync_order.py": "196505cf8e1fa198c07c118479a6847bc2b921efb8f01be98651e01f302bbf85" + }, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0", + "context/auto_include_example.py": "0c278ed716298149777bc7082827eb8aa6d80dac264c66b6cded6871232f2df7", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/sync_tui_python.json b/.pdd/meta/sync_tui_python.json index 6eecff6cd9..5aeacd8d92 100644 --- a/.pdd/meta/sync_tui_python.json +++ b/.pdd/meta/sync_tui_python.json @@ -1,6 +1,6 @@ { - "pdd_version": "0.0.257.dev0", - "timestamp": "2026-06-01T18:19:03.939345+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.292194+00:00", "command": "update", "prompt_hash": "5eb3bb0151fa49a4f51f87c06051545bfa07a3bdf27718f23616a6a301deefa2", "code_hash": "941cbbd6026742784188c66b97d64479daa733ffe1ff2d3d11f464e8a0e10992", diff --git a/.pdd/meta/template_expander_python.json b/.pdd/meta/template_expander_python.json new file mode 100644 index 0000000000..bd51a1492f --- /dev/null +++ b/.pdd/meta/template_expander_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.292975+00:00", + "command": "fix", + "prompt_hash": "a4c8ce5c8c337cf066e40130e120241bb16ae0c40ef874618269301f602c4d6c", + "code_hash": "bcaa670f334b9fa7726aa85906e70125ad2cd4b7e7b62a8e39f8bdd06c975edb", + "example_hash": null, + "test_hash": "5e04901bfb8617572d25b38c955c9b34a4d7da39d3fc7a91ce94ae146fc7203d", + "test_files": { + "test_template_expander.py": "5e04901bfb8617572d25b38c955c9b34a4d7da39d3fc7a91ce94ae146fc7203d" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/template_registry_python.json b/.pdd/meta/template_registry_python.json new file mode 100644 index 0000000000..aa3d9581ed --- /dev/null +++ b/.pdd/meta/template_registry_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.293861+00:00", + "command": "fix", + "prompt_hash": "28ec666c9c2ed6e91cf715d33bf544910ac0ccc825e10bca2fc3d514f65ea6b3", + "code_hash": "3b1b6758f82296d4d6aacef5bdd9a8fd597107ddd3cda8b8aa021102958308f2", + "example_hash": null, + "test_hash": "2fc00042ec2438d46a5c9ffb389fe1b54fe3aa610be391a83910130277431f21", + "test_files": { + "test_template_registry.py": "2fc00042ec2438d46a5c9ffb389fe1b54fe3aa610be391a83910130277431f21" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/test_context_packer_python.json b/.pdd/meta/test_context_packer_python.json new file mode 100644 index 0000000000..61e63cb56f --- /dev/null +++ b/.pdd/meta/test_context_packer_python.json @@ -0,0 +1,16 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.294900+00:00", + "command": "fix", + "prompt_hash": "e64578e078de825466c5b754a15fa32b4c50bdda7f7d34879d92d3c2efa1d6c7", + "code_hash": "27c7d14747d38df8edf24c8ef2ce1b7ca21d8f2eea38aec02ba06042ee144c0b", + "example_hash": "525c92e6dfd423097b29845182f7bfaca2ae8aea56da3bddbd00176e0480a509", + "test_hash": "f8d9545dfdace65a889ef4f4506716bb70cb4864453c6eb8717e5f14522e3abb", + "test_files": { + "test_test_context_packer.py": "f8d9545dfdace65a889ef4f4506716bb70cb4864453c6eb8717e5f14522e3abb" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/pytest_slicer.py": "c02a5488fe6f7248ed387636d5824cb2f1876544d3991e43ef253648a36e49a2" + } +} \ No newline at end of file diff --git a/.pdd/meta/trace_main_python.json b/.pdd/meta/trace_main_python.json new file mode 100644 index 0000000000..7cd1e09d9e --- /dev/null +++ b/.pdd/meta/trace_main_python.json @@ -0,0 +1,18 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.296297+00:00", + "command": "fix", + "prompt_hash": "2e2223ee3b5d3af757758c871e32a57e8ede5c485cbd8db0f89a3ed04b73c874", + "code_hash": "48e9cc107b5af5668c031ba532b93a685a2399ae2d8bad482ef269dcd561a5b8", + "example_hash": "c1f43ebf55c05af801640f5f8331ee7a674aef9e3f383b0e5ffee7abc7246fe1", + "test_hash": "b36552a48543bb90256f2d5eebd344dd57be51b49c7fcfeaffb8ff3d35f6bd2d", + "test_files": { + "test_trace_main.py": "b36552a48543bb90256f2d5eebd344dd57be51b49c7fcfeaffb8ff3d35f6bd2d" + }, + "include_deps": { + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/trace_example.py": "de4d5d1ca347ea7ef9acd11dc80e86188917f61355e68db9c397ca6fd5108f99" + } +} \ No newline at end of file diff --git a/.pdd/meta/trace_python.json b/.pdd/meta/trace_python.json new file mode 100644 index 0000000000..e940d4e750 --- /dev/null +++ b/.pdd/meta/trace_python.json @@ -0,0 +1,19 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.298078+00:00", + "command": "fix", + "prompt_hash": "42c3207f2f44e00b5ae055219f4a518548fd3c129c037be591edc92f8ff9673f", + "code_hash": "9ff879812f1def2c14995b939a3b6541590ee68a7f62b25e57064ac7d642fbcb", + "example_hash": "de4d5d1ca347ea7ef9acd11dc80e86188917f61355e68db9c397ca6fd5108f99", + "test_hash": "c0d73d7b507978339d7b99706919ff1c14f9199cc6c8a77d1712e155bafdea82", + "test_files": { + "test_trace.py": "c0d73d7b507978339d7b99706919ff1c14f9199cc6c8a77d1712e155bafdea82", + "test_trace_main.py": "b36552a48543bb90256f2d5eebd344dd57be51b49c7fcfeaffb8ff3d35f6bd2d" + }, + "include_deps": { + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "pdd/llm_invoke.py": "2c423dbe12dd22d1acfca6c658bfef36eaf3b016626ae4a2845576500bfcb54b", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56" + } +} \ No newline at end of file diff --git a/.pdd/meta/track_cost_python.json b/.pdd/meta/track_cost_python.json index eeeedde189..6e9008beec 100644 --- a/.pdd/meta/track_cost_python.json +++ b/.pdd/meta/track_cost_python.json @@ -1,13 +1,13 @@ { - "pdd_version": "0.0.246.dev34", - "timestamp": "2026-05-21T04:24:21.384951+00:00", + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.298731+00:00", "command": "update", - "prompt_hash": "413975fcaf5d900d1c7edd631ad843a00c1ebe983e70812f86e689821c6218fa", - "code_hash": "3a1282ced73cc96c0428a006db619282c23c31aed6a50e2d427f106505ebd0ce", + "prompt_hash": "caff809a3e28802d4aa83f89a1b9b4c183e484713d91f8324444093817c272ad", + "code_hash": "b8776071f0f9c97a4fd10d6f9bf55faab99dde3757d47e836ef51d1c41b8513c", "example_hash": "bd60efaa7d6274b0e4d1743ac05785461af65ec785b254730288d08774f72ed6", - "test_hash": "2d272934a7ce4f40ffb1555451ed71fb09ff5fb451b7fd5a6893374968e99d0f", + "test_hash": "62f0edf3a67830bb686edd5de6b053b0d8bf1e861547608979616beef8ca6f29", "test_files": { - "test_track_cost.py": "2d272934a7ce4f40ffb1555451ed71fb09ff5fb451b7fd5a6893374968e99d0f" + "test_track_cost.py": "62f0edf3a67830bb686edd5de6b053b0d8bf1e861547608979616beef8ca6f29" }, "include_deps": {} -} +} \ No newline at end of file diff --git a/.pdd/meta/unfinished_prompt_python.json b/.pdd/meta/unfinished_prompt_python.json new file mode 100644 index 0000000000..f1ad6d2d0e --- /dev/null +++ b/.pdd/meta/unfinished_prompt_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.299735+00:00", + "command": "fix", + "prompt_hash": "5f54934748a49136dbd656ec52e97590e7fd493d4299dbf01f111a3a4b78ed3c", + "code_hash": "b012d1c6fab0579ae674d2d6e26be119dd9288c7ba2741b75af516be7a4564bf", + "example_hash": "5d4d74064e5b845391f035fb8631903ec2f39a54a90713f9c13dc24d2eb73c4f", + "test_hash": "c39f40da08752722ae6ee7279d1fcade01150a122bea1fbb76f02faa091cbf63", + "test_files": { + "test_unfinished_prompt.py": "c39f40da08752722ae6ee7279d1fcade01150a122bea1fbb76f02faa091cbf63" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f" + } +} \ No newline at end of file diff --git a/.pdd/meta/update_main_python.json b/.pdd/meta/update_main_python.json new file mode 100644 index 0000000000..eadf44f4b9 --- /dev/null +++ b/.pdd/meta/update_main_python.json @@ -0,0 +1,28 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.303197+00:00", + "command": "fix", + "prompt_hash": "eb13aca686c206ea223dc9b6093627de4f37b158303323caa579311d65ac06e7", + "code_hash": "968fb4ec74af3245c758d975888e418c0ec2251edc14d56d545648538ccc10a6", + "example_hash": "9fe01fd19196c2604c91d101e49db849e0e1c9fabafeb2ad831a9c91b2f26416", + "test_hash": "d9edcd6b05c12d2121cce4cd042102a92e546025fe660611dd20a04235816e26", + "test_files": { + "test_update_main.py": "d9edcd6b05c12d2121cce4cd042102a92e546025fe660611dd20a04235816e26" + }, + "include_deps": { + "context/agentic_common_example.py": "c1538a115a3a33da228e7f6cc048f2a99023cd2c83a0efef382e49c477b24ad3", + "context/agentic_update_example.py": "967254cd1c636ac5ba09afa0446312ffc1f28d3041ed453a4c6588c931f722c4", + "context/api_key_scanner_example.py": "4b9f880a30445f25edffc394857ae0c218097726c41ab996a03fcbc4350b5482", + "context/architecture_sync_example.py": "b6158dbc7ca3fd32665528562225f6739ec521a071d845acab0f7caaea221ae0", + "context/auto_include_example.py": "0c278ed716298149777bc7082827eb8aa6d80dac264c66b6cded6871232f2df7", + "context/construct_paths_example.py": "412cb260650bcbd628fba647db917a8d32f8191df2e1c32cc22f22c0c11b1740", + "context/ctx_obj_params.prompt": "829e2ab327e37b75afcd0dcf3893dcaec9012a89a1f6e5198ec7cfc0395d2782", + "context/git_update_example.py": "811304cee2cb890879ee68a816aed49dff2e7b50cfdca3125e16e0ae99819574", + "context/metadata_sync_example.py": "704453b3386724eef225b61c48f2fe922e1a973ac211c494e1d955dd44bcca11", + "context/operation_log_example.py": "2660def7012d9b79bad3db33cf399ac603cc1c35dff0b064cbdd2638a013de1d", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/sync_determine_operation_example.py": "225d20ea0210bbf52650a9f424879ff495e574dd2411eb4b86592a6d06cf59a5", + "context/update_prompt_example.py": "56b9d8c7aa823c2996a3dc5741a47844e223bfd088366d74e48b6a099b5a11c4", + "pdd/validate_prompt_includes.py": "fa97b72a58534e255768634f928f4cfdd2130a7a4d8b296da3eefdf77d47855b" + } +} \ No newline at end of file diff --git a/.pdd/meta/update_model_costs_python.json b/.pdd/meta/update_model_costs_python.json new file mode 100644 index 0000000000..c5fb35954c --- /dev/null +++ b/.pdd/meta/update_model_costs_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.304549+00:00", + "command": "fix", + "prompt_hash": "b21d140be33ba15fa136934ddb6862522cad498a6d33c9d4ffc9d7777dfa5755", + "code_hash": "ca390bd220734fbd7f4588b1a060628dfa9f298335af827f616902ff60a7f38d", + "example_hash": "90c9e0bab64d7495f55ba5876f8beab75ef40037508b5e9800ebc6afb5b55f5e", + "test_hash": "aae551a19112ec7eea9b151f2818fdf5c7b49d8ef23ca9371257e31b2df1e2fa", + "test_files": { + "test_update_model_costs.py": "aae551a19112ec7eea9b151f2818fdf5c7b49d8ef23ca9371257e31b2df1e2fa" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/update_prompt_python.json b/.pdd/meta/update_prompt_python.json new file mode 100644 index 0000000000..a44ae3087d --- /dev/null +++ b/.pdd/meta/update_prompt_python.json @@ -0,0 +1,21 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.306811+00:00", + "command": "fix", + "prompt_hash": "1c92d709faafdcb2346184c538c57eff6afbbcf25e974f10d59f5eaaabf52916", + "code_hash": "4237e3fb40f2c1bb8f60f2d22305b80365f54c7834c1e97d997fcf803d52417e", + "example_hash": "56b9d8c7aa823c2996a3dc5741a47844e223bfd088366d74e48b6a099b5a11c4", + "test_hash": "dec5cce3090585615f7b97459b55c0800ac95e954f100ee73c88318f5cb99927", + "test_files": { + "test_update_prompt.py": "dec5cce3090585615f7b97459b55c0800ac95e954f100ee73c88318f5cb99927" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254", + "context/change/14/initial_change.py": "fec3fba35a4710e02375bdb017bcf33882c2a3b690733fc9d5c6021c1602785c", + "context/change/15/initial_cli.py": "b607c3e67702ad3bc5e61cc40ce85571e4ff09015de96016c8d7d56eb78b8c74", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "pdd/llm_invoke.py": "2c423dbe12dd22d1acfca6c658bfef36eaf3b016626ae4a2845576500bfcb54b", + "pdd/load_prompt_template.py": "903bb43e90d778fa04ba42cd8e329ac42e1b419bb372268f5955f26de43cce47", + "pdd/preprocess.py": "d04a6cfc261c1e88076398a34d0bf96c4285061668866c5ad305e4b605ffdf56" + } +} \ No newline at end of file diff --git a/.pdd/meta/user_story_tests_python.json b/.pdd/meta/user_story_tests_python.json new file mode 100644 index 0000000000..18436eb065 --- /dev/null +++ b/.pdd/meta/user_story_tests_python.json @@ -0,0 +1,15 @@ +{ + "pdd_version": "0.0.300.dev0", + "timestamp": "2026-07-09T22:54:16.624434+00:00", + "command": "fix", + "prompt_hash": "9c69ba13bbca4e3b342eff0d14380e7e9a1bd5ff95fee922f4677fdad0d85bac", + "code_hash": "48a1597e9b7cd409ab62e9ef6b005f5fdb29699207c24f640b149f7e79adf79b", + "example_hash": "53214544b7af564ee41f178b9cbabc5162c9c6648638366f609a2ec5a46398d3", + "test_hash": "8f9e336d3366396f4b8ff783eabf3c49df24a173c11a70972a5e13bc9ed43259", + "test_files": { + "test_user_story_tests.py": "8f9e336d3366396f4b8ff783eabf3c49df24a173c11a70972a5e13bc9ed43259" + }, + "include_deps": { + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/.pdd/meta/validate_prompt_includes_python.json b/.pdd/meta/validate_prompt_includes_python.json new file mode 100644 index 0000000000..ef3b9f64fa --- /dev/null +++ b/.pdd/meta/validate_prompt_includes_python.json @@ -0,0 +1,13 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.309213+00:00", + "command": "fix", + "prompt_hash": "8a1052a5a135f25c7a0106f13a19977606a55fdfb4b06124f708c4db8ede6f54", + "code_hash": "fa97b72a58534e255768634f928f4cfdd2130a7a4d8b296da3eefdf77d47855b", + "example_hash": "675dfe58e40d3d3d67b4d586475fb365ed6c0a4852de4c3354f219c972323a69", + "test_hash": null, + "test_files": {}, + "include_deps": { + "context/auto_include_example.py": "0c278ed716298149777bc7082827eb8aa6d80dac264c66b6cded6871232f2df7" + } +} \ No newline at end of file diff --git a/.pdd/meta/xml_tagger_python.json b/.pdd/meta/xml_tagger_python.json new file mode 100644 index 0000000000..386d914dac --- /dev/null +++ b/.pdd/meta/xml_tagger_python.json @@ -0,0 +1,17 @@ +{ + "pdd_version": "0.0.299", + "timestamp": "2026-07-09T20:41:18.310193+00:00", + "command": "fix", + "prompt_hash": "9d86a05cae62bf793be21f5b99d1bd77a82299b1b85146da2cac32c74ca978dd", + "code_hash": "9cb3c692f9bf5c5eb54d758887a26268aafe51f327f0996e6476121f452ffbec", + "example_hash": "3b4606a0ac11cb728a5aa657d457312c83f2d710f53f4ac94d35675f7e911f60", + "test_hash": "fb48083ac847a187654f68c8fa45050199661306ebfa326366b3e7037d92fb25", + "test_files": { + "test_xml_tagger.py": "fb48083ac847a187654f68c8fa45050199661306ebfa326366b3e7037d92fb25" + }, + "include_deps": { + "context/llm_invoke_example.py": "3e66413118ec2f8e8940e7a835932920309aeeb775d23ce728d78299fb99e2ec", + "context/load_prompt_template_example.py": "a1cd6619182c6c951f5856dda4070e202875a5884bbfab9cc191d24de2f4951f", + "context/python_preamble.prompt": "0388ed131bf986f8752e1bc4c81e4da0460cfe2908ec8c60b1314edbab768254" + } +} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index 04353b3255..e308dd6a80 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,12 @@ # Repository Guidelines +## Prompt-First Workflow (Read First) +Prompts are the source of truth; code is regenerated from them. This block enforces the doctrine's #1 anti-pattern, Prompt Drift (`docs/prompt-driven-development-doctrine.md` "Anti-Patterns"; `CONTRIBUTING.md` "Core Philosophy"). +- Behavior change: edit `pdd/prompts/_python.prompt` first, regenerate with `pdd sync ` (or `make generate MODULE=`), then adjust tests. +- Edited `pdd/.py` directly (hotfix): run `pdd update --sync-metadata` to back-propagate the change into the prompt and re-stamp the fingerprint BEFORE opening the PR; name the back-propagated units in the PR body. +- Never hand-edit `.pdd/meta/*.json` or `architecture.json`; use `pdd sync-architecture` or the fingerprint stamper (`scripts/stamp_fingerprints.py`). +- Tests accumulate: append tests; never delete passing tests when regenerating. + ## Project Structure & Module Organization Core CLI logic lives in `pdd/`, where each module aligns with a prompt template in `prompts/` (for example, `pdd/code_generator.py` pairs with `prompts/code_generator_python.prompt`). Generated examples drop into `context/`, CSV data into `data/`, and packaged artifacts into `dist/`. Public examples live under `examples/`, and central documentation, diagrams, and onboarding notes reside in `docs/`. diff --git a/CLAUDE.md b/CLAUDE.md index bf4723039c..6e6fb2e13f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,6 +2,13 @@ This repository contains the public PDD CLI. Keep changes focused on the open-source package and avoid adding private deployment or credential-backed workflow details. +## Prompt-First Workflow (Read First) +Prompts are the source of truth; code is regenerated from them. This block enforces the doctrine's #1 anti-pattern, Prompt Drift (`docs/prompt-driven-development-doctrine.md` "Anti-Patterns"; `CONTRIBUTING.md` "Core Philosophy"). +- Behavior change: edit `pdd/prompts/_python.prompt` first, regenerate with `pdd sync ` (or `make generate MODULE=`), then adjust tests. +- If you edited `pdd/.py` directly (hotfix): run `pdd update --sync-metadata` to back-propagate the change into the prompt and re-stamp the fingerprint BEFORE opening the PR; list the back-propagated units in the PR body. +- Never hand-edit `.pdd/meta/*.json` or `architecture.json`; use `pdd sync-architecture` or the fingerprint stamper (`scripts/stamp_fingerprints.py`). +- Tests accumulate: append tests; never delete passing tests when regenerating. + ## Commands - Install dependencies: `pip install -e ".[dev]"` or `pip install -e .` - Run all tests: `pytest -vv tests/` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5eeaf8716b..743cb147da 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,6 +13,7 @@ Thank you for considering contributing to PDD CLI! This guide summarizes how we - Keep spec/README, prompts, code, examples, and tests in sync. - Tests accumulate: append tests; don’t regenerate or remove them. - Modular prompt graph: prompts compose via includes. +- Coding agents follow the same rules; see `AGENTS.md`, `CLAUDE.md`, and `GEMINI.md`. The #1 doctrine anti‑pattern is Prompt Drift (`docs/prompt-driven-development-doctrine.md`, “Anti‑Patterns”). ## The “Dev Unit” Each complete change ideally includes four elements: @@ -21,6 +22,23 @@ Each complete change ideally includes four elements: - Example interface: usage examples that enable prompt composability. - Tests: appended to relevant test files to reproduce bugs or validate features. +## Fingerprints (`.pdd/meta`) +Every dev unit has a committed fingerprint in `.pdd/meta/_.json` recording the +SHA-256 of its prompt (plus `` deps), code, example, and tests. This committed set is +the oracle `pdd sync` trusts to decide whether a unit changed — a stale or missing fingerprint +makes sync see phantom drift and regenerate a mature module. So: + +- **Never hand-edit `.pdd/meta`.** Regenerate the whole set with the stamper: + ```bash + python scripts/stamp_fingerprints.py # restamp every unit + python scripts/stamp_fingerprints.py --check # verify (what CI runs; exits non-zero on drift) + ``` +- **All** dev units are tracked (no `.gitignore` allowlist). If you add or change a prompt/code + file, run the stamper and commit the updated `.pdd/meta/*.json`. CI's `--check` gate fails + otherwise. +- A unit that `pdd sync` genuinely cannot fingerprint (ambiguous module identity, or no code file + on disk) is listed with a justification in `scripts/fingerprint_waivers.json`. + ## Contribution Workflow 1) Get set up - Fork the repository and clone your fork. @@ -38,7 +56,7 @@ Each complete change ideally includes four elements: 4) Implement - Prefer using PDD to regenerate code from prompts. -- If editing code directly, run `pdd update` to sync changes back into prompts (until all prompts are public, maintainers may help sync). +- If editing code directly, run `pdd update --sync-metadata` to sync changes back into prompts and re‑stamp the fingerprint / reconcile `architecture.json` before opening the PR (until all prompts are public, maintainers may help sync). Never hand‑edit `.pdd/meta/*.json` or `architecture.json`; use `pdd sync-architecture` or the fingerprint stamper (`scripts/stamp_fingerprints.py`). - Keep example interfaces updated to reflect new/changed behavior. - Useful prompt tags when needed: ``, ``, ``, ``. diff --git a/GEMINI.md b/GEMINI.md index b16e567758..542db9a435 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -4,6 +4,13 @@ This repository contains the public PDD CLI. > **Migration note (Issue #1152):** PDD now supports Google's new Antigravity CLI (`agy`) alongside the legacy Gemini CLI (`gemini`). Both map to the `google` agentic provider. Selection precedence: `PDD_AGENTIC_PROVIDER=antigravity` (pin Antigravity) > `PDD_GOOGLE_CLI=agy|gemini|auto` (binary picker; `auto` prefers `agy` when installed and credentialed, but uses legacy `gemini` when both binaries are installed and the only Google auth signal is `~/.gemini/oauth_creds.json`) > auto-detect. Antigravity can authenticate through OAuth, keyring-backed Google subscription sign-in, `ANTIGRAVITY_API_KEY`/`GOOGLE_API_KEY`, Vertex AI env auth, or PDD's compatibility bridge from `GEMINI_API_KEY`; legacy `gemini` remains the explicit rollback path and uses its own OAuth store or `GEMINI_API_KEY`/`GOOGLE_API_KEY`. Google announced consumer-tier Gemini CLI cutoff on **2026-06-18**. +## Prompt-First Workflow (Read First) +Prompts are the source of truth; code is regenerated from them. This block enforces the doctrine's #1 anti-pattern, Prompt Drift (`docs/prompt-driven-development-doctrine.md` "Anti-Patterns"; `CONTRIBUTING.md` "Core Philosophy"). +- Behavior change: edit `pdd/prompts/_python.prompt` first, regenerate with `pdd sync ` (or `make generate MODULE=`), then adjust tests. +- Edited `pdd/.py` directly (hotfix): run `pdd update --sync-metadata` to back-propagate the change into the prompt and re-stamp the fingerprint BEFORE opening the PR; name the back-propagated units in the PR body. +- Never hand-edit `.pdd/meta/*.json` or `architecture.json`; use `pdd sync-architecture` or the fingerprint stamper (`scripts/stamp_fingerprints.py`). +- Tests accumulate: append tests; never delete passing tests when regenerating. + ## PDD CLI Development - This is a Python CLI tool. diff --git a/README.md b/README.md index 11344fc39a..b3d7656819 100644 --- a/README.md +++ b/README.md @@ -1040,6 +1040,7 @@ Options: - `--skip-tests`: Skip unit test generation and fixing - `--target-coverage FLOAT`: Desired code coverage percentage (default is 90.0) - `--compress`: Use AST-based compression for Python few-shot examples (strips docstrings and logic-external comments). Helps fit more context into limited LLM windows without losing executable logic. +- `--fresh`: Disable the default surgical/edit-shaped regeneration of a mature module. By default, when a module already has non-empty code and its prompt changed, `pdd sync` edits the existing code in place (feeding the current code plus the prompt delta to the generator) so declared public symbols are preserved rather than dropped by a from-scratch rewrite. With `--fresh`, sync uses standard generation, which regenerates the module from scratch when the prompt change is large — use it when you intend a large rewrite rather than an in-place edit. New/empty modules are always generated fresh, and the public-surface / declared-interface gate still guards either path. `--fresh` acts on the standard multi-step single-module sync; in one-session/agentic sync the code is regenerated by the agent session, so `--fresh` only affects from-scratch (re)generation there. Single-module sync only: passing `--fresh` to project-wide (no-argument) or GitHub-issue agentic sync raises a `UsageError`. - `--dry-run`: Display real-time sync analysis instead of running sync operations. For no-argument project-wide sync, this prints the dependency-ordered module list and estimated cost without executing any module syncs, plus a single compact roll-up of modules outside the Tier 1 (`generate` / `auto-deps`) scope — bucketed by reason (e.g. `Out of Tier 1 scope: 42 example, 31 test, 18 verify, 12 update, 74 no-prompt fixture`) instead of one warning line per skipped entry. When zero modules are stale, the `0 stale module(s)` fragment is rendered in green so the success signal is visually unambiguous. Actionable architecture-graph warnings (ambiguous or unresolved cross-arch dependencies) are still printed individually in yellow. For single-module sync, it performs the same state analysis as a normal sync run but without acquiring exclusive locks or executing operations. Passing the top-level `pdd --verbose` flag (see above) restores the legacy per-module enumeration after the compact roll-up — one yellow warning line per module outside the Tier 1 scope — for debugging. - `--snapshot-context`: Capture the fully expanded prompt context used for generation, including nondeterministic ``, ``, and `` outputs. The run manifest is `.pdd/evidence/runs/.json`; snapshot artifacts are in `.pdd/evidence/runs//`. Replay can later reconstruct the same prompt/context from the recorded run artifact. - `--compressed-context / --no-compressed-context`: Enable or disable compressed sync context for generation and repair phases. This option is tri-state internally: omitting it lets `.pddrc` `defaults.compressed_context` apply, `--compressed-context` forces it on, and `--no-compressed-context` forces it off. When enabled, sync builds bounded phase packages from the prompt, existing tests, examples when present, contract sections, and recent repair evidence, then passes those packages to generate, verify, test, and fix attempts. The sync result records whether compression was used and whether any agentic fallback was needed. @@ -1157,7 +1158,7 @@ pdd sync --no-one-session https://github.com/myorg/myrepo/issues/100 - **Smart Lock Management**: Prevents concurrent sync operations with automatic stale lock cleanup - Detects which files already exist and are up-to-date - Skips unnecessary steps (e.g., won't regenerate code if prompt hasn't changed) -- Uses git integration to detect changes and determine incremental vs full regeneration +- Uses git integration to detect changes; regenerates mature modules surgically (edit-shaped) by default so declared symbols are preserved, falling back to full regeneration for new modules or when `--fresh` is passed - Accumulates tests over time rather than replacing them (in a single test file per target) - Automatically handles dependencies between steps - **Compressed Context Telemetry**: Sync results and logs record whether compressed context was requested, the effective value after CLI and `.pddrc` resolution, whether it was actually applied for each phase, the source inputs used to build it, and whether the run fell back to agentic repair. This makes replay and benchmark comparisons distinguish normal sync from compressed-context sync. @@ -3832,7 +3833,7 @@ Here are some common issues and their solutions: 9. **Sync-Specific Issues**: - **"Another sync is running"**: Check for stale locks in `.pdd/locks/` directory and remove if process no longer exists - **Complex conflict resolution problems**: Use `pdd --verbose sync --dry-run basename` to see detailed LLM reasoning and decision analysis - - **State corruption or unexpected behavior**: Delete `.pdd/meta/{basename}_{language}.json` to reset fingerprint state + - **State corruption or unexpected behavior**: Re-stamp the unit's fingerprint deterministically (no LLM) — use `pdd reconcile {basename} --backfill` to rebuild a missing/invalid fingerprint, or `pdd reconcile {basename} --heal` to re-stamp a drifted (but present) one. Run `pdd reconcile {basename}` first to see which state it's in. Do NOT hand-edit or delete `.pdd/meta/*.json` — the committed fingerprints are the drift oracle and must be regenerated, not edited (see CONTRIBUTING.md). - **Animation display issues**: Sync operations work in background; animation is visual feedback only and doesn't affect functionality - **Fingerprint mismatches**: Use `pdd sync --dry-run basename` to see what changes were detected and why operations were recommended diff --git a/architecture.json b/architecture.json index 8d8cbb5574..267590ae21 100644 --- a/architecture.json +++ b/architecture.json @@ -5505,7 +5505,7 @@ "commands": [ { "name": "sync_main", - "signature": "(ctx: click.Context, basename: str, max_attempts: Optional[int], budget: Optional[float], skip_verify: bool, skip_tests: bool, target_coverage: float, dry_run: bool, no_steer: bool = False, steer_timeout: Optional[float] = None, agentic_mode: bool = False, one_session: bool = False, compress: bool = False, evidence: bool = False, snapshot_context: bool = False, compressed_context: Optional[bool] = None) -> Tuple[Dict[str, Any], float, str]", + "signature": "(ctx: click.Context, basename: str, max_attempts: Optional[int], budget: Optional[float], skip_verify: bool, skip_tests: bool, target_coverage: float, dry_run: bool, no_steer: bool = False, steer_timeout: Optional[float] = None, agentic_mode: bool = False, one_session: bool = False, compress: bool = False, fresh: bool = False, evidence: bool = False, snapshot_context: bool = False, compressed_context: Optional[bool] = None) -> Tuple[Dict[str, Any], float, str]", "returns": "Tuple[Dict[str, Any], float, str]" } ] @@ -6917,8 +6917,7 @@ "reason": "Orchestrates the 18-step agentic test workflow with exploratory testing and plan validation.", "description": "Coordinates duplicate checking, docs review, clarification, frontend detection, test planning, plan enhancement, coverage assessment, manual testing, regression tests, test generation, plan validation, and PR submission.", "dependencies": [ - "agentic_common_python.prompt", - "load_prompt_template_python.prompt" + "agentic_common_python.prompt" ], "priority": 196, "filename": "agentic_test_orchestrator_python.prompt", @@ -11623,5 +11622,1146 @@ ] } } + }, + { + "reason": "Discovers API keys needed by the user's configured models, checking existence across shell, .env, and PDD config with source transparency.", + "description": "Discovers API keys needed by the user's configured models, checking existence across shell, .env, and PDD config with source transparency.", + "dependencies": [], + "priority": 251, + "filename": "api_key_scanner_python.prompt", + "filepath": "pdd/api_key_scanner.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "classes": [ + { + "name": "KeyInfo", + "kind": "dataclass" + } + ], + "functions": [ + { + "name": "get_provider_key_names", + "signature": "() -> List[str]", + "returns": "List[str]" + }, + { + "name": "scan_environment", + "signature": "() -> Dict[str, KeyInfo]", + "returns": "Dict[str, KeyInfo]" + } + ] + } + } + }, + { + "reason": "Cross-validate architecture.json dependency entries against tags in prompts.", + "description": "Cross-validate architecture.json dependency entries against tags in prompts.", + "dependencies": [], + "priority": 252, + "filename": "architecture_include_validation_python.prompt", + "filepath": "pdd/architecture_include_validation.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "collect_architecture_include_validation_warnings", + "signature": "(project_root: Path, *, skip_bundled_sample_arch: bool = True) -> List[str]", + "returns": "List[str]" + }, + { + "name": "run_validate_arch_includes_cli", + "signature": "(project_root: Path, *, strict: bool, quiet: bool) -> None", + "returns": "None" + }, + { + "name": "list_validate_arch_include_warnings", + "signature": "(project_root: Path, *, strict: bool) -> List[str]", + "returns": "List[str]" + }, + { + "name": "print_architecture_include_validation_warnings", + "signature": "(*, quiet: bool, verbose: bool = False) -> None", + "returns": "None" + }, + { + "name": "resolve_architecture_prompt_path", + "signature": "(filename: str, project_root: Path) -> Path", + "returns": "Path" + }, + { + "name": "validate_prompt_contract_context", + "signature": "(prompt_path: Path, output_path: Path, project_root: Path, architecture_path: Optional[Path] = None, prompt_content: Optional[str] = None, require_prompt_local_source_context: bool = False) -> List[str]", + "returns": "List[str]" + }, + { + "name": "cross_validate_architecture_with_prompt_includes", + "signature": "(arch_data: List[Dict[str, Any]], project_root: Path) -> List[str]", + "returns": "List[str]" + } + ] + } + } + }, + { + "reason": "Update architecture.json dependencies after auto-deps inserts new module tags.", + "description": "Update architecture.json dependencies after auto-deps inserts new module tags.", + "dependencies": [], + "priority": 253, + "filename": "auto_deps_architecture_python.prompt", + "filepath": "pdd/auto_deps_architecture.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "extract_include_paths_from_prompt_text", + "signature": "(text: str) -> Set[str]", + "returns": "Set[str]" + }, + { + "name": "merge_auto_deps_includes_into_architecture", + "signature": "(project_root: Path, written_prompt_path: Path, old_prompt_text: str, new_prompt_text: str, *, dry_run: bool = False) -> Dict[str, Any]", + "returns": "Dict[str, Any]" + }, + { + "name": "merge_auto_deps_includes_from_cwd", + "signature": "(written_prompt_path: Path, old_prompt_text: str, new_prompt_text: str, *, dry_run: bool = False) -> Dict[str, Any]", + "returns": "Dict[str, Any]" + } + ] + } + } + }, + { + "reason": "Detect PDD module basenames affected by a git diff.", + "description": "Detect PDD module basenames affected by a git diff.", + "dependencies": [], + "priority": 254, + "filename": "ci_detect_changed_modules_python.prompt", + "filepath": "pdd/ci_detect_changed_modules.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "detect", + "signature": "(diff_base: str) -> list[str]", + "returns": "list[str]" + }, + { + "name": "main", + "signature": "() -> int", + "returns": "int" + } + ] + } + } + }, + { + "reason": "Centralizes PDD CLI status progress messaging so every command reports start, current step, waiting-on-IO/LLM, success, and failure with one consistent, skimmable, non-duplicative, brand-themed vocabulary instead of ad-hoc per-module prints.", + "description": "Consistent status & progress messaging for the PDD CLI.", + "dependencies": [], + "priority": 255, + "filename": "cli_status_python.prompt", + "filepath": "pdd/cli_status.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "classes": [ + { + "name": "Status", + "kind": "enum" + }, + { + "name": "StatusMessage", + "kind": "dataclass" + }, + { + "name": "StatusReporter", + "kind": "class" + } + ], + "functions": [ + { + "name": "StatusMessage.glyph", + "signature": "(self) -> str", + "returns": "str" + }, + { + "name": "StatusMessage.role", + "signature": "(self) -> str", + "returns": "str" + }, + { + "name": "StatusMessage.render_plain", + "signature": "(self) -> str", + "returns": "str" + }, + { + "name": "StatusMessage.render_markup", + "signature": "(self) -> str", + "returns": "str" + }, + { + "name": "StatusReporter.emit", + "signature": "(self, message: StatusMessage) -> StatusMessage", + "returns": "StatusMessage" + }, + { + "name": "StatusReporter.start", + "signature": "(self, text: str, *, next_step: Optional[str] = None) -> StatusMessage", + "returns": "StatusMessage" + }, + { + "name": "StatusReporter.step", + "signature": "(self, text: str) -> StatusMessage", + "returns": "StatusMessage" + }, + { + "name": "StatusReporter.success", + "signature": "(self, text: str, *, next_step: Optional[str] = None) -> StatusMessage", + "returns": "StatusMessage" + }, + { + "name": "StatusReporter.failure", + "signature": "(self, text: str, *, reason: Optional[str] = None, suggestions: Sequence[str] = ()) -> StatusMessage", + "returns": "StatusMessage" + }, + { + "name": "StatusReporter.waiting", + "signature": "(self, text: str, *, on: str = 'LLM') -> Iterator[StatusMessage]", + "returns": "Iterator[StatusMessage]" + }, + { + "name": "from_context", + "signature": "(ctx, command: Optional[str] = None) -> StatusReporter", + "returns": "StatusReporter" + } + ] + } + } + }, + { + "reason": "Centralizes the PDD CLI color system so every command renders commands, tags, labels, and states with one consistent, brand-derived palette instead of ad-hoc per-module colors.", + "description": "Central CLI color system for PDD.", + "dependencies": [], + "priority": 256, + "filename": "cli_theme_python.prompt", + "filepath": "pdd/cli_theme.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "apply_global_color_preference", + "signature": "(preference: Optional[bool]) -> Callable[[], None]", + "returns": "Callable[[], None]" + }, + { + "name": "hex_to_ansi", + "signature": "(hex_color: str) -> str", + "returns": "str" + }, + { + "name": "get_console", + "signature": "(**kwargs) -> Console", + "returns": "Console" + }, + { + "name": "style", + "signature": "(role: str, text: str) -> str", + "returns": "str" + } + ] + } + } + }, + { + "reason": "Centralized config resolution for all commands, ensuring consistent priority ordering.", + "description": "Centralized config resolution for all commands.", + "dependencies": [], + "priority": 257, + "filename": "config_resolution_python.prompt", + "filepath": "pdd/config_resolution.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "set_cli_compression_override", + "signature": "(cli_config: Optional[Dict[str, Any]]) -> None", + "returns": "None" + }, + { + "name": "merge_cli_compression_override", + "signature": "(additional: Dict[str, Any]) -> None", + "returns": "None" + }, + { + "name": "get_cli_compression_override", + "signature": "() -> Optional[Dict[str, Any]]", + "returns": "Optional[Dict[str, Any]]" + }, + { + "name": "effective_compression_config", + "signature": "(pddrc_config: Dict[str, Any]) -> Dict[str, Any]", + "returns": "Dict[str, Any]" + }, + { + "name": "apply_compression_env", + "signature": "(config: Dict[str, Any]) -> None", + "returns": "None" + }, + { + "name": "resolve_effective_config", + "signature": "(ctx: click.Context, resolved_config: Dict[str, Any], param_overrides: Optional[Dict[str, Any]] = None) -> Dict[str, Any]", + "returns": "Dict[str, Any]" + } + ] + } + } + }, + { + "reason": "Detects and reports conflicts within prompt files.", + "description": "Detects and reports conflicts within prompt files.", + "dependencies": [], + "priority": 258, + "filename": "conflicts_in_prompts_python.prompt", + "filepath": "pdd/conflicts_in_prompts.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "classes": [ + { + "name": "ConflictChange", + "kind": "class" + }, + { + "name": "ConflictResponse", + "kind": "class" + } + ], + "functions": [ + { + "name": "conflicts_in_prompts", + "signature": "(prompt1: str, prompt2: str, strength: float = DEFAULT_STRENGTH, temperature: float = 0, time: float = DEFAULT_TIME, verbose: bool = False) -> Tuple[List[dict], float, str]", + "returns": "Tuple[List[dict], float, str]" + }, + { + "name": "main", + "signature": "()" + } + ] + } + } + }, + { + "reason": "Resolves configuration, validates input files, and determines output paths.", + "description": "Resolves configuration, validates input files, and determines output paths.", + "dependencies": [], + "priority": 259, + "filename": "construct_paths_python.prompt", + "filepath": "pdd/construct_paths.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "list_available_contexts", + "signature": "(start_path: Optional[Path] = None) -> list[str]", + "returns": "list[str]" + }, + { + "name": "detect_context_for_file", + "signature": "(file_path: str, repo_root: Optional[str] = None) -> Tuple[Optional[str], Dict[str, Any]]", + "returns": "Tuple[Optional[str], Dict[str, Any]]" + }, + { + "name": "resolve_effective_config", + "signature": "(*, cli_options: Optional[Dict[str, Any]] = None, context_override: Optional[str] = None, cwd: Optional[Path] = None, prompt_file: Optional[str] = None, basename_hint: Optional[str] = None, quiet: bool = False) -> Tuple[Optional[str], Optional[Path], Dict[str, Any], Dict[str, Any], Dict[str, Any]]", + "returns": "Tuple[Optional[str], Optional[Path], Dict[str, Any], Dict[str, Any], Dict[str, Any]]" + }, + { + "name": "get_tests_dir_from_config", + "signature": "(start_path: Optional[Path] = None) -> Optional[Path]", + "returns": "Optional[Path]" + }, + { + "name": "get_language_outputs", + "signature": "(language_name: str) -> set[str]", + "returns": "set[str]" + }, + { + "name": "construct_paths", + "signature": "(input_file_paths: Dict[str, str], force: bool, quiet: bool, command: str, command_options: Optional[Dict[str, Any]], create_error_file: bool = True, context_override: Optional[str] = None, confirm_callback: Optional[Callable[[str, str], bool]] = None, path_resolution_mode: Optional[str] = None) -> Tuple[Dict[str, Any], Dict[str, str], Dict[str, str], str]", + "returns": "Tuple[Dict[str, Any], Dict[str, str], Dict[str, str], str]" + } + ] + } + } + }, + { + "reason": "File editor module that uses Claude 3.7 to edit files based on natural language instructions.", + "description": "File editor module that uses Claude 3.7 to edit files based on natural language instructions.", + "dependencies": [], + "priority": 260, + "filename": "edit_file_python.prompt", + "filepath": "pdd/edit_file.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "classes": [ + { + "name": "EditFileState", + "kind": "class" + } + ], + "functions": [ + { + "name": "calculate_hash", + "signature": "(content: str) -> str", + "returns": "str" + }, + { + "name": "read_file_content", + "signature": "(file_path: str) -> tuple[Optional[str], Optional[str]]", + "returns": "tuple[Optional[str], Optional[str]]" + }, + { + "name": "write_file_content", + "signature": "(file_path: str, content: str) -> bool", + "returns": "bool" + }, + { + "name": "start_editing", + "signature": "(state: EditFileState) -> EditFileState", + "returns": "EditFileState" + }, + { + "name": "format_tools_for_claude", + "signature": "(tools)" + }, + { + "name": "plan_edits", + "signature": "(state: EditFileState) -> EditFileState", + "returns": "EditFileState" + }, + { + "name": "execute_edit", + "signature": "(state: EditFileState) -> EditFileState", + "returns": "EditFileState" + }, + { + "name": "handle_error", + "signature": "(state: EditFileState) -> EditFileState", + "returns": "EditFileState" + }, + { + "name": "decide_next_step", + "signature": "(state: EditFileState) -> Literal['execute_edit', 'handle_error', END]", + "returns": "Literal['execute_edit', 'handle_error', END]" + }, + { + "name": "check_edit_result", + "signature": "(state: EditFileState) -> Literal['plan_edits', 'handle_error']", + "returns": "Literal['plan_edits', 'handle_error']" + }, + { + "name": "edit_file", + "signature": "(file_path: str, edit_instructions: str, mcp_config_path: str = None) -> tuple[bool, Optional[str]]", + "returns": "tuple[bool, Optional[str]]" + }, + { + "name": "main", + "signature": "()" + }, + { + "name": "run_edit_in_subprocess", + "signature": "(file_path: str, edit_instructions: str) -> Tuple[bool, Optional[str]]", + "returns": "Tuple[bool, Optional[str]]" + } + ] + } + } + }, + { + "reason": "CLI subcommand that garbage-collects orphaned .pdd/extracts/ entries not referenced by any prompt.", + "description": "CLI commands for managing the .pdd/extracts/ directory.", + "dependencies": [ + "include_query_extractor_python.prompt", + "path_resolution_python.prompt", + "preprocess_python.prompt" + ], + "priority": 261, + "filename": "extracts_prune_python.prompt", + "filepath": "pdd/extracts_prune.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "parse_include_tags", + "signature": "(text: str) -> list[tuple[str, str]]", + "returns": "list[tuple[str, str]]" + }, + { + "name": "extracts", + "signature": "()" + }, + { + "name": "prune", + "signature": "(ctx: click.Context, force: bool) -> None", + "returns": "None" + } + ] + } + } + }, + { + "reason": "Persistent Firecrawl scrape cache using SQLite with TTL, URL normalization, and LRU cleanup.", + "description": "Firecrawl caching module for PDD.", + "dependencies": [], + "priority": 262, + "filename": "firecrawl_cache_python.prompt", + "filepath": "pdd/firecrawl_cache.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "classes": [ + { + "name": "FirecrawlCache", + "kind": "class" + } + ], + "functions": [ + { + "name": "FirecrawlCache.get", + "signature": "(self, url: str) -> Optional[str]", + "returns": "Optional[str]" + }, + { + "name": "FirecrawlCache.set", + "signature": "(self, url: str, content: str, ttl_hours: Optional[int] = None, metadata: Optional[Dict[str, Any]] = None) -> bool", + "returns": "bool" + }, + { + "name": "FirecrawlCache.clear", + "signature": "(self) -> int", + "returns": "int" + }, + { + "name": "FirecrawlCache.get_stats", + "signature": "(self) -> Dict[str, Any]", + "returns": "Dict[str, Any]" + }, + { + "name": "get_firecrawl_cache", + "signature": "() -> FirecrawlCache", + "returns": "FirecrawlCache" + }, + { + "name": "clear_firecrawl_cache", + "signature": "() -> int", + "returns": "int" + }, + { + "name": "get_firecrawl_cache_stats", + "signature": "() -> dict", + "returns": "dict" + } + ] + } + } + }, + { + "reason": "Fixes a failing code module and its calling program using an LLM, given the prompt, code, and captured errors.", + "description": "Fixes a failing code module and its calling program using an LLM, given the prompt, code, and captured errors.", + "dependencies": [], + "priority": 263, + "filename": "fix_code_module_errors_python.prompt", + "filepath": "pdd/fix_code_module_errors.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "classes": [ + { + "name": "CodeFix", + "kind": "class" + } + ], + "functions": [ + { + "name": "validate_inputs", + "signature": "(program: str, prompt: str, code: str, errors: str, strength: float) -> None", + "returns": "None" + }, + { + "name": "fix_code_module_errors", + "signature": "(program: str, prompt: str, code: str, errors: str, strength: float = DEFAULT_STRENGTH, temperature: float = 0, time: float = DEFAULT_TIME, verbose: bool = False, program_path: str = '', code_path: str = '') -> Tuple[bool, bool, str, str, str, float, str]", + "returns": "Tuple[bool, bool, str, str, str, float, str]" + } + ] + } + } + }, + { + "reason": "Deterministic completion checks for provider-finished generations.", + "description": "Deterministic completion checks for provider-finished generations.", + "dependencies": [], + "priority": 264, + "filename": "generation_completion_python.prompt", + "filepath": "pdd/generation_completion.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "completion_check_tail", + "signature": "(text: str, max_chars: int = 600) -> str", + "returns": "str" + }, + { + "name": "provider_reports_completion", + "signature": "(finish_reason: Any) -> bool", + "returns": "bool" + }, + { + "name": "provider_finished_structurally", + "signature": "(output: Any, finish_reason: Any, language: str) -> bool", + "returns": "bool" + }, + { + "name": "output_is_structurally_complete", + "signature": "(output: str, language: str) -> bool", + "returns": "bool" + }, + { + "name": "strip_outer_code_fence", + "signature": "(output: str) -> Tuple[str, bool]", + "returns": "Tuple[str, bool]" + }, + { + "name": "python_code_parses", + "signature": "(code: str) -> bool", + "returns": "bool" + }, + { + "name": "json_parses", + "signature": "(text: str) -> bool", + "returns": "bool" + } + ] + } + } + }, + { + "reason": "Module to retrieve file extensions for programming languages.", + "description": "Module to retrieve file extensions for programming languages.", + "dependencies": [], + "priority": 265, + "filename": "get_extension_python.prompt", + "filepath": "pdd/get_extension.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "get_extension", + "signature": "(language: str) -> str", + "returns": "str" + } + ] + } + } + }, + { + "reason": "Module to retrieve run commands for programming languages.", + "description": "Module to retrieve run commands for programming languages.", + "dependencies": [], + "priority": 266, + "filename": "get_run_command_python.prompt", + "filepath": "pdd/get_run_command.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "get_run_command", + "signature": "(extension: str) -> str", + "returns": "str" + }, + { + "name": "get_run_command_for_file", + "signature": "(file_path: str) -> str", + "returns": "str" + } + ] + } + } + }, + { + "reason": "Handles semantic extraction from files using LLMs with persistent caching for reproducibility.", + "description": "Semantic extraction from files using LLMs with persistent caching.", + "dependencies": [ + "llm_invoke_python.prompt", + "load_prompt_template_python.prompt", + "path_resolution_python.prompt", + "preprocess_python.prompt" + ], + "priority": 267, + "filename": "include_query_extractor_python.prompt", + "filepath": "pdd/include_query_extractor.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "classes": [ + { + "name": "RepeatedRetrievalQueryError", + "kind": "class" + }, + { + "name": "IncludeQueryExtractor", + "kind": "class" + } + ], + "functions": [ + { + "name": "compute_cache_key", + "signature": "(source_file_path: str, query: str) -> str", + "returns": "str" + }, + { + "name": "IncludeQueryExtractor.reset_session", + "signature": "(cls) -> None", + "returns": "None" + }, + { + "name": "IncludeQueryExtractor.extract", + "signature": "(self, file_path: str, query: str) -> str", + "returns": "str" + } + ] + } + } + }, + { + "reason": "Loads and caches prompt templates from the prompts directory.", + "description": "Loads and caches prompt templates from the prompts directory.", + "dependencies": [], + "priority": 268, + "filename": "load_prompt_template_python.prompt", + "filepath": "pdd/load_prompt_template.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "print_formatted", + "signature": "(message: str) -> None", + "returns": "None" + }, + { + "name": "load_prompt_template", + "signature": "(prompt_name: str) -> Optional[str]", + "returns": "Optional[str]" + } + ] + } + } + }, + { + "reason": "Tests individual models via litellm.completion() with direct API key passing and diagnostics.", + "description": "Tests individual models via litellm.completion() with direct API key passing and diagnostics.", + "dependencies": [], + "priority": 269, + "filename": "model_tester_python.prompt", + "filepath": "pdd/model_tester.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "test_model_interactive", + "signature": "() -> None", + "returns": "None" + } + ] + } + } + }, + { + "reason": "Runs example generation, crash-fix, verify, test generation, and test-fix steps in a single agentic session instead of sequential sync/fix/test calls.", + "description": "One-session sync: run example, crash-fix, verify, test, and fix steps in a single agentic session instead of separate sessions per step.", + "dependencies": [ + "code_generator_main_python.prompt", + "fix_code_loop_python.prompt", + "fix_error_loop_python.prompt", + "fix_verification_errors_loop_python.prompt", + "generate_test_python.prompt" + ], + "priority": 270, + "filename": "one_session_sync_python.prompt", + "filepath": "pdd/one_session_sync.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "build_one_session_prompt", + "signature": "(basename: str, language: str, pdd_files: Dict[str, Path], project_root: Path, *, target_coverage: float = 90.0) -> str", + "returns": "str" + }, + { + "name": "run_one_session_sync", + "signature": "(basename: str, language: str, pdd_files: Dict[str, Path], project_root: Path, *, target_coverage: float = 90.0, budget: Optional[float] = None, verbose: bool = False, quiet: bool = False, timeout: Optional[float] = None) -> Dict[str, Any]", + "returns": "Dict[str, Any]" + } + ] + } + } + }, + { + "reason": "Creates and auto-generates .pddrc configuration files with sensible defaults, including multi-context support for monorepos.", + "description": "Creates and auto-generates .pddrc configuration files with sensible defaults, including multi-context support for monorepos.", + "dependencies": [], + "priority": 271, + "filename": "pddrc_initializer_python.prompt", + "filepath": "pdd/pddrc_initializer.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "offer_pddrc_init", + "signature": "() -> bool", + "returns": "bool" + }, + { + "name": "infer_contexts_from_scan", + "signature": "(scan_dir: str, repo_root: str, code_files: List[str]) -> Dict[str, Dict]", + "returns": "Dict[str, Dict]" + }, + { + "name": "ensure_pddrc_for_scan", + "signature": "(scan_dir: str, repo_root: str, code_files: List[str], quiet: bool = False) -> Optional[Path]", + "returns": "Optional[Path]" + } + ] + } + } + }, + { + "reason": "Orchestrates the complete PDD sync workflow by coordinating operations and animations in parallel, serving as the core engine for the `pdd sync` command.", + "description": "Orchestrates the complete PDD sync workflow by coordinating operations and animations in parallel, serving as the core engine for the `pdd sync` command.", + "dependencies": [], + "priority": 272, + "filename": "pin_example_hack_python.prompt", + "filepath": "pdd/pin_example_hack.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "classes": [ + { + "name": "PendingStateUpdate", + "kind": "dataclass" + }, + { + "name": "AtomicStateUpdate", + "kind": "class" + } + ], + "functions": [ + { + "name": "AtomicStateUpdate.set_run_report", + "signature": "(self, report: Dict[str, Any], path: Path)" + }, + { + "name": "AtomicStateUpdate.set_fingerprint", + "signature": "(self, fingerprint: Dict[str, Any], path: Path)" + }, + { + "name": "load_sync_log", + "signature": "(basename: str, language: str) -> List[Dict[str, Any]]", + "returns": "List[Dict[str, Any]]" + }, + { + "name": "create_sync_log_entry", + "signature": "(decision, budget_remaining: float) -> Dict[str, Any]", + "returns": "Dict[str, Any]" + }, + { + "name": "update_sync_log_entry", + "signature": "(entry: Dict[str, Any], result: Dict[str, Any], duration: float) -> Dict[str, Any]", + "returns": "Dict[str, Any]" + }, + { + "name": "append_sync_log", + "signature": "(basename: str, language: str, entry: Dict[str, Any])" + }, + { + "name": "log_sync_event", + "signature": "(basename: str, language: str, event: str, details: Dict[str, Any] = None)" + }, + { + "name": "save_run_report", + "signature": "(report: Dict[str, Any], basename: str, language: str, atomic_state: Optional['AtomicStateUpdate'] = None)" + }, + { + "name": "sync_orchestration", + "signature": "(basename: str, target_coverage: float = 90.0, language: str = 'python', prompts_dir: str = 'prompts', code_dir: str = 'src', examples_dir: str = 'examples', tests_dir: str = 'tests', max_attempts: int = 3, budget: float = 10.0, skip_verify: bool = False, skip_tests: bool = False, dry_run: bool = False, force: bool = False, strength: float = DEFAULT_STRENGTH, temperature: float = 0.0, time_param: float = 0.25, verbose: bool = False, quiet: bool = False, output_cost: Optional[str] = None, review_examples: bool = False, local: bool = False, context_config: Optional[Dict[str, str]] = None, context_override: Optional[str] = None, confirm_callback: Optional[Callable[[str, str], bool]] = None) -> Dict[str, Any]", + "returns": "Dict[str, Any]" + } + ] + } + } + }, + { + "reason": "Manages LLM providers: browse reference CSV to add models, custom providers, and model removal with shell-aware credential storage.", + "description": "Manages LLM providers: browse reference CSV to add models, custom providers, and model removal with shell-aware credential storage.", + "dependencies": [ + "api_key_scanner_python.prompt" + ], + "priority": 273, + "filename": "provider_manager_python.prompt", + "filepath": "pdd/provider_manager.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "parse_api_key_vars", + "signature": "(api_key_field: str) -> List[str]", + "returns": "List[str]" + }, + { + "name": "api_key_aliases", + "signature": "(key_name: str) -> List[str]", + "returns": "List[str]" + }, + { + "name": "expand_api_key_vars", + "signature": "(api_key_field: str) -> List[str]", + "returns": "List[str]" + }, + { + "name": "api_key_requirements_satisfied", + "signature": "(api_key_field: str, configured_key_names: Iterable[str]) -> bool", + "returns": "bool" + }, + { + "name": "resolve_api_key_from_env", + "signature": "(key_name: str, env: Optional[Dict[str, str]] = None, *, include_llm_invoke_aliases: bool = False) -> Tuple[Optional[str], Optional[str]]", + "returns": "Tuple[Optional[str], Optional[str]]" + }, + { + "name": "preferred_api_key_name", + "signature": "(key_name: str) -> str", + "returns": "str" + }, + { + "name": "is_multi_credential", + "signature": "(api_key_field: str) -> bool", + "returns": "bool" + }, + { + "name": "add_provider_from_registry", + "signature": "() -> bool", + "returns": "bool" + }, + { + "name": "add_custom_provider", + "signature": "() -> bool", + "returns": "bool" + }, + { + "name": "remove_models_by_provider", + "signature": "() -> bool", + "returns": "bool" + }, + { + "name": "remove_individual_models", + "signature": "() -> bool", + "returns": "bool" + } + ] + } + } + }, + { + "reason": "Python Environment Detector", + "description": "Python Environment Detector", + "dependencies": [], + "priority": 274, + "filename": "python_env_detector_python.prompt", + "filepath": "pdd/python_env_detector.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "detect_host_python_executable", + "signature": "() -> str", + "returns": "str" + }, + { + "name": "get_environment_info", + "signature": "() -> dict", + "returns": "dict" + }, + { + "name": "is_in_virtual_environment", + "signature": "() -> bool", + "returns": "bool" + }, + { + "name": "get_environment_type", + "signature": "() -> str", + "returns": "str" + } + ] + } + } + }, + { + "reason": "Shared helpers for mapping --time floats to provider reasoning effort.", + "description": "Shared helpers for mapping --time floats to provider reasoning effort.", + "dependencies": [], + "priority": 275, + "filename": "reasoning_python.prompt", + "filepath": "pdd/reasoning.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "time_to_effort_level", + "signature": "(time: float) -> EffortLevel", + "returns": "EffortLevel" + } + ] + } + } + }, + { + "reason": "Utility for expanding path templates with dynamic placeholders for project layout generation.", + "description": "Template expansion utility for output path configuration.", + "dependencies": [], + "priority": 276, + "filename": "template_expander_python.prompt", + "filepath": "pdd/template_expander.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "expand_template", + "signature": "(template: str, context: Dict[str, Any]) -> str", + "returns": "str" + } + ] + } + } + }, + { + "reason": "Utilities for validating and cleaning tags in generated prompt content.", + "description": "Utilities for validating and cleaning tags in generated prompt content.", + "dependencies": [], + "priority": 277, + "filename": "validate_prompt_includes_python.prompt", + "filepath": "pdd/validate_prompt_includes.py", + "tags": [], + "interface": { + "type": "module", + "module": { + "functions": [ + { + "name": "validate_include_tag", + "signature": "(file_path: str, base_dir: str) -> bool", + "returns": "bool" + }, + { + "name": "validate_prompt_includes", + "signature": "(content: str, base_dir: str | Path = '.', remove_invalid: bool = False) -> Tuple[str, List[str]]", + "returns": "Tuple[str, List[str]]" + }, + { + "name": "sanitize_prompt_output", + "signature": "(content: str, output_path: str | Path) -> Tuple[str, List[str]]", + "returns": "Tuple[str, List[str]]" + } + ] + } + } + }, + { + "reason": "Registers the `pdd checkup simplify` CLI command (multi-provider simplify integration).", + "description": "Defines the `simplify` Click command that dispatches to run_checkup_simplify across engines (claude/codex/gemini/opencode/auto), with --apply/--engine/--path options; a thin command wrapper over pdd/checkup_simplify.py.", + "dependencies": [], + "priority": 278, + "filename": "commands/checkup_simplify_python.prompt", + "filepath": "pdd/commands/checkup_simplify.py", + "tags": [], + "interface": { + "type": "module", + "functions": [ + { + "name": "checkup_simplify", + "signature": "(ctx: click.Context)", + "returns": "Optional[Tuple[str, float, str]]" + } + ] + } + }, + { + "reason": "Detects whether pdd runs in a remote/SSH or headless session (for interactive-flow decisions).", + "description": "is_remote_session() inspects SSH_* env vars, DISPLAY, and WSL/WSLg to return (is_remote, reason); used to gate interactive prompts and browser flows.", + "dependencies": [], + "priority": 279, + "filename": "core/remote_session_python.prompt", + "filepath": "pdd/core/remote_session.py", + "tags": [], + "interface": { + "type": "module", + "functions": [ + { + "name": "is_remote_session", + "signature": "()", + "returns": "Tuple[bool, str]" + }, + { + "name": "should_skip_browser", + "signature": "(explicit_flag: Optional[bool])", + "returns": "Tuple[bool, str]" + } + ] + } } ] diff --git a/architecture_waivers.json b/architecture_waivers.json new file mode 100644 index 0000000000..73bd845d40 --- /dev/null +++ b/architecture_waivers.json @@ -0,0 +1,113 @@ +{ + "_README": "Waiver + orphan tracking for architecture.json completeness. Consumed by tests/test_architecture_completeness.py, which asserts a module<->entry bijection: every top-level pdd/*.py module (after .pddignore) has exactly one architecture.json entry UNLESS it is listed in 'modules_without_architecture_entry'. The 'orphans' sections are visibility/ratchet tracking (prompt-no-code, code-no-prompt, code-no-test) so these gaps are explicit, not silent. Keep this file exact: adding/removing a module, prompt, or test without updating the relevant list fails the test with an actionable diff. To clear a waiver, add the missing architecture entry (pdd sync-architecture / author it) and delete the line here.", + "version": 1, + "known_registry_exceptions": { + "_README": "Pre-existing architecture.json data-quality issues that predate this gate. Documented (not fixed) so the gate is green on main while still failing on NEW occurrences. Ratchet these down over time.", + "duplicate_filepaths": { + "pdd/user_story_tests.py": "Pre-existing double-registration (two entries, priority 6 and 241, same filename/filepath). Tolerated by sync (_architecture_sync_modules merges duplicates by output path). TODO: dedupe to a single entry.", + "pdd/coverage_contracts.py": "Pre-existing double-registration (two entries, priority 6 and 241, same filename/filepath). Tolerated by sync (merged by output path). TODO: dedupe to a single entry." + }, + "entries_without_description": { + "test_context_packer_python.prompt": "Pre-existing entry predating the 'description' field convention. TODO: add a description.", + "compressed_sync_context_python.prompt": "Pre-existing entry predating the 'description' field convention. TODO: add a description." + } + }, + "modules_without_architecture_entry": { + "__main__": "Package entry point for `python -m pdd`; thin CLI shim, not a prompt-generated unit.", + "_selector_parse": "Private leaf helper (underscore) for parsing include `select` attributes; no standalone prompt.", + "architecture_sync_helper": "Hand-written filename-normalization helper for architecture_sync; no standalone prompt.", + "checkup_target": "Deterministic `pdd checkup` target classifier; hand-written, no generating prompt.", + "compression_reporting": "Deterministic context-compression reporting helper (#877); hand-written, no prompt.", + "context_snapshot_policy": "Deterministic policy checks for snapshotted prompt context; hand-written, no prompt.", + "continuous_sync": "Deterministic continuous-sync classification/reconciliation reporting; hand-written, no prompt.", + "drift_main": "CLI entry wiring for `pdd checkup drift`; hand-written `_main`, no generating prompt.", + "grounding_provenance": "Shared grounding-metadata helpers for llm_invoke/evidence manifests; hand-written leaf, no prompt.", + "interface_semantics": "Deterministic interface-conformance helpers; hand-written, no prompt.", + "json_atomic": "Dependency-light atomic JSON write utility (guards architecture.json); hand-written leaf, no prompt.", + "json_invocation": "Dependency-light leaf detecting JSON-only CLI invocations; hand-written, no prompt.", + "language_extensions": "Canonical file-extension source-of-truth table; hand-written leaf, no prompt.", + "list_drift_detection": "Deterministic hardcoded-list-vs-canonical drift detector (pdd_cloud#1405); hand-written, no prompt.", + "prompt_gate": "Prompt-quality gate config/workflow hooks; hand-written, no generating prompt.", + "source_set_model": "Deterministic resolver of prompt-shaped checkup targets to `.prompt` paths; hand-written, no prompt.", + "story_test_generation": "Deterministic pytest generator from user-story files; hand-written, no prompt.", + "sync_graph_order_consistency": "Deterministic architecture.json-vs- sync-order consistency detector; hand-written, no prompt.", + "test_result": "Shared return-type dataclass for test-generation commands (#1072); hand-written leaf, no prompt.", + "waiver_policy": "Shared waiver-status classification helpers; hand-written leaf, no prompt." + }, + "orphans": { + "_README": "Informational tracking of adjacent gaps. These do NOT need architecture.json entries; they are listed so the gaps are visible and can be ratcheted down.", + "prompt_without_code": { + "Makefile": "Makefile_makefile.prompt generates the repo Makefile; output is not a pdd/*.py module.", + "core_dump_smoke": "core_dump_smoke_python.prompt is a smoke-test fixture (trivial add()); not a real module.", + "gate": "gate_python.prompt/gate_policy_python.prompt; code lives at pdd/commands/gate.py (CLI command), not top-level pdd/gate.py.", + "language_format": "language_format_csv.prompt generates a language data table (CSV); not a pdd/*.py module.", + "llm_model": "llm_model_csv.prompt generates the LLM model reference (CSV); not a pdd/*.py module.", + "main_gen_prompt": "Meta-prompt that generates prompt text; emits a prompt, not a pdd/*.py module.", + "pdd_completion": "pdd_completion_{bash,fish,zsh}.prompt generate shell-completion scripts; not a pdd/*.py module.", + "pdd_theme": "pdd_theme_json.prompt generates theme reference data (JSON); theme code is pdd/cli_theme.py.", + "prompt_tester": "prompt_tester_python.prompt has no committed pdd/prompt_tester.py (standalone tool). TODO justify/reconcile.", + "pypi_description": "pypi_description_restructuredtext.prompt generates the PyPI long description (rST); not a pdd/*.py module.", + "pyproject": "pyproject_toml.prompt generates pyproject.toml; not a pdd/*.py module.", + "regression": "regression_bash.prompt generates the regression harness script; not a pdd/*.py module.", + "regression_analysis": "regression_analysis_log.prompt emits regression analysis text; not a pdd/*.py module.", + "resurface_check": "resurface_check_Python.prompt is a pdd_cloud Rising Stars Cloud Function; code lives in the pdd_cloud repo, not here.", + "run_generated": "run_generated_python.prompt has no committed pdd/run_generated.py. TODO justify/reconcile.", + "task_routing": "task_routing_csv.prompt generates routing config (CSV); not a pdd/*.py module." + }, + "code_without_prompt": { + "__main__": "Package entry point; see modules_without_architecture_entry.", + "_selector_parse": "Private include-select parser; see modules_without_architecture_entry.", + "architecture_sync_helper": "architecture_sync filename helper; see modules_without_architecture_entry.", + "checkup_target": "checkup target classifier; see modules_without_architecture_entry.", + "compression_reporting": "context-compression reporting; see modules_without_architecture_entry.", + "context_snapshot_policy": "snapshot policy checks; see modules_without_architecture_entry.", + "continuous_sync": "continuous-sync classification; see modules_without_architecture_entry.", + "drift_main": "`pdd checkup drift` CLI wiring; see modules_without_architecture_entry.", + "gate_main": "`pdd checkup gate` CLI wiring; hand-written `_main`, already registered in architecture.json without a generating prompt.", + "grounding_provenance": "grounding metadata helpers; see modules_without_architecture_entry.", + "interface_semantics": "interface-conformance helpers; see modules_without_architecture_entry.", + "json_atomic": "atomic JSON writer; see modules_without_architecture_entry.", + "json_invocation": "JSON-only invocation detector; see modules_without_architecture_entry.", + "language_extensions": "canonical extension table; see modules_without_architecture_entry.", + "list_drift_detection": "hardcoded-list drift detector; see modules_without_architecture_entry.", + "prompt_gate": "prompt-quality gate hooks; see modules_without_architecture_entry.", + "source_set_model": "checkup target->.prompt resolver; see modules_without_architecture_entry.", + "story_test_generation": "user-story pytest generator; see modules_without_architecture_entry.", + "sync_graph_order_consistency": "sync-order consistency detector; see modules_without_architecture_entry.", + "test_result": "shared test-command return type; see modules_without_architecture_entry.", + "waiver_policy": "waiver-status helpers; see modules_without_architecture_entry." + }, + "code_without_test": { + "__main__": "Thin `python -m pdd` shim; exercised via CLI end-to-end tests. TODO: add tests/test___main__.py or document coverage.", + "_keyring_timeout": "Bounded keyring wrappers; TODO add tests/test__keyring_timeout.py.", + "_selector_parse": "Include-select parser; TODO add tests/test__selector_parse.py.", + "architecture_sync_helper": "Filename-normalization helper; covered indirectly by test_architecture_sync. TODO add dedicated test.", + "checkup_file_selection": "checkup simplify file discovery; TODO add tests/test_checkup_file_selection.py.", + "checkup_interactive_main": "Interactive checkup orchestration; TODO add tests/test_checkup_interactive_main.py.", + "checkup_planner": "Checkup tool planners; TODO add tests/test_checkup_planner.py.", + "checkup_simplify": "checkup simplify core; TODO add tests/test_checkup_simplify.py.", + "checkup_simplify_claude": "Claude /simplify helpers; TODO add tests/test_checkup_simplify_claude.py.", + "checkup_tools": "Checkup engine wrappers; TODO add tests/test_checkup_tools.py.", + "cli_branding": "User-facing branding strings; low-risk constants. TODO add tests/test_cli_branding.py.", + "compression_reporting": "context-compression reporting; TODO add tests/test_compression_reporting.py.", + "config_resolution": "Centralized config resolution; TODO add tests/test_config_resolution.py.", + "contract_ir": "Contract authoring IR; TODO add tests/test_contract_ir.py.", + "edit_file": "LLM file editor; TODO add tests/test_edit_file.py.", + "evidence_store": "Evidence manifest loader; TODO add tests/test_evidence_store.py.", + "gate_policy": "checkup gate YAML policy loader; TODO add tests/test_gate_policy.py.", + "json_atomic": "atomic JSON writer; TODO add tests/test_json_atomic.py.", + "python_env_detector": "Host Python env detector; TODO add tests/test_python_env_detector.py.", + "test_result": "shared test-command return type; TODO add tests/test_test_result.py.", + "validate_prompt_includes": " validation/cleanup; TODO add tests/test_validate_prompt_includes.py." + }, + "commands_without_prompt": { + "_README": "Informational: pdd/commands/*.py CLI wrappers that are hand-written (no generating prompt), consistent with each other. Not enforced by the module<->entry bijection (that gate is scoped to top-level pdd/*.py). Listed so the gap is explicit, not silent (#1969 review nit).", + "resolve": "`pdd resolve` CONFLICT-finalization command; hand-written wrapper over sync_determine_operation/operation_log primitives, no generating prompt.", + "reconcile": "`pdd reconcile` deterministic fingerprint stamper; hand-written wrapper over continuous_sync, no generating prompt.", + "drift": "`pdd checkup drift` command wrapper; hand-written, no prompt.", + "coverage": "`pdd coverage` command wrapper; hand-written, no prompt.", + "extracts": "`pdd extracts` command wrapper; hand-written, no prompt.", + "checkup_snapshot": "`pdd checkup snapshot` command wrapper; hand-written, no prompt." + } + } +} diff --git a/pdd/commands/__init__.py b/pdd/commands/__init__.py index 21e8725eb1..187b2cda2f 100644 --- a/pdd/commands/__init__.py +++ b/pdd/commands/__init__.py @@ -24,6 +24,7 @@ from .firecrawl import firecrawl_cache from .story import story_cli from .reconcile import install_hooks, reconcile +from .resolve import resolve def register_commands(cli: click.Group) -> None: """Register all subcommands with the main CLI group.""" @@ -55,6 +56,7 @@ def register_commands(cli: click.Group) -> None: cli.add_command(which) cli.add_command(reconcile) cli.add_command(install_hooks) + cli.add_command(resolve) # Register templates group directly to commands dict to handle nesting if needed, # or just add_command works for groups too. diff --git a/pdd/commands/maintenance.py b/pdd/commands/maintenance.py index b8ca343784..ad20b3bc91 100644 --- a/pdd/commands/maintenance.py +++ b/pdd/commands/maintenance.py @@ -211,6 +211,19 @@ def _write_sync_evidence_manifest( "(auto-deps tags and generate preprocess expansion)." ), ) +@click.option( + "--fresh", + is_flag=True, + default=False, + help=( + "Disable the default surgical (edit-shaped) regeneration for a mature " + "module. By default `pdd sync` edits existing module code in place so " + "declared symbols are preserved; with --fresh, sync uses standard " + "generation, which regenerates the module from scratch when the prompt " + "change is large. Use it when you intend a large rewrite rather than an " + "in-place edit. Single-module sync only." + ), +) @click.option( "--compressed-context/--no-compressed-context", default=None, @@ -279,6 +292,7 @@ def sync( evidence: bool, snapshot_context: bool, compress: bool, + fresh: bool, compressed_context: Optional[bool], model: Optional[str] = None, compress_examples: Optional[bool] = None, @@ -367,6 +381,8 @@ def _restore_model_default(_prev=_prev_model_default): if basename is None: if snapshot_context: raise click.UsageError("--snapshot-context is only supported for single-module sync.") + if fresh: + raise click.UsageError("--fresh is only supported for single-module sync.") if durable or durable_branch or no_resume or durable_max_parallel is not None: raise click.UsageError("Durable sync options require a GitHub issue URL.") effective_one_session = one_session if one_session is not None else False @@ -411,6 +427,8 @@ def _restore_model_default(_prev=_prev_model_default): if _is_github_issue_url(basename): if snapshot_context: raise click.UsageError("--snapshot-context is only supported for single-module sync.") + if fresh: + raise click.UsageError("--fresh is only supported for single-module sync.") if not durable and ( durable_branch is not None or no_resume or durable_max_parallel is not None ): @@ -491,6 +509,7 @@ def _restore_model_default(_prev=_prev_model_default): one_session=effective_one_session, snapshot_context=snapshot_context, compress=compress, + fresh=fresh, compressed_context=effective_compressed_context, ) if evidence: diff --git a/pdd/commands/reconcile.py b/pdd/commands/reconcile.py index 0eb95ff6d4..2b8098c448 100644 --- a/pdd/commands/reconcile.py +++ b/pdd/commands/reconcile.py @@ -10,7 +10,13 @@ import click -from ..continuous_sync import build_report, project_root +from ..continuous_sync import ( + CheckResult, + build_report, + project_root, + resolve_units, + run_check, +) def _pre_commit_hook_path(root: Path) -> Path: @@ -49,15 +55,47 @@ def _emit_report(report: dict, as_json: bool) -> None: click.echo(json.dumps(report, indent=2, sort_keys=True)) return summary = report["summary"] + parts = [ + f"metadata_stale={summary['metadata_stale']}", + f"conflicts={summary['conflicts']}", + f"unbaselined={summary['unbaselined']}", + f"failures={summary['failures']}", + ] + if report.get("stamped"): + parts.append(f"stamped={len(report['stamped'])}") + click.echo(" ".join(parts)) + + +def _emit_check(result: CheckResult, as_json: bool) -> None: + """Render a ``--check`` verification result (parity with the CI stamper).""" + if as_json: + payload = { + "ok": result.ok, + "stampable": result.stampable, + "ignored": result.ignored, + "waived": result.waived, + "missing": result.missing, + "stale": [{"basename": b, "fields": f} for b, f in result.stale], + "no_code": result.no_code, + } + click.echo(json.dumps(payload, indent=2, sort_keys=True)) + return click.echo( - f"metadata_stale={summary['metadata_stale']} " - f"conflicts={summary['conflicts']} " - f"unbaselined={summary['unbaselined']} " - f"failures={summary['failures']}" + f"checked {result.stampable} stampable units; " + f"ignored={result.ignored} waived={result.waived}" ) + for name in result.no_code: + click.echo(f" no code file (add a waiver): {name}", err=True) + for name in result.missing: + click.echo(f" missing fingerprint: {name}", err=True) + for name, fields in result.stale: + click.echo(f" stale: {name}: {', '.join(fields)}", err=True) + if result.ok: + click.echo("all fingerprints current") @click.command("reconcile") +@click.argument("basename", required=False) @click.option( "--json", "as_json", @@ -65,6 +103,12 @@ def _emit_report(report: dict, as_json: bool) -> None: default=False, help="Emit machine-readable JSON.", ) +@click.option( + "--check", + is_flag=True, + default=False, + help="Verify fingerprints are current; exit non-zero on drift (no writes).", +) @click.option( "--strict", is_flag=True, @@ -75,7 +119,20 @@ def _emit_report(report: dict, as_json: bool) -> None: "--heal", is_flag=True, default=False, - help="Refresh valid stale fingerprints without LLM calls.", + help="Stamp single-sided drift to the current tree (no LLM, no regen).", +) +@click.option( + "--accept-current", + "accept_current", + is_flag=True, + default=False, + help="Stamp CONFLICT units, accepting the current tree as the agreed baseline.", +) +@click.option( + "--backfill", + is_flag=True, + default=False, + help="Stamp UNBASELINED units (missing/invalid fingerprint) to set a baseline.", ) @click.option( "--ledger", @@ -87,27 +144,59 @@ def _emit_report(report: dict, as_json: bool) -> None: "--module", "module_name", default=None, - help="Limit reconciliation to one module basename.", + help="Alias for the BASENAME argument (limit to one unit).", ) @click.pass_context def reconcile( ctx: click.Context, + basename: Optional[str], as_json: bool, + check: bool, strict: bool, heal: bool, + accept_current: bool, + backfill: bool, ledger: bool, module_name: Optional[str], ) -> None: # pylint: disable=too-many-arguments,too-many-positional-arguments - """Classify and optionally reconcile PDD fingerprint drift.""" + """Classify PDD fingerprint drift and stamp the safe cases (no LLM). + + With no flags, reports each unit's status without writing. ``--heal`` stamps + single-sided drift; ``--accept-current`` additionally stamps CONFLICT units; + ``--backfill`` additionally stamps UNBASELINED units. ``--check`` is a + read-only verification (same contract as the CI stamper) that exits non-zero + on any non-current unit. BASENAME (or ``--module``) limits the run to one unit. + """ ctx.ensure_object(dict) if as_json: ctx.obj["_suppress_result_summary"] = True + target = basename or module_name + modules = [target] if target else None + + # A targeted run that matches no unit must fail loudly, not silently pass with + # 0 units (#1969 review pass 2 finding 3: a typo'd basename would otherwise + # exit 0 / ok:true and skip the intended unit in CI/runbook checks). + if target is not None and not resolve_units(project_root(), modules=modules): + raise click.ClickException( + f"No PDD unit matches '{target}'. Run `pdd reconcile` (no argument) to " + "verify all units, or check the basename / --module spelling." + ) + + if check: + result = run_check(modules=modules) + _emit_check(result, as_json) + if not result.ok: + raise click.exceptions.Exit(1) + return + report = build_report( consumer="reconcile", - modules=[module_name] if module_name else None, + modules=modules, heal=heal, ledger=ledger, + accept_current=accept_current, + backfill=backfill, ) _emit_report(report, as_json) if strict and not report["ok"]: diff --git a/pdd/commands/resolve.py b/pdd/commands/resolve.py new file mode 100644 index 0000000000..91e15d6bbc --- /dev/null +++ b/pdd/commands/resolve.py @@ -0,0 +1,312 @@ +"""`pdd resolve` — resolve a both-changed CONFLICT unit. + +A CONFLICT arises when a unit's prompt AND one or more derived artifacts (code, +example, test) both changed since the last synced fingerprint. `pdd sync` refuses +to auto-pick a winner there (see ``sync_determine_operation`` #1929); this command +lets the user finalize the resolution explicitly: + +* ``--accept-current`` — deterministic, no LLM. Stamp the current working tree as + the agreed baseline for the unit (re-fingerprint), so the co-edited prompt and + code are recorded as the new last-synced ancestor. Use this after hand-merging. +* ``--prompt-wins`` / ``--code-wins`` — documented previews of the LLM-backed + strategies (regenerate code from the prompt / back-propagate code into the + prompt). These are not yet automated; they print what WOULD run and exit + non-zero. + +Deliberately self-contained: classification reuses ``sync_determine_operation``'s +own changed-artifact primitive (so the CONFLICT definition — prompt-side AND +derived-side both moved vs the stored fingerprint — matches the sync decision +path exactly), and stamping uses the shared runtime ``save_fingerprint`` writer. +It does NOT depend on ``continuous_sync``/``reconcile`` (owned by the sibling +#1927 effort); any overlap with ``reconcile --accept-current`` is consolidated at +merge. +""" +from __future__ import annotations + +import json +from pathlib import Path +from typing import Dict, List, Optional, Tuple + +import click + +from ..operation_log import get_fingerprint_path, save_fingerprint +from ..sync_determine_operation import ( + Fingerprint, + _changed_artifacts_from_hashes, + calculate_current_hashes, + get_pdd_file_paths, + read_fingerprint, +) + +# Fingerprint commands treated as "settled" workflow states. Preserving one of +# these (rather than resetting to a running op) keeps a stamped baseline from +# re-triggering verify/test on the next sync. +_SETTLED_COMMANDS = {"verify", "test", "fix", "update"} + + +def _resolve_paths(basename: str, language: str) -> Optional[Dict[str, Path]]: + """Resolve the unit's artifact paths (or None if resolution fails).""" + try: + return get_pdd_file_paths(basename, language, prompts_dir="prompts") + except Exception: # pylint: disable=broad-except + return None + + +def _classify( + basename: str, + language: str, + paths: Dict[str, Path], +) -> Tuple[Optional[Fingerprint], List[str], str]: + """Classify the unit against its stored fingerprint without mutating anything. + + Returns ``(fingerprint, changed_artifacts, classification)``. CONFLICT means + the prompt AND at least one derived artifact both moved vs the fingerprint — + identical to the both-changed trigger in ``_prompt_derived_conflict_decision``. + """ + fingerprint = read_fingerprint(basename, language, paths=paths) + if fingerprint is None: + return None, [], "UNBASELINED" + current = calculate_current_hashes(paths, stored_include_deps=fingerprint.include_deps) + changes = _changed_artifacts_from_hashes(fingerprint, paths, current) + derived = [item for item in changes if item != "prompt"] + if not changes: + classification = "IN_SYNC" + elif "prompt" in changes and derived: + classification = "CONFLICT" + else: + classification = "DRIFT" + return fingerprint, changes, classification + + +def _accept_current( + basename: str, + language: str, + paths: Dict[str, Path], + as_json: bool, + quiet: bool, + force: bool = False, +) -> int: + """Stamp the current tree as the agreed baseline for the unit. + + Only a CONFLICT (prompt AND a derived artifact both changed) is stampable by + default: ``--accept-current`` is a CONFLICT-resolution tool, not a general + drift silencer. Single-sided drift (code-only / prompt-only) must go through + ``pdd update`` / ``pdd sync`` so the stale side is actually reconciled, not + silently baselined away; ``--force`` overrides for the rare "accept current as + truth" case. + + Transactional at the command level: it re-fingerprints, then re-classifies and + only reports success when the unit lands IN_SYNC. Any other post-state is + surfaced as a failure rather than a silent partial stamp. + """ + before_fp, before_changes, before_class = _classify(basename, language, paths) + if before_class == "IN_SYNC": + if as_json: + click.echo(json.dumps({ + "basename": basename, + "language": language, + "strategy": "accept-current", + "before": "IN_SYNC", + "after": "IN_SYNC", + "changed_files": [], + "resolved": True, + }, indent=2, sort_keys=True)) + elif not quiet: + click.echo(f"'{basename}' ({language}) is already in sync; nothing to resolve.") + return 0 + if before_class != "CONFLICT" and not force: + moved = ", ".join(before_changes) or "no tracked artifacts" + # Only `pdd resolve` takes --language; pdd sync/update/reconcile do not, and + # `pdd update` targets the code FILE path (#1969 review pass 2 finding 1). + code_target = str(paths.get("code")) if paths.get("code") else basename + if before_class == "UNBASELINED": + guidance = f"`pdd reconcile {basename} --backfill` to baseline it" + else: # DRIFT — single-sided (code-only or prompt-only) + guidance = ( + f"`pdd update {code_target}` (back-propagate code->prompt) or " + f"`pdd sync {basename}` (regenerate) to reconcile the stale side, or " + f"`pdd reconcile {basename} --heal` to stamp only" + ) + if as_json: + click.echo(json.dumps({ + "basename": basename, + "language": language, + "strategy": "accept-current", + "before": before_class, + "changed_files": before_changes, + "resolved": False, + "error": "not-a-conflict", + }, indent=2, sort_keys=True)) + elif not quiet: + click.echo( + f"'{basename}' ({language}) is {before_class} ({moved}), not a CONFLICT. " + "--accept-current only resolves CONFLICTs (prompt AND code both changed " + f"since the last sync). Run {guidance}. Pass --force to stamp the current " + "tree as the baseline anyway.", + err=True, + ) + return 2 + command = ( + before_fp.command + if before_fp is not None and before_fp.command in _SETTLED_COMMANDS + else "fix" + ) + save_fingerprint(basename, language, command, paths, 0.0, "resolve") + _after_fp, _after_changes, after_class = _classify(basename, language, paths) + + resolved = after_class == "IN_SYNC" + result = { + "basename": basename, + "language": language, + "strategy": "accept-current", + "before": before_class, + "after": after_class, + "changed_files": before_changes, + "fingerprint_path": str(get_fingerprint_path(basename, language, paths=paths)), + "resolved": resolved, + } + if as_json: + click.echo(json.dumps(result, indent=2, sort_keys=True)) + elif not quiet: + if resolved: + moved = ", ".join(before_changes) or "no tracked" + click.echo( + f"Resolved '{basename}' ({language}) with --accept-current: " + f"stamped the current tree as the new baseline " + f"(was {before_class}: {moved} changed)." + ) + else: + click.echo( + f"Could not fully resolve '{basename}' ({language}): after stamping " + f"it is {after_class}, not IN_SYNC. Ensure the prompt and its derived " + f"artifacts all exist on disk, then retry." + ) + return 0 if resolved else 1 + + +def _preview_llm_strategy(basename: str, language: str, strategy: str) -> int: + """Print what a not-yet-automated LLM strategy WOULD run; return non-zero.""" + # pdd sync takes a bare basename; pdd update takes the code file path; neither + # accepts --language (#1969 review pass 2 finding 1). Only `pdd resolve` does. + suffix = "" if language.lower() == "python" else f" --language {language}" + if strategy == "prompt-wins": + would_run = f"pdd sync {basename}" + effect = "regenerate the code from the prompt (an LLM generation)" + else: # code-wins + paths = _resolve_paths(basename, language) + code_target = str(paths["code"]) if paths and paths.get("code") else basename + would_run = f"pdd update {code_target}" + effect = "back-propagate the code into the prompt (an LLM update)" + click.echo( + f"--{strategy} would run `{would_run}` to {effect}, then re-stamp the " + f"fingerprint. This LLM-backed strategy is not yet automated by " + f"`pdd resolve`." + ) + click.echo( + "Run that command yourself, or use " + f"`pdd resolve {basename}{suffix} --accept-current` to keep the current " + "tree as the baseline." + ) + return 2 + + +@click.command("resolve") +@click.argument("basename") +@click.option( + "--language", + default="python", + show_default=True, + help="Language of the unit to resolve.", +) +@click.option( + "--accept-current", + "accept_current", + is_flag=True, + default=False, + help="Stamp the current tree as the agreed baseline (deterministic, no LLM).", +) +@click.option( + "--prompt-wins", + "prompt_wins", + is_flag=True, + default=False, + help="[preview] Regenerate code from the prompt (not yet automated).", +) +@click.option( + "--code-wins", + "code_wins", + is_flag=True, + default=False, + help="[preview] Back-propagate code into the prompt (not yet automated).", +) +@click.option( + "--force", + is_flag=True, + default=False, + help="Allow --accept-current on non-CONFLICT (single-sided) drift, stamping " + "the current tree as truth without reconciling the stale side.", +) +@click.option( + "--json", + "as_json", + is_flag=True, + default=False, + help="Emit machine-readable JSON.", +) +@click.pass_context +def resolve( + ctx: click.Context, + basename: str, + language: str, + accept_current: bool, + prompt_wins: bool, + code_wins: bool, + force: bool, + as_json: bool, +) -> None: + # pylint: disable=too-many-arguments,too-many-positional-arguments + """Resolve a CONFLICT unit (prompt and code both changed since last sync). + + Choose exactly one strategy: --accept-current keeps the current files as the + new baseline; --prompt-wins / --code-wins preview the LLM strategies. + """ + ctx.ensure_object(dict) + if as_json: + ctx.obj["_suppress_result_summary"] = True + quiet = ctx.obj.get("quiet", False) + + chosen = [ + name + for name, flag in ( + ("--accept-current", accept_current), + ("--prompt-wins", prompt_wins), + ("--code-wins", code_wins), + ) + if flag + ] + if len(chosen) != 1: + raise click.UsageError( + "Choose exactly one of --accept-current, --prompt-wins, --code-wins." + ) + + if prompt_wins: + raise click.exceptions.Exit( + _preview_llm_strategy(basename, language, "prompt-wins") + ) + if code_wins: + raise click.exceptions.Exit( + _preview_llm_strategy(basename, language, "code-wins") + ) + + paths = _resolve_paths(basename, language) + if paths is None or ( + read_fingerprint(basename, language, paths=paths) is None + and not paths["prompt"].exists() + ): + raise click.ClickException( + f"No PDD unit '{basename}' ({language}) found to resolve. " + f"Run `pdd sync` to see tracked units." + ) + raise click.exceptions.Exit( + _accept_current(basename, language, paths, as_json, quiet, force) + ) diff --git a/pdd/continuous_sync.py b/pdd/continuous_sync.py index a1ae64baeb..20fa6cda55 100644 --- a/pdd/continuous_sync.py +++ b/pdd/continuous_sync.py @@ -1,29 +1,88 @@ -"""Deterministic continuous-sync classification and reconciliation reports.""" +"""Deterministic continuous-sync classification and reconciliation (shared core). + +This is the single implementation behind two surfaces: + +* ``pdd reconcile`` (``pdd/commands/reconcile.py``) — classify drift and, for the + safe cases, stamp ``.pdd/meta`` fingerprints to the current tree. +* ``scripts/stamp_fingerprints.py`` — the CI stamper/verifier, now a thin wrapper + over this module (issue #1927: no second hashing implementation). + +Unit/path resolution follows the repo's prompt tree and ``architecture.json``: +every ``pdd/prompts/[sub/]_python.prompt`` is one unit whose basename keeps +its subdirectory (``commands/firecrawl``, not the flattened ``commands_firecrawl``), +and whose code path honours an ``architecture.json`` ``filepath`` override, else the +``pdd/.py`` convention. Each unit maps to its own +``.pdd/meta/_python.json`` fingerprint. Hashing uses pdd's real +functions (``sync_determine_operation.calculate_current_hashes`` / +``operation_log.save_fingerprint``) so a reconcile stamp is byte-identical to what +``pdd sync`` would write. Zero LLM calls, no code/test regeneration. + +Classification vocabulary (unchanged from #1954, the CONFLICT definition is shared +with the sync decision path in ``sync_determine_operation``): a unit is IN_SYNC, +one of the single-sided drift states (DOC/PROMPT/CODE/TEST/EXAMPLE/DERIVED_CHANGED), +CONFLICT (prompt-side *and* code/derived-side both moved vs the stored meta), +UNBASELINED (missing / structurally invalid fingerprint) or FAILURE (resolution +error). Machine-readable verdicts additionally carry the issue #884 shape +(``status`` / ``reasons`` / ``affected_artifacts`` / ``remediation``). +""" +# pylint: disable=too-many-lines from __future__ import annotations +import contextlib +import fnmatch +import glob import json +import os import subprocess -from dataclasses import dataclass +from dataclasses import dataclass, field from datetime import datetime, timezone -from itertools import combinations from pathlib import Path -from typing import Any, Dict, Iterable, List, Optional +from typing import Any, Dict, Iterable, List, Optional, Tuple -from .operation_log import ( - _safe_basename, - get_fingerprint_path, - infer_module_identity, - save_fingerprint, -) +from .operation_log import _safe_basename, save_fingerprint from .sync_determine_operation import ( Fingerprint, calculate_current_hashes, - calculate_sha256, - get_pdd_file_paths, read_fingerprint, ) +# --- Constants --------------------------------------------------------------- + +LANGUAGE = "python" +PROMPT_SUFFIX = f"_{LANGUAGE}.prompt" + +# Hash fields compared to decide currency (metadata fields pdd_version / +# timestamp / command are not content-derived and are never compared). +HASH_FIELDS: Tuple[str, ...] = ( + "prompt_hash", + "code_hash", + "example_hash", + "test_hash", + "test_files", + "include_deps", +) + +# Fingerprint dataclass field order (mirrors sync_determine_operation.Fingerprint / +# operation_log.save_fingerprint's asdict(...) json.dump order). Re-exported for the +# stamper wrapper's byte-format test. +FIELD_ORDER: Tuple[str, ...] = ( + "pdd_version", + "timestamp", + "command", + "prompt_hash", + "code_hash", + "example_hash", + "test_hash", + "test_files", + "include_deps", +) + +# ``command`` recorded when a unit is stamped for the first time (backfill). 'fix' +# satisfies sync's completion gates and triggers none of its special branches; it +# is the modal command among committed fingerprints. +NEW_UNIT_COMMAND = "fix" +IN_SYNC_CLASSIFICATION = "IN_SYNC" DRIFT_CLASSIFICATIONS = { "DOC_CHANGED", "PROMPT_CHANGED", @@ -36,15 +95,26 @@ UNBASELINED_CLASSIFICATION = "UNBASELINED" FAILURE_CLASSIFICATION = "FAILURE" +# Issue #884 coarse verdict statuses (status / reasons / affected_artifacts / +# remediation is the report shape the drift classifier converges on). +STATUS_CURRENT = "current" +STATUS_STALE = "stale" +STATUS_STAMPED = "stamped" +STATUS_CONFLICT = "conflict" +STATUS_UNBASELINED = "unbaselined" +STATUS_FAILURE = "failure" -@dataclass(frozen=True) -class SyncUnit: - """A prompt-derived unit discovered for deterministic classification.""" - basename: str - language: str - prompt_path: Path - prompts_dir: Path +def _norm(value: Any) -> Any: + """Normalise empty containers / falsey hashes to ``None`` for comparison. + + Mirrors the stamper's ``(x or None)`` currency check so an absent artifact + (``None``) and an empty ``{}`` map compare equal. + """ + return value or None + + +# --- Project root ------------------------------------------------------------ def project_root(start: Optional[Path] = None) -> Path: @@ -70,233 +140,409 @@ def project_root(start: Optional[Path] = None) -> Path: return current -def _prompts_dir_for(prompt_path: Path) -> Path: - """Return the concrete prompts directory to pass into path resolution.""" - return prompt_path.parent +@contextlib.contextmanager +def _chdir(root: Path): + """Run with ``cwd == root`` so resolution + dep keys are repo-relative. + + pdd's ``calculate_prompt_hash`` resolves refs against ``Path.cwd()`` + and stores ``include_deps`` keys relative to it; the committed fingerprints use + repo-relative keys, so hashing must run from the project root to match. + """ + prev = Path.cwd() + os.chdir(root) + try: + yield + finally: + os.chdir(prev) + + +# --- Repo layout ------------------------------------------------------------- + + +@dataclass(frozen=True) +class _Layout: # pylint: disable=too-many-instance-attributes + """Resolved repo paths a reconcile run operates on.""" + + root: Path + prompts_root: Path + meta_dir: Path + context_root: Path + tests_root: Path + code_root: Path + architecture_file: Path + waivers_file: Path + + +def _layout_for(root: Path) -> _Layout: + """Derive the repo layout from ``root`` (the PDD project root). + + Prompts live under ``pdd/prompts`` in a PDD-managed repo; a ``prompts`` + symlink/dir at the root (the repo's ``prompts -> pdd/prompts`` shim, or a + consumer whose prompts sit at the top level) is accepted as a fallback. + """ + root = root.resolve() + prompts_root = root / "pdd" / "prompts" + if not prompts_root.exists() and (root / "prompts").exists(): + prompts_root = root / "prompts" + return _Layout( + root=root, + prompts_root=prompts_root, + meta_dir=root / ".pdd" / "meta", + context_root=root / "context", + tests_root=root / "tests", + code_root=root / "pdd", + architecture_file=root / "architecture.json", + waivers_file=root / "scripts" / "fingerprint_waivers.json", + ) -def _prompt_units(prompt_root: Path) -> List[SyncUnit]: - units: List[SyncUnit] = [] - for prompt_path in sorted(prompt_root.rglob("*_*.prompt")): - basename, language = infer_module_identity(prompt_path) - if basename is None or language is None: +# --- architecture.json resolution -------------------------------------------- +# pdd's get_pdd_file_paths consults architecture.json FIRST for a module's code +# filepath (issue #225) and disambiguates the example/test stem for a bare leaf +# that maps to more than one module (issue #1677). Both are pure JSON+path logic; +# they are mirrored here (matching scripts/stamp_fingerprints.py byte-for-byte) +# because get_pdd_file_paths otherwise flattens a subdir'd basename without an +# architecture entry (commands/firecrawl -> pdd/firecrawl.py instead of the real +# pdd/commands/firecrawl.py). + +_ArchMaps = Tuple[Dict[str, set], Dict[str, set], frozenset] +_ARCH_CACHE: Dict[Path, _ArchMaps] = {} + + +def _load_architecture(architecture_file: Path) -> _ArchMaps: + """Return ``(by_filename, by_leaf, ambiguous_leaves)`` from architecture.json.""" + cached = _ARCH_CACHE.get(architecture_file) + if cached is not None: + return cached + by_filename: Dict[str, set] = {} + by_leaf: Dict[str, set] = {} + if not architecture_file.is_file(): + result: _ArchMaps = (by_filename, by_leaf, frozenset()) + _ARCH_CACHE[architecture_file] = result + return result + try: + data = json.loads(architecture_file.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError): + result = (by_filename, by_leaf, frozenset()) + _ARCH_CACHE[architecture_file] = result + return result + if isinstance(data, list): + modules = data + elif isinstance(data, dict): + modules = data.get("modules", []) + else: + modules = [] + for module in modules: + if not isinstance(module, dict): continue - units.append( - SyncUnit( - basename=basename, - language=language, - prompt_path=prompt_path, - prompts_dir=_prompts_dir_for(prompt_path), - ) - ) - return units + filename = str(module.get("filename") or "").strip() + filepath = str(module.get("filepath") or "").strip() + if not filename or not filepath or filename.endswith("_LLM.prompt"): + continue + fp = Path(filepath).as_posix() + by_filename.setdefault(filename.lower(), set()).add(fp) + by_leaf.setdefault(Path(filename).name.lower(), set()).add(fp) + ambiguous = frozenset( + leaf[: -len(f"_{LANGUAGE}.prompt")] + for leaf, fps in by_leaf.items() + if leaf.endswith(f"_{LANGUAGE}.prompt") and len(fps) > 1 + ) + result = (by_filename, by_leaf, ambiguous) + _ARCH_CACHE[architecture_file] = result + return result + + +def _resolve_code_and_stem( + basename: str, layout: _Layout, arch_maps: _ArchMaps +) -> Tuple[Path, str]: + """Return ``(code_path, artifact_stem)`` mirroring pdd's resolution. + + ``code_path`` is the architecture.json filepath when the prompt is listed + there, else ``pdd/.py`` (subdirectory preserved). ``artifact_stem`` + is the example/test stem: the code file's stem, replaced by a disambiguated + ``_safe_basename()`` when the bare leaf is ambiguous. + """ + by_filename, by_leaf, ambiguous = arch_maps + leaf = basename.rsplit("/", 1)[-1] + pf_full = f"{basename}_{LANGUAGE}.prompt".lower() + pf_leaf = f"{leaf}_{LANGUAGE}.prompt".lower() + + arch_fp: Optional[str] = None + full_fps = by_filename.get(pf_full) + if full_fps and len(full_fps) == 1: + arch_fp = next(iter(full_fps)) + if arch_fp is None: + leaf_fps = by_leaf.get(pf_leaf, set()) + if len(leaf_fps) == 1: + arch_fp = next(iter(leaf_fps)) + elif len(leaf_fps) > 1: + aligned = [ + fp + for fp in leaf_fps + if Path(fp).with_suffix("").as_posix().endswith(basename) + ] + if len(aligned) == 1: + arch_fp = aligned[0] + + if arch_fp: + code = layout.root / arch_fp + if leaf in ambiguous: + stem = _safe_basename(Path(arch_fp).with_suffix("").as_posix()) + else: + stem = Path(arch_fp).stem + else: + code = layout.code_root / f"{basename}.py" + stem = leaf + return code, stem -def _matches_module(unit: SyncUnit, wanted: set[str]) -> bool: - return ( - unit.basename in wanted - or _safe_basename(unit.basename) in wanted - or unit.prompt_path.stem in wanted +# --- Unit enumeration & path resolution -------------------------------------- + + +@dataclass(frozen=True) +class Unit: # pylint: disable=too-many-instance-attributes + """A single PDD dev unit resolved from a ``*_python.prompt`` file.""" + + basename: str + language: str + prompt: Path + code: Path + example: Path + test: Path + test_files: Tuple[Path, ...] + meta: Path + + +def _discover_test_files(test_dir: Path, stem: str) -> List[Path]: + """Mirror pdd's Bug #156 globbing: sorted ``test_*.py`` in ``test_dir``.""" + primary = test_dir / f"test_{stem}.py" + if test_dir.is_dir(): + pattern = glob.escape(f"test_{stem}") + matches = sorted(test_dir.glob(f"{pattern}*.py")) + else: + matches = [primary] if primary.exists() else [] + return matches or [primary] + + +def _build_unit(basename: str, prompt: Path, layout: _Layout, arch_maps: _ArchMaps) -> Unit: + """Resolve every artifact path for ``basename`` (stamper-parity resolution).""" + subdir = str(Path(basename).parent) if "/" in basename else "" + code, stem = _resolve_code_and_stem(basename, layout, arch_maps) + example_dir = layout.context_root / subdir if subdir else layout.context_root + test_dir = layout.tests_root / subdir if subdir else layout.tests_root + return Unit( + basename=basename, + language=LANGUAGE, + prompt=prompt, + code=code, + example=example_dir / f"{stem}_example.py", + test=test_dir / f"test_{stem}.py", + test_files=tuple(_discover_test_files(test_dir, stem)), + meta=layout.meta_dir / f"{_safe_basename(basename)}_{LANGUAGE}.json", ) -def _metadata_identity(path: Path) -> Optional[tuple[str, str]]: - stem = path.stem - if stem.endswith("_run") or "_" not in stem: +def infer_basename(prompt_path: Path, prompts_root: Path) -> Optional[str]: + """Return the unit basename for a ``*_python.prompt`` file, or None. + + Basename is the path relative to ``prompts_root`` with the ``_python.prompt`` + suffix dropped and subdirectories preserved (e.g. ``commands/which``). + """ + name = prompt_path.name + if not name.endswith(PROMPT_SUFFIX): return None - basename, language = stem.rsplit("_", 1) - if not basename or not language: + try: + rel = prompt_path.relative_to(prompts_root) + except ValueError: return None - return basename, language - + stem = rel.name[: -len(PROMPT_SUFFIX)] + subdir = rel.parent + if str(subdir) == ".": + return stem + return (subdir / stem).as_posix() -def _module_token_basename(token: str, language: str) -> str: - basename = token[:-7] if token.endswith(".prompt") else token - language_suffix = f"_{language}" - if basename.endswith(language_suffix): - return basename[: -len(language_suffix)] - return basename +def enumerate_units( + layout: _Layout, + arch_maps: _ArchMaps, + modules: Optional[Iterable[str]] = None, +) -> List[Unit]: + """Return every python unit under the prompts root, sorted by basename. -def _requested_basename_for_identity( - identity: tuple[str, str], - wanted: set[str], -) -> Optional[str]: - safe_basename, language = identity - for item in sorted(wanted): - basename = _module_token_basename(item, language) - if basename == safe_basename or _safe_basename(basename) == safe_basename: - return basename - return None + ``modules`` (a set of basenames, safe-basenames or prompt stems) filters the + result; ``None`` returns all units. + """ + wanted = set(modules or []) + units: List[Unit] = [] + if not layout.prompts_root.exists(): + return units + for prompt in sorted(layout.prompts_root.rglob(f"*{PROMPT_SUFFIX}")): + basename = infer_basename(prompt, layout.prompts_root) + if basename is None: + continue + if wanted and not _unit_wanted(basename, prompt, wanted): + continue + units.append(_build_unit(basename, prompt, layout, arch_maps)) + return units -def _identity_matches_wanted(identity: tuple[str, str], wanted: set[str]) -> bool: - safe_basename, language = identity +def _unit_wanted(basename: str, prompt: Path, wanted: set) -> bool: + """Match a unit against ``--module`` / ``[BASENAME]`` selectors.""" return ( - safe_basename in wanted - or f"{safe_basename}_{language}" in wanted - or f"{safe_basename}_{language}.prompt" in wanted - or _requested_basename_for_identity(identity, wanted) is not None + basename in wanted + or _safe_basename(basename) in wanted + or prompt.stem in wanted + or basename.rsplit("/", 1)[-1] in wanted ) -def _prompt_path_for_basename(prompt_root: Path, basename: str, language: str) -> Path: - parts = Path(basename).parts - if len(parts) <= 1: - return prompt_root / f"{basename}_{language}.prompt" - return prompt_root.joinpath(*parts[:-1], f"{parts[-1]}_{language}.prompt") - - -def _slash_candidates(safe_basename: str) -> List[str]: - parts = safe_basename.split("_") - if len(parts) <= 1: - return [] - - candidates: List[str] = [] - separators = range(1, len(parts)) - for count in range(1, len(parts)): - for slash_positions in combinations(separators, count): - chunks: List[str] = [parts[0]] - for index, part in enumerate(parts[1:], start=1): - if index in slash_positions: - chunks.append("/") - else: - chunks.append("_") - chunks.append(part) - candidates.append("".join(chunks)) - return candidates - - -def _existing_artifact_score( - basename: str, - language: str, - prompt_root: Path, -) -> int: - try: - paths = get_pdd_file_paths(basename, language, prompts_dir=str(prompt_root)) - except Exception: - return 0 - score = 0 - for artifact in ("code", "example", "test"): - path = paths.get(artifact) - if path is not None and path.exists(): - score += 1 - return score - - -def _infer_basename_from_artifacts( - safe_basename: str, - language: str, - prompt_root: Path, -) -> str: - best = safe_basename - best_score = _existing_artifact_score(best, language, prompt_root) - for candidate in _slash_candidates(safe_basename): - score = _existing_artifact_score(candidate, language, prompt_root) - if score > best_score: - best = candidate - best_score = score - return best - - -def _unit_from_metadata_identity( - identity: tuple[str, str], - prompt_root: Path, - prompt_index: Dict[tuple[str, str], SyncUnit], - requested_basename: Optional[str] = None, -) -> SyncUnit: - safe_basename, language = identity - prompt_unit = prompt_index.get((safe_basename, language)) - if prompt_unit is not None: - return prompt_unit - - basename = requested_basename or _infer_basename_from_artifacts( - safe_basename, - language, - prompt_root, - ) - prompt_path = _prompt_path_for_basename(prompt_root, basename, language) - return SyncUnit( - basename=basename, - language=language, - prompt_path=prompt_path, - prompts_dir=prompt_root, - ) +def resolve_units( + root: Path, modules: Optional[Iterable[str]] = None +) -> List[Unit]: + """Public: resolve every python unit under ``root`` (stamper-parity paths).""" + layout = _layout_for(Path(root)) + arch_maps = _load_architecture(layout.architecture_file) + return enumerate_units(layout, arch_maps, modules=modules) -def _metadata_identities(meta_root: Path) -> List[tuple[str, str]]: - identities: List[tuple[str, str]] = [] - seen: set[tuple[str, str]] = set() - if not meta_root.exists(): - return identities - for path in sorted(meta_root.glob("*_*.json")): - identity = _metadata_identity(path) - if identity is None or identity in seen: - continue - seen.add(identity) - identities.append(identity) - return identities +def resolve_unit(basename: str, root: Path, prompt: Optional[Path] = None) -> Unit: + """Public: resolve one unit's artifact paths against ``root``. + ``prompt`` defaults to ``/_python.prompt``. + """ + layout = _layout_for(Path(root)) + arch_maps = _load_architecture(layout.architecture_file) + if prompt is None: + prompt = layout.prompts_root / f"{basename}{PROMPT_SUFFIX}" + return _build_unit(basename, Path(prompt), layout, arch_maps) -def discover_units( - root: Optional[Path] = None, - modules: Optional[Iterable[str]] = None, -) -> List[SyncUnit]: - """Discover prompt-backed units under ``root``.""" - base = project_root(root) - wanted = set(modules or []) - prompt_root = base / "prompts" - prompt_units = _prompt_units(prompt_root) if prompt_root.exists() else [] - meta_root = base / ".pdd" / "meta" - metadata_identities = _metadata_identities(meta_root) - - prompt_index: Dict[tuple[str, str], SyncUnit] = {} - for unit in prompt_units: - prompt_index.setdefault((_safe_basename(unit.basename), unit.language), unit) - - if wanted: - units: List[SyncUnit] = [] - seen: set[tuple[str, str, Path]] = set() - for identity in metadata_identities: - if not _identity_matches_wanted(identity, wanted): - continue - unit = _unit_from_metadata_identity( - identity, - prompt_root, - prompt_index, - requested_basename=_requested_basename_for_identity(identity, wanted), - ) - dedupe_key = (unit.basename, unit.language, unit.prompt_path) - if dedupe_key in seen: - continue - seen.add(dedupe_key) - units.append(unit) - for unit in prompt_units: - if not _matches_module(unit, wanted): - continue - dedupe_key = (unit.basename, unit.language, unit.prompt_path) - if dedupe_key in seen: - continue - seen.add(dedupe_key) - units.append(unit) - return units - if not metadata_identities: - return prompt_units +# --- .pddignore & waivers ---------------------------------------------------- - units: List[SyncUnit] = [] - seen: set[tuple[str, str, Path]] = set() - for identity in metadata_identities: - unit = _unit_from_metadata_identity(identity, prompt_root, prompt_index) - dedupe_key = (unit.basename, unit.language, unit.prompt_path) - if dedupe_key in seen: + +def load_pddignore(root: Path) -> List[str]: + """Load ``.pddignore`` patterns from ``root`` (comments/blank stripped).""" + path = root / ".pddignore" + patterns: List[str] = [] + if not path.is_file(): + return patterns + for line in path.read_text(encoding="utf-8").splitlines(): + line = line.strip() + if not line or line.startswith("#"): continue - seen.add(dedupe_key) - units.append(unit) - return units + patterns.append(line) + return patterns -def _load_fingerprint_json(path: Path) -> tuple[Optional[Dict[str, Any]], Optional[str]]: +def is_pddignored(code_path: Path, patterns: List[str], root: Path) -> bool: + """Mirror update_main._is_pddignored against the repo-relative code path.""" + try: + rel_path = code_path.resolve().relative_to(root.resolve()).as_posix() + except ValueError: + return False + basename = code_path.name + for pat in patterns: + if pat.endswith("/"): + dir_name = pat.rstrip("/") + parts = rel_path.split("/") + if dir_name in parts[:-1]: + return True + else: + if fnmatch.fnmatch(rel_path, pat): + return True + if fnmatch.fnmatch(basename, pat): + return True + return False + + +def load_waivers(waivers_file: Path) -> Dict[str, str]: + """Return ``{basename: reason}`` from the committed waiver file.""" + if not waivers_file.is_file(): + return {} + try: + data = json.loads(waivers_file.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError): + return {} + waivers: Dict[str, str] = {} + for entry in data.get("waivers", []): + if isinstance(entry, dict) and entry.get("basename"): + waivers[entry["basename"]] = entry.get("reason", "") + return waivers + + +def partition_units( + units: List[Unit], pddignore: List[str], waivers: Dict[str, str], root: Path +) -> Tuple[List[Unit], List[Unit], List[Unit], List[Unit]]: + """Split units into ``(stampable, ignored, waived, no_code)`` buckets. + + * ignored: code path matches ``.pddignore`` (pdd never tracks these). + * waived: basename appears in the committed waiver file. + * no_code: code file absent and not waived (nothing to hash). + * stampable: everything else. + """ + stampable: List[Unit] = [] + ignored: List[Unit] = [] + waived: List[Unit] = [] + no_code: List[Unit] = [] + for unit in units: + if is_pddignored(unit.code, pddignore, root): + ignored.append(unit) + elif unit.basename in waivers: + waived.append(unit) + elif not unit.code.exists(): + no_code.append(unit) + else: + stampable.append(unit) + return stampable, ignored, waived, no_code + + +# --- Hashing (pdd's real functions) ------------------------------------------ + + +def _paths_for_hashing(unit: Unit) -> Dict[str, Any]: + """Build the ``paths`` dict pdd's ``calculate_current_hashes`` expects.""" + return { + "prompt": unit.prompt, + "code": unit.code, + "example": unit.example, + "test": unit.test, + "test_files": list(unit.test_files), + } + + +def compute_current_hashes( + unit: Unit, stored_deps: Optional[Dict[str, str]] = None +) -> Dict[str, Any]: + """Recompute all fingerprint hash fields for ``unit`` via pdd's real hasher. + + Must run under ``_chdir(root)`` so resolution matches the committed + fingerprints (see the public ``hashes_for`` for a self-contained variant). + """ + return calculate_current_hashes( + _paths_for_hashing(unit), stored_include_deps=stored_deps + ) + + +def hashes_for( + unit: Unit, root: Path, stored_deps: Optional[Dict[str, str]] = None +) -> Dict[str, Any]: + """Public: recompute ``unit``'s hashes with cwd anchored at ``root``.""" + with _chdir(Path(root)): + return compute_current_hashes(unit, stored_deps) + + +def _read_fingerprint_obj(unit: Unit) -> Optional[Fingerprint]: + """Read ``unit``'s committed fingerprint, or None if missing/invalid. + + ``get_fingerprint_path(basename, paths=...)`` resolves to ``unit.meta`` (pinned + by the resolution parity check), so read_fingerprint reads the same file the + stamper writes. + """ + return read_fingerprint(unit.basename, unit.language, paths=_paths_for_hashing(unit)) + + +def _load_fingerprint_json(path: Path) -> Tuple[Optional[Dict[str, Any]], Optional[str]]: + """Return ``(data, error)`` distinguishing a missing vs unparseable meta.""" if not path.exists(): return None, "missing" try: @@ -308,175 +554,55 @@ def _load_fingerprint_json(path: Path) -> tuple[Optional[Dict[str, Any]], Option return data, None -def _paths_as_json(paths: Dict[str, Any], root: Path) -> Dict[str, Any]: - payload: Dict[str, Any] = {} - for key, value in paths.items(): - if isinstance(value, Path): - try: - payload[key] = value.resolve().relative_to(root.resolve()).as_posix() - except (OSError, ValueError): - payload[key] = str(value) - elif isinstance(value, list): - payload[key] = [str(item) for item in value] - else: - payload[key] = str(value) - return payload +def _hashes_equal(stored: Fingerprint, current: Dict[str, Any]) -> bool: + """True iff every content hash field matches (the currency test).""" + return all( + _norm(getattr(stored, field_name, None)) == _norm(current.get(field_name)) + for field_name in HASH_FIELDS + ) + + +# --- Classification ---------------------------------------------------------- def _changed_artifacts( - fingerprint: Fingerprint, - paths: Dict[str, Path], - current_hashes: Dict[str, Any], + fingerprint: Fingerprint, paths: Dict[str, Any], current_hashes: Dict[str, Any] ) -> List[str]: + """Return the changed artifact labels (doc/prompt/code/example/test).""" changes: List[str] = [] - if current_hashes.get("prompt_hash") != fingerprint.prompt_hash: - if (current_hashes.get("include_deps") or {}) != ( - fingerprint.include_deps or {} - ): + if _norm(current_hashes.get("prompt_hash")) != _norm(fingerprint.prompt_hash): + if _norm(current_hashes.get("include_deps")) != _norm(fingerprint.include_deps): changes.append("doc") else: changes.append("prompt") if ( paths.get("code") and paths["code"].exists() - and current_hashes.get("code_hash") != fingerprint.code_hash + and _norm(current_hashes.get("code_hash")) != _norm(fingerprint.code_hash) ): changes.append("code") if ( paths.get("example") and paths["example"].exists() - and current_hashes.get("example_hash") != fingerprint.example_hash + and _norm(current_hashes.get("example_hash")) != _norm(fingerprint.example_hash) ): changes.append("example") if ( paths.get("test") and paths["test"].exists() - and current_hashes.get("test_hash") != fingerprint.test_hash + and _norm(current_hashes.get("test_hash")) != _norm(fingerprint.test_hash) ): changes.append("test") return changes -def _missing_fingerprinted_artifacts( - fingerprint: Fingerprint, - paths: Dict[str, Path], -) -> List[str]: - missing: List[str] = [] - field_by_artifact = { - "prompt": "prompt_hash", - "code": "code_hash", - "example": "example_hash", - "test": "test_hash", - } - for artifact, field in field_by_artifact.items(): - path = paths.get(artifact) - if getattr(fingerprint, field) and (path is None or not path.exists()): - missing.append(artifact) - return missing - - -def _artifact_search_name( - artifact: str, - basename: str, - expected_path: Path, -) -> Optional[str]: - suffix = expected_path.suffix - leaf = Path(basename).name - if artifact == "code": - return f"{leaf}{suffix}" - if artifact == "example": - return f"{leaf}_example{suffix}" - if artifact == "test": - return f"test_{leaf}{suffix}" - return None - - -def _find_matching_artifact( - root: Path, - filename: str, - expected_hash: str, -) -> Optional[Path]: - matches: List[Path] = [] - for candidate in root.rglob(filename): - if any(part in {".git", ".pdd", "__pycache__"} for part in candidate.parts): - continue - if candidate.is_file() and calculate_sha256(candidate) == expected_hash: - matches.append(candidate) - if len(matches) == 1: - return matches[0] - return None - - -def _repair_missing_fingerprinted_paths( - paths: Dict[str, Path], - fingerprint: Fingerprint, - basename: str, - root: Path, -) -> Dict[str, Path]: - repaired = dict(paths) - field_by_artifact = { - "code": "code_hash", - "example": "example_hash", - "test": "test_hash", - } - for artifact, field in field_by_artifact.items(): - path = repaired.get(artifact) - expected_hash = getattr(fingerprint, field) - if path is None or path.exists() or not expected_hash: - continue - filename = _artifact_search_name(artifact, basename, path) - if not filename: - continue - match = _find_matching_artifact(root, filename, expected_hash) - if match is None: - continue - repaired[artifact] = match - if artifact == "test": - repaired["test_files"] = [match] - return repaired - - -def _missing_required_hashes(fingerprint: Fingerprint, paths: Dict[str, Path]) -> List[str]: - missing: List[str] = [] - field_by_artifact = { - "prompt": "prompt_hash", - "code": "code_hash", - "example": "example_hash", - "test": "test_hash", - } - for artifact, field in field_by_artifact.items(): - path = paths.get(artifact) - if path is not None and path.exists() and not getattr(fingerprint, field): - missing.append(field) - return missing - - -def _operation_for(classification: str, changes: List[str]) -> str: - # pylint: disable=too-many-return-statements - if classification == "IN_SYNC": - return "nothing" - if classification == "DOC_CHANGED": - return "reconcile" - if classification == "PROMPT_CHANGED": - return "generate" - if classification == "CODE_CHANGED": - return "update" - if classification == "TEST_CHANGED": - return "test" - if classification == "EXAMPLE_CHANGED": - return "verify" - if classification == "CONFLICT": - return "conflict" - if classification in {"UNBASELINED", "FAILURE"}: - return "none" - if "code" in changes or "example" in changes: - return "verify" - if "test" in changes: - return "test" - return "reconcile" - - def _classification_for_changes(changes: List[str]) -> str: + """Map changed artifacts to a classification. + + CONFLICT is prompt-side (prompt/doc) AND code/derived-side both moved — the + same definition the sync decision path uses (issue #1929 owns that path; this + keeps the two aligned). + """ # pylint: disable=too-many-return-statements derived = [item for item in changes if item not in {"prompt", "doc"}] if any(item in changes for item in ("prompt", "doc")) and derived: @@ -493,160 +619,219 @@ def _classification_for_changes(changes: List[str]) -> str: return "EXAMPLE_CHANGED" if changes: return "DERIVED_CHANGED" - return "IN_SYNC" + return IN_SYNC_CLASSIFICATION -def classify_unit(unit: SyncUnit, root: Optional[Path] = None) -> Dict[str, Any]: - # pylint: disable=broad-exception-caught - """Classify one sync unit without mutating files.""" - base = project_root(root) - try: - paths = get_pdd_file_paths( - unit.basename, - unit.language, - prompts_dir=str(unit.prompts_dir), - ) - except Exception as exc: # pragma: no cover - surfaced in JSON report - return { - "basename": unit.basename, - "language": unit.language, - "classification": FAILURE_CLASSIFICATION, - "operation": "none", - "reason": f"path resolution failed: {exc}", - "changed_files": [], - "metadata_valid": False, - "paths": {"prompt": str(unit.prompt_path)}, - } +def _operation_for(classification: str, changes: List[str]) -> str: + """Map a classification to the pdd operation that would resolve it.""" + mapping = { + IN_SYNC_CLASSIFICATION: "nothing", + "DOC_CHANGED": "reconcile", + "PROMPT_CHANGED": "generate", + "CODE_CHANGED": "update", + "TEST_CHANGED": "test", + "EXAMPLE_CHANGED": "verify", + CONFLICT_CLASSIFICATION: "conflict", + UNBASELINED_CLASSIFICATION: "none", + FAILURE_CLASSIFICATION: "none", + } + if classification in mapping: + return mapping[classification] + if "code" in changes or "example" in changes: + return "verify" + if "test" in changes: + return "test" + return "reconcile" - fp_path = get_fingerprint_path(unit.basename, unit.language, paths=paths) - _raw_fp, raw_error = _load_fingerprint_json(fp_path) - if raw_error is not None: - return { - "basename": unit.basename, - "language": unit.language, - "classification": UNBASELINED_CLASSIFICATION, - "operation": "none", - "reason": f"fingerprint {raw_error}", - "changed_files": [], - "metadata_valid": False, - "fingerprint_path": str(fp_path), - "paths": _paths_as_json(paths, base), - } - fingerprint = read_fingerprint(unit.basename, unit.language, paths=paths) - if fingerprint is None: - return { - "basename": unit.basename, - "language": unit.language, - "classification": UNBASELINED_CLASSIFICATION, - "operation": "none", - "reason": "fingerprint invalid", - "changed_files": [], - "metadata_valid": False, - "fingerprint_path": str(fp_path), - "paths": _paths_as_json(paths, base), - } +def _status_for(classification: str) -> str: + """Map a classification to the coarse issue #884 status.""" + if classification == IN_SYNC_CLASSIFICATION: + return STATUS_CURRENT + if classification in DRIFT_CLASSIFICATIONS: + return STATUS_STALE + if classification == CONFLICT_CLASSIFICATION: + return STATUS_CONFLICT + if classification == UNBASELINED_CLASSIFICATION: + return STATUS_UNBASELINED + return STATUS_FAILURE - if not unit.prompt_path.exists(): - paths = _repair_missing_fingerprinted_paths( - paths, - fingerprint, - unit.basename, - base, - ) - missing_hashes = _missing_required_hashes(fingerprint, paths) - if missing_hashes: - return { - "basename": unit.basename, - "language": unit.language, - "classification": FAILURE_CLASSIFICATION, - "operation": "none", - "reason": "incomplete metadata: " + ", ".join(sorted(missing_hashes)), - "changed_files": [], - "metadata_valid": False, - "fingerprint_path": str(fp_path), - "paths": _paths_as_json(paths, base), - "failure": "incomplete_metadata", - } +def _remediation_for(basename: str, classification: str) -> str: + """Suggested next command for a non-current unit.""" + if classification in (IN_SYNC_CLASSIFICATION, FAILURE_CLASSIFICATION): + return "" + if classification == "PROMPT_CHANGED": + return f"pdd sync {basename}" + if classification == "CODE_CHANGED": + return f"pdd update {basename}" + if classification == CONFLICT_CLASSIFICATION: + return f"pdd reconcile {basename} --accept-current # or resolve the conflict" + if classification == UNBASELINED_CLASSIFICATION: + return f"pdd reconcile {basename} --backfill" + # DOC/TEST/EXAMPLE/DERIVED — a plain reconcile stamp is the fix. + return f"pdd reconcile {basename}" - missing_artifacts = _missing_fingerprinted_artifacts(fingerprint, paths) - if missing_artifacts: - return { - "basename": unit.basename, - "language": unit.language, - "classification": FAILURE_CLASSIFICATION, - "operation": "none", - "reason": "missing fingerprinted artifacts: " - + ", ".join(sorted(missing_artifacts)), - "changed_files": missing_artifacts, - "metadata_valid": True, - "fingerprint_path": str(fp_path), - "paths": _paths_as_json(paths, base), - "failure": "missing_artifacts", - } - current_hashes = calculate_current_hashes( - paths, - stored_include_deps=fingerprint.include_deps, - ) - changes = _changed_artifacts(fingerprint, paths, current_hashes) - classification = _classification_for_changes(changes) - return { +def _rel(path: Path, root: Path) -> str: + try: + return path.resolve().relative_to(root.resolve()).as_posix() + except (OSError, ValueError): + return str(path) + + +def _verdict( + unit: Unit, + layout: _Layout, + *, + classification: str, + changes: List[str], + reason: str, + metadata_valid: bool, + current_hashes: Optional[Dict[str, Any]] = None, +) -> Dict[str, Any]: + # pylint: disable=too-many-arguments + """Assemble one unit's report entry (legacy fields + issue #884 shape).""" + entry: Dict[str, Any] = { "basename": unit.basename, "language": unit.language, "classification": classification, + "status": _status_for(classification), "operation": _operation_for(classification, changes), - "reason": ( - "all hashes match fingerprint" - if classification == "IN_SYNC" - else f"{', '.join(changes)} changed since fingerprint" - ), + "reason": reason, + "reasons": [reason] if reason else [], "changed_files": changes, - "metadata_valid": True, - "fingerprint_path": str(fp_path), - "paths": _paths_as_json(paths, base), - "hashes": { + "affected_artifacts": changes, + "remediation": _remediation_for(unit.basename, classification), + "metadata_valid": metadata_valid, + "fingerprint_path": _rel(unit.meta, layout.root), + "paths": { + "prompt": _rel(unit.prompt, layout.root), + "code": _rel(unit.code, layout.root), + "example": _rel(unit.example, layout.root), + "test": _rel(unit.test, layout.root), + "test_files": [_rel(p, layout.root) for p in unit.test_files], + }, + } + if current_hashes is not None: + entry["hashes"] = { "prompt_hash": current_hashes.get("prompt_hash"), "code_hash": current_hashes.get("code_hash"), "example_hash": current_hashes.get("example_hash"), "test_hash": current_hashes.get("test_hash"), - }, - } + } + return entry + + +def classify_unit(unit: Unit, layout: _Layout) -> Dict[str, Any]: + """Classify one unit without mutating files. Runs under ``_chdir(root)``.""" + _raw, raw_error = _load_fingerprint_json(unit.meta) + if raw_error is not None: + return _verdict( + unit, + layout, + classification=UNBASELINED_CLASSIFICATION, + changes=[], + reason=f"fingerprint {raw_error}", + metadata_valid=False, + ) + fingerprint = _read_fingerprint_obj(unit) + if fingerprint is None: + return _verdict( + unit, + layout, + classification=UNBASELINED_CLASSIFICATION, + changes=[], + reason="fingerprint invalid", + metadata_valid=False, + ) + current = compute_current_hashes(unit, fingerprint.include_deps) + changes = _changed_artifacts(fingerprint, _paths_for_hashing(unit), current) + classification = _classification_for_changes(changes) + reason = ( + "all hashes match fingerprint" + if classification == IN_SYNC_CLASSIFICATION + else f"{', '.join(changes)} changed since fingerprint" + ) + return _verdict( + unit, + layout, + classification=classification, + changes=changes, + reason=reason, + metadata_valid=True, + current_hashes=current, + ) + + +# --- Stamping (idempotent, byte-identical to pdd sync) ----------------------- + + +def stamp_unit(unit: Unit) -> bool: + """Stamp ``unit``'s fingerprint to the current tree iff its hashes changed. + + Idempotent by construction: a unit whose content hashes already match the + stored fingerprint is NOT rewritten (its timestamp is preserved and no file is + written), so a second consecutive run is a no-op and unchanged units never + churn (issue #1932 / pdd_cloud restamp-churn addendum). Returns True iff a + file was written. Must run under ``_chdir(root)``. + """ + stored = _read_fingerprint_obj(unit) + current = compute_current_hashes( + unit, stored.include_deps if stored is not None else None + ) + if stored is not None and _hashes_equal(stored, current): + return False + command = NEW_UNIT_COMMAND + if stored is not None and stored.command: + command = stored.command + save_fingerprint(unit.basename, unit.language, command, paths=_paths_for_hashing(unit)) + return True + + +def stamp_units(units: Iterable[Unit], root: Path) -> List[str]: + """Public: idempotently stamp each unit with cwd anchored at ``root``. + + Returns the basenames actually written (unchanged units are skipped). + """ + written: List[str] = [] + with _chdir(Path(root)): + for unit in units: + if stamp_unit(unit): + written.append(unit.basename) + return written + + +# --- Reporting --------------------------------------------------------------- def _build_summary(units: List[Dict[str, Any]]) -> Dict[str, int]: return { "metadata_stale": sum( - 1 for unit in units if unit["classification"] in DRIFT_CLASSIFICATIONS + 1 for u in units if u["classification"] in DRIFT_CLASSIFICATIONS ), "conflicts": sum( - 1 for unit in units if unit["classification"] == CONFLICT_CLASSIFICATION + 1 for u in units if u["classification"] == CONFLICT_CLASSIFICATION ), "unbaselined": sum( - 1 - for unit in units - if unit["classification"] == UNBASELINED_CLASSIFICATION + 1 for u in units if u["classification"] == UNBASELINED_CLASSIFICATION ), "failures": sum( - 1 for unit in units if unit["classification"] == FAILURE_CLASSIFICATION + 1 for u in units if u["classification"] == FAILURE_CLASSIFICATION ), - "synced": sum(1 for unit in units if unit["classification"] == "IN_SYNC"), + "synced": sum(1 for u in units if u["classification"] == IN_SYNC_CLASSIFICATION), + "stamped": sum(1 for u in units if u.get("status") == STATUS_STAMPED), "total": len(units), } -def _append_ledger( - root: Path, - consumer: str, - units: List[Dict[str, Any]], -) -> Optional[str]: +def _append_ledger(root: Path, consumer: str, units: List[Dict[str, Any]]) -> Optional[str]: ledger_path = root / ".pdd" / "drift-ledger.jsonl" ledger_path.parent.mkdir(parents=True, exist_ok=True) wrote = False with ledger_path.open("a", encoding="utf-8") as handle: for unit in units: - if unit["classification"] == "IN_SYNC": + if unit["classification"] == IN_SYNC_CLASSIFICATION: continue entry = { "timestamp": datetime.now(timezone.utc).isoformat(), @@ -654,6 +839,7 @@ def _append_ledger( "basename": unit["basename"], "language": unit["language"], "classification": unit["classification"], + "status": unit.get("status"), "operation": unit["operation"], "changed_files": unit.get("changed_files", []), "reason": unit.get("reason", ""), @@ -663,30 +849,23 @@ def _append_ledger( return str(ledger_path) if wrote else None -def _heal_units( - units: List[SyncUnit], - classified: List[Dict[str, Any]], -) -> List[str]: - healed: List[str] = [] - by_key = {(unit.basename, unit.language): unit for unit in units} - for item in classified: - if item["classification"] not in DRIFT_CLASSIFICATIONS: - continue - unit = by_key.get((item["basename"], item["language"])) - if unit is None: - continue - paths = get_pdd_file_paths( - unit.basename, - unit.language, - prompts_dir=str(unit.prompts_dir), - ) - fingerprint = read_fingerprint(unit.basename, unit.language, paths=paths) - operation = "fix" - if fingerprint and fingerprint.command in {"verify", "test", "fix", "update"}: - operation = fingerprint.command - save_fingerprint(unit.basename, unit.language, operation, paths, 0.0, "reconcile") - healed.append(unit.basename) - return healed +def _stampable_now( + classification: str, *, heal: bool, accept_current: bool, backfill: bool +) -> bool: + """Should a unit with this classification be stamped this run? + + ``heal`` stamps single-sided drift (the safe cases). CONFLICT (both sides + moved) is stamped only with an explicit ``--accept-current`` (the human + declares the current tree the agreed state); UNBASELINED only with + ``--backfill``. Each flag is scoped to exactly its class. + """ + if classification in DRIFT_CLASSIFICATIONS: + return heal + if classification == CONFLICT_CLASSIFICATION: + return accept_current + if classification == UNBASELINED_CLASSIFICATION: + return backfill + return False def build_report( @@ -696,51 +875,144 @@ def build_report( modules: Optional[Iterable[str]] = None, heal: bool = False, ledger: bool = False, + accept_current: bool = False, + backfill: bool = False, ) -> Dict[str, Any]: - """Build a shared continuous-sync JSON report.""" + # pylint: disable=too-many-arguments,too-many-locals + """Build the shared continuous-sync report, optionally stamping safe cases. + + ``heal`` stamps single-sided drift; ``accept_current`` additionally stamps + CONFLICT units; ``backfill`` additionally stamps UNBASELINED units. Stamping + is idempotent and re-classifies afterwards so the reported status reflects the + post-stamp tree (a stamped unit becomes ``current`` with ``status=stamped``). + """ base = project_root(root) - units = discover_units(base, modules=modules) - classified = [classify_unit(unit, base) for unit in units] - healed = _heal_units(units, classified) if heal else [] - if healed: - classified = [classify_unit(unit, base) for unit in units] + layout = _layout_for(base) + arch_maps = _load_architecture(layout.architecture_file) + units = enumerate_units(layout, arch_maps, modules=modules) + pddignore = load_pddignore(layout.root) + waivers = load_waivers(layout.waivers_file) + stampable, _ignored, _waived, _no_code = partition_units( + units, pddignore, waivers, layout.root + ) + + do_stamp = heal or accept_current or backfill + stamped: List[str] = [] + with _chdir(layout.root): + classified = [classify_unit(u, layout) for u in stampable] + if do_stamp: + by_name = {u.basename: u for u in stampable} + for entry in classified: + if _stampable_now( + entry["classification"], + heal=heal, + accept_current=accept_current, + backfill=backfill, + ): + unit = by_name.get(entry["basename"]) + if unit is not None and stamp_unit(unit): + stamped.append(unit.basename) + if stamped: + stamped_set = set(stamped) + classified = [classify_unit(u, layout) for u in stampable] + for entry in classified: + if entry["basename"] in stamped_set: + entry["status"] = STATUS_STAMPED + summary = _build_summary(classified) - ledger_path = _append_ledger(base, consumer, classified) if ledger else None + ledger_path = _append_ledger(layout.root, consumer, classified) if ledger else None ok = ( summary["metadata_stale"] == 0 and summary["conflicts"] == 0 and summary["unbaselined"] == 0 and summary["failures"] == 0 ) - report = { + report: Dict[str, Any] = { "ok": ok, "consumer": consumer, - "project_root": str(base), + "project_root": str(layout.root), "summary": summary, "metadata_stale": summary["metadata_stale"], "units": classified, - "drift": [ - unit - for unit in classified - if unit["classification"] in DRIFT_CLASSIFICATIONS - ], + "drift": [u for u in classified if u["classification"] in DRIFT_CLASSIFICATIONS], "conflicts": [ - unit - for unit in classified - if unit["classification"] == CONFLICT_CLASSIFICATION + u for u in classified if u["classification"] == CONFLICT_CLASSIFICATION ], "unbaselined": [ - unit - for unit in classified - if unit["classification"] == UNBASELINED_CLASSIFICATION + u for u in classified if u["classification"] == UNBASELINED_CLASSIFICATION ], "failures": [ - unit - for unit in classified - if unit["classification"] == FAILURE_CLASSIFICATION + u for u in classified if u["classification"] == FAILURE_CLASSIFICATION ], - "healed": healed, + "healed": stamped, + "stamped": stamped, } if ledger_path: report["ledger_path"] = ledger_path return report + + +# --- Verification (--check) -------------------------------------------------- + + +@dataclass +class CheckResult: + """Outcome of a read-only ``--check`` verification.""" + + ok: bool + stampable: int + ignored: int + waived: int + missing: List[str] = field(default_factory=list) + stale: List[Tuple[str, List[str]]] = field(default_factory=list) + no_code: List[str] = field(default_factory=list) + + +def run_check( + root: Optional[Path] = None, modules: Optional[Iterable[str]] = None +) -> CheckResult: + # pylint: disable=too-many-locals + """Verify committed fingerprints are current (read-only, no writes). + + Contract mirrors ``scripts/stamp_fingerprints.py --check``: waived/ignored + units are excluded; a stampable unit with a missing/invalid fingerprint or any + stale hash field, or an unwaived unit with no code file, makes the result not + ``ok``. + """ + base = project_root(root) + layout = _layout_for(base) + arch_maps = _load_architecture(layout.architecture_file) + units = enumerate_units(layout, arch_maps, modules=modules) + pddignore = load_pddignore(layout.root) + waivers = load_waivers(layout.waivers_file) + stampable, ignored, waived, no_code = partition_units( + units, pddignore, waivers, layout.root + ) + + missing: List[str] = [] + stale: List[Tuple[str, List[str]]] = [] + with _chdir(layout.root): + for unit in stampable: + stored = _read_fingerprint_obj(unit) + if stored is None: + missing.append(unit.basename) + continue + current = compute_current_hashes(unit, stored.include_deps) + diffs = [ + field_name + for field_name in HASH_FIELDS + if _norm(getattr(stored, field_name, None)) != _norm(current.get(field_name)) + ] + if diffs: + stale.append((unit.basename, diffs)) + unwaived_no_code = [u.basename for u in no_code] + ok = not (missing or stale or unwaived_no_code) + return CheckResult( + ok=ok, + stampable=len(stampable), + ignored=len(ignored), + waived=len(waived), + missing=sorted(missing), + stale=sorted(stale), + no_code=sorted(unwaived_no_code), + ) diff --git a/pdd/prompts/agentic_test_generate_python.prompt b/pdd/prompts/agentic_test_generate_python.prompt index be58850bc7..66f23e0690 100644 --- a/pdd/prompts/agentic_test_generate_python.prompt +++ b/pdd/prompts/agentic_test_generate_python.prompt @@ -4,61 +4,52 @@ Write the pdd/agentic_test_generate.py module. % Role & Scope -Agentic test generation for non-Python languages. Instead of using a single LLM -call (which often produces incorrect test file extensions or doesn't follow the -correct framework), this module delegates to an agentic CLI that can: -1. Explore the project structure -2. Discover test frameworks (Jest, JUnit, Go test, Cargo test, etc.) -3. Generate tests -4. Run them to verify they pass +Provides agentic test generation for non-Python languages. Instead of a single LLM call, this module delegates to an agentic CLI to explore the project, discover test frameworks, generate tests, and run them. % Entry Point -`run_agentic_test_generate(prompt_file, code_file, output_test_file, *, verbose: bool = False, quiet: bool = False, repair_directive: str | None = None)` +`run_agentic_test_generate(prompt_file: Path, code_file: Path, output_test_file: Path, *, verbose: bool = False, quiet: bool = False, repair_directive: str | None = None) -> TestResult` -Returns: `tuple[str, float, str, bool, str]` → `(generated_content, cost, model_name, success, error_message)` -- generated_content: The content of the generated test file -- cost: Estimated LLM cost -- model_name: Model/provider used (formatted as "agentic-{provider}") -- success: Whether the agentic test generation succeeded (tests passed) -- error_message: Empty string on success; on failure, the error/message from run_agentic_task +Returns: `TestResult` (imported from `.test_result` containing `content`, `cost`, `model`, `agentic_success`, and `error_message`). +- `content`: Generated test file content (empty string if none) +- `cost`: Estimated LLM cost +- `model`: Formatted as `"agentic-{provider}"` or `"agentic-cli"` (fallback) +- `agentic_success`: True if tests passed, False if failed, None if early error +- `error_message`: Empty string on success, else the error/failure message % LLM Prompt Template -Uses `agentic_test_generate_LLM` (loaded via load_prompt_template). - -Template placeholders: -- `{prompt_path}`: Absolute path to prompt file -- `{code_path}`: Absolute path to code file -- `{test_path}`: Absolute path to output test file -- `{project_root}`: Absolute path to project root -- `{prompt_content}`: Content of prompt file -- `{code_content}`: Content of code file +Uses template `agentic_test_generate_LLM` (loaded via `load_prompt_template`). +Template placeholders to format: +- `prompt_path`: Absolute path to prompt file +- `code_path`: Absolute path to code file +- `test_path`: Absolute path to output test file +- `project_root`: Absolute path to project root +- `prompt_content`: Content of prompt file (augmented with repair_directive if present) +- `code_content`: Content of code file % Requirements -1. Single-pass agentic execution (no retry loops) -2. Record file mtimes before agent runs, detect changes after -3. Agent writes the test file directly; module reads it back for return value -4. If expected output file doesn't exist, check changed_files for any new test/spec files. When the agent wrote to such an alternate path (e.g. `__tests__/foo.test.ts` instead of the canonical `output_test_file`), the OUTER churn check in `cmd_test_main` compares the alt-path's NEW content against the CANONICAL path's pre-existing content (empty when canonical didn't exist), letting a 20-test alt file shrink to 1 without tripping `TestChurnError`. To close that gap, BEFORE running the agent take a working-tree content snapshot of every existing file matching `_is_test_output_path` under `project_root` via `_snapshot_pre_test_contents(project_root, mtimes_before.keys()) -> dict[Path, str]`; this captures tracked-clean, tracked-dirty, AND untracked files alike (so neither an untracked alt-path test nor uncommitted local edits get treated as first-time generation, and a restore-on-failure never silently discards dirty work). Bound per-file at ~1 MiB to keep snapshot memory predictable; files above the cap fall through to first-time-generation behavior. Lazy-import `_is_test_output_path` from `code_generator_main` to avoid a circular dependency. AFTER the agent runs, when the alt-path fallback fires, look up the alt path's prior content from this snapshot (NOT from `git show HEAD:` — that would miss untracked alt files entirely and on restore would clobber the user's local dirty edits with HEAD) and call `_verify_test_churn(existing_code=prior, generated_code=alt_content, prompt_name=prompt_file.name, output_path=str(alt_path), prompt_content=prompt_content)`. On `TestChurnError`: set `total_cost`/`model_name` on the exception, RESTORE the alt path's snapshotted pre-run content to disk (so the repair loop sees the actual pre-sync working-tree state), then re-raise. Lazy-import `_verify_test_churn`/`TestChurnError` from `code_generator_main`. -5. Parse JSON from agent output to get success/message metadata -6. Return empty string for content if no test file was generated -7. **Repair-directive injection via explicit kwarg** (#1012, F-A / F-H): `run_agentic_test_generate` accepts a `repair_directive: str | None = None` keyword-only parameter. After reading `prompt_content` from the prompt file on disk and BEFORE passing it into the agent instruction template format, if `repair_directive` is non-empty (after stripping) append a `...` block to `prompt_content` containing the stripped directive. This mirrors the `` injection in `code_generator_main` and the native-path injection in `cmd_test_main`, so test-op retries from `sync_orchestration._run_test_op_with_churn_retry` actually see a different prompt on the second attempt. **`run_agentic_test_generate` MUST NOT read `PDD_REPAIR_DIRECTIVE` from `os.environ`**: a stale outer value would otherwise contaminate direct invocations that have no active retry context. The retry helpers source the directive from their loop-local state and pass it explicitly through `cmd_test_main(..., repair_directive=...)` which forwards it here. Do NOT mutate the on-disk prompt file — only the in-process `prompt_content` flowing into `template.format(...)` is augmented. +1. **Agent and Template Loading**: Check available agents via `get_available_agents()`. If empty, return `TestResult` with `success=False` and a clear error message. Load the prompt template; if it fails, return `TestResult` with `success=False` and error. +2. **Repair Directive Injection**: Accepts a `repair_directive` kwarg. If non-empty, append it to `prompt_content` wrapped in a `` block before formatting. Do NOT read `PDD_REPAIR_DIRECTIVE` from the environment or mutate the on-disk file. +3. **Mtime Recording**: Before running the agent, record file mtimes across the project root using `_get_file_mtimes(root: Path)`. Ignore `.git`, `__pycache__`, `.venv`, `venv`, `node_modules`, `.idea`, `.vscode`, `.pdd`. +4. **Pre-test Snapshotted Contents**: Before the run, snapshot the contents of existing test-like files under `project_root` via `_snapshot_pre_test_contents` (for files in candidate paths matching `_is_test_output_path` and sized <= 1 MiB). Lazy-import `_is_test_output_path` from `code_generator_main` to avoid a circular dependency. +5. **Agent Task Execution**: Execute the task via `run_agentic_task(instruction=instruction, cwd=project_root, verbose=verbose, quiet=quiet, label="test-generate", timeout=AGENTIC_STEP_TIMEOUT_SECONDS, max_retries=DEFAULT_MAX_RETRIES)`. +6. **Output Parsing**: + - Detect changed files using mtimes comparison. + - Read the canonical test file via `_read_generated_test_file`. + - Parse JSON metadata from agent output using `_extract_json_from_text` (handles raw JSON and markdown blocks). If missing JSON but agent succeeded and generated a test file, infer success and set a default message. +7. **Test Churn Sweep**: + - Skip sweep if env flags (`PDD_SKIP_TEST_CHURN_GATE`, `PDD_SKIP_CONFORMANCE`) are enabled or if the prompt allows churn. + - Otherwise, iterate through all pre-run snapshotted files: + - **Deletion**: If a snapshotted file was deleted, treat as maximal churn (`churn_ratio=1.0`), restore all snapshotted files from pre-test contents, and raise `TestChurnError` with cost, model, and a specific repair directive. + - **Rewrite**: If a snapshotted file was changed, verify churn using `_verify_test_churn(existing_code, generated_code, prompt_name, output_path, prompt_content)`. On `TestChurnError`, set its cost/model, restore all snapshotted files, and raise/re-raise. + - Lazy-import `_verify_test_churn`, `TestChurnError`, `_get_test_churn_threshold`, `_prompt_allows_test_churn`, and `_env_flag_enabled` from `code_generator_main`. +8. **Alternative Path Fallback**: If the expected canonical test file doesn't exist but changed files contain test/spec files, read the first match as the generated content. % Helper Functions - -### `_get_file_mtimes(root: Path) -> dict[Path, float]` -Recursively scan directory to record file modification times. -Ignores: .git, __pycache__, .venv, venv, node_modules, .idea, .vscode, .pdd - -### `_extract_json_from_text(text: str) -> dict[str, Any] | None` -Extract JSON object from text (handles markdown code blocks and raw JSON). - -### `_read_generated_test_file(test_file: Path) -> str` -Read the generated test file content if it exists, empty string otherwise. - -### `_snapshot_pre_test_contents(project_root: Path, candidate_paths: Iterable[Path]) -> dict[Path, str]` -Read the on-disk content of every file in `candidate_paths` that lives under `project_root` and matches `_is_test_output_path`. Skips files >1 MiB and silently drops paths that fail `OSError`. Used to capture the alt-path churn baseline BEFORE the agent runs, so tracked-clean / tracked-dirty / untracked test files are all covered uniformly. The dict's later `.get(alt_path, "")` lookup and restore-on-failure step give the repair loop the real pre-sync working-tree content instead of HEAD. - -### `_detect_changed_files(before, after, project_root) -> list[str]` -Detect which files changed between two mtime snapshots (new, modified, deleted). +- `_get_file_mtimes(root: Path) -> dict[Path, float]`: Scans root recursively and returns modification times, ignoring hidden/venv/node_modules directories. +- `_extract_json_from_text(text: str) -> dict[str, Any] | None`: Extracts and decodes JSON from text or markdown blocks. +- `_snapshot_pre_test_contents(project_root: Path, candidate_paths: Iterable[Path]) -> dict[Path, str]`: Reads and returns the content of candidate files under project_root matching `_is_test_output_path` and under 1 MiB. +- `_read_generated_test_file(test_file: Path) -> str`: Reads and returns the test file content, or empty string on error/nonexistence. +- `_detect_changed_files(before: dict[Path, float], after: dict[Path, float], project_root: Path) -> list[str]`: Compares pre- and post-run snapshots to return relative paths of added, modified, or deleted files. % Dependencies diff --git a/pdd/prompts/checkup_prompt_apply_python.prompt b/pdd/prompts/checkup_prompt_apply_python.prompt index fefa9d0a40..ce5b06bad1 100644 --- a/pdd/prompts/checkup_prompt_apply_python.prompt +++ b/pdd/prompts/checkup_prompt_apply_python.prompt @@ -21,18 +21,22 @@ Write `pdd/checkup_prompt_apply.py`. % Requirements - Validate approved patch kind and target path before any write. -- Allow prompt source targets including `pdd/prompts/**/*.prompt`, excluding `*_LLM.prompt`. -- Continue forbidding tests, generated/context paths, implementation files under `pdd/`, absolute paths, and traversal outside the repository root. -- In `--dry-run`, record accepted/rejected patches and write the apply log, but do not mutate files or create backups. -- For real applies, create backups under `.pdd/evidence/checkups/backups//`, write atomically, then run a deterministic postflight checkup. -- Any rejected approved patch must remain inspectable with `status="rejected"`, include a reason, return a non-zero exit code, and must not report `postflight_status="pass"` unless all approved patches were valid and postflight passed. +- Supported target paths: `.prompt` files (excluding `*_LLM.prompt`) and `user_stories/story__*.md`. +- Reject forbidden target paths (e.g., tests, absolute paths, traversal outside the repo root, or files under `pdd/` except `pdd/prompts/**/*.prompt`). +- Supported patch kinds: `vocab_definition`, `contract_rule`, `coverage_line`, `waiver`, `story_template`, `append_covers`, and `full_rewrite`. +- Patch application strategies: + - `full_rewrite` replaces the whole file content. + - `append_covers` appends the replacement to the end of the file. + - Other kinds insert at the specified anchor line, or append to the end of file if not found/invalid. +- For real applies: backup modified files under `.pdd/evidence/checkups/backups//`, write atomically, and run a deterministic postflight checkup. +- In `--dry-run`: do not mutate files or write backups, but log accepted/rejected findings. R1 - Rejected approvals fail honestly -If any approved patch is rejected, `ApplyRunResult.exit_code` MUST be non-zero and `postflight_status` MUST NOT be `pass`. +If any approved patch is rejected, `ApplyRunResult.exit_code` MUST be non-zero and `postflight_status` MUST be `rejected`. -R2 - Prompt source allowlist -`pdd/prompts/**/*.prompt` MAY be repaired, while `pdd/**/*.py`, `tests/**`, generated artifacts, absolute paths, and traversal targets MUST be rejected. +R2 - Target path rules +`.prompt` files (excluding `*_LLM.prompt`) and `user_stories/story__*.md` MAY be repaired, while other paths MUST be rejected. R3 - Dry-run is read-only When `dry_run=True`, no prompt bytes or backup files may be written. diff --git a/pdd/prompts/commands/maintenance_python.prompt b/pdd/prompts/commands/maintenance_python.prompt index 6326e5a8b0..55856e9e9b 100644 --- a/pdd/prompts/commands/maintenance_python.prompt +++ b/pdd/prompts/commands/maintenance_python.prompt @@ -43,6 +43,7 @@ - `--skip-tests` (`is_flag=True`, `default=False`): Skip unit test generation/fixing. - `--target-coverage` (`type=float`, `default=None`): Desired coverage percentage. Help mentions "Default: 90.0 or .pddrc value." - `--dry-run` (`is_flag=True`, `default=False`): Analyze sync state without executing operations. + - `--fresh` (`is_flag=True`, `default=False`): Disable the default surgical/edit-shaped regeneration of a mature module (#1938 Pillar A). For single-module sync, forward to `sync_main()` as `fresh`; the single-module sync path drives `code_generator_main` with `force_incremental_flag=(not fresh)`, so mature modules are edited in place by default and, when `--fresh` is passed, standard generation is used instead (which regenerates the module from scratch when the prompt change is large). `--fresh` is single-module only: when BASENAME is omitted (global sync) or is a GitHub issue URL (agentic sync), raise `click.UsageError("--fresh is only supported for single-module sync.")` BEFORE dispatch — exactly like `--snapshot-context` — rather than silently dropping the flag. Help: "Disable the default surgical (edit-shaped) regeneration for a mature module; with --fresh, sync uses standard generation, which regenerates the module from scratch when the prompt change is large. Use it when you intend a large rewrite rather than an in-place edit. Single-module sync only." - `--snapshot-context` (`is_flag=True`, `default=False`): Capture replayable expanded prompt context for generation-time prompt expansion. For single-module sync, forward to `sync_main`. For project-wide sync, forward only if the downstream global sync path supports it; otherwise raise `click.UsageError`. For GitHub issue sync, either forward explicitly through `run_agentic_sync` when supported or raise `click.UsageError` so unsupported snapshot requests are not silently ignored. - `--compressed-context/--no-compressed-context` (Click flag/no-flag syntax, `default=None`): TRI-STATE. `None` means "no CLI value supplied"; `True` and `False` are explicit user choices. Enable phase-aware compressed context for sync generation and repair phases. This is distinct from `--snapshot-context`: snapshots record replayable expanded prompt context, while compressed context supplies bounded prompt/test/example/contract evidence to generate, verify, test, and fix phases. Resolve the effective value as `compressed_context if compressed_context is not None else .pddrc defaults.compressed_context if present else False`. Forward the resolved bool to `sync_main`, `_run_global_sync_dispatch`, and `_run_agentic_sync_dispatch` when the downstream path supports it; if the user explicitly requests compressed context on a path that cannot honor it, raise a clear `click.UsageError` instead of silently ignoring it. - `--log` (`is_flag=True`, `default=False`, `hidden=True`): Deprecated. When set, emit `click.echo(click.style("Warning: --log is deprecated, use --dry-run instead.", fg="yellow"), err=True)` and then set `dry_run = True` before any dispatch. diff --git a/pdd/prompts/commands/modify_python.prompt b/pdd/prompts/commands/modify_python.prompt index 8a5855df99..9ac836679c 100644 --- a/pdd/prompts/commands/modify_python.prompt +++ b/pdd/prompts/commands/modify_python.prompt @@ -43,33 +43,41 @@ This module provides Click commands (`split`, `change`, `update`) for prompt man 3. **change**: - Doc: "Modify an input prompt file based on a change prompt or issue." - - Args: `args` (nargs=-1). Options: `--manual`, `--budget` (5.0), `--output`, `--csv`, `--timeout-adder` (0.0), `--no-github-state`, `--clean-restart`, `--prompt-checkup {warn,strict}`, `--no-prompt-checkup`, `--interactive`. - - Store the prompt-checkup options in `ctx.obj` using keys compatible with workflow prompt gates (`prompt_checkup`, `no_prompt_checkup`, `interactive`) before calling agentic or manual change logic. + - Args: `args` (nargs=-1). Options: `--manual`, `--budget` (5.0), `--output`, `--csv`, `--timeout-adder` (0.0), `--no-github-state`, `--clean-restart`, `--prompt-checkup {warn,strict}`, `--no-prompt-checkup`, `--interactive`, `--apply`, `--preflight`, `--json` (param as_json), `--evidence` (flag, default False). + - Store the prompt-checkup options (`prompt_checkup`, `no_prompt_checkup`, `interactive`, `apply`) in `ctx.obj` before calling change logic. + - **`--preflight` / `--json`**: When `--preflight` is set, call `build_report(consumer="change-preflight")` from `..continuous_sync`. If `as_json` is True, set `ctx.obj["_suppress_result_summary"] = True` and print JSON formatted report with `indent=2, sort_keys=True`. Otherwise, print formatted summary string with fields: `metadata_stale`, `conflicts`, `unbaselined`, `failures`. Returns `None`. - **Agentic (default)**: 1 arg (issue_url). Calls `run_agentic_change(use_github_state=not no_github_state, clean_restart=clean_restart, ...)`. When `--no-github-state` is set, temporarily set environment variable `PDD_NO_GITHUB_STATE=1` for the duration of the agentic change call and restore the previous value afterward; this disables both hidden GitHub state persistence and visible orchestrator step comments. Displays: Status, Message, Cost (4 decimals), Model, and changed files. Suppress the "Model: ..." line if the model name is empty, "unknown", or "N/A" (case-insensitive). Exit(1) on failure. - - After a successful agentic or manual change that writes prompt files, call `maybe_run_workflow_prompt_gate(...)` with the changed prompt paths, the resolved project root, `prompt_checkup`, `no_prompt_checkup`, and `interactive`. If the gate returns a blocking result, print its message and raise `click.exceptions.Exit(exit_code)` so code generation/modification does not continue after unresolved strict prompt findings. + - After a successful agentic or manual change that writes prompt files, call `maybe_run_workflow_prompt_gate(...)` with the changed prompt paths, the resolved project root, `prompt_checkup`, `no_prompt_checkup`, `interactive`, and `apply`. If the gate returns a blocking result, print its message and raise `click.exceptions.Exit(exit_code)` so code generation/modification does not continue after unresolved strict prompt findings. - `--interactive` only enables interactive prompt repair when explicitly passed, and must require a TTY through the shared prompt-gate/checkup path. In strict mode, interactive repair must be followed by a deterministic re-check; unresolved findings after skip, preview-only, rejected patch, or no-op repair must fail closed. - **`--clean-restart` flag** (agentic mode only): Boolean flag (default `False`). Help text: "Discard any persisted solving state for this issue and start a fresh full pdd-issue flow from the default base branch, ignoring any previously generated change/issue-N branch artifacts. Use when recovering from a stopped or wrong-model run." Validation: MUST raise `click.UsageError` when combined with `--manual` ("--clean-restart is only valid in agentic mode and cannot be used with --manual") or when the single agentic argument is not a GitHub issue URL ("--clean-restart can only be used with an agentic GitHub issue URL."). When set, forwarded to `run_agentic_change` as `clean_restart=True`; otherwise passes `clean_restart=False`. - **Manual (`--manual`)**: - - `--csv`: 2 args (change_file, input_code: directory). - - Standard: 3 args (change_file, input_code: file, input_prompt). - - Validation: `input_code` must match expected type (dir for CSV, file otherwise). Check existence for all files. + - `--csv`: 2 args (change_file, input_code: directory). Raises `click.UsageError("Cannot use --csv and specify an INPUT_PROMPT_FILE simultaneously.")` if 3 arguments are provided. Raises `click.UsageError("INPUT_CODE must be a directory when using --csv")` if `input_code` is a file. If not 2 args, raises "CSV mode requires 2 arguments: CSV_FILE CODE_DIRECTORY". + - Standard: 3 args (change_file, input_code: file, input_prompt). Raises `click.UsageError("INPUT_PROMPT_FILE is required when not using --csv")` if 2 arguments are provided. Raises `click.UsageError("INPUT_CODE must be a file when not using --csv")` if `input_code` is a directory. If other arg count, raises "Manual mode requires 3 arguments: CHANGE_PROMPT INPUT_CODE INPUT_PROMPT". + - Validation: Check existence for all files (`change_file`, `input_code`, and `input_prompt` if provided). - Calls `change_main` with `ctx`, `use_csv`, `budget`, and relevant files. + - Parses gate block exit from result via `parse_prompt_gate_block_exit(result)` if result is a string, and if not None raises `click.exceptions.Exit(gate_exit)`. + - **`--evidence`**: When set, call `write_evidence_manifest` after successful run: + - In manual mode: with `command="pdd change"`, `prompt_file=input_prompt`, `output_files=[output] if output else ()`, `model=model`, `cost_usd=cost`, and `temperature`. + - In agentic mode: with `command="pdd change"`, `output_files=changed_files`, `model=model`, `cost_usd=cost`, `temperature`, and `basename="agentic-change"`. 4. **update**: - Doc: "Update the original prompt file based on code changes." - - Args: `files` (nargs=-1). Options: `--all` (param `all_`), `--extensions`, `--directory`, `--git`, `--output`, `--simple`, `--base-branch` (default `"main"`), `--budget` (float, default `None`), `--dry-run`, `--sync-metadata` (flag, default `False`). + - Args: `files` (nargs=-1). Options: `--all` (param `all_`), `--extensions`, `--directory`, `--git`, `--output`, `--simple`, `--base-branch` (default `"main"`), `--budget` (float, default `None`), `--dry-run`, `--json` (param as_json), `--sync-metadata` (flag, default `False`). - Decorators (in order): `@click.pass_context`, `@log_operation(operation="update", clears_run_report=True)`, `@track_cost`. - **Validation** (UsageError, raised before try/except so they propagate): - 2 args requires `--git`; 3 args forbids `--git`; max 3 args. - `--all` forbidden with file paths. - `--budget`, when set, must be > 0. - Repo mode (0 args or `--all`) forbids `--git` and `--output`. - - File modes forbid `--extensions`, `--directory`, non-default `--base-branch`, `--dry-run`, and `--budget`. + - File modes forbid `--extensions`, `--directory`, non-default `--base-branch`, `--dry-run`, `--json`, and `--budget`. + - In repo mode: `--json` is only supported with `--dry-run` (raises `click.UsageError("--json is only supported with update --all --dry-run.")` if `as_json` but not `dry_run`). + - In file modes: `--json` is forbidden (raises `click.UsageError("--json is only valid in repository-wide dry-run mode.")` if `as_json`). - **Modes**: - 0 args or `--all`: `repo=True`, all file params `None`. - 1 arg: `modified_code_file=files[0]` (regeneration). - 2 args: `input_prompt_file=files[0]`, `modified_code_file=files[1]` (git-based). - 3 args: `input_prompt_file=files[0]`, `modified_code_file=files[1]`, `input_code_file=files[2]`. + - **Repo Dry-Run JSON**: If repo mode, `dry_run`, and `as_json` are all True: call `build_report(consumer="update-dry-run")` from `..continuous_sync`, set `ctx.obj["_suppress_result_summary"] = True`, print the JSON report with `indent=2, sort_keys=True`, and return `None`. - **`--sync-metadata`** (opt-in): When set, `update_main` runs the shared `run_metadata_sync` orchestrator after the prompt update — preserves or seeds prompt PDD tags from the architecture entry, reconciles the architecture.json entry, clears stale run reports, and finalizes the fingerprint last. Compatible with all modes (single-file, regeneration, repo). `failed` in any stage causes the command to exit non-zero (raises `click.exceptions.Exit(1)`); `skipped` is acceptable and passes through (no `architecture.json`, unregistered modules, LLM-first tag refresh pending #870 — not implemented here). Help text: "After update, run the shared metadata-sync orchestrator (preserve/seed PDD tags, reconcile architecture.json entry, clear stale run reports, finalize fingerprint last). On any stage failed, exits non-zero. Stages may report skipped for legitimate cases (no architecture.json, unregistered modules). LLM-first refresh of stale-but-present tags is tracked at #870 and is NOT invoked here." - Calls `update_main(ctx, input_prompt_file, modified_code_file, input_code_file, output, use_git=git, repo, extensions, directory, simple, base_branch, budget, dry_run, sync_metadata=sync_metadata)` and returns its result (or `None` if it returns `None`). diff --git a/pdd/prompts/server/token_counter_python.prompt b/pdd/prompts/server/token_counter_python.prompt index bd5c175968..a4bcbd60ad 100644 --- a/pdd/prompts/server/token_counter_python.prompt +++ b/pdd/prompts/server/token_counter_python.prompt @@ -6,11 +6,13 @@ "module": { "functions": [ {"name": "count_tokens", "signature": "(text: str, model: str = \"gpt-4o\") -> int", "returns": "int"}, + {"name": "count_tokens_for_messages", "signature": "(messages: Any, model: str = \"gpt-4o\") -> int", "returns": "int"}, {"name": "get_context_limit", "signature": "(model: str) -> Optional[int]", "returns": "Optional[int]"}, - {"name": "estimate_cost", "signature": "(token_count, model, pricing_csv) -> Optional[CostEstimate]", "returns": "Optional[CostEstimate]"}, - {"name": "estimate_completion_cost", "signature": "(input_tokens, predicted_output_tokens, model, pricing_csv) -> Optional[CostEstimate]", "returns": "Optional[CostEstimate]"}, + {"name": "get_max_output_tokens", "signature": "(model: str) -> Optional[int]", "returns": "Optional[int]"}, + {"name": "estimate_cost", "signature": "(token_count: int, model: str, pricing_csv: Optional[Union[str, Path]] = None) -> Optional[CostEstimate]", "returns": "Optional[CostEstimate]"}, + {"name": "estimate_completion_cost", "signature": "(input_tokens: int, predicted_output_tokens: int, model: str, pricing_csv: Optional[Union[str, Path]] = None) -> Optional[CostEstimate]", "returns": "Optional[CostEstimate]"}, {"name": "default_pricing_csv", "signature": "() -> Optional[Path]", "returns": "Optional[Path]"}, - {"name": "get_token_metrics", "signature": "(text, model, pricing_csv) -> TokenMetrics", "returns": "TokenMetrics"} + {"name": "get_token_metrics", "signature": "(text: str, model: str = \"claude-sonnet-4-20250514\", pricing_csv: Optional[Path] = None) -> TokenMetrics", "returns": "TokenMetrics"} ] } } @@ -19,62 +21,80 @@ % You are an expert Python engineer. Create a `token_counter` module for token counting and cost estimation. % Role & Scope -A utility module that counts tokens and looks up model context windows using litellm, and estimates costs from pricing CSV data. Used by the PDD server for prompt/context analysis. +A utility module that counts tokens, looks up model limits via litellm, and estimates costs from pricing CSV data. Used by the PDD server for prompt/context analysis. context/python_preamble.prompt % Requirements 1. Function: `count_tokens(text: str, model: str = "gpt-4o") -> int` - Use `litellm.token_counter(model=model, messages=[{"role": "user", "content": text}])` - - Fall back to tiktoken cl100k_base encoding if litellm raises any exception - - Return 0 for empty text + - Protect all litellm calls with a daemon-thread timeout (default 5.0s, configured via package-internal `_LITELLM_CALL_TIMEOUT_SEC` to prevent blocking OAuth provider-detection hangs). + - Fall back to tiktoken cl100k_base encoding if litellm raises any exception or times out. + - Return 0 for empty or None text. + +1a. Function: `count_tokens_for_messages(messages: Any, model: str = "gpt-4o") -> int` + - Use `litellm.token_counter` with the provided messages list. + - Protect with the daemon-thread timeout, falling back to tiktoken cl100k_base on error/timeout. + - Best-effort flattening of messages to a single string on fallback. + 2. Function: `get_context_limit(model: str) -> Optional[int]` - - Use `litellm.get_model_info(model)["max_input_tokens"]` to look up the input context window - - Return `None` if litellm raises any exception (unknown model — caller should skip validation) - - Do NOT use a hardcoded MODEL_CONTEXT_LIMITS dict + - Use `litellm.get_model_info(model)["max_input_tokens"]`. + - Protect with the daemon-thread timeout. + - Return `None` if litellm raises any exception or times out (caller should skip context-window validation). + - Do NOT use a hardcoded MODEL_CONTEXT_LIMITS dict. + +2a. Function: `get_max_output_tokens(model: str) -> Optional[int]` + - Get the max completion token budget via `litellm.get_model_info(model)["max_output_tokens"]`. + - Protect with the daemon-thread timeout; return None if unknown, times out, raises, or if limit <= 0. + 3. Function: `estimate_cost(token_count, model, pricing_csv) -> Optional[CostEstimate]` - - Preserve this existing input-only API for current callers - - Treat `token_count` as input tokens and do not require callers to provide output tokens - - Accept `pricing_csv` as either a `pathlib.Path` or a string path - - Use rows with a valid input rate even when an older CSV lacks an output rate + - Preserve this input-only API for current callers (treating `token_count` as input tokens). + - Accept `pricing_csv` as either a `pathlib.Path` or a string path. + - Use rows with a valid input rate even when an older CSV lacks an output rate. + 4. Function: `estimate_completion_cost(input_tokens, predicted_output_tokens, model, pricing_csv) -> Optional[CostEstimate]` - - Use `(input_tokens / 1_000_000) * input_rate + (predicted_output_tokens / 1_000_000) * output_rate` - - Return input cost, output cost, and total cost for deterministic pre-flight previews - - Accept `pricing_csv` as either a `pathlib.Path` or a string path - - When `pricing_csv` is None, resolve the canonical CSV via `default_pricing_csv()` so previews work without an explicit path; an explicitly supplied path is used as-is and never silently replaced by the default - - Require both valid input and valid output rates; return None when output pricing is absent + - Use `(input_tokens / 1_000_000) * input_rate + (predicted_output_tokens / 1_000_000) * output_rate`. + - Require both valid input and valid output rates; return None when output pricing is absent or None. + - Accept `pricing_csv` as either a `pathlib.Path` or a string path. + - When `pricing_csv` is None, resolve the canonical CSV via `default_pricing_csv()`; an explicitly supplied path is used as-is and never replaced by the default. + 4a. Function: `default_pricing_csv() -> Optional[pathlib.Path]` - - Return the first existing pricing CSV in order: `$HOME/.pdd/llm_model.csv`, then `/.pdd/llm_model.csv`, then the bundled `pdd/data/llm_model.csv` (resolve the bundled path relative to this module: `pdd/server/` -> `pdd/data/`) - - Return None when none exist, preserving the "cost is explicitly unknown" contract - - Only `estimate_completion_cost` falls back to this default; `estimate_cost` and `get_token_metrics` keep treating a None `pricing_csv` as "no pricing" -5. Function: `get_token_metrics(text, model, pricing_csv) -> TokenMetrics` combining all metrics -6. Dataclass `CostEstimate`: input_cost, output_cost, total_cost, model, matched_model, tokens, input_tokens, output_tokens, cost_per_million, input_cost_per_million, output_cost_per_million, currency (default USD) - - Preserve existing caller expectations for `input_cost`, `tokens`, and `cost_per_million` - - For input-only estimates, `tokens` remains the input token count and `cost_per_million` remains the input rate -7. Dataclass `TokenMetrics`: token_count, context_limit (Optional[int]), context_usage_percent (Optional[float]), cost_estimate -8. Both dataclasses have `to_dict()` methods for serialization -9. `CostEstimate.to_dict()` includes all new input/output/total fields and preserves legacy keys so existing API models and tests remain compatible - - Also expose `input_rate_per_million` and `output_rate_per_million` keys (aliases of `input_cost_per_million`/`output_cost_per_million`) so estimate-mode consumers that read per-million rates work against the single canonical `CostEstimate` -10. Pricing loaded from CSV with columns: model, input, output - - `input` and `output` are USD costs per million tokens - - Missing, blank, or invalid input pricing makes that row unavailable for cost estimation - - Missing, blank, or invalid output pricing makes that row unavailable for output-inclusive completion estimates, but must not break the legacy input-only `estimate_cost` API - - Explicit zero input or output pricing is valid known pricing, not unknown pricing - - Do not infer prices from another row when a row is malformed -11. Model matching for cost: exact match first, then conservative partial/substring match against valid priced rows only -12. Unknown model pricing returns None; do NOT fall back to default model prices because that invents a cost -13. Unknown models, missing pricing CSVs, and unavailable pricing rows still allow token counts and context metrics through `get_token_metrics`, but `cost_estimate` is explicitly None -14. Cache pricing data with lru_cache; do NOT cache litellm calls (litellm has its own cache) + - Return the first existing pricing CSV in order: `$HOME/.pdd/llm_model.csv`, then `/.pdd/llm_model.csv`, then the bundled `pdd/data/llm_model.csv` (resolved relative to this module: `pdd/server/` -> `pdd/data/`). + - Return None when none exist. + - Only `estimate_completion_cost` falls back to this default; `estimate_cost` and `get_token_metrics` keep treating a None `pricing_csv` as "no pricing". + +5. Function: `get_token_metrics(text, model, pricing_csv) -> TokenMetrics` combining all metrics. + +6. Dataclass `CostEstimate`: input_cost, output_cost, total_cost, model, matched_model, tokens, input_tokens, output_tokens, cost_per_million, input_cost_per_million, output_cost_per_million, currency (default USD). + - Populate compatibility/default fields during initialization if not provided (e.g. `total_cost = input_cost + output_cost`, `matched_model = model`, `input_tokens = tokens`, `output_tokens = 0`, `input_cost_per_million = cost_per_million`). + +7. Dataclass `TokenMetrics`: token_count, context_limit (Optional[int]), context_usage_percent (Optional[float]), cost_estimate. + +8. Both dataclasses have `to_dict()` methods for serialization. + - Round floats in dictionary serialization (`input_cost`, `output_cost`, `total_cost` to 6 decimal places, `context_usage_percent` to 2 decimal places). + - `CostEstimate.to_dict()` includes legacy keys and exposes `input_rate_per_million` and `output_rate_per_million` (aliases of `input_cost_per_million`/`output_cost_per_million`). + +9. Pricing loaded from CSV with columns: provider, model, input, output, interactive_only. + - `input` and `output` are USD costs per million tokens. + - Missing, blank, or invalid input pricing makes that row unavailable for cost estimation. + - Missing, blank, or invalid output pricing makes that row unavailable for output-inclusive completion estimates, but must not break the legacy input-only `estimate_cost` API. + - Genuinely free/local rows (e.g. `lm_studio`, `ollama`, `local` providers/prefixes, or model containing "local"/"free") with zero rates are valid known free prices. + - Subscription-backed zero rates (e.g. `Github Copilot`, `OpenAI ChatGPT`, `Z.AI Coding Plan` providers/prefixes, or `interactive_only=True` rows with zero prices) are treated as unknown (return None cost estimate). + - Genuinely paid rows take precedence over zero-rate rows for the same model ID (first non-zero entry wins). + - Do not infer prices from another row when a row is malformed. + +10. Model matching for cost: + - Exact match first (case-insensitive). + - Conservative matching: allows matching a provider-qualified requested model (e.g. `openai/gpt-4o`) to an unqualified CSV model (e.g. `gpt-4o`), or base name with a numeric suffix (e.g. matching `gpt-4-0613` to `gpt-4`). + - MUST NOT allow a bare requested model name to borrow/match a provider-scoped CSV model (e.g. requested `gpt-4o` must NOT match `openai/gpt-4o` or `azure/gpt-4o`). + - Unknown model pricing returns None; do NOT fall back to default model prices. + +11. Unknown models, missing pricing CSVs, and unavailable pricing rows still allow token counts and context metrics through `get_token_metrics`, but `cost_estimate` is explicitly None. + +12. Cache pricing data loading with `lru_cache(maxsize=1)`. Do NOT cache litellm calls. % Dependencies -- litellm: for token counting and context window lookup (primary) +- litellm: for token counting, message token counting, and context window lookup (primary) - tiktoken: fallback for token counting only -- CSV file at .pdd/llm_model.csv for pricing data using the PDD llm_model.csv shape with at least model,input,output +- CSV file at .pdd/llm_model.csv for pricing data - Tests should use deterministic fixture CSV rows rather than relying on live user pricing files - -% Instructions -- `get_context_limit` returning None means the model is unknown — callers must skip context validation, not raise an error -- Context usage percent is None when context_limit is None -- Input-only cost calculation: (token_count / 1_000_000) * input_cost_per_million -- Completion cost calculation: input cost plus output cost using separate input and output rates -- Context usage is percentage: (tokens / limit) * 100 diff --git a/pdd/prompts/sync_determine_operation_python.prompt b/pdd/prompts/sync_determine_operation_python.prompt index 4e4d10de72..1698240239 100644 --- a/pdd/prompts/sync_determine_operation_python.prompt +++ b/pdd/prompts/sync_determine_operation_python.prompt @@ -21,7 +21,7 @@ You are an expert Python developer. Your task is to implement the core decision- ### Requirements: -1. **Authoritative Fingerprints**: Compare current file SHA256 hashes against `.pdd/meta/{safe_basename}_{lang}.json`. Sanitize basenames containing `/` (e.g., `core/cloud` → `core_cloud`) for metadata filenames. When using sanitized basenames in glob patterns (e.g., in `_check_example_success_history`), apply `glob.escape()` so that special characters like `[`, `]`, `(`, `)` in basenames are treated as literals, not glob metacharacters. `get_meta_dir(project_root=None, paths=None)` resolves the meta directory in this order (issue #1211): explicit `project_root` → upward `.pddrc` from any file in `paths` (handles subproject .pddrc BELOW run CWD) → nearest `.pddrc` walking up from CWD → run CWD. `read_fingerprint(basename, language, paths=None)` and `read_run_report(basename, language, paths=None)` accept the same `paths` hint and forward it. `_perform_sync_analysis` resolves `_initial_paths = get_pdd_file_paths(...)` once at the top and threads them into the early `read_fingerprint` / `read_run_report` reads plus `_check_example_success_history`; `_is_workflow_complete` threads its own `paths` argument into every read it makes. The prompt-and-derived-files-both-changed conflict branch resolves the deletion target via `get_meta_dir(paths=paths)` — NOT a bare `get_meta_dir()` — so the unlink happens in the same `.pdd/meta` the reads came from. Without that, the read sees a subproject fingerprint while the delete targets the parent CWD orphan, and the recursive `_perform_sync_analysis` call re-enters with the same stale state (infinite loop / repeated decisions). This ensures sync decisions read from, and reset against, the subproject's `.pdd/meta` rather than an orphan one under a parent CWD. +1. **Authoritative Fingerprints**: Compare current file SHA256 hashes against `.pdd/meta/{safe_basename}_{lang}.json`. Sanitize basenames containing `/` (e.g., `core/cloud` → `core_cloud`) for metadata filenames. When using sanitized basenames in glob patterns (e.g., in `_check_example_success_history`), apply `glob.escape()` so that special characters like `[`, `]`, `(`, `)` in basenames are treated as literals, not glob metacharacters. `get_meta_dir(project_root=None, paths=None)` resolves the meta directory in this order (issue #1211): explicit `project_root` → upward `.pddrc` from any file in `paths` (handles subproject .pddrc BELOW run CWD) → nearest `.pddrc` walking up from CWD → run CWD. `read_fingerprint(basename, language, paths=None)` and `read_run_report(basename, language, paths=None)` accept the same `paths` hint and forward it. `_perform_sync_analysis` resolves `_initial_paths = get_pdd_file_paths(...)` once at the top and threads them into the early `read_fingerprint` / `read_run_report` reads plus `_check_example_success_history`; `_is_workflow_complete` threads its own `paths` argument into every read it makes. The prompt-and-derived-files-both-changed CONFLICT branch resolves its metadata paths via `get_meta_dir(paths=paths)` — NOT a bare `get_meta_dir()` — so the preserved fingerprint/run-report paths reported in the decision `details['metadata_preserved']` come from the same `.pdd/meta` the reads came from (a subproject `.pdd/meta` below the run CWD, not a parent-CWD orphan). The branch is non-destructive: it never deletes metadata and never recurses into a fresh no-fingerprint analysis (#1929) — it returns a terminal `fail_and_request_manual_merge` decision, so there is no stale-state re-entry (previously an infinite-loop / repeated-decision hazard). This ensures sync decisions read from the subproject's `.pdd/meta` rather than an orphan one under a parent CWD. 2. **Robust Locking**: `SyncLock` context manager using `fcntl`/`msvcrt` file-descriptor locking. Handle re-entrancy (same PID), stale locks (`psutil.pid_exists`), and cleanup on failure. `log_mode=True` bypasses locking entirely and implies read-only analysis. 3. **Deterministic Decision Priority** (strict ordering): 1. Auto-deps completion → always `generate` after `auto-deps` (prevents infinite loop). @@ -34,7 +34,7 @@ You are an expert Python developer. Your task is to implement the core decision- 4. Coverage gaps → `test_extend` (Python only) or `all_synced` (non-Python, since coverage tooling is Python-specific). When `PDD_DISABLE_TEST_EXTEND` is truthy (`1`, `true`, `yes`, or `on`, case-insensitive), Python coverage gaps MUST also return the existing `all_synced` no-op shape with `test_extend_skipped: True` and a skip reason citing the disabled `test_extend` guard instead of returning `test_extend`. This suppression affects only coverage-driven `test_extend`; all other operation priorities remain unchanged. 5. Missing derived files → code → example → test (respecting `skip_tests`/`skip_verify` flags). 6. Isolated replay/repair override → when `isolated_replay_or_repair=True`, missing or stale examples must not preempt code replay/repair decisions. Existing examples may be read as context, but the decision engine must not schedule `example` unless the caller explicitly requested example regeneration. - 7. Conflict: prompt + derived files both changed → return `fail_and_request_manual_merge` with an explicit conflict reason and details that identify the changed artifacts. Preserve the existing fingerprint and run-report in both mutable and read-only/log analysis; do not delete metadata, re-run fresh analysis, or choose either the prompt or derived artifact as the winner. Derived-only changes → `verify` or `test` (not a conflict). + 7. Conflict: prompt + derived files both changed → return `fail_and_request_manual_merge` with `details['classification'] == 'CONFLICT'`. The `reason` must be actionable on its own — it names the unit, states which artifacts moved, and gives the exact resolution commands: `pdd resolve --accept-current` (finalize a hand-merge, deterministic) plus the real LLM commands that let one side win — `pdd update ` (code-wins: back-propagate code into the prompt) and `pdd sync ` (prompt-wins: regenerate code from the prompt), with a `--language` suffix for non-Python units. Do NOT advertise `pdd resolve --prompt-wins` / `--code-wins` in the reason: those flags are non-automated previews, so point at the runnable commands instead (#1969 review finding 3). `details` carries `basename`, `language`, `changed_files`, and a `resolution_commands` map (keys `accept_current`, `prompt_wins`→`pdd sync`, `code_wins`→`pdd update`). Preserve the existing fingerprint and run-report in both mutable and read-only/log analysis; do not delete metadata, re-run fresh analysis, or choose either the prompt or derived artifact as the winner. Derived-only changes → `verify` or `test` (not a conflict). 4. **Skip Flags**: `skip_tests` and `skip_verify` parameters flow through `sync_determine_operation`, `_is_workflow_complete`, and `_handle_missing_expected_files`. When `skip_tests=True`, low coverage returns `all_synced` instead of `test`/`test_extend`. When both `skip_tests=True` and `skip_verify=True`, workflow completion requires only the prompt/code/example file set and must not require run reports, test reports, or verification fingerprints. Stale or failing cached test-results from `run_report` must be ignored (do not recommend `fix` or `crash`) when `skip_tests` is active. 5. **Context Override**: All path-resolving functions accept `context_override` to select a specific `.pddrc` context. `_relative_basename_for_context` strips context-specific prefixes from basenames for template expansion. For prompts_dir values, extract the prefix after the 'prompts/' segment wherever it appears in the path (e.g., 'prompts/frontend' → 'frontend', 'extensions/app/prompts/frontend' → 'frontend'). Use `_extract_prefix_from_prompts_dir()` from `construct_paths` for this. 6. **Path Resolution (Issue #237, #1169, #1303)**: Use `construct_paths` for configuration-aware pathing. If `.pddrc` has an `outputs` template section, use `_generate_paths_from_templates`. `_resolve_prompts_root` resolves prompts_dir relative to `.pddrc` location. Case-insensitive prompt file lookups must return the actual on-disk filename casing even when a lowercased candidate path reports `exists()` on case-insensitive filesystems. When the constructed prompt path does not exist, `_find_prompt_file` searches recursively through `prompts_root` subdirectories for a case-insensitive filename match (both basename and language suffix are case-insensitive). When multiple nested files share the basename, `context_override` scopes to the correct `.pddrc` context subdirectory, then basename directory hints disambiguate, then ties are broken by shallowest path-part count then lexicographic order. This ensures prompts in nested directories (e.g., `prompts/src/clients/firestore_client_Python.prompt`) are found even when `get_pdd_file_paths` is called with a flat basename and lowercase language. `_resolve_prompt_path_from_architecture` also performs a recursive case-insensitive search when its naively-joined path does not exist on disk. When architecture.json supplies a `filepath`, use the basename-resolved `.pddrc` context for example/test directories rather than cwd/default context, so repos with context-specific examples (e.g., `context/`, `context/commands/`) do not drift to `examples/`. When the prompt file does not exist yet (new module creation), `.pddrc` context's `prompts_dir` prefix is applied to the fallback path. @@ -83,7 +83,7 @@ You are an expert Python developer. Your task is to implement the core decision- 5. **Decision Logic (`sync_determine_operation`)**: Accepts `basename, language, target_coverage, budget, log_mode, prompts_dir, skip_tests, skip_verify, context_override, read_only`, plus an optional isolated replay/repair indicator consumed by sync orchestration. Delegates to `_perform_sync_analysis` (with or without lock). Follow the priority order in Requirement 3. 6. **Isolated replay/repair mode:** Preserve the default missing-file priority for ordinary full sync. When the caller marks the analysis as an isolated generation replay or code repair, missing or stale example files must not preempt `generate`, `verify`, or `fix` unless the requested operation explicitly requires example regeneration. Include a decision reason explaining that example generation was skipped for isolated replay/repair so dry-run output and operation logs are auditable. 7. **Workflow Completion (`_is_workflow_complete`)**: Requires all files exist, run_report with success, coverage > 0 when tests pass, non-stale test hashes, and fingerprint command in `{verify, test, fix, update}` (not prefixed with `skip:`). If both `skip_tests` and `skip_verify` are true, return complete after required code/example existence checks because the caller explicitly requested metadata-only sync analysis. -8. **LLM Conflict Resolution**: `analyze_conflict_with_llm` gathers git diffs (with brace-escaping for `.format()`), formats `sync_analysis_LLM` template, calls `llm_invoke` with `temperature=0`. Falls back to `fail_and_request_manual_merge` on any error or confidence < 0.75. +8. **LLM Conflict Resolution (reserved, not in the decision path)**: `analyze_conflict_with_llm` gathers git diffs (with brace-escaping for `.format()`), formats `sync_analysis_LLM` template, calls `llm_invoke` with `temperature=0`, and falls back to `fail_and_request_manual_merge` on any error or confidence < 0.75. Per #1929 this helper is NOT wired into `sync_determine_operation`: the deterministic decision tree never auto-picks a winner for the both-changed case (it returns the explicit CONFLICT decision above). The function is retained only as the LLM backend reserved for a future `pdd resolve`-driven agentic 3-way merge. ### Deliverables: @@ -93,5 +93,5 @@ A standalone Python module `sync_determine_operation.py` containing: - `get_pdd_file_paths`, `_generate_paths_from_templates`, and hashing utilities. - `sync_determine_operation` (primary entry point) and `_perform_sync_analysis`. - `_check_example_success_history`, `validate_expected_files`, `_handle_missing_expected_files`. -- `analyze_conflict_with_llm` (LLM fallback). +- `analyze_conflict_with_llm` (reserved LLM backend for `pdd resolve`; not wired into the decision path — #1929). - `estimate_operation_cost`, `check_for_dependencies`. diff --git a/pdd/prompts/sync_main_python.prompt b/pdd/prompts/sync_main_python.prompt index 7343df20bd..7712e9aa09 100644 --- a/pdd/prompts/sync_main_python.prompt +++ b/pdd/prompts/sync_main_python.prompt @@ -5,7 +5,7 @@ "type": "cli", "cli": { "commands": [ - {"name": "sync_main", "signature": "(ctx: click.Context, basename: str, max_attempts: Optional[int], budget: Optional[float], skip_verify: bool, skip_tests: bool, target_coverage: float, dry_run: bool, no_steer: bool = False, steer_timeout: Optional[float] = None, agentic_mode: bool = False, one_session: bool = False, compress: bool = False, evidence: bool = False, snapshot_context: bool = False, compressed_context: Optional[bool] = None) -> Tuple[Dict[str, Any], float, str]", "returns": "Tuple[Dict[str, Any], float, str]"} + {"name": "sync_main", "signature": "(ctx: click.Context, basename: str, max_attempts: Optional[int], budget: Optional[float], skip_verify: bool, skip_tests: bool, target_coverage: float, dry_run: bool, no_steer: bool = False, steer_timeout: Optional[float] = None, agentic_mode: bool = False, one_session: bool = False, compress: bool = False, fresh: bool = False, evidence: bool = False, snapshot_context: bool = False, compressed_context: Optional[bool] = None) -> Tuple[Dict[str, Any], float, str]", "returns": "Tuple[Dict[str, Any], float, str]"} ] } } @@ -41,6 +41,7 @@ - `steer_timeout` (Optional[float]): Steering choice timeout in seconds. None = default timeout. - `agentic_mode` (bool): When True, Python behaves like non-Python languages (uses agentic path for operations) - `one_session` (bool): When True, run example/crash/verify/test/fix in a single agentic session instead of separate steps + - `fresh` (bool): Issue #1938 Pillar A. When False (default), regenerate mature modules surgically (edit-shaped) by passing `force_incremental_flag=True` to `code_generator_main` so declared public symbols are preserved instead of dropped by a full "rebirth" regeneration. When True (`pdd sync --fresh`), pass `force_incremental_flag=False` to restore standard generation. Forward `fresh` to every `sync_orchestration()` call and use `force_incremental_flag=(not fresh)` in the one-session generation loop. Note: in one-session mode the module is otherwise (re)generated by the agent session (`run_one_session_sync`), so the one-session `code_generator_main` generate — and therefore `fresh` — only takes effect when the code file is missing or `--force` triggers a from-scratch generation; for an already-existing mature module in one-session mode the code edit is agent-driven and `fresh` does not change it. The effective path for `fresh` is the standard multi-step single-module sync. Default False. - `snapshot_context` (bool): When True, capture replayable expanded prompt context for generation in `code_generator_main` and `sync_orchestration`. Default False. - `compressed_context` (Optional[bool]): Tri-state request flag for phase-aware compressed context. `None` means no explicit CLI value was supplied; resolve the effective setting from CLI value when not None, then `.pddrc` default context `defaults.compressed_context`, then `False`. Display the effective setting with other sync settings and never print raw compressed prompt/test/example content. - `compress` (bool): Use AST-based compression (`mode="compressed"`) for Python includes when no explicit include mode is set. Default False. @@ -111,7 +112,7 @@ - Mutually exclusive with `skip_tests` and `skip_verify` (raise UsageError if combined) - Use `get_pdd_file_paths()` from `sync_determine_operation` to resolve module file paths - **Fingerprint check**: If not force mode, call `sync_determine_operation()` to check if module is already synced; skip if operation is "nothing" - - **Phase 1 (Generation)**: If code file doesn't exist or `force`, run `code_generator_main()` to generate the code file; track the generation cost as `pre_cost`. Pass `output_from_config=True` — the resolved code path comes from `get_pdd_file_paths()` (which uses `.pddrc` `generate_output_path`), not an explicit user `--output` flag, so front-matter `output:` should still take precedence over the .pddrc default. Forward `snapshot_context` into every generation attempt. When the effective compressed-context request flag is true, build a phase-specific compressed context package immediately before each LLM-backed generation attempt and pass that mapping to `code_generator_main(compressed_context=package)`. Never pass the boolean request flag itself to `code_generator_main`. If package construction fails, pass `None`, continue with full-context behavior, and record `used=false` compression metadata with the failure reason. + - **Phase 1 (Generation)**: If code file doesn't exist or `force`, run `code_generator_main()` to generate the code file; track the generation cost as `pre_cost`. Pass `output_from_config=True` — the resolved code path comes from `get_pdd_file_paths()` (which uses `.pddrc` `generate_output_path`), not an explicit user `--output` flag, so front-matter `output:` should still take precedence over the .pddrc default. Pass `force_incremental_flag=(not fresh)` so a mature module (existing non-empty code file) is regenerated surgically/edit-shaped by default and only fully regenerated when the operator passes `--fresh` (#1938 Pillar A); `code_generator_main` still falls back to full generation for a new/empty file or when the original prompt cannot be determined. Forward `snapshot_context` into every generation attempt. When the effective compressed-context request flag is true, build a phase-specific compressed context package immediately before each LLM-backed generation attempt and pass that mapping to `code_generator_main(compressed_context=package)`. Never pass the boolean request flag itself to `code_generator_main`. If package construction fails, pass `None`, continue with full-context behavior, and record `used=false` compression metadata with the failure reason. - **Ambiguous module names (issue #1677)**: catch `AmbiguousModuleError` (imported from `sync_determine_operation`; a `ValueError` subclass raised by `get_pdd_file_paths` when a bare basename maps to ≥2 distinct architecture outputs) around module/path resolution and the per-language loop, and fail the command hard with the conflicting-target list (shown even under `--quiet`, exit non-zero). Never fall through to first-match-wins generation for an ambiguous bare basename. - **Architecture-conformance / public-surface / test-churn retry (#866, #1012)**: Wrap the `code_generator_main()` call in a retry loop bounded by `MAX_CONFORMANCE_ATTEMPTS` (imported from `pdd.agentic_sync_runner`). The loop catches three typed exceptions raised from `code_generator_main`: `ArchitectureConformanceError`, `PublicSurfaceRegressionError`, and `TestChurnError`. For each, add `exc.total_cost` to `pre_cost`, remember `exc.model_name`, set `PDD_REPAIR_DIRECTIVE` to `exc.repair_directive`, and retry only if budget remains. - Track the failure signature across attempts: sorted `missing_symbols` for conformance, sorted `removed_symbols` for public-surface, `(round(churn_ratio, 2), pre_line_count)` for test-churn. If the next attempt fails with the **identical** signature, abort the loop (no progress) instead of looping forever. Test-churn additionally caps at ONE repair attempt — the additive-only directive either lands the first try or stays stuck. @@ -145,7 +146,7 @@ - Track remaining_budget across languages (reduce after each language's cost) - Skip subsequent languages if budget exhausted - Aggregate results and costs from all language syncs - - Forward steerability controls (`no_steer`, resolved `steer_timeout`), `compress`, `evidence`, and #877 compression flags (`compress_examples`, `compress_test_context`, `context_compression`, `compression_fallback`) to each sync_orchestration call + - Forward steerability controls (`no_steer`, resolved `steer_timeout`), `compress`, `fresh`, `evidence`, and #877 compression flags (`compress_examples`, `compress_test_context`, `context_compression`, `compression_fallback`) to each sync_orchestration call - **Auto-submit on success**: After sync_orchestration returns success for a language (and not local mode), call `_auto_submit_example()` to submit the prompt+code pair to PDD Cloud, same as one-session mode. Use `get_pdd_file_paths()` to resolve the module file paths needed for submission. Wrap in try/except and log warnings on failure (do not abort sync). 11. User Feedback (unless quiet=True): diff --git a/pdd/prompts/sync_orchestration_python.prompt b/pdd/prompts/sync_orchestration_python.prompt index 638faa76ad..3096b8b8c3 100644 --- a/pdd/prompts/sync_orchestration_python.prompt +++ b/pdd/prompts/sync_orchestration_python.prompt @@ -5,7 +5,7 @@ "type": "module", "module": { "functions": [ - {"name": "sync_orchestration", "signature": "(basename: str, target_coverage: float = 90.0, language: str = \"python\", prompts_dir: str = \"prompts\", code_dir: str = \"src\", examples_dir: str = \"examples\", tests_dir: str = \"tests\", max_attempts: int = 3, budget: float = 10.0, skip_verify: bool = False, skip_tests: bool = False, dry_run: bool = False, force: bool = False, strength: float = DEFAULT_STRENGTH, temperature: float = 0.0, time_param: float = 0.25, verbose: bool = False, quiet: bool = False, output_cost: Optional[str] = None, review_examples: bool = False, local: bool = False, context_config: Optional[Dict[str, str]] = None, context_override: Optional[str] = None, confirm_callback: Optional[Callable[[str, str], bool]] = None, no_steer: bool = False, steer_timeout: float = DEFAULT_STEER_TIMEOUT_S, agentic_mode: bool = False, one_session: bool = False, compress: bool = False, evidence: bool = False, snapshot_context: bool = False, compressed_context: bool = False) -> Dict[str, Any]", "returns": "Dict[str, Any]"} + {"name": "sync_orchestration", "signature": "(basename: str, target_coverage: float = 90.0, language: str = \"python\", prompts_dir: str = \"prompts\", code_dir: str = \"src\", examples_dir: str = \"examples\", tests_dir: str = \"tests\", max_attempts: int = 3, budget: float = 10.0, skip_verify: bool = False, skip_tests: bool = False, dry_run: bool = False, force: bool = False, strength: float = DEFAULT_STRENGTH, temperature: float = 0.0, time_param: float = 0.25, verbose: bool = False, quiet: bool = False, output_cost: Optional[str] = None, review_examples: bool = False, local: bool = False, context_config: Optional[Dict[str, str]] = None, context_override: Optional[str] = None, confirm_callback: Optional[Callable[[str, str], bool]] = None, no_steer: bool = False, steer_timeout: float = DEFAULT_STEER_TIMEOUT_S, agentic_mode: bool = False, one_session: bool = False, compress: bool = False, fresh: bool = False, evidence: bool = False, snapshot_context: bool = False, compressed_context: bool = False) -> Dict[str, Any]", "returns": "Dict[str, Any]"} ] } } @@ -73,6 +73,7 @@ def sync_orchestration( steer_timeout: float = DEFAULT_STEER_TIMEOUT_S, agentic_mode: bool = False, compress: bool = False, + fresh: bool = False, evidence: bool = False, snapshot_context: bool = False, compressed_context: bool = False, @@ -253,7 +254,7 @@ For every call to `sync_determine_operation`, compute `isolated_replay_or_repair ### Dry-Run Mode -When `dry_run=True`, display the sync log at `.pdd/meta/{basename}_{language.lower()}_sync.log` (JSONL format) and return immediately. Normal mode shows concise output; verbose mode shows full details including decision type, confidence, and budget info. +When `dry_run=True`, first surface the CURRENT decision via `_display_dry_run_decision(...)` — it calls `sync_determine_operation(..., log_mode=True, read_only=True)` (no lock, no fingerprint/metadata mutation) and prints a `Current analysis:` line, prefixing a pending `CONFLICT` (prompt+derived both changed) with its actionable resolution message (#1929) so a never-synced conflict is visible before any log entry exists. Then display the sync log at `.pdd/meta/{basename}_{language.lower()}_sync.log` (JSONL format) and return immediately. Normal mode shows concise output; verbose mode shows full details including decision type, confidence, and budget info. Dry-run must not write snapshot artifacts. If nondeterministic context can be detected without executing dynamic tags, report that a snapshot would be required, but do not execute shell/web/query tags solely for dry-run detection. @@ -305,6 +306,7 @@ When `skip_verify` or `skip_tests` is set: - For the **auto-deps** operation, invoke `auto_deps_main()` with the resolved prompt path, directory path, CSV path, and `_skip_finalization=True`. Pass `compress=compress`. - For the **generate** operation, invoke `code_generator_main()` with the resolved `pdd_files['prompt']` and `pdd_files['code']` (both `.resolve()`'d to absolute paths). Pass `output_from_config=True` — the resolved code path comes from `get_pdd_file_paths()` (which uses `.pddrc generate_output_path`), not an explicit user `--output`, so front-matter `output:` should still take precedence over the .pddrc default. Forward `compress` and `snapshot_context` during normal generation and during conformance retry attempts. When compressed context is requested, build a fresh `phase="generate"` package for each initial attempt and retry, then pass it as `compressed_context=` to `code_generator_main`; never pass the boolean request flag. The one-session sync path in `sync_main` follows the same convention. + - **Surgical regeneration for mature modules (#1938 Pillar A)**: pass `force_incremental_flag=(not fresh)` to `code_generator_main` — i.e. by DEFAULT (`fresh=False`) mature modules are regenerated surgically (edit-shaped): `code_generator_main` feeds the existing on-disk code plus the prompt delta to the incremental generator so declared public symbols are preserved in place rather than dropped by a full "rebirth" regeneration. When the operator passes `pdd sync --fresh` (`fresh=True`), forward `force_incremental_flag=False` to restore full regeneration (for intended large rewrites). This applies to the initial generate attempt AND the conformance retry attempts; `code_generator_main` itself keeps the safety fallbacks — it performs full generation for a new/empty module or when the original prompt cannot be determined, and it disables incremental generation whenever a `PDD_REPAIR_DIRECTIVE` is active so repair retries still reach the full generator (#1724). The public-surface / declared-interface gate remains the guarantee; surgical generation only lowers drift frequency at the source. The one-session sync path in `sync_main` passes the same `force_incremental_flag=(not fresh)`. - **Ambiguous module names (issue #1677)**: `get_pdd_file_paths` raises `AmbiguousModuleError` (a `ValueError` subclass listing the conflicting targets) when a bare basename maps to ≥2 distinct architecture outputs. `sync_orchestration` imports it from `sync_determine_operation` and must let it propagate in BOTH the dry-run/log-display path and the main path — do NOT swallow it in the broad per-operation `except` — so an ambiguous bare basename fails the sync fast instead of generating to a guessed default path. - **Architecture-conformance / public-surface / test-churn retry (#866, #1012)**: Wrap the **generate** invocation of `code_generator_main()` in a repair loop bounded by `MAX_CONFORMANCE_ATTEMPTS` (imported from `pdd.agentic_sync_runner`). The loop catches three typed exceptions from `code_generator_main` — `ArchitectureConformanceError`, `PublicSurfaceRegressionError`, and `TestChurnError` — and routes each through the same `PDD_REPAIR_DIRECTIVE` plumbing: - On `ArchitectureConformanceError`: accumulate `exc.total_cost`/`exc.model_name`, set `PDD_REPAIR_DIRECTIVE` to `exc.repair_directive`, and retry only while budget remains; abort early (without further attempts) when the next failure carries the **identical** sorted missing-symbol set as the previous one (no progress is being made). On exhaustion call `build_conformance_hard_failure_from_error(last_exc, basename)` and `print()` the structured block to **stderr** (it contains `prompt:`, `output:`, `expected:`, `found:`, `missing:`, `Reproduce locally: pdd sync `, and the `--- env ---` fingerprint) before re-raising `last_exc`. @@ -389,6 +391,7 @@ entry = create_log_entry( ### Local helpers (implement in this module) +- `_display_dry_run_decision(basename, language, target_coverage, prompts_dir, skip_tests, skip_verify, context_override, quiet)` - Read-only helper (#1929) that computes the current `sync_determine_operation(..., log_mode=True, read_only=True)` decision and prints a `Current analysis:` line for `--dry-run`, prefixing a `CONFLICT` classification with its actionable reason. Best-effort: swallow any analysis error and return `None` so the log replay still runs; suppress output when `quiet`. - `_display_sync_log(basename, language, verbose, paths=None)` - Format and display sync log using `load_operation_log()`; pass `paths=pdd_files` for subproject anchoring (issue #1211). Suppress the `Model:` header if the model name is empty, "unknown", or "N/A" (case-insensitive). - `_create_mock_context(**kwargs)` - Create Click context for command execution - `_parse_test_output(output, language)` - Parse test runner output to extract (tests_passed, tests_failed, coverage) diff --git a/pdd/sync_determine_operation.py b/pdd/sync_determine_operation.py index 99e0e35a24..90ea6c2e75 100644 --- a/pdd/sync_determine_operation.py +++ b/pdd/sync_determine_operation.py @@ -2013,6 +2013,49 @@ def _changed_artifacts_from_hashes( return changes +def _conflict_command_suffix(language: str) -> str: + """CLI suffix that disambiguates non-Python units in resolution commands.""" + return "" if language.lower() == "python" else f" --language {language}" + + +def _describe_conflict_artifacts(changes: List[str]) -> str: + """Human phrase for the co-edited artifacts, prompt first ('prompt and code').""" + order = ("prompt", "code", "example", "test") + ordered = [item for item in order if item in changes] + ordered += [item for item in changes if item not in ordered] + if not ordered: + return "prompt and derived artifacts" + if len(ordered) == 1: + return ordered[0] + if len(ordered) == 2: + return f"{ordered[0]} and {ordered[1]}" + return ", ".join(ordered[:-1]) + f", and {ordered[-1]}" + + +def _conflict_resolution_commands( + basename: str, language: str, code_path: Optional[Path] = None +) -> Dict[str, str]: + """Exact commands offered for a CONFLICT unit — all runnable today. + + ``accept_current`` finalizes a hand-merge deterministically; ``prompt_wins`` / + ``code_wins`` are the real commands (``pdd sync`` / ``pdd update``) that let one + side win. (The ``pdd resolve --prompt-wins`` / ``--code-wins`` flags are only + non-automated previews of these, so the message points at the real commands.) + + Command-shape matters (#1969 review, pass 2): only ``pdd resolve`` accepts + ``--language``. ``pdd sync`` takes a bare basename (it syncs every language + variant of it) and ``pdd update`` takes the CODE FILE path — neither accepts a + ``--language`` suffix, so they must not get one. + """ + suffix = _conflict_command_suffix(language) + code_target = str(code_path) if code_path else f"path/to/{basename} code file" + return { + "accept_current": f"pdd resolve {basename}{suffix} --accept-current", + "prompt_wins": f"pdd sync {basename}", + "code_wins": f"pdd update {code_target}", + } + + def _prompt_derived_conflict_decision( *, basename: str, @@ -2022,21 +2065,48 @@ def _prompt_derived_conflict_decision( fingerprint: Optional[Fingerprint], read_only: bool, ) -> SyncDecision: - """Return the explicit conflict decision for prompt+derived co-edits.""" + """Return the explicit, non-destructive CONFLICT decision for prompt+derived co-edits. + + The prompt AND one or more derived artifacts both changed since the last + synced fingerprint. pdd must not auto-pick a winner here (regenerating would + discard the manual code edits; `pdd update` would discard the prompt edits), + so this returns the terminal ``fail_and_request_manual_merge`` operation with + an explicit ``CONFLICT`` classification. The fingerprint and run report are + preserved (never deleted) as the record of the last-synced ancestor so that + ``pdd resolve`` can finalize an informed choice. The ``reason`` is written to + be actionable on its own: it names the unit, what moved, and the exact + resolution commands. + """ meta_dir = get_meta_dir(paths=paths) safe_bn = _safe_basename(basename) fp_path = meta_dir / f"{safe_bn}_{language.lower()}.json" rr_path = meta_dir / f"{safe_bn}_{language.lower()}_run.json" + resolution_commands = _conflict_resolution_commands( + basename, language, code_path=paths.get("code") if paths else None + ) + moved = _describe_conflict_artifacts(changes) + reason = ( + f"CONFLICT: '{basename}' — {moved} changed since the last sync, so pdd " + f"will not auto-pick a winner (that would discard your edits). After hand-" + f"merging, run `{resolution_commands['accept_current']}` to record the current " + f"files as the new baseline. To let one side win instead, run " + f"`{resolution_commands['code_wins']}` (back-propagate the code into the " + f"prompt) or `{resolution_commands['prompt_wins']}` (regenerate the code from " + f"the prompt)." + ) return SyncDecision( operation='fail_and_request_manual_merge', - reason='Prompt and derived artifacts changed since the last fingerprint; manual conflict resolution required', + reason=reason, confidence=1.0, estimated_cost=estimate_operation_cost('fail_and_request_manual_merge'), details={ 'decision_type': 'heuristic', 'classification': 'CONFLICT', + 'basename': basename, + 'language': language, 'changed_files': changes, 'read_only': read_only, + 'resolution_commands': resolution_commands, 'metadata_preserved': [ str(path) for path in (fp_path, rr_path) if path.exists() ], @@ -3323,14 +3393,22 @@ def analyze_conflict_with_llm( ) -> SyncDecision: """ Resolve complex sync conflicts using an LLM. - + + NOTE (#1929): this helper is NOT part of the deterministic decision path. + The both-changed (prompt+derived) case is classified explicitly as CONFLICT + by ``_prompt_derived_conflict_decision`` and surfaced to the user via + ``pdd resolve`` — the decision tree never auto-picks a winner. This function + is retained only as the LLM backend reserved for a future + ``pdd resolve``-driven agentic 3-way merge; it must not be wired back into + ``sync_determine_operation`` as an automatic conflict resolver. + Args: basename: The base name for the PDD unit language: The programming language fingerprint: The last known good state changed_files: List of files that have changed prompts_dir: Directory containing prompt files - + Returns: SyncDecision object with LLM-recommended operation """ diff --git a/pdd/sync_main.py b/pdd/sync_main.py index 8ca29b5a12..c8f72cdff4 100644 --- a/pdd/sync_main.py +++ b/pdd/sync_main.py @@ -610,6 +610,7 @@ def sync_main( agentic_mode: bool = False, one_session: bool = False, compress: bool = False, + fresh: bool = False, evidence: bool = False, snapshot_context: bool = False, compressed_context: bool = False, @@ -1064,7 +1065,14 @@ def _one_session_compressed_context( prompt_file=str(pdd_files["prompt"].resolve()), output=str(pdd_files["code"].resolve()), original_prompt_file_path=None, - force_incremental_flag=False, + # Surgical (edit-shaped) regeneration by + # default for mature modules; --fresh + # (fresh=True) restores full regeneration + # (#1938 Pillar A). code_generator_main + # still falls back to full generation for + # new/empty modules or when the original + # prompt can't be determined. + force_incremental_flag=not fresh, language=resolved_language, # output is .pddrc-derived (get_pdd_file_paths uses # context config), so let front-matter override it. @@ -1272,6 +1280,7 @@ def _one_session_compressed_context( agentic_mode=agentic_mode, snapshot_context=snapshot_context, compressed_context=compressed_context, + fresh=fresh, ) # Post-sync: auto-submit example to cloud on success (multi-step path) diff --git a/pdd/sync_orchestration.py b/pdd/sync_orchestration.py index 4e7e70c145..d57804364c 100644 --- a/pdd/sync_orchestration.py +++ b/pdd/sync_orchestration.py @@ -1889,6 +1889,50 @@ def _create_mock_context(**kwargs) -> click.Context: return ctx +def _display_dry_run_decision( + basename: str, + language: str, + target_coverage: float, + prompts_dir: str, + skip_tests: bool, + skip_verify: bool, + context_override: Optional[str], + quiet: bool, +) -> Optional[SyncDecision]: + """Print the current read-only sync decision for a ``--dry-run`` (#1929). + + A dry-run otherwise only replays the operation log, so a freshly-created + CONFLICT (prompt AND a derived artifact both changed since the fingerprint) + is invisible until a real sync runs. This computes the current decision in + ``read_only``/``log_mode`` — no lock, no fingerprint or metadata mutation — + and surfaces it, giving a pending CONFLICT its actionable resolution message + without any side effects. Best-effort: any analysis error is swallowed so the + log replay below still runs. + """ + try: + decision = sync_determine_operation( + basename, + language, + target_coverage, + prompts_dir=prompts_dir, + skip_tests=skip_tests, + skip_verify=skip_verify, + context_override=context_override, + log_mode=True, + read_only=True, + ) + except Exception: # pylint: disable=broad-except + return None + if decision is None or quiet: + return decision + classification = (decision.details or {}).get('classification') + if classification == 'CONFLICT': + print(f"Current analysis: CONFLICT — {decision.reason}") + else: + print(f"Current analysis: {decision.operation} — {decision.reason}") + return decision + + def _display_sync_log( basename: str, language: str, @@ -2003,12 +2047,22 @@ def sync_orchestration( steer_timeout: float = DEFAULT_STEER_TIMEOUT_S, agentic_mode: bool = False, compress: bool = False, + fresh: bool = False, evidence: bool = False, snapshot_context: bool = False, compressed_context: bool = False, ) -> Dict[str, Any]: """ Orchestrates the complete PDD sync workflow with parallel animation. + + ``fresh`` (issue #1938 Pillar A): when False (the default), the generate + operation regenerates mature modules surgically (edit-shaped) by driving + ``code_generator_main`` with ``force_incremental_flag=True`` so declared + public symbols are preserved instead of being dropped by a full "rebirth" + regeneration. Pass ``fresh=True`` (``pdd sync --fresh``) to restore full + regeneration. ``code_generator_main`` still falls back to full generation + for new/empty modules or when the original prompt cannot be determined, and + conformance repair retries still force full regeneration. """ # Handle None values from CLI (Issue #194) - defense in depth if target_coverage is None: @@ -2046,6 +2100,19 @@ def sync_orchestration( raise except Exception: _dry_paths = None + # #1929: surface the CURRENT decision (especially a pending CONFLICT) so a + # dry-run reports what sync WOULD do, not just past log entries. Read-only: + # no lock, fingerprint, or metadata mutation. + _display_dry_run_decision( + basename, + language, + target_coverage, + prompts_dir, + skip_tests, + skip_verify, + context_override, + quiet, + ) if _dry_paths: return _display_sync_log(basename, language, verbose, paths=_dry_paths) return _display_sync_log(basename, language, verbose) @@ -2746,7 +2813,7 @@ def sync_worker_logic(): for _conform_attempt in range(MAX_CONFORMANCE_ATTEMPTS): try: # Use absolute paths to avoid path_resolution_mode mismatch between sync (cwd) and generate (config_base) - result = code_generator_main(ctx, prompt_file=str(pdd_files['prompt'].resolve()), output=str(pdd_files['code'].resolve()), original_prompt_file_path=None, force_incremental_flag=False, output_from_config=True, compress=compress, snapshot_context=snapshot_context, compressed_context=_phase_compressed_context('generate', os.environ.get("PDD_REPAIR_DIRECTIVE"))) + result = code_generator_main(ctx, prompt_file=str(pdd_files['prompt'].resolve()), output=str(pdd_files['code'].resolve()), original_prompt_file_path=None, force_incremental_flag=not fresh, output_from_config=True, compress=compress, snapshot_context=snapshot_context, compressed_context=_phase_compressed_context('generate', os.environ.get("PDD_REPAIR_DIRECTIVE"))) last_conform_exc = None break except ( diff --git a/scripts/build_prompt_catchup_queue.py b/scripts/build_prompt_catchup_queue.py new file mode 100644 index 0000000000..978cebee46 --- /dev/null +++ b/scripts/build_prompt_catchup_queue.py @@ -0,0 +1,324 @@ +#!/usr/bin/env python3 +"""Build the ranked *code-ahead* work queue for the prompt catch-up pass (issue A2). + +A "code-ahead" unit is one whose module code (``pdd/.py``) has a more recent +commit than its prompt (``pdd/prompts/_python.prompt``): code landed without a +corresponding prompt update, so "regenerate from prompt" would resurrect stale +behavior (doctrine anti-pattern "Prompt Drift"). This script enumerates every +python unit (reusing ``stamp_fingerprints.enumerate_units`` so code/prompt paths +resolve exactly as the fingerprint oracle sees them), then for each computes: + +* ``code_last`` / ``prompt_last``: most recent commit author-date (ALL history) of + the code file and the prompt file. A unit is code-ahead when + ``code_last > prompt_last``. +* ``days_ahead``: how many days the code leads the prompt. +* churn since ``--since`` (default 2026-04-01), split by what each commit touched: + - ``churn_lines`` / ``churn_commits``: added+deleted lines / commit count over + commits that touched the **code but not the prompt** (the drift-producing + commits). This is the ranking weight. + - ``prompt_only_commits`` / ``both_commits``: commits touching only the prompt / + both — surfaced so a reviewer can spot units whose prompt was ALSO edited + independently (``conflict_risk``: ``pdd update`` there could clobber a + deliberate prompt change; do not auto-update). +* ``code_loc``: current line count of the code file (pilot-size filter). + +Ranked by (churn_lines, days_ahead, churn_commits) descending. Output JSON is the +committed work-queue artifact (``scripts/prompt_catchup_queue.json``). Stdlib only, +no ``pdd`` import, no LLM. +""" +from __future__ import annotations + +import argparse +import json +import subprocess +import sys +from datetime import datetime +from pathlib import Path +from typing import Dict, List, Optional, Tuple + +SCRIPTS_DIR = Path(__file__).resolve().parent +REPO_ROOT = SCRIPTS_DIR.parent +sys.path.insert(0, str(SCRIPTS_DIR)) + +import stamp_fingerprints as sf # noqa: E402 (stdlib-only, no side effects) + +DEFAULT_SINCE = "2026-04-01" + + +def _git(args: List[str]) -> str: + """Run a read-only git command in the repo, returning stdout.""" + out = subprocess.run( + ["git", "-C", str(REPO_ROOT), *args], + capture_output=True, + text=True, + check=False, + ) + return out.stdout or "" + + +def _rel(path: Path) -> str: + """Repo-relative POSIX path for a file under the repo root.""" + return path.resolve().relative_to(REPO_ROOT).as_posix() + + +def _normalize_numstat_path(field: str) -> str: + """Normalize a --numstat path field, resolving git rename syntax to the new path. + + Handles both ``old => new`` and ``dir/{old => new}/leaf`` forms so churn lands + on the current path. Binary files show ``-`` counts and are handled by callers. + """ + field = field.strip() + if "=>" not in field: + return field + if "{" in field and "}" in field: + pre = field[: field.index("{")] + inner = field[field.index("{") + 1 : field.index("}")] + post = field[field.index("}") + 1 :] + new = inner.split("=>")[-1].strip() + return (pre + new + post).replace("//", "/") + return field.split("=>")[-1].strip() + + +def load_window_commits(since: str) -> Tuple[Dict[str, List[dict]], Dict[str, set]]: + """Parse one ``git log --numstat`` pass over the window. + + Returns ``(by_file, commit_files)`` where ``by_file[path]`` is a list of + ``{sha, date, added, deleted}`` for every commit in ``[since, now]`` that + touched ``path``, and ``commit_files[sha]`` is the set of paths that commit + touched. Author date (``%aI``) is used throughout. + """ + raw = _git( + [ + "log", + f"--since={since}T00:00:00", + "--no-merges", + "--numstat", + "--date=iso-strict", + "--format=@@C@@%H|%aI", + ] + ) + by_file: Dict[str, List[dict]] = {} + commit_files: Dict[str, set] = {} + sha = "" + date = "" + for line in raw.splitlines(): + if line.startswith("@@C@@"): + sha, date = line[len("@@C@@") :].split("|", 1) + commit_files.setdefault(sha, set()) + continue + if not line.strip() or not sha: + continue + parts = line.split("\t") + if len(parts) != 3: + continue + added_s, deleted_s, path_field = parts + path = _normalize_numstat_path(path_field) + added = 0 if added_s == "-" else int(added_s) + deleted = 0 if deleted_s == "-" else int(deleted_s) + by_file.setdefault(path, []).append( + {"sha": sha, "date": date, "added": added, "deleted": deleted} + ) + commit_files[sha].add(path) + return by_file, commit_files + + +def load_last_commit_dates(targets: set) -> Dict[str, str]: + """Most-recent commit author-date (ALL history) for each path in ``targets``. + + One reverse-chronological ``git log --name-only`` pass; the first time a path + appears is its most recent commit. Stops early once every target is found. + """ + raw = _git(["log", "--name-status", "--date=iso-strict", "--format=@@C@@%aI"]) + last: Dict[str, str] = {} + remaining = set(targets) + date = "" + for line in raw.splitlines(): + if line.startswith("@@C@@"): + date = line[len("@@C@@") :].strip() + continue + if not line.strip() or not remaining: + continue + # name-status rows: "M\tpath" or "R100\told\tnew"; take the last field. + parts = line.split("\t") + path = _normalize_numstat_path(parts[-1]) + if path in remaining: + last[path] = date + remaining.discard(path) + if not remaining: + break + return last + + +def _iso_date(s: Optional[str]) -> Optional[datetime]: + if not s: + return None + try: + return datetime.fromisoformat(s) + except ValueError: + return None + + +def prompt_last_is_standalone( + prompt_commits: List[dict], code_rel: str, commit_files: Dict[str, set] +) -> bool: + """True when the prompt's most recent (non-merge, in-window) commit did NOT touch code. + + For a code-ahead unit this is the precise conflict signal: the current prompt + state came from a standalone prompt edit never reconciled with code via a joint + commit, so ``pdd update`` (code -> prompt) could clobber deliberate prompt + intent. If the prompt's last commit also touched the code (a proper joint sync) + the divergence is pure staleness and safe to back-propagate. + + Uses the pathspec-free ``commit_files`` map (full file set per commit) built by + ``load_window_commits`` -- a per-unit ``git log -- `` cannot be used + because the pathspec filters ``--name-only`` down to just the prompt, hiding the + code file. Returns False when the prompt has no in-window commits (pure + staleness: the prompt has not been touched since the window start). + """ + if not prompt_commits: + return False + latest = max(prompt_commits, key=lambda c: c["date"]) + return code_rel not in commit_files.get(latest["sha"], set()) + + +def _code_loc(path: Path) -> Optional[int]: + try: + return sum(1 for _ in path.open("r", encoding="utf-8", errors="ignore")) + except OSError: + return None + + +def build_queue(since: str) -> dict: + units = sf.enumerate_units() + by_file, commit_files = load_window_commits(since) + + targets: set = set() + resolved: List[Tuple[sf.Unit, str, str]] = [] + for unit in units: + if not unit.prompt.exists() or not unit.code.exists(): + continue + code_rel = _rel(unit.code) + prompt_rel = _rel(unit.prompt) + targets.add(code_rel) + targets.add(prompt_rel) + resolved.append((unit, code_rel, prompt_rel)) + + last_dates = load_last_commit_dates(targets) + + rows: List[dict] = [] + for unit, code_rel, prompt_rel in resolved: + code_last = last_dates.get(code_rel) + prompt_last = last_dates.get(prompt_rel) + cd = _iso_date(code_last) + pd = _iso_date(prompt_last) + code_ahead = bool(cd and (pd is None or cd > pd)) + days_ahead = round((cd - pd).total_seconds() / 86400.0, 2) if (cd and pd) else None + + code_commits = by_file.get(code_rel, []) + churn_lines = 0 + churn_commits = 0 + both_commits = 0 + last_code_only_date: Optional[str] = None + for c in code_commits: + touched_prompt = prompt_rel in commit_files.get(c["sha"], set()) + if touched_prompt: + both_commits += 1 + else: + churn_commits += 1 + churn_lines += c["added"] + c["deleted"] + if last_code_only_date is None or c["date"] > last_code_only_date: + last_code_only_date = c["date"] + + prompt_commits = by_file.get(prompt_rel, []) + prompt_only_commits = sum( + 1 for c in prompt_commits if code_rel not in commit_files.get(c["sha"], set()) + ) + last_prompt_only_date: Optional[str] = None + for c in prompt_commits: + if code_rel not in commit_files.get(c["sha"], set()): + if last_prompt_only_date is None or c["date"] > last_prompt_only_date: + last_prompt_only_date = c["date"] + + # Conflict risk: the prompt's most recent commit was a standalone prompt + # edit (touched prompt, not code) never reconciled with code by a joint + # commit -- a deliberate prompt change `pdd update` could overwrite. + # Reviewer must inspect before running update; flagged, never auto-updated. + # Only computed for code-ahead units (the update candidates). + conflict_risk = code_ahead and prompt_last_is_standalone( + prompt_commits, code_rel, commit_files + ) + + rows.append( + { + "basename": unit.basename, + "code": code_rel, + "prompt": prompt_rel, + "meta": _rel(unit.meta) if unit.meta.exists() else None, + "code_last": code_last, + "prompt_last": prompt_last, + "code_ahead": code_ahead, + "days_ahead": days_ahead, + "churn_lines": churn_lines, + "churn_commits": churn_commits, + "both_commits": both_commits, + "prompt_only_commits": prompt_only_commits, + "last_code_only_date": last_code_only_date, + "last_prompt_only_date": last_prompt_only_date, + "conflict_risk": conflict_risk, + "code_loc": _code_loc(unit.code), + } + ) + + ahead = [r for r in rows if r["code_ahead"]] + ahead.sort( + key=lambda r: (r["churn_lines"], r["days_ahead"] or 0, r["churn_commits"]), + reverse=True, + ) + for i, r in enumerate(ahead, 1): + r["rank"] = i + + total_units = len(rows) + ahead_with_churn = [r for r in ahead if r["churn_lines"] > 0] + return { + "generated_at": datetime.now().astimezone().isoformat(), + "since": since, + "generator": "scripts/build_prompt_catchup_queue.py", + "definition": ( + "code_ahead = code_last commit newer than prompt_last commit (all " + "history). churn_lines = added+deleted on the code file over commits " + "since `since` that touched code but NOT the prompt (drift commits). " + "Ranked by (churn_lines, days_ahead, churn_commits) desc." + ), + "stats": { + "total_python_units": total_units, + "code_ahead_units": len(ahead), + "code_ahead_with_window_churn": len(ahead_with_churn), + "conflict_risk_units": sum(1 for r in ahead if r["conflict_risk"]), + }, + "queue": ahead, + } + + +def main(argv: Optional[List[str]] = None) -> int: + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument("--since", default=DEFAULT_SINCE, help="Churn window start (YYYY-MM-DD).") + parser.add_argument( + "--output", + default=str(SCRIPTS_DIR / "prompt_catchup_queue.json"), + help="Output JSON path.", + ) + args = parser.parse_args(argv) + data = build_queue(args.since) + Path(args.output).write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8") + s = data["stats"] + print( + f"wrote {args.output}: {s['code_ahead_units']} code-ahead / " + f"{s['total_python_units']} units " + f"({s['code_ahead_with_window_churn']} with window churn, " + f"{s['conflict_risk_units']} conflict-risk)" + ) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/fingerprint_waivers.json b/scripts/fingerprint_waivers.json new file mode 100644 index 0000000000..d0f8bb2dba --- /dev/null +++ b/scripts/fingerprint_waivers.json @@ -0,0 +1,21 @@ +{ + "_comment": "Units enumerated from pdd/prompts/*_python.prompt that intentionally have NO committed .pdd/meta fingerprint. scripts/stamp_fingerprints.py --check excludes these from both the coverage requirement and hash verification. Keep the list minimal and justified; a unit belongs here only when `pdd sync` itself cannot produce a fingerprint for it (ambiguous module identity, or no code file on disk).", + "waivers": [ + { + "basename": "cli", + "reason": "Ambiguous module identity: the bare leaf 'cli' maps to two files in architecture.json (pdd/cli.py and pdd/core/cli.py), so `pdd sync cli` raises AmbiguousModuleError and never writes a fingerprint. The path-qualified unit core/cli is stamped for pdd/core/cli.py; pdd/cli.py is a thin re-export shim reached only by path-qualified sync." + }, + { + "basename": "gate", + "reason": "Ambiguous module identity: the bare leaf 'gate' maps to two files in architecture.json (pdd/commands/gate.py and pdd/gate_main.py) and has no pdd/gate.py by .pddrc convention, so `pdd sync gate` raises AmbiguousModuleError. The path-qualified unit commands/gate is stamped for pdd/commands/gate.py; pdd/gate_main.py has no prompt of its own." + }, + { + "basename": "core_dump_smoke", + "reason": "No code file on disk: pdd/core_dump_smoke.py does not exist, so there is nothing to hash. The prompt is a smoke-test fixture with no generated module." + }, + { + "basename": "server/routes/session", + "reason": "No code file on disk: pdd/server/routes/session.py does not exist (session handling lives elsewhere), so there is nothing to hash." + } + ] +} diff --git a/scripts/prompt_catchup_queue.json b/scripts/prompt_catchup_queue.json new file mode 100644 index 0000000000..ee8fe8eb84 --- /dev/null +++ b/scripts/prompt_catchup_queue.json @@ -0,0 +1,1439 @@ +{ + "generated_at": "2026-07-09T13:18:28.864683-07:00", + "since": "2026-04-01", + "generator": "scripts/build_prompt_catchup_queue.py", + "definition": "code_ahead = code_last commit newer than prompt_last commit (all history). churn_lines = added+deleted on the code file over commits since `since` that touched code but NOT the prompt (drift commits). Ranked by (churn_lines, days_ahead, churn_commits) desc.", + "stats": { + "total_python_units": 229, + "code_ahead_units": 75, + "code_ahead_with_window_churn": 75, + "conflict_risk_units": 43 + }, + "queue": [ + { + "basename": "checkup_agent", + "code": "pdd/checkup_agent.py", + "prompt": "pdd/prompts/checkup_agent_python.prompt", + "meta": ".pdd/meta/checkup_agent_python.json", + "code_last": "2026-06-10T18:09:20-07:00", + "prompt_last": "2026-06-10T11:01:51-07:00", + "code_ahead": true, + "days_ahead": 0.3, + "churn_lines": 1844, + "churn_commits": 15, + "both_commits": 2, + "prompt_only_commits": 1, + "last_code_only_date": "2026-06-10T18:09:20-07:00", + "last_prompt_only_date": "2026-06-10T11:01:51-07:00", + "conflict_risk": true, + "code_loc": 1081, + "rank": 1 + }, + { + "basename": "cli_detector", + "code": "pdd/cli_detector.py", + "prompt": "pdd/prompts/cli_detector_python.prompt", + "meta": ".pdd/meta/cli_detector_python.json", + "code_last": "2026-06-16T12:31:10-07:00", + "prompt_last": "2026-06-15T21:46:51Z", + "code_ahead": true, + "days_ahead": 0.91, + "churn_lines": 1486, + "churn_commits": 3, + "both_commits": 8, + "prompt_only_commits": 4, + "last_code_only_date": "2026-06-16T12:31:10-07:00", + "last_prompt_only_date": "2026-06-15T21:46:51Z", + "conflict_risk": true, + "code_loc": 1002, + "rank": 2 + }, + { + "basename": "commands/checkup_simplify", + "code": "pdd/checkup_simplify.py", + "prompt": "pdd/prompts/commands/checkup_simplify_python.prompt", + "meta": ".pdd/meta/commands_checkup_simplify_python.json", + "code_last": "2026-06-10T11:24:07-07:00", + "prompt_last": "2026-05-27T14:31:23-07:00", + "code_ahead": true, + "days_ahead": 13.87, + "churn_lines": 1165, + "churn_commits": 8, + "both_commits": 1, + "prompt_only_commits": 0, + "last_code_only_date": "2026-06-10T11:24:07-07:00", + "last_prompt_only_date": null, + "conflict_risk": false, + "code_loc": 962, + "rank": 3 + }, + { + "basename": "contract_check", + "code": "pdd/contract_check.py", + "prompt": "pdd/prompts/contract_check_python.prompt", + "meta": ".pdd/meta/contract_check_python.json", + "code_last": "2026-06-01T20:53:52-07:00", + "prompt_last": "2026-05-27T12:05:21-07:00", + "code_ahead": true, + "days_ahead": 5.37, + "churn_lines": 1054, + "churn_commits": 5, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-06-01T20:53:52-07:00", + "last_prompt_only_date": "2026-05-27T12:05:21-07:00", + "conflict_risk": true, + "code_loc": 946, + "rank": 4 + }, + { + "basename": "evidence_manifest", + "code": "pdd/evidence_manifest.py", + "prompt": "pdd/prompts/evidence_manifest_python.prompt", + "meta": ".pdd/meta/evidence_manifest_python.json", + "code_last": "2026-06-03T14:01:48-07:00", + "prompt_last": "2026-06-03T02:01:41Z", + "code_ahead": true, + "days_ahead": 0.79, + "churn_lines": 1044, + "churn_commits": 16, + "both_commits": 6, + "prompt_only_commits": 3, + "last_code_only_date": "2026-06-03T14:01:48-07:00", + "last_prompt_only_date": "2026-06-03T01:10:09Z", + "conflict_risk": false, + "code_loc": 801, + "rank": 5 + }, + { + "basename": "update_main", + "code": "pdd/update_main.py", + "prompt": "pdd/prompts/update_main_python.prompt", + "meta": ".pdd/meta/update_main_python.json", + "code_last": "2026-06-23T17:41:58-07:00", + "prompt_last": "2026-06-18T21:24:14-07:00", + "code_ahead": true, + "days_ahead": 4.85, + "churn_lines": 1033, + "churn_commits": 19, + "both_commits": 7, + "prompt_only_commits": 7, + "last_code_only_date": "2026-06-23T17:41:58-07:00", + "last_prompt_only_date": "2026-06-18T21:24:14-07:00", + "conflict_risk": true, + "code_loc": 2045, + "rank": 6 + }, + { + "basename": "split_validation", + "code": "pdd/split_validation.py", + "prompt": "pdd/prompts/split_validation_python.prompt", + "meta": ".pdd/meta/split_validation_python.json", + "code_last": "2026-06-04T17:57:48-07:00", + "prompt_last": "2026-05-07T19:16:44-07:00", + "code_ahead": true, + "days_ahead": 27.95, + "churn_lines": 1016, + "churn_commits": 2, + "both_commits": 2, + "prompt_only_commits": 2, + "last_code_only_date": "2026-06-04T17:57:48-07:00", + "last_prompt_only_date": "2026-05-06T21:30:35-07:00", + "conflict_risk": false, + "code_loc": 1116, + "rank": 7 + }, + { + "basename": "core/cli", + "code": "pdd/core/cli.py", + "prompt": "pdd/prompts/core/cli_python.prompt", + "meta": ".pdd/meta/core_cli_python.json", + "code_last": "2026-07-08T18:16:00-07:00", + "prompt_last": "2026-07-08T13:42:51-07:00", + "code_ahead": true, + "days_ahead": 0.19, + "churn_lines": 921, + "churn_commits": 38, + "both_commits": 6, + "prompt_only_commits": 5, + "last_code_only_date": "2026-07-08T18:16:00-07:00", + "last_prompt_only_date": "2026-07-03T09:56:47-07:00", + "conflict_risk": false, + "code_loc": 1123, + "rank": 8 + }, + { + "basename": "architecture_sync", + "code": "pdd/architecture_sync.py", + "prompt": "pdd/prompts/architecture_sync_python.prompt", + "meta": ".pdd/meta/architecture_sync_python.json", + "code_last": "2026-06-09T15:58:34-07:00", + "prompt_last": "2026-06-03T21:29:45-07:00", + "code_ahead": true, + "days_ahead": 5.77, + "churn_lines": 910, + "churn_commits": 9, + "both_commits": 6, + "prompt_only_commits": 3, + "last_code_only_date": "2026-06-09T15:58:34-07:00", + "last_prompt_only_date": "2026-05-18T13:50:20-07:00", + "conflict_risk": false, + "code_loc": 2033, + "rank": 9 + }, + { + "basename": "checkup_prompt_main", + "code": "pdd/checkup_prompt_main.py", + "prompt": "pdd/prompts/checkup_prompt_main_python.prompt", + "meta": ".pdd/meta/checkup_prompt_main_python.json", + "code_last": "2026-06-10T14:45:41-07:00", + "prompt_last": "2026-06-09T19:04:40Z", + "code_ahead": true, + "days_ahead": 1.11, + "churn_lines": 855, + "churn_commits": 12, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-06-10T14:45:41-07:00", + "last_prompt_only_date": "2026-06-09T19:04:40Z", + "conflict_risk": true, + "code_loc": 685, + "rank": 10 + }, + { + "basename": "prompt_repair", + "code": "pdd/prompt_repair.py", + "prompt": "pdd/prompts/prompt_repair_python.prompt", + "meta": ".pdd/meta/prompt_repair_python.json", + "code_last": "2026-06-10T14:45:41-07:00", + "prompt_last": "2026-06-06T11:03:30-07:00", + "code_ahead": true, + "days_ahead": 4.15, + "churn_lines": 836, + "churn_commits": 8, + "both_commits": 2, + "prompt_only_commits": 2, + "last_code_only_date": "2026-06-10T14:45:41-07:00", + "last_prompt_only_date": "2026-06-06T10:42:18-07:00", + "conflict_risk": false, + "code_loc": 713, + "rank": 11 + }, + { + "basename": "coverage_contracts", + "code": "pdd/coverage_contracts.py", + "prompt": "pdd/prompts/coverage_contracts_python.prompt", + "meta": ".pdd/meta/coverage_contracts_python.json", + "code_last": "2026-07-02T12:01:45-07:00", + "prompt_last": "2026-06-22T23:13:25Z", + "code_ahead": true, + "days_ahead": 9.83, + "churn_lines": 788, + "churn_commits": 9, + "both_commits": 1, + "prompt_only_commits": 3, + "last_code_only_date": "2026-07-02T12:01:45-07:00", + "last_prompt_only_date": "2026-06-29T21:27:31Z", + "conflict_risk": true, + "code_loc": 994, + "rank": 12 + }, + { + "basename": "commands/maintenance", + "code": "pdd/commands/maintenance.py", + "prompt": "pdd/prompts/commands/maintenance_python.prompt", + "meta": ".pdd/meta/commands_maintenance_python.json", + "code_last": "2026-07-09T04:59:18-07:00", + "prompt_last": "2026-07-08T18:16:00-07:00", + "code_ahead": true, + "days_ahead": 0.45, + "churn_lines": 771, + "churn_commits": 21, + "both_commits": 9, + "prompt_only_commits": 7, + "last_code_only_date": "2026-07-09T04:59:18-07:00", + "last_prompt_only_date": "2026-06-02T20:43:19Z", + "conflict_risk": false, + "code_loc": 1013, + "rank": 13 + }, + { + "basename": "checkup_report", + "code": "pdd/checkup_report.py", + "prompt": "pdd/prompts/checkup_report_python.prompt", + "meta": ".pdd/meta/checkup_report_python.json", + "code_last": "2026-06-10T16:25:17-07:00", + "prompt_last": "2026-06-10T00:36:59-07:00", + "code_ahead": true, + "days_ahead": 0.66, + "churn_lines": 710, + "churn_commits": 10, + "both_commits": 0, + "prompt_only_commits": 2, + "last_code_only_date": "2026-06-10T16:25:17-07:00", + "last_prompt_only_date": "2026-06-10T00:36:59-07:00", + "conflict_risk": true, + "code_loc": 527, + "rank": 14 + }, + { + "basename": "preprocess", + "code": "pdd/preprocess.py", + "prompt": "pdd/prompts/preprocess_python.prompt", + "meta": ".pdd/meta/preprocess_python.json", + "code_last": "2026-06-23T14:31:23-07:00", + "prompt_last": "2026-06-23T13:58:59-07:00", + "code_ahead": true, + "days_ahead": 0.02, + "churn_lines": 682, + "churn_commits": 26, + "both_commits": 9, + "prompt_only_commits": 3, + "last_code_only_date": "2026-06-23T14:31:23-07:00", + "last_prompt_only_date": "2026-06-08T17:30:50-07:00", + "conflict_risk": false, + "code_loc": 1488, + "rank": 15 + }, + { + "basename": "codex_subscription", + "code": "pdd/codex_subscription.py", + "prompt": "pdd/prompts/codex_subscription_python.prompt", + "meta": ".pdd/meta/codex_subscription_python.json", + "code_last": "2026-05-30T19:12:21-07:00", + "prompt_last": "2026-05-29T16:53:21-07:00", + "code_ahead": true, + "days_ahead": 1.1, + "churn_lines": 657, + "churn_commits": 5, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-05-30T19:12:21-07:00", + "last_prompt_only_date": "2026-05-29T16:53:21-07:00", + "conflict_risk": true, + "code_loc": 459, + "rank": 16 + }, + { + "basename": "user_story_tests", + "code": "pdd/user_story_tests.py", + "prompt": "pdd/prompts/user_story_tests_python.prompt", + "meta": ".pdd/meta/user_story_tests_python.json", + "code_last": "2026-07-06T15:30:33-07:00", + "prompt_last": "2026-07-02T19:22:29Z", + "code_ahead": true, + "days_ahead": 4.13, + "churn_lines": 636, + "churn_commits": 13, + "both_commits": 5, + "prompt_only_commits": 7, + "last_code_only_date": "2026-07-06T15:30:33-07:00", + "last_prompt_only_date": "2026-07-02T19:22:29Z", + "conflict_risk": true, + "code_loc": 1738, + "rank": 17 + }, + { + "basename": "gate", + "code": "pdd/gate_main.py", + "prompt": "pdd/prompts/gate_python.prompt", + "meta": null, + "code_last": "2026-06-06T09:32:54-07:00", + "prompt_last": "2026-05-28T14:38:50-07:00", + "code_ahead": true, + "days_ahead": 8.79, + "churn_lines": 626, + "churn_commits": 7, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-06-06T09:32:54-07:00", + "last_prompt_only_date": "2026-05-28T14:38:50-07:00", + "conflict_risk": true, + "code_loc": 462, + "rank": 18 + }, + { + "basename": "checkup_interactive_main", + "code": "pdd/checkup_interactive_main.py", + "prompt": "pdd/prompts/checkup_interactive_main_python.prompt", + "meta": ".pdd/meta/checkup_interactive_main_python.json", + "code_last": "2026-06-10T16:12:44-07:00", + "prompt_last": "2026-06-10T00:36:59-07:00", + "code_ahead": true, + "days_ahead": 0.65, + "churn_lines": 592, + "churn_commits": 6, + "both_commits": 2, + "prompt_only_commits": 0, + "last_code_only_date": "2026-06-10T16:12:44-07:00", + "last_prompt_only_date": null, + "conflict_risk": false, + "code_loc": 548, + "rank": 19 + }, + { + "basename": "track_cost", + "code": "pdd/track_cost.py", + "prompt": "pdd/prompts/track_cost_python.prompt", + "meta": ".pdd/meta/track_cost_python.json", + "code_last": "2026-06-16T12:20:03-07:00", + "prompt_last": "2026-06-15T22:56:19Z", + "code_ahead": true, + "days_ahead": 0.85, + "churn_lines": 555, + "churn_commits": 6, + "both_commits": 3, + "prompt_only_commits": 9, + "last_code_only_date": "2026-06-16T12:20:03-07:00", + "last_prompt_only_date": "2026-06-15T22:56:19Z", + "conflict_risk": true, + "code_loc": 241, + "rank": 20 + }, + { + "basename": "sync_main", + "code": "pdd/sync_main.py", + "prompt": "pdd/prompts/sync_main_python.prompt", + "meta": ".pdd/meta/sync_main_python.json", + "code_last": "2026-06-23T17:41:58-07:00", + "prompt_last": "2026-06-23T14:25:48-07:00", + "code_ahead": true, + "days_ahead": 0.14, + "churn_lines": 551, + "churn_commits": 20, + "both_commits": 9, + "prompt_only_commits": 5, + "last_code_only_date": "2026-06-23T17:41:58-07:00", + "last_prompt_only_date": "2026-06-23T14:25:48-07:00", + "conflict_risk": true, + "code_loc": 1413, + "rank": 21 + }, + { + "basename": "commands/modify", + "code": "pdd/commands/modify.py", + "prompt": "pdd/prompts/commands/modify_python.prompt", + "meta": ".pdd/meta/commands_modify_python.json", + "code_last": "2026-07-09T04:59:18-07:00", + "prompt_last": "2026-06-12T15:51:13-07:00", + "code_ahead": true, + "days_ahead": 26.55, + "churn_lines": 409, + "churn_commits": 16, + "both_commits": 11, + "prompt_only_commits": 3, + "last_code_only_date": "2026-07-09T04:59:18-07:00", + "last_prompt_only_date": "2026-06-10T11:24:07-07:00", + "conflict_risk": false, + "code_loc": 639, + "rank": 22 + }, + { + "basename": "routing_policy", + "code": "pdd/routing_policy.py", + "prompt": "pdd/prompts/routing_policy_python.prompt", + "meta": ".pdd/meta/routing_policy_python.json", + "code_last": "2026-06-16T13:19:28-07:00", + "prompt_last": "2026-06-15T22:31:12Z", + "code_ahead": true, + "days_ahead": 0.91, + "churn_lines": 384, + "churn_commits": 1, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-06-16T13:19:28-07:00", + "last_prompt_only_date": "2026-06-15T22:31:12Z", + "conflict_risk": true, + "code_loc": 384, + "rank": 23 + }, + { + "basename": "commands/gate", + "code": "pdd/commands/gate.py", + "prompt": "pdd/prompts/commands/gate_python.prompt", + "meta": ".pdd/meta/commands_gate_python.json", + "code_last": "2026-06-05T16:09:59-07:00", + "prompt_last": "2026-05-28T14:38:50-07:00", + "code_ahead": true, + "days_ahead": 8.06, + "churn_lines": 378, + "churn_commits": 3, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-06-05T16:09:59-07:00", + "last_prompt_only_date": "2026-05-28T14:38:50-07:00", + "conflict_risk": true, + "code_loc": 308, + "rank": 24 + }, + { + "basename": "sync_order", + "code": "pdd/sync_order.py", + "prompt": "pdd/prompts/sync_order_python.prompt", + "meta": ".pdd/meta/sync_order_python.json", + "code_last": "2026-06-02T17:13:54-07:00", + "prompt_last": "2026-05-21T15:15:27-07:00", + "code_ahead": true, + "days_ahead": 12.08, + "churn_lines": 362, + "churn_commits": 5, + "both_commits": 2, + "prompt_only_commits": 2, + "last_code_only_date": "2026-06-02T22:49:32Z", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": false, + "code_loc": 693, + "rank": 25 + }, + { + "basename": "evidence_store", + "code": "pdd/evidence_store.py", + "prompt": "pdd/prompts/evidence_store_python.prompt", + "meta": ".pdd/meta/evidence_store_python.json", + "code_last": "2026-05-29T18:35:11-07:00", + "prompt_last": "2026-05-28T14:38:50-07:00", + "code_ahead": true, + "days_ahead": 1.16, + "churn_lines": 352, + "churn_commits": 3, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-05-29T18:35:11-07:00", + "last_prompt_only_date": "2026-05-28T14:38:50-07:00", + "conflict_risk": true, + "code_loc": 186, + "rank": 26 + }, + { + "basename": "agentic_split", + "code": "pdd/agentic_split.py", + "prompt": "pdd/prompts/agentic_split_python.prompt", + "meta": ".pdd/meta/agentic_split_python.json", + "code_last": "2026-05-25T15:39:43-07:00", + "prompt_last": "2026-05-07T20:02:18-07:00", + "code_ahead": true, + "days_ahead": 17.82, + "churn_lines": 343, + "churn_commits": 4, + "both_commits": 3, + "prompt_only_commits": 1, + "last_code_only_date": "2026-05-25T15:39:43-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": false, + "code_loc": 456, + "rank": 27 + }, + { + "basename": "story_regression", + "code": "pdd/story_regression.py", + "prompt": "pdd/prompts/story_regression_python.prompt", + "meta": ".pdd/meta/story_regression_python.json", + "code_last": "2026-07-02T12:08:00-07:00", + "prompt_last": "2026-06-29T12:31:20-07:00", + "code_ahead": true, + "days_ahead": 2.98, + "churn_lines": 338, + "churn_commits": 4, + "both_commits": 1, + "prompt_only_commits": 1, + "last_code_only_date": "2026-07-02T12:08:00-07:00", + "last_prompt_only_date": "2026-06-22T23:13:25Z", + "conflict_risk": false, + "code_loc": 362, + "rank": 28 + }, + { + "basename": "server/token_counter", + "code": "pdd/server/token_counter.py", + "prompt": "pdd/prompts/server/token_counter_python.prompt", + "meta": ".pdd/meta/server_token_counter_python.json", + "code_last": "2026-07-06T19:47:19-07:00", + "prompt_last": "2026-06-04T14:19:46-07:00", + "code_ahead": true, + "days_ahead": 32.23, + "churn_lines": 337, + "churn_commits": 6, + "both_commits": 4, + "prompt_only_commits": 2, + "last_code_only_date": "2026-07-06T19:47:19-07:00", + "last_prompt_only_date": "2026-05-25T15:52:33-07:00", + "conflict_risk": false, + "code_loc": 645, + "rank": 29 + }, + { + "basename": "architecture_include_validation", + "code": "pdd/architecture_include_validation.py", + "prompt": "pdd/prompts/architecture_include_validation_python.prompt", + "meta": ".pdd/meta/architecture_include_validation_python.json", + "code_last": "2026-05-27T20:33:13-07:00", + "prompt_last": "2026-05-27T17:19:07-07:00", + "code_ahead": true, + "days_ahead": 0.13, + "churn_lines": 332, + "churn_commits": 7, + "both_commits": 3, + "prompt_only_commits": 3, + "last_code_only_date": "2026-05-27T20:33:13-07:00", + "last_prompt_only_date": "2026-05-18T13:22:45-07:00", + "conflict_risk": false, + "code_loc": 770, + "rank": 30 + }, + { + "basename": "checkup_prompt_apply", + "code": "pdd/checkup_prompt_apply.py", + "prompt": "pdd/prompts/checkup_prompt_apply_python.prompt", + "meta": ".pdd/meta/checkup_prompt_apply_python.json", + "code_last": "2026-06-10T16:12:44-07:00", + "prompt_last": "2026-06-10T00:36:59-07:00", + "code_ahead": true, + "days_ahead": 0.65, + "churn_lines": 313, + "churn_commits": 2, + "both_commits": 2, + "prompt_only_commits": 0, + "last_code_only_date": "2026-06-10T16:12:44-07:00", + "last_prompt_only_date": null, + "conflict_risk": false, + "code_loc": 332, + "rank": 31 + }, + { + "basename": "agentic_architecture_orchestrator", + "code": "pdd/agentic_architecture_orchestrator.py", + "prompt": "pdd/prompts/agentic_architecture_orchestrator_python.prompt", + "meta": ".pdd/meta/agentic_architecture_orchestrator_python.json", + "code_last": "2026-06-02T22:49:32Z", + "prompt_last": "2026-05-07T20:15:16Z", + "code_ahead": true, + "days_ahead": 26.11, + "churn_lines": 308, + "churn_commits": 9, + "both_commits": 1, + "prompt_only_commits": 4, + "last_code_only_date": "2026-06-02T22:49:32Z", + "last_prompt_only_date": "2026-05-07T20:15:16Z", + "conflict_risk": true, + "code_loc": 1946, + "rank": 32 + }, + { + "basename": "agentic_test_generate", + "code": "pdd/agentic_test_generate.py", + "prompt": "pdd/prompts/agentic_test_generate_python.prompt", + "meta": ".pdd/meta/agentic_test_generate_python.json", + "code_last": "2026-06-23T17:41:58-07:00", + "prompt_last": "2026-05-18T12:50:30-07:00", + "code_ahead": true, + "days_ahead": 36.2, + "churn_lines": 293, + "churn_commits": 5, + "both_commits": 3, + "prompt_only_commits": 3, + "last_code_only_date": "2026-06-23T17:41:58-07:00", + "last_prompt_only_date": "2026-05-17T18:17:01-07:00", + "conflict_risk": false, + "code_loc": 499, + "rank": 33 + }, + { + "basename": "checkup_interactive_session", + "code": "pdd/checkup_interactive_session.py", + "prompt": "pdd/prompts/checkup_interactive_session_python.prompt", + "meta": ".pdd/meta/checkup_interactive_session_python.json", + "code_last": "2026-06-09T15:08:22-07:00", + "prompt_last": "2026-06-09T12:22:58-07:00", + "code_ahead": true, + "days_ahead": 0.11, + "churn_lines": 260, + "churn_commits": 2, + "both_commits": 1, + "prompt_only_commits": 1, + "last_code_only_date": "2026-06-09T15:08:22-07:00", + "last_prompt_only_date": "2026-06-09T12:22:58-07:00", + "conflict_risk": true, + "code_loc": 130, + "rank": 34 + }, + { + "basename": "checkup_file_selection", + "code": "pdd/checkup_file_selection.py", + "prompt": "pdd/prompts/checkup_file_selection_python.prompt", + "meta": ".pdd/meta/checkup_file_selection_python.json", + "code_last": "2026-05-27T21:31:30-07:00", + "prompt_last": "2026-05-27T14:31:23-07:00", + "code_ahead": true, + "days_ahead": 0.29, + "churn_lines": 243, + "churn_commits": 3, + "both_commits": 1, + "prompt_only_commits": 0, + "last_code_only_date": "2026-05-27T21:31:30-07:00", + "last_prompt_only_date": null, + "conflict_risk": false, + "code_loc": 259, + "rank": 35 + }, + { + "basename": "checkup_planner", + "code": "pdd/checkup_planner.py", + "prompt": "pdd/prompts/checkup_planner_python.prompt", + "meta": ".pdd/meta/checkup_planner_python.json", + "code_last": "2026-06-10T14:45:41-07:00", + "prompt_last": "2026-06-10T10:13:54-07:00", + "code_ahead": true, + "days_ahead": 0.19, + "churn_lines": 238, + "churn_commits": 4, + "both_commits": 0, + "prompt_only_commits": 3, + "last_code_only_date": "2026-06-10T14:45:41-07:00", + "last_prompt_only_date": "2026-06-10T10:13:54-07:00", + "conflict_risk": true, + "code_loc": 207, + "rank": 36 + }, + { + "basename": "core/errors", + "code": "pdd/core/errors.py", + "prompt": "pdd/prompts/core/errors_python.prompt", + "meta": ".pdd/meta/core_errors_python.json", + "code_last": "2026-07-06T16:17:24-07:00", + "prompt_last": "2026-06-20T13:56:42-07:00", + "code_ahead": true, + "days_ahead": 16.1, + "churn_lines": 232, + "churn_commits": 5, + "both_commits": 1, + "prompt_only_commits": 2, + "last_code_only_date": "2026-07-06T16:17:24-07:00", + "last_prompt_only_date": "2026-05-25T15:52:33-07:00", + "conflict_risk": false, + "code_loc": 259, + "rank": 37 + }, + { + "basename": "server/routes/architecture", + "code": "pdd/server/routes/architecture.py", + "prompt": "pdd/prompts/server/routes/architecture_python.prompt", + "meta": ".pdd/meta/server_routes_architecture_python.json", + "code_last": "2026-06-01T18:53:07-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 31.8, + "churn_lines": 227, + "churn_commits": 2, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-06-01T18:53:07-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 480, + "rank": 38 + }, + { + "basename": "checkup_simplify_engines", + "code": "pdd/checkup_simplify_engines.py", + "prompt": "pdd/prompts/checkup_simplify_engines_python.prompt", + "meta": ".pdd/meta/checkup_simplify_engines_python.json", + "code_last": "2026-05-28T14:46:46-07:00", + "prompt_last": "2026-05-28T14:01:52-07:00", + "code_ahead": true, + "days_ahead": 0.03, + "churn_lines": 203, + "churn_commits": 2, + "both_commits": 1, + "prompt_only_commits": 0, + "last_code_only_date": "2026-05-28T14:46:46-07:00", + "last_prompt_only_date": null, + "conflict_risk": false, + "code_loc": 201, + "rank": 39 + }, + { + "basename": "path_resolution", + "code": "pdd/path_resolution.py", + "prompt": "pdd/prompts/path_resolution_python.prompt", + "meta": ".pdd/meta/path_resolution_python.json", + "code_last": "2026-05-26T20:22:50-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 25.86, + "churn_lines": 182, + "churn_commits": 8, + "both_commits": 0, + "prompt_only_commits": 2, + "last_code_only_date": "2026-05-26T20:22:50-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 188, + "rank": 40 + }, + { + "basename": "config_resolution", + "code": "pdd/config_resolution.py", + "prompt": "pdd/prompts/config_resolution_python.prompt", + "meta": ".pdd/meta/config_resolution_python.json", + "code_last": "2026-06-18T18:00:49-07:00", + "prompt_last": "2026-06-02T20:43:19Z", + "code_ahead": true, + "days_ahead": 16.18, + "churn_lines": 182, + "churn_commits": 6, + "both_commits": 1, + "prompt_only_commits": 2, + "last_code_only_date": "2026-06-18T18:00:49-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": false, + "code_loc": 201, + "rank": 41 + }, + { + "basename": "get_language", + "code": "pdd/get_language.py", + "prompt": "pdd/prompts/get_language_python.prompt", + "meta": ".pdd/meta/get_language_python.json", + "code_last": "2026-05-26T13:08:10-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 25.56, + "churn_lines": 128, + "churn_commits": 5, + "both_commits": 0, + "prompt_only_commits": 2, + "last_code_only_date": "2026-05-26T13:08:10-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 42, + "rank": 42 + }, + { + "basename": "get_test_command", + "code": "pdd/get_test_command.py", + "prompt": "pdd/prompts/get_test_command_python.prompt", + "meta": ".pdd/meta/get_test_command_python.json", + "code_last": "2026-05-26T13:08:10-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 25.56, + "churn_lines": 127, + "churn_commits": 6, + "both_commits": 0, + "prompt_only_commits": 2, + "last_code_only_date": "2026-05-26T13:08:10-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 123, + "rank": 43 + }, + { + "basename": "grounding_policy", + "code": "pdd/grounding_policy.py", + "prompt": "pdd/prompts/grounding_policy_python.prompt", + "meta": ".pdd/meta/grounding_policy_python.json", + "code_last": "2026-05-29T09:21:44-07:00", + "prompt_last": "2026-05-28T20:08:35-07:00", + "code_ahead": true, + "days_ahead": 0.55, + "churn_lines": 127, + "churn_commits": 2, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-05-29T09:21:44-07:00", + "last_prompt_only_date": "2026-05-28T20:08:35-07:00", + "conflict_risk": true, + "code_loc": 119, + "rank": 44 + }, + { + "basename": "commands/context", + "code": "pdd/commands/context.py", + "prompt": "pdd/prompts/commands/context_python.prompt", + "meta": ".pdd/meta/commands_context_python.json", + "code_last": "2026-06-17T14:11:15-07:00", + "prompt_last": "2026-06-12T15:38:36-07:00", + "code_ahead": true, + "days_ahead": 4.94, + "churn_lines": 121, + "churn_commits": 4, + "both_commits": 10, + "prompt_only_commits": 0, + "last_code_only_date": "2026-06-17T14:11:15-07:00", + "last_prompt_only_date": null, + "conflict_risk": false, + "code_loc": 397, + "rank": 45 + }, + { + "basename": "commands/contracts", + "code": "pdd/commands/contracts.py", + "prompt": "pdd/prompts/commands/contracts_python.prompt", + "meta": ".pdd/meta/commands_contracts_python.json", + "code_last": "2026-06-01T20:53:52-07:00", + "prompt_last": "2026-05-27T12:05:21-07:00", + "code_ahead": true, + "days_ahead": 5.37, + "churn_lines": 106, + "churn_commits": 3, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-06-01T20:53:52-07:00", + "last_prompt_only_date": "2026-05-27T12:05:21-07:00", + "conflict_risk": true, + "code_loc": 94, + "rank": 46 + }, + { + "basename": "auto_include", + "code": "pdd/auto_include.py", + "prompt": "pdd/prompts/auto_include_python.prompt", + "meta": ".pdd/meta/auto_include_python.json", + "code_last": "2026-06-02T19:12:39-07:00", + "prompt_last": "2026-06-02T22:49:32Z", + "code_ahead": true, + "days_ahead": 0.14, + "churn_lines": 106, + "churn_commits": 2, + "both_commits": 0, + "prompt_only_commits": 4, + "last_code_only_date": "2026-06-02T19:12:39-07:00", + "last_prompt_only_date": "2026-06-02T22:49:32Z", + "conflict_risk": true, + "code_loc": 551, + "rank": 47 + }, + { + "basename": "change_main", + "code": "pdd/change_main.py", + "prompt": "pdd/prompts/change_main_python.prompt", + "meta": ".pdd/meta/change_main_python.json", + "code_last": "2026-06-10T14:45:41-07:00", + "prompt_last": "2026-05-07T17:01:09-07:00", + "code_ahead": true, + "days_ahead": 33.91, + "churn_lines": 92, + "churn_commits": 11, + "both_commits": 1, + "prompt_only_commits": 2, + "last_code_only_date": "2026-06-10T14:45:41-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": false, + "code_loc": 641, + "rank": 48 + }, + { + "basename": "operation_log", + "code": "pdd/operation_log.py", + "prompt": "pdd/prompts/operation_log_python.prompt", + "meta": ".pdd/meta/operation_log_python.json", + "code_last": "2026-06-11T17:45:56-07:00", + "prompt_last": "2026-06-03T02:01:41Z", + "code_ahead": true, + "days_ahead": 8.95, + "churn_lines": 89, + "churn_commits": 3, + "both_commits": 4, + "prompt_only_commits": 3, + "last_code_only_date": "2026-06-11T17:45:56-07:00", + "last_prompt_only_date": "2026-06-01T18:25:33Z", + "conflict_risk": false, + "code_loc": 857, + "rank": 49 + }, + { + "basename": "checkup_gates", + "code": "pdd/checkup_gates.py", + "prompt": "pdd/prompts/checkup_gates_python.prompt", + "meta": ".pdd/meta/checkup_gates_python.json", + "code_last": "2026-06-08T17:50:39-07:00", + "prompt_last": "2026-06-06T12:31:51-07:00", + "code_ahead": true, + "days_ahead": 2.22, + "churn_lines": 79, + "churn_commits": 5, + "both_commits": 11, + "prompt_only_commits": 0, + "last_code_only_date": "2026-06-08T17:50:39-07:00", + "last_prompt_only_date": null, + "conflict_risk": false, + "code_loc": 3975, + "rank": 50 + }, + { + "basename": "commands/utility", + "code": "pdd/commands/utility.py", + "prompt": "pdd/prompts/commands/utility_python.prompt", + "meta": ".pdd/meta/commands_utility_python.json", + "code_last": "2026-05-27T18:34:51-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 26.79, + "churn_lines": 75, + "churn_commits": 3, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-05-27T18:34:51-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 138, + "rank": 51 + }, + { + "basename": "context_generator_main", + "code": "pdd/context_generator_main.py", + "prompt": "pdd/prompts/context_generator_main_python.prompt", + "meta": ".pdd/meta/context_generator_main_python.json", + "code_last": "2026-06-01T14:54:50-07:00", + "prompt_last": "2026-06-01T14:22:07-07:00", + "code_ahead": true, + "days_ahead": 0.02, + "churn_lines": 67, + "churn_commits": 3, + "both_commits": 6, + "prompt_only_commits": 6, + "last_code_only_date": "2026-06-01T14:54:50-07:00", + "last_prompt_only_date": "2026-06-01T19:05:26Z", + "conflict_risk": true, + "code_loc": 302, + "rank": 52 + }, + { + "basename": "detect_change_main", + "code": "pdd/detect_change_main.py", + "prompt": "pdd/prompts/detect_change_main_python.prompt", + "meta": ".pdd/meta/detect_change_main_python.json", + "code_last": "2026-06-11T11:32:23-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 41.49, + "churn_lines": 60, + "churn_commits": 1, + "both_commits": 0, + "prompt_only_commits": 2, + "last_code_only_date": "2026-06-11T11:32:23-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 134, + "rank": 53 + }, + { + "basename": "server/__init__", + "code": "pdd/commands/__init__.py", + "prompt": "pdd/prompts/server/__init___python.prompt", + "meta": null, + "code_last": "2026-07-09T04:59:18-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 69.22, + "churn_lines": 46, + "churn_commits": 18, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-07-09T04:59:18-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 68, + "rank": 54 + }, + { + "basename": "server/routes/__init__", + "code": "pdd/commands/__init__.py", + "prompt": "pdd/prompts/server/routes/__init___python.prompt", + "meta": null, + "code_last": "2026-07-09T04:59:18-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 69.22, + "churn_lines": 46, + "churn_commits": 18, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-07-09T04:59:18-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 68, + "rank": 55 + }, + { + "basename": "cli", + "code": "pdd/cli.py", + "prompt": "pdd/prompts/cli_python.prompt", + "meta": null, + "code_last": "2026-06-05T14:24:53-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 35.61, + "churn_lines": 44, + "churn_commits": 10, + "both_commits": 0, + "prompt_only_commits": 2, + "last_code_only_date": "2026-06-05T14:24:53-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 84, + "rank": 56 + }, + { + "basename": "preprocess_main", + "code": "pdd/preprocess_main.py", + "prompt": "pdd/prompts/preprocess_main_python.prompt", + "meta": ".pdd/meta/preprocess_main_python.json", + "code_last": "2026-06-03T14:03:59-07:00", + "prompt_last": "2026-06-02T22:49:32Z", + "code_ahead": true, + "days_ahead": 0.93, + "churn_lines": 39, + "churn_commits": 4, + "both_commits": 3, + "prompt_only_commits": 3, + "last_code_only_date": "2026-06-03T14:03:59-07:00", + "last_prompt_only_date": "2026-05-05T23:50:27-07:00", + "conflict_risk": false, + "code_loc": 156, + "rank": 57 + }, + { + "basename": "commands/__init__", + "code": "pdd/commands/__init__.py", + "prompt": "pdd/prompts/commands/__init___python.prompt", + "meta": null, + "code_last": "2026-07-09T04:59:18-07:00", + "prompt_last": "2026-07-03T09:56:47-07:00", + "code_ahead": true, + "days_ahead": 5.79, + "churn_lines": 38, + "churn_commits": 15, + "both_commits": 3, + "prompt_only_commits": 11, + "last_code_only_date": "2026-07-09T04:59:18-07:00", + "last_prompt_only_date": "2026-07-03T09:56:47-07:00", + "conflict_risk": true, + "code_loc": 68, + "rank": 58 + }, + { + "basename": "agentic_fix", + "code": "pdd/agentic_fix.py", + "prompt": "pdd/prompts/agentic_fix_python.prompt", + "meta": ".pdd/meta/agentic_fix_python.json", + "code_last": "2026-06-23T17:41:58-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 53.75, + "churn_lines": 35, + "churn_commits": 3, + "both_commits": 0, + "prompt_only_commits": 2, + "last_code_only_date": "2026-06-23T17:41:58-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 461, + "rank": 59 + }, + { + "basename": "cmd_test_main", + "code": "pdd/cmd_test_main.py", + "prompt": "pdd/prompts/cmd_test_main_python.prompt", + "meta": ".pdd/meta/cmd_test_main_python.json", + "code_last": "2026-06-03T14:34:08-07:00", + "prompt_last": "2026-06-03T02:01:41Z", + "code_ahead": true, + "days_ahead": 0.81, + "churn_lines": 25, + "churn_commits": 2, + "both_commits": 3, + "prompt_only_commits": 2, + "last_code_only_date": "2026-06-03T14:34:08-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": false, + "code_loc": 621, + "rank": 60 + }, + { + "basename": "get_extension", + "code": "pdd/get_extension.py", + "prompt": "pdd/prompts/get_extension_python.prompt", + "meta": ".pdd/meta/get_extension_python.json", + "code_last": "2026-05-26T13:08:10-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 25.56, + "churn_lines": 20, + "churn_commits": 2, + "both_commits": 0, + "prompt_only_commits": 2, + "last_code_only_date": "2026-05-26T13:08:10-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 65, + "rank": 61 + }, + { + "basename": "get_run_command", + "code": "pdd/get_run_command.py", + "prompt": "pdd/prompts/get_run_command_python.prompt", + "meta": ".pdd/meta/get_run_command_python.json", + "code_last": "2026-05-26T13:08:10-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 25.56, + "churn_lines": 20, + "churn_commits": 2, + "both_commits": 0, + "prompt_only_commits": 2, + "last_code_only_date": "2026-05-26T13:08:10-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 75, + "rank": 62 + }, + { + "basename": "postprocess_0", + "code": "pdd/postprocess_0.py", + "prompt": "pdd/prompts/postprocess_0_python.prompt", + "meta": ".pdd/meta/postprocess_0_python.json", + "code_last": "2026-05-25T15:39:43-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 24.67, + "churn_lines": 20, + "churn_commits": 2, + "both_commits": 0, + "prompt_only_commits": 2, + "last_code_only_date": "2026-05-25T15:39:43-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 52, + "rank": 63 + }, + { + "basename": "pddrc_initializer", + "code": "pdd/pddrc_initializer.py", + "prompt": "pdd/prompts/pddrc_initializer_python.prompt", + "meta": ".pdd/meta/pddrc_initializer_python.json", + "code_last": "2026-05-25T15:39:43-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 24.67, + "churn_lines": 19, + "churn_commits": 3, + "both_commits": 0, + "prompt_only_commits": 2, + "last_code_only_date": "2026-05-25T15:39:43-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 522, + "rank": 64 + }, + { + "basename": "agentic_update", + "code": "pdd/agentic_update.py", + "prompt": "pdd/prompts/agentic_update_python.prompt", + "meta": ".pdd/meta/agentic_update_python.json", + "code_last": "2026-06-23T17:41:58-07:00", + "prompt_last": "2026-05-23T19:18:25Z", + "code_ahead": true, + "days_ahead": 31.22, + "churn_lines": 10, + "churn_commits": 2, + "both_commits": 1, + "prompt_only_commits": 5, + "last_code_only_date": "2026-06-23T17:41:58-07:00", + "last_prompt_only_date": "2026-05-23T19:18:25Z", + "conflict_risk": true, + "code_loc": 578, + "rank": 65 + }, + { + "basename": "agentic_crash", + "code": "pdd/agentic_crash.py", + "prompt": "pdd/prompts/agentic_crash_python.prompt", + "meta": ".pdd/meta/agentic_crash_python.json", + "code_last": "2026-06-23T17:41:58-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 53.75, + "churn_lines": 9, + "churn_commits": 1, + "both_commits": 0, + "prompt_only_commits": 2, + "last_code_only_date": "2026-06-23T17:41:58-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 562, + "rank": 66 + }, + { + "basename": "template_registry", + "code": "pdd/template_registry.py", + "prompt": "pdd/prompts/template_registry_python.prompt", + "meta": ".pdd/meta/template_registry_python.json", + "code_last": "2026-05-25T18:10:25-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 24.77, + "churn_lines": 8, + "churn_commits": 1, + "both_commits": 0, + "prompt_only_commits": 2, + "last_code_only_date": "2026-05-25T18:10:25-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 258, + "rank": 67 + }, + { + "basename": "failure_classification", + "code": "pdd/failure_classification.py", + "prompt": "pdd/prompts/failure_classification_python.prompt", + "meta": ".pdd/meta/failure_classification_python.json", + "code_last": "2026-06-16T00:56:22Z", + "prompt_last": "2026-06-15T22:08:50Z", + "code_ahead": true, + "days_ahead": 0.12, + "churn_lines": 7, + "churn_commits": 1, + "both_commits": 0, + "prompt_only_commits": 3, + "last_code_only_date": "2026-06-16T00:56:22Z", + "last_prompt_only_date": "2026-06-15T22:08:50Z", + "conflict_risk": true, + "code_loc": 109, + "rank": 68 + }, + { + "basename": "server/routes/commands", + "code": "pdd/server/routes/commands.py", + "prompt": "pdd/prompts/server/routes/commands_python.prompt", + "meta": ".pdd/meta/server_routes_commands_python.json", + "code_last": "2026-05-07T21:31:28-07:00", + "prompt_last": "2026-04-30T23:40:47-07:00", + "code_ahead": true, + "days_ahead": 6.91, + "churn_lines": 6, + "churn_commits": 2, + "both_commits": 0, + "prompt_only_commits": 1, + "last_code_only_date": "2026-05-07T21:31:28-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": true, + "code_loc": 929, + "rank": 69 + }, + { + "basename": "agentic_bug", + "code": "pdd/agentic_bug.py", + "prompt": "pdd/prompts/agentic_bug_python.prompt", + "meta": ".pdd/meta/agentic_bug_python.json", + "code_last": "2026-06-01T20:48:24-07:00", + "prompt_last": "2026-05-25T16:15:33-07:00", + "code_ahead": true, + "days_ahead": 7.19, + "churn_lines": 5, + "churn_commits": 2, + "both_commits": 1, + "prompt_only_commits": 2, + "last_code_only_date": "2026-06-01T20:48:24-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": false, + "code_loc": 329, + "rank": 70 + }, + { + "basename": "commands/misc", + "code": "pdd/commands/misc.py", + "prompt": "pdd/prompts/commands/misc_python.prompt", + "meta": ".pdd/meta/commands_misc_python.json", + "code_last": "2026-06-01T20:31:32-07:00", + "prompt_last": "2026-05-29T01:16:35Z", + "code_ahead": true, + "days_ahead": 4.09, + "churn_lines": 5, + "churn_commits": 1, + "both_commits": 1, + "prompt_only_commits": 1, + "last_code_only_date": "2026-06-01T20:31:32-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": false, + "code_loc": 98, + "rank": 71 + }, + { + "basename": "cli_status", + "code": "pdd/cli_status.py", + "prompt": "pdd/prompts/cli_status_python.prompt", + "meta": ".pdd/meta/cli_status_python.json", + "code_last": "2026-06-12T15:38:36-07:00", + "prompt_last": "2026-06-11T11:32:23-07:00", + "code_ahead": true, + "days_ahead": 1.17, + "churn_lines": 4, + "churn_commits": 1, + "both_commits": 1, + "prompt_only_commits": 0, + "last_code_only_date": "2026-06-12T15:38:36-07:00", + "last_prompt_only_date": null, + "conflict_risk": false, + "code_loc": 268, + "rank": 72 + }, + { + "basename": "split", + "code": "pdd/split.py", + "prompt": "pdd/prompts/split_python.prompt", + "meta": ".pdd/meta/split_python.json", + "code_last": "2026-06-02T22:49:32Z", + "prompt_last": "2026-05-06T00:19:27-07:00", + "code_ahead": true, + "days_ahead": 27.65, + "churn_lines": 2, + "churn_commits": 1, + "both_commits": 0, + "prompt_only_commits": 4, + "last_code_only_date": "2026-06-02T22:49:32Z", + "last_prompt_only_date": "2026-05-06T00:19:27-07:00", + "conflict_risk": true, + "code_loc": 131, + "rank": 73 + }, + { + "basename": "core/utils", + "code": "pdd/core/utils.py", + "prompt": "pdd/prompts/core/utils_python.prompt", + "meta": ".pdd/meta/core_utils_python.json", + "code_last": "2026-06-05T15:31:37-07:00", + "prompt_last": "2026-05-25T15:52:33-07:00", + "code_ahead": true, + "days_ahead": 10.99, + "churn_lines": 2, + "churn_commits": 1, + "both_commits": 1, + "prompt_only_commits": 1, + "last_code_only_date": "2026-06-05T15:31:37-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": false, + "code_loc": 118, + "rank": 74 + }, + { + "basename": "agentic_test", + "code": "pdd/agentic_test.py", + "prompt": "pdd/prompts/agentic_test_python.prompt", + "meta": ".pdd/meta/agentic_test_python.json", + "code_last": "2026-06-01T20:48:24-07:00", + "prompt_last": "2026-05-25T16:15:33-07:00", + "code_ahead": true, + "days_ahead": 7.19, + "churn_lines": 1, + "churn_commits": 1, + "both_commits": 1, + "prompt_only_commits": 2, + "last_code_only_date": "2026-06-01T20:48:24-07:00", + "last_prompt_only_date": "2026-04-30T23:40:47-07:00", + "conflict_risk": false, + "code_loc": 312, + "rank": 75 + } + ] +} diff --git a/scripts/stamp_fingerprints.py b/scripts/stamp_fingerprints.py new file mode 100644 index 0000000000..874b8c6e01 --- /dev/null +++ b/scripts/stamp_fingerprints.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 +"""Stamp / verify ``.pdd/meta`` fingerprints for every PDD dev unit. + +Thin wrapper over ``pdd.continuous_sync`` (issue #1927: one implementation shared +with ``pdd reconcile``). The committed ``.pdd/meta/.json`` fingerprints are +the oracle ``pdd sync`` trusts to decide whether a unit changed; this script keeps +that oracle current for the whole tree and verifies it in CI: + +* ``stamp`` (default): for every stampable unit, recompute all hashes from the + files on disk and write the fingerprint JSON byte-identically to how ``pdd`` + writes it. Idempotent — a unit whose content hashes already match is not + rewritten (its timestamp is preserved and no file is written), so a repo-wide + restamp touches only genuinely-changed units. + +* ``--check``: recompute every stampable unit's hashes and compare to the committed + fingerprint; exit non-zero listing units that are stale or missing a fingerprint. + This is the CI gate (``.github/workflows/unit-tests.yml``); its output shape and + exit codes are unchanged. + +Hashing and unit/path resolution live in ``pdd.continuous_sync`` and use pdd's real +hash functions, so a fingerprint this script writes equals what ``pdd sync`` would +compute. Historically this file vendored a stdlib-only copy of pdd's hashing to run +without importing pdd; issue #1927 removed that second implementation, so the +script now imports pdd (CI installs the package before running the gate). +""" +from __future__ import annotations + +import argparse +import json +import sys +from pathlib import Path +from typing import Dict, List, Optional + +# Ensure the repo root is importable when run as ``scripts/stamp_fingerprints.py`` +# (CI also ``pip install -e``s the package, but this keeps a bare checkout working). +REPO_ROOT = Path(__file__).resolve().parent.parent +if str(REPO_ROOT) not in sys.path: + sys.path.insert(0, str(REPO_ROOT)) + +from pdd import continuous_sync as _cs # pylint: disable=wrong-import-position,no-name-in-module + +# Re-exported for callers/tests that imported these from the stamper. +PROMPTS_ROOT = REPO_ROOT / "pdd" / "prompts" +META_DIR = REPO_ROOT / ".pdd" / "meta" +WAIVERS_FILE = REPO_ROOT / "scripts" / "fingerprint_waivers.json" +FIELD_ORDER = _cs.FIELD_ORDER +HASH_FIELDS = _cs.HASH_FIELDS +NEW_UNIT_COMMAND = _cs.NEW_UNIT_COMMAND + + +# --- Resolution / enumeration (delegated to pdd.continuous_sync) -------------- + + +def Unit(basename: str, prompt: Optional[Path] = None): # pylint: disable=invalid-name + """Resolve one unit against the repo layout (compat shim for the stamper API). + + ``prompt`` defaults to the conventional ``pdd/prompts/_python.prompt``. + """ + return _cs.resolve_unit(basename, REPO_ROOT, prompt) + + +def enumerate_units(): + """Return every python unit under ``pdd/prompts`` sorted by basename.""" + return _cs.resolve_units(REPO_ROOT) + + +def load_pddignore() -> List[str]: + """Load ``.pddignore`` patterns from the repo root.""" + return _cs.load_pddignore(REPO_ROOT) + + +def load_waivers() -> Dict[str, str]: + """Return ``{basename: reason}`` from the committed waiver file.""" + return _cs.load_waivers(WAIVERS_FILE) + + +def classify(units, pddignore, waivers): + """Split units into ``(stampable, ignored, waived, no_code)`` buckets.""" + return _cs.partition_units(units, pddignore, waivers, REPO_ROOT) + + +def read_meta(meta_path: Path) -> Optional[Dict]: + """Read an existing fingerprint JSON, or None if absent/unreadable.""" + meta_path = Path(meta_path) + if not meta_path.exists(): + return None + try: + data = json.loads(meta_path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError): + return None + return data if isinstance(data, dict) else None + + +def compute_hashes(unit, stored_deps: Optional[Dict[str, str]] = None) -> Dict: + """Recompute all fingerprint hash fields for a unit via pdd's real hasher.""" + return _cs.hashes_for(unit, REPO_ROOT, stored_deps) + + +def write_meta(meta_path: Path, content: Dict) -> None: + """Write meta JSON byte-identically to pdd (indent=2, no trailing newline).""" + meta_path = Path(meta_path) + meta_path.parent.mkdir(parents=True, exist_ok=True) + with open(meta_path, "w", encoding="utf-8") as handle: + json.dump(content, handle, indent=2) + + +# --- Commands ---------------------------------------------------------------- + + +def cmd_stamp(_args: argparse.Namespace) -> int: + """Recompute and write fingerprints for every stampable unit (idempotent).""" + units = enumerate_units() + pddignore = load_pddignore() + waivers = load_waivers() + stampable, ignored, waived, no_code = classify(units, pddignore, waivers) + + written = len(_cs.stamp_units(stampable, REPO_ROOT)) + + print( + f"stamped {written} of {len(stampable)} units " + f"(unchanged {len(stampable) - written}); " + f"ignored={len(ignored)} waived={len(waived)} no_code={len(no_code)}" + ) + if no_code: + print("WARNING: units with no code file (add to waivers):", file=sys.stderr) + for unit in no_code: + print(f" {unit.basename} -> {unit.code}", file=sys.stderr) + return 1 + return 0 + + +def cmd_check(_args: argparse.Namespace) -> int: + """Recompute hashes and diff against committed metas. Non-zero on drift.""" + result = _cs.run_check() + print( + f"checked {result.stampable} stampable units; " + f"ignored={result.ignored} waived={result.waived}" + ) + if result.no_code: + print( + f"\n{len(result.no_code)} unit(s) have no code file and no waiver:", + file=sys.stderr, + ) + for name in result.no_code: + print(f" {name}", file=sys.stderr) + if result.missing: + print( + f"\n{len(result.missing)} unit(s) missing a committed fingerprint:", + file=sys.stderr, + ) + for name in result.missing: + print(f" {name}", file=sys.stderr) + if result.stale: + print( + f"\n{len(result.stale)} unit(s) have stale fingerprints " + f"(run: python scripts/stamp_fingerprints.py):", + file=sys.stderr, + ) + for name, diffs in result.stale: + print(f" {name}: {', '.join(diffs)}", file=sys.stderr) + if result.ok: + print("all fingerprints current") + return 0 + return 1 + + +def main(argv: Optional[List[str]] = None) -> int: + """Stamp (default) or verify (``--check``) the repo's ``.pdd/meta`` fingerprints.""" + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument( + "--check", + action="store_true", + help="Verify committed fingerprints are current; exit non-zero on drift.", + ) + args = parser.parse_args(argv) + if args.check: + return cmd_check(args) + return cmd_stamp(args) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tests/commands/test_maintenance.py b/tests/commands/test_maintenance.py index c26180762d..62f56675fa 100644 --- a/tests/commands/test_maintenance.py +++ b/tests/commands/test_maintenance.py @@ -100,6 +100,51 @@ def test_sync_dry_run(self, runner, base_ctx_obj): assert result.exit_code == 0 assert mock_sm.call_args.kwargs["dry_run"] is True + def test_sync_fresh_flag_forwarded(self, runner, base_ctx_obj): + """`pdd sync --fresh` forwards fresh=True to sync_main; default is + fresh=False so mature modules regenerate surgically (#1938 Pillar A).""" + mock_result = ({"success": True}, 0.0, "none") + cli = _make_cli(sync, base_ctx_obj) + + with patch("pdd.commands.maintenance.sync_main", return_value=mock_result) as mock_sm: + result = runner.invoke(cli, ["sync", "calc", "--fresh"], catch_exceptions=False) + assert result.exit_code == 0 + assert mock_sm.call_args.kwargs["fresh"] is True + + with patch("pdd.commands.maintenance.sync_main", return_value=mock_result) as mock_sm: + result = runner.invoke(cli, ["sync", "calc"], catch_exceptions=False) + assert result.exit_code == 0 + assert mock_sm.call_args.kwargs["fresh"] is False + + def test_sync_fresh_rejected_on_global_sync(self, runner, base_ctx_obj): + """--fresh is single-module only: reject on no-argument global sync + instead of silently dropping it (#1938).""" + cli = _make_cli(sync, base_ctx_obj) + + with patch("pdd.commands.maintenance._run_global_sync_dispatch") as mock_global, \ + patch("pdd.commands.maintenance.run_agentic_sync") as mock_agentic: + result = runner.invoke(cli, ["sync", "--fresh"]) + + assert result.exit_code != 0 + assert "single-module sync" in result.output + mock_global.assert_not_called() + mock_agentic.assert_not_called() + + def test_sync_fresh_rejected_on_agentic_url_sync(self, runner, base_ctx_obj): + """--fresh is single-module only: reject on GitHub-issue agentic sync + instead of silently dropping it (#1938).""" + cli = _make_cli(sync, base_ctx_obj) + + with patch("pdd.commands.maintenance._is_github_issue_url", return_value=True), \ + patch("pdd.commands.maintenance.run_agentic_sync") as mock_agentic: + result = runner.invoke(cli, [ + "sync", "https://github.com/org/repo/issues/7", "--fresh", + ]) + + assert result.exit_code != 0 + assert "single-module sync" in result.output + mock_agentic.assert_not_called() + def test_sync_without_basename_dispatches_global_sync_not_durable( self, runner, diff --git a/tests/test_architecture_completeness.py b/tests/test_architecture_completeness.py new file mode 100644 index 0000000000..70df27f5a9 --- /dev/null +++ b/tests/test_architecture_completeness.py @@ -0,0 +1,280 @@ +"""Bijection gate for architecture.json completeness. + +``architecture.json`` is the module registry that drives project-wide sync: a +no-arg ``pdd sync`` enumerates modules *from* this file +(``pdd.agentic_sync._architecture_sync_modules``), so any top-level ``pdd/*.py`` +module without an entry is invisible to global sync and dependency-ordered heal. + +This test asserts a **bijection**: every top-level ``pdd/*.py`` module (after +applying ``.pddignore``) has exactly one architecture entry, UNLESS it is listed +in ``architecture_waivers.json`` under ``modules_without_architecture_entry`` +(hand-written utilities/CLI wiring with no generating prompt). It also asserts +every entry's ``filepath`` and ``filename`` exist on disk, and ratchets the +adjacent orphan gaps (prompt-no-code, code-no-prompt, code-no-test) so new gaps +must be documented rather than appearing silently. + +To fix a failure: +- module missing an entry -> run ``pdd sync-architecture`` or author the entry, + or (for promptless hand-written modules) add it to the waiver with a reason. +- stale waiver -> delete the line once the module has an entry / is removed. +- new orphan -> add a one-line justification under the relevant ``orphans`` list. +""" +from __future__ import annotations + +import json +from pathlib import Path +from typing import Dict, List, Set + +import pytest + +from pdd.agentic_sync_runner import _basename_from_architecture_filename +from pdd.architecture_registry import extract_modules +from pdd.update_main import _is_pddignored, _load_pddignore + +REPO_ROOT = Path(__file__).resolve().parent.parent +ARCHITECTURE_JSON = REPO_ROOT / "architecture.json" +WAIVERS_JSON = REPO_ROOT / "architecture_waivers.json" + +# Fields present in every architecture entry today (the hard invariant). +REQUIRED_ENTRY_FIELDS = ( + "reason", + "dependencies", + "priority", + "filename", + "filepath", + "tags", + "interface", +) + + +# --------------------------------------------------------------------------- # +# Fixtures / shared computation # +# --------------------------------------------------------------------------- # +@pytest.fixture(scope="module") +def entries() -> List[dict]: + return extract_modules(json.loads(ARCHITECTURE_JSON.read_text(encoding="utf-8"))) + + +@pytest.fixture(scope="module") +def waivers() -> dict: + return json.loads(WAIVERS_JSON.read_text(encoding="utf-8")) + + +def _candidate_modules() -> Dict[str, Path]: + """Top-level ``pdd/*.py`` modules that survive ``.pddignore`` (the sync scope).""" + patterns, pddignore_root = _load_pddignore(str(REPO_ROOT)) + modules: Dict[str, Path] = {} + for path in sorted(REPO_ROOT.glob("pdd/*.py")): + if _is_pddignored(str(path), pddignore_root, patterns): + continue + modules[path.stem] = path + return modules + + +def _covered_toplevel_modules(entries: List[dict]) -> Set[str]: + """Module basenames covered by an entry whose ``filepath`` is ``pdd/.py``.""" + covered: Set[str] = set() + for entry in entries: + filepath = str(entry.get("filepath") or "").replace("\\", "/").strip().lstrip("/") + if not filepath: + continue + path = Path(filepath) + if path.parent == Path("pdd") and path.suffix == ".py": + covered.add(path.stem) + return covered + + +def _prompt_basenames() -> Dict[str, List[str]]: + """Top-level ``prompts/*.prompt`` grouped by module basename (LLM-only skipped).""" + grouped: Dict[str, List[str]] = {} + for prompt in sorted((REPO_ROOT / "prompts").glob("*.prompt")): + base = _basename_from_architecture_filename(prompt.name) + if base: + grouped.setdefault(base, []).append(prompt.name) + return grouped + + +def _resolve_prompt(filename: str) -> bool: + filename = (filename or "").replace("\\", "/").lstrip("/") + if not filename: + return False + candidates = [ + REPO_ROOT / "prompts" / filename, + REPO_ROOT / filename, + REPO_ROOT / "prompts" / Path(filename).name, + REPO_ROOT / "pdd" / "prompts" / filename, + REPO_ROOT / "pdd" / "prompts" / Path(filename).name, + ] + return any(candidate.exists() for candidate in candidates) + + +def _bijection_gap(entries: List[dict], waivers: dict): + """Return (undocumented_missing, stale_waivers) for the module<->entry bijection.""" + modules = set(_candidate_modules()) + covered = _covered_toplevel_modules(entries) & modules + missing = modules - covered + waived = set(waivers.get("modules_without_architecture_entry", {})) + undocumented_missing = sorted(missing - waived) + # stale = waived but the module now has an entry, or no longer exists on disk + stale = sorted((waived & covered) | {m for m in waived if m not in modules}) + return undocumented_missing, stale + + +# --------------------------------------------------------------------------- # +# Structural sanity # +# --------------------------------------------------------------------------- # +def test_architecture_json_is_nonempty_list(entries: List[dict]) -> None: + assert entries, "architecture.json parsed to zero module entries" + + +def test_entries_have_required_fields(entries: List[dict]) -> None: + offenders: List[str] = [] + for entry in entries: + missing = [f for f in REQUIRED_ENTRY_FIELDS if f not in entry] + if missing: + offenders.append(f"{entry.get('filename') or entry.get('filepath')!r}: missing {missing}") + assert not offenders, "architecture entries missing required fields:\n " + "\n ".join(offenders) + + +def test_entries_have_description(entries: List[dict], waivers: dict) -> None: + """Every entry should carry a description, minus documented pre-existing gaps.""" + allowed = set(waivers["known_registry_exceptions"]["entries_without_description"]) + offenders = sorted( + str(e.get("filename")) + for e in entries + if "description" not in e and str(e.get("filename")) not in allowed + ) + assert not offenders, ( + "architecture entries missing 'description' (add one, or document under " + "known_registry_exceptions.entries_without_description):\n " + "\n ".join(offenders) + ) + + +def test_no_duplicate_filepaths(entries: List[dict], waivers: dict) -> None: + """Bijection direction: at most one entry per code file (minus tracked pre-existing dupes).""" + allowed = set(waivers["known_registry_exceptions"]["duplicate_filepaths"]) + seen: Dict[str, int] = {} + for entry in entries: + fp = str(entry.get("filepath") or "").strip() + if fp: + seen[fp] = seen.get(fp, 0) + 1 + dupes = {fp: n for fp, n in seen.items() if n > 1 and fp not in allowed} + assert not dupes, ( + "duplicate filepaths in architecture.json (one entry per module expected; " + "document a known pre-existing dupe under known_registry_exceptions.duplicate_filepaths):\n " + + "\n ".join(f"{fp} x{n}" for fp, n in sorted(dupes.items())) + ) + + +def test_every_entry_points_at_real_files(entries: List[dict]) -> None: + """Every entry's filepath (code) and filename (prompt) must exist on disk.""" + bad_filepath: List[str] = [] + bad_filename: List[str] = [] + for entry in entries: + fp = str(entry.get("filepath") or "").replace("\\", "/").strip().lstrip("/") + if not fp or not (REPO_ROOT / fp).exists(): + bad_filepath.append(f"{entry.get('filename')!r} -> filepath {entry.get('filepath')!r}") + if not _resolve_prompt(entry.get("filename", "")): + bad_filename.append(f"{entry.get('filename')!r} (filepath {entry.get('filepath')!r})") + msg = [] + if bad_filepath: + msg.append("entries whose filepath is missing on disk:\n " + "\n ".join(bad_filepath)) + if bad_filename: + msg.append("entries whose filename prompt is missing on disk:\n " + "\n ".join(bad_filename)) + assert not msg, "\n".join(msg) + + +# --------------------------------------------------------------------------- # +# The bijection gate # +# --------------------------------------------------------------------------- # +def test_module_entry_bijection(entries: List[dict], waivers: dict) -> None: + undocumented_missing, stale = _bijection_gap(entries, waivers) + problems: List[str] = [] + if undocumented_missing: + problems.append( + "top-level pdd/*.py modules with no architecture.json entry and no waiver:\n " + + "\n ".join(undocumented_missing) + + "\n-> run `pdd sync-architecture` / author an entry, or add each to " + "architecture_waivers.json 'modules_without_architecture_entry' with a one-line reason." + ) + if stale: + problems.append( + "stale waivers in architecture_waivers.json 'modules_without_architecture_entry' " + "(module now has an entry or no longer exists):\n " + "\n ".join(stale) + + "\n-> delete these lines." + ) + assert not problems, "\n\n".join(problems) + + +def test_waived_modules_have_no_entry(entries: List[dict], waivers: dict) -> None: + """A waived module must genuinely lack an entry (keeps the waiver honest).""" + covered = _covered_toplevel_modules(entries) + both = sorted(set(waivers.get("modules_without_architecture_entry", {})) & covered) + assert not both, ( + "modules both waived AND present in architecture.json (remove the waiver):\n " + + "\n ".join(both) + ) + + +# --------------------------------------------------------------------------- # +# Orphan ratchet (new gaps must be documented; fixing a gap needs no edit) # +# --------------------------------------------------------------------------- # +def test_prompt_without_code_ratchet(waivers: dict) -> None: + prompt_bases = _prompt_basenames() + computed = {b for b in prompt_bases if not (REPO_ROOT / "pdd" / f"{b}.py").exists()} + documented = set(waivers["orphans"]["prompt_without_code"]) + undocumented = sorted(computed - documented) + assert not undocumented, ( + "prompt(s) with no corresponding pdd/.py not documented in " + "architecture_waivers.json orphans.prompt_without_code:\n " + "\n ".join(undocumented) + ) + + +def test_code_without_prompt_ratchet(waivers: dict) -> None: + modules = _candidate_modules() + prompt_bases = _prompt_basenames() + computed = {m for m in modules if m not in prompt_bases} + documented = set(waivers["orphans"]["code_without_prompt"]) + undocumented = sorted(computed - documented) + assert not undocumented, ( + "module(s) with no top-level prompt not documented in " + "architecture_waivers.json orphans.code_without_prompt:\n " + "\n ".join(undocumented) + ) + + +def test_code_without_test_ratchet(waivers: dict) -> None: + modules = _candidate_modules() + computed = {m for m in modules if not (REPO_ROOT / "tests" / f"test_{m}.py").exists()} + documented = set(waivers["orphans"]["code_without_test"]) + undocumented = sorted(computed - documented) + assert not undocumented, ( + "module(s) with no tests/test_.py not documented in " + "architecture_waivers.json orphans.code_without_test:\n " + "\n ".join(undocumented) + ) + + +# --------------------------------------------------------------------------- # +# Meta: prove the gate has teeth (acceptance criterion) # +# --------------------------------------------------------------------------- # +def test_gate_detects_removed_entry(entries: List[dict], waivers: dict) -> None: + """Deleting a covered module's entry must surface as an undocumented gap.""" + covered = sorted(_covered_toplevel_modules(entries) & set(_candidate_modules())) + assert covered, "expected at least one covered top-level module" + victim = "construct_paths" if "construct_paths" in covered else covered[0] + pruned = [e for e in entries if Path(str(e.get("filepath") or "")).stem != victim] + undocumented_missing, _ = _bijection_gap(pruned, waivers) + assert victim in undocumented_missing, ( + f"bijection gate failed to detect a removed entry for {victim!r}" + ) + + +def test_gate_detects_unwaived_new_module(entries: List[dict], waivers: dict) -> None: + """A module present neither as an entry nor a waiver must fail the gate.""" + phantom = "zzz_phantom_module_for_test" + waived = set(waivers.get("modules_without_architecture_entry", {})) + covered = _covered_toplevel_modules(entries) + # Simulate the gap computation with an extra on-disk module. + modules = set(_candidate_modules()) | {phantom} + missing = modules - (covered & modules) + undocumented = sorted(missing - waived) + assert phantom in undocumented, "bijection gate failed to detect an unwaived new module" diff --git a/tests/test_continuous_sync.py b/tests/test_continuous_sync.py new file mode 100644 index 0000000000..b14302bc00 --- /dev/null +++ b/tests/test_continuous_sync.py @@ -0,0 +1,314 @@ +"""Tests for pdd.continuous_sync — the shared reconcile/stamp core (issue #1927). + +Covers the four reconcile verdicts (current / stamped / conflict / unbaselined), +the --accept-current and --backfill stamping scopes, the issue #884 --json verdict +shape, idempotency (second run is a no-op; a single changed unit touches only its +own fingerprint), the two resolution regressions the #1954 command got wrong +(commands/firecrawl, core/remote_session vs remote_session), and script<->command +parity on the committed tree. +""" +import importlib.util +import json +from pathlib import Path + +import pytest +from click.testing import CliRunner + +from pdd import continuous_sync as cs +from pdd.commands.reconcile import reconcile + +REPO_ROOT = Path(__file__).resolve().parent.parent +STAMPER_PATH = REPO_ROOT / "scripts" / "stamp_fingerprints.py" + +_PDDRC = """version: "1.0" +contexts: + default: + paths: ["**"] + defaults: + generate_output_path: "pdd/" + test_output_path: "tests/" + example_output_path: "context/" + default_language: "python" +""" + + +# --- Fixture repo helpers ---------------------------------------------------- + + +def _make_repo(tmp_path: Path) -> Path: + (tmp_path / ".pddrc").write_text(_PDDRC, encoding="utf-8") + for sub in ("pdd/prompts", "context", "tests", ".pdd/meta"): + (tmp_path / sub).mkdir(parents=True, exist_ok=True) + return tmp_path + + +def _write_unit( # pylint: disable=too-many-arguments + root: Path, + basename: str, + *, + prompt: str = "describe the module\n", + code: str = "VALUE = 1\n", + example: str | None = None, + test: str | None = None, +) -> str: + """Write a flat unit's prompt/code (+optional example/test) into ``root``.""" + (root / "pdd" / "prompts" / f"{basename}_python.prompt").write_text( + prompt, encoding="utf-8" + ) + (root / "pdd" / f"{basename}.py").write_text(code, encoding="utf-8") + if example is not None: + (root / "context" / f"{basename}_example.py").write_text(example, encoding="utf-8") + if test is not None: + (root / "tests" / f"test_{basename}.py").write_text(test, encoding="utf-8") + return basename + + +def _classify_one(root: Path, basename: str) -> dict: + report = cs.build_report(consumer="test", root=root, modules=[basename]) + units = report["units"] + assert units, f"no unit classified for {basename}" + return units[0] + + +# --- Four statuses ----------------------------------------------------------- + + +def test_status_current(tmp_path): + """A baselined, unmodified unit is IN_SYNC / status current.""" + root = _make_repo(tmp_path) + _write_unit(root, "alpha") + cs.stamp_units(cs.resolve_units(root), root) # baseline + verdict = _classify_one(root, "alpha") + assert verdict["classification"] == "IN_SYNC" + assert verdict["status"] == "current" + assert verdict["remediation"] == "" + + +def test_status_stamped_on_heal(tmp_path): + """Single-sided drift is stamped by --heal and becomes current.""" + root = _make_repo(tmp_path) + _write_unit(root, "alpha", code="VALUE = 1\n") + cs.stamp_units(cs.resolve_units(root), root) + (root / "pdd" / "alpha.py").write_text("VALUE = 2 # hotfix\n", encoding="utf-8") + + drift = _classify_one(root, "alpha") + assert drift["classification"] == "CODE_CHANGED" + assert drift["status"] == "stale" + assert drift["remediation"] == "pdd update alpha" + + report = cs.build_report(consumer="test", root=root, modules=["alpha"], heal=True) + assert report["stamped"] == ["alpha"] + assert report["units"][0]["status"] == "stamped" + # Re-classify: now current. + assert _classify_one(root, "alpha")["classification"] == "IN_SYNC" + + +def test_status_conflict_not_stamped_without_accept(tmp_path): + """Prompt AND code both moved is CONFLICT; --heal must not stamp it.""" + root = _make_repo(tmp_path) + _write_unit(root, "alpha", prompt="v0\n", code="VALUE = 1\n") + cs.stamp_units(cs.resolve_units(root), root) + (root / "pdd" / "prompts" / "alpha_python.prompt").write_text("v1\n", encoding="utf-8") + (root / "pdd" / "alpha.py").write_text("VALUE = 2\n", encoding="utf-8") + + verdict = _classify_one(root, "alpha") + assert verdict["classification"] == "CONFLICT" + assert verdict["status"] == "conflict" + assert "--accept-current" in verdict["remediation"] + + # --heal alone leaves a conflict untouched. + healed = cs.build_report(consumer="test", root=root, modules=["alpha"], heal=True) + assert not healed["stamped"] + assert _classify_one(root, "alpha")["classification"] == "CONFLICT" + + +def test_status_unbaselined_not_stamped_without_backfill(tmp_path): + """A unit with no fingerprint is UNBASELINED; --heal must not stamp it.""" + root = _make_repo(tmp_path) + _write_unit(root, "alpha") # never baselined -> no meta + + verdict = _classify_one(root, "alpha") + assert verdict["classification"] == "UNBASELINED" + assert verdict["status"] == "unbaselined" + assert "--backfill" in verdict["remediation"] + + healed = cs.build_report(consumer="test", root=root, modules=["alpha"], heal=True) + assert not healed["stamped"] + assert _classify_one(root, "alpha")["classification"] == "UNBASELINED" + + +# --- Stamping scopes --------------------------------------------------------- + + +def test_accept_current_stamps_only_conflict(tmp_path): + """--accept-current stamps CONFLICT units and nothing else (no --heal).""" + root = _make_repo(tmp_path) + _write_unit(root, "conf", prompt="v0\n", code="C0\n") + _write_unit(root, "drift", code="D0\n") + cs.stamp_units(cs.resolve_units(root), root) + # conf: both sides move -> CONFLICT; drift: code only -> CODE_CHANGED + (root / "pdd" / "prompts" / "conf_python.prompt").write_text("v1\n", encoding="utf-8") + (root / "pdd" / "conf.py").write_text("C1\n", encoding="utf-8") + (root / "pdd" / "drift.py").write_text("D1\n", encoding="utf-8") + + report = cs.build_report(consumer="test", root=root, accept_current=True) + assert report["stamped"] == ["conf"] # drift NOT stamped (heal not set) + assert _classify_one(root, "drift")["classification"] == "CODE_CHANGED" + + +def test_backfill_stamps_only_unbaselined(tmp_path): + """--backfill stamps UNBASELINED units and nothing else (no --heal).""" + root = _make_repo(tmp_path) + _write_unit(root, "fresh") # unbaselined + _write_unit(root, "drift", code="D0\n") + # baseline only 'drift', then drift it + cs.stamp_units([cs.resolve_unit("drift", root)], root) + (root / "pdd" / "drift.py").write_text("D1\n", encoding="utf-8") + + report = cs.build_report(consumer="test", root=root, backfill=True) + assert report["stamped"] == ["fresh"] # drift NOT stamped + assert _classify_one(root, "fresh")["classification"] == "IN_SYNC" + assert _classify_one(root, "drift")["classification"] == "CODE_CHANGED" + + +# --- issue #884 verdict shape ------------------------------------------------ + + +def test_json_verdict_shape(tmp_path): + """Every verdict carries the #884 keys; report is JSON-serialisable.""" + root = _make_repo(tmp_path) + _write_unit(root, "alpha") + cs.stamp_units(cs.resolve_units(root), root) + (root / "pdd" / "prompts" / "alpha_python.prompt").write_text("changed\n", encoding="utf-8") + + report = cs.build_report(consumer="test", root=root, modules=["alpha"]) + json.dumps(report) # must not raise + verdict = report["units"][0] + for key in ("status", "reasons", "affected_artifacts", "remediation", "classification"): + assert key in verdict + assert verdict["classification"] == "PROMPT_CHANGED" + assert verdict["affected_artifacts"] == ["prompt"] + assert isinstance(verdict["reasons"], list) and verdict["reasons"] + assert verdict["remediation"] == "pdd sync alpha" + + +# --- Idempotency ------------------------------------------------------------- + + +def test_second_stamp_run_is_noop(tmp_path): + """A repo-wide stamp writes changed units once; the second run writes nothing.""" + root = _make_repo(tmp_path) + _write_unit(root, "alpha", code="A0\n") + _write_unit(root, "beta", code="B0\n") + cs.stamp_units(cs.resolve_units(root), root) # baseline both + (root / "pdd" / "alpha.py").write_text("A1\n", encoding="utf-8") + + first = cs.stamp_units(cs.resolve_units(root), root) + assert first == ["alpha"] + second = cs.stamp_units(cs.resolve_units(root), root) + assert not second # pdd#1932 DoD: second consecutive run is a no-op + + +def test_single_unit_touch(tmp_path): + """Stamping touches only the changed unit's fingerprint file.""" + root = _make_repo(tmp_path) + for name in ("alpha", "beta", "gamma"): + _write_unit(root, name, code=f"{name} = 0\n") + cs.stamp_units(cs.resolve_units(root), root) + + names = ("alpha", "beta", "gamma") + metas = {name: root / ".pdd" / "meta" / f"{name}_python.json" for name in names} + before = {name: path.read_bytes() for name, path in metas.items()} + (root / "pdd" / "beta.py").write_text("beta = 999\n", encoding="utf-8") + + written = cs.stamp_units(cs.resolve_units(root), root) + assert written == ["beta"] + assert metas["alpha"].read_bytes() == before["alpha"] # untouched + assert metas["gamma"].read_bytes() == before["gamma"] # untouched + assert metas["beta"].read_bytes() != before["beta"] # rewritten + + +# --- Resolution regressions (real repo) -------------------------------------- + + +@pytest.mark.parametrize( + "basename", ["commands/firecrawl", "core/remote_session", "remote_session"] +) +def test_resolution_regression_units_are_current(basename): + """The units #1954 mis-resolved now classify IN_SYNC on the committed tree. + + commands/firecrawl (underscore-basename resolved a stray flat test) and the + core/remote_session vs remote_session leaf collision were the two false + positives; correct resolution clears both. + """ + verdict = _classify_one(REPO_ROOT, basename) + assert verdict["classification"] == "IN_SYNC", verdict["reason"] + + +# --- Script <-> command parity ---------------------------------------------- + + +def _load_stamper(): + spec = importlib.util.spec_from_file_location("stamp_fingerprints", STAMPER_PATH) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +def test_script_and_command_agree_on_clean_tree(): + """The CI stamper and `pdd reconcile --check` agree the committed tree is current.""" + assert cs.run_check(root=REPO_ROOT).ok is True + stamper = _load_stamper() + assert stamper.main(["--check"]) == 0 + + +# --- reconcile CLI surface --------------------------------------------------- + + +def test_cli_check_exit_codes(tmp_path, monkeypatch): + """`pdd reconcile --check` exits 0 when current, non-zero on drift (no writes).""" + root = _make_repo(tmp_path) + _write_unit(root, "alpha", code="A0\n") + cs.stamp_units(cs.resolve_units(root), root) + monkeypatch.chdir(root) + runner = CliRunner() + + ok = runner.invoke(reconcile, ["--check"]) + assert ok.exit_code == 0, ok.output + assert "all fingerprints current" in ok.output + + (root / "pdd" / "alpha.py").write_text("A1\n", encoding="utf-8") + drifted = runner.invoke(reconcile, ["--check"]) + assert drifted.exit_code == 1, drifted.output + + +def test_cli_json_basename(tmp_path, monkeypatch): + """`pdd reconcile BASENAME --json` reports only that unit, with the #884 shape.""" + root = _make_repo(tmp_path) + _write_unit(root, "alpha") + _write_unit(root, "beta") + cs.stamp_units(cs.resolve_units(root), root) + monkeypatch.chdir(root) + + result = CliRunner().invoke(reconcile, ["alpha", "--json"]) + assert result.exit_code == 0, result.output + payload = json.loads(result.output) + assert [u["basename"] for u in payload["units"]] == ["alpha"] + assert payload["units"][0]["status"] == "current" + + +def test_cli_no_match_target_errors(tmp_path, monkeypatch): + """A targeted reconcile that matches no unit must FAIL, not silently pass with 0 + units (#1969 review pass 2 finding 3 — a typo would otherwise exit 0 / ok:true).""" + root = _make_repo(tmp_path) + _write_unit(root, "alpha") + cs.stamp_units(cs.resolve_units(root), root) + monkeypatch.chdir(root) + + for args in (["definitely_not_a_unit", "--check"], ["definitely_not_a_unit", "--check", "--json"]): + result = CliRunner().invoke(reconcile, args) + assert result.exit_code != 0, result.output + assert "No PDD unit matches" in result.output + # A real target still works. + ok = CliRunner().invoke(reconcile, ["alpha", "--check"]) + assert ok.exit_code == 0, ok.output diff --git a/tests/test_resolve_command.py b/tests/test_resolve_command.py new file mode 100644 index 0000000000..32520aba8a --- /dev/null +++ b/tests/test_resolve_command.py @@ -0,0 +1,260 @@ +"""Tests for the ``pdd resolve`` command (#1929). + +``pdd resolve`` finalizes a both-changed CONFLICT unit: +* ``--accept-current`` deterministically stamps the current tree as the baseline; +* ``--prompt-wins`` / ``--code-wins`` preview the not-yet-automated LLM strategies. + +The setup builds a real on-disk unit with a committed fingerprint, then co-edits +the prompt and the code so the unit classifies as CONFLICT — the exact state the +command exists to resolve. +""" +from __future__ import annotations + +import json +import os +from datetime import datetime, timezone +from pathlib import Path +from typing import Tuple + +from click.testing import CliRunner + +from pdd.commands.resolve import resolve +from pdd.sync_determine_operation import ( + calculate_sha256, + get_pdd_file_paths, + read_fingerprint, +) + +BASENAME = "widget" +LANGUAGE = "python" + + +def _write(path: Path, content: str) -> str: + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content, encoding="utf-8") + return calculate_sha256(path) + + +def _make_conflict_unit(base: Path) -> Tuple[str, str, Path]: + """Create a synced unit then co-edit prompt + code so it becomes CONFLICT.""" + os.chdir(base) + (base / ".pdd" / "meta").mkdir(parents=True, exist_ok=True) + (base / "prompts").mkdir(exist_ok=True) + + prompt_hash = _write( + base / "prompts" / f"{BASENAME}_{LANGUAGE}.prompt", + "Generate a simple function.\n", + ) + paths = get_pdd_file_paths(BASENAME, LANGUAGE, prompts_dir="prompts") + code_hash = _write(Path(paths["code"]), "def value():\n return 1\n") + example_hash = _write(Path(paths["example"]), "print(1)\n") + test_hash = _write(Path(paths["test"]), "def test_v():\n assert True\n") + + fp_path = base / ".pdd" / "meta" / f"{BASENAME}_{LANGUAGE}.json" + fp_path.write_text( + json.dumps( + { + "pdd_version": "test", + "timestamp": datetime.now(timezone.utc).isoformat(), + "command": "fix", + "prompt_hash": prompt_hash, + "code_hash": code_hash, + "example_hash": example_hash, + "test_hash": test_hash, + "test_files": {Path(paths["test"]).name: test_hash}, + "include_deps": {}, + } + ), + encoding="utf-8", + ) + + # Co-edit BOTH the prompt and the code -> both-changed CONFLICT. + _write( + base / "prompts" / f"{BASENAME}_{LANGUAGE}.prompt", + "Generate a CHANGED function.\n", + ) + _write(Path(paths["code"]), "def value():\n return 2\n") + return BASENAME, LANGUAGE, fp_path + + +def _make_code_only_drift_unit(base: Path) -> Tuple[str, str, Path]: + """Create a synced unit then edit ONLY the code -> single-sided DRIFT (not CONFLICT).""" + _make_conflict_unit(base) + fp_path = base / ".pdd" / "meta" / f"{BASENAME}_{LANGUAGE}.json" + paths = get_pdd_file_paths(BASENAME, LANGUAGE, prompts_dir="prompts") + # Re-sync by stamping current, then edit only the code so the prompt is unchanged. + runner = CliRunner() + runner.invoke(resolve, [BASENAME, "--accept-current"], obj={}) + _write(Path(paths["code"]), "def value():\n return 99\n") + return BASENAME, LANGUAGE, fp_path + + +def test_accept_current_refuses_code_only_drift(tmp_path): + """#1969 review finding 2: --accept-current must NOT silently baseline single-sided + drift; it directs the user to pdd update / pdd sync instead and does not stamp.""" + runner = CliRunner() + with runner.isolated_filesystem() as tmp: + base = Path(tmp) + _make_code_only_drift_unit(base) + paths = get_pdd_file_paths(BASENAME, LANGUAGE, prompts_dir="prompts") + before = read_fingerprint(BASENAME, LANGUAGE, paths=paths) + + result = runner.invoke(resolve, [BASENAME, "--accept-current"], obj={}) + + assert result.exit_code == 2, result.output + assert "not a CONFLICT" in result.output + assert "pdd update" in result.output and "pdd sync" in result.output + # Fingerprint is NOT changed by the refused resolve. + after = read_fingerprint(BASENAME, LANGUAGE, paths=paths) + assert after.code_hash == before.code_hash + + +def test_force_stamps_code_only_drift(tmp_path): + """--force is the explicit escape hatch to accept current-as-truth on drift.""" + runner = CliRunner() + with runner.isolated_filesystem() as tmp: + base = Path(tmp) + _make_code_only_drift_unit(base) + paths = get_pdd_file_paths(BASENAME, LANGUAGE, prompts_dir="prompts") + current = calculate_sha256(Path(paths["code"])) + + result = runner.invoke(resolve, [BASENAME, "--accept-current", "--force"], obj={}) + + assert result.exit_code == 0, result.output + after = read_fingerprint(BASENAME, LANGUAGE, paths=paths) + assert after.code_hash == current + + +def test_accept_current_noop_when_already_in_sync(tmp_path): + runner = CliRunner() + with runner.isolated_filesystem() as tmp: + base = Path(tmp) + _make_conflict_unit(base) + runner.invoke(resolve, [BASENAME, "--accept-current"], obj={}) # -> IN_SYNC + result = runner.invoke(resolve, [BASENAME, "--accept-current"], obj={}) + assert result.exit_code == 0, result.output + assert "already in sync" in result.output + + +def test_accept_current_resolves_conflict_to_in_sync(): + runner = CliRunner() + with runner.isolated_filesystem() as tmp: + base = Path(tmp) + _make_conflict_unit(base) + + result = runner.invoke(resolve, [BASENAME, "--accept-current"], obj={}) + + assert result.exit_code == 0, result.output + assert "Resolved 'widget'" in result.output + # Idempotent: a second accept-current now sees the unit already IN_SYNC. + again = runner.invoke(resolve, [BASENAME, "--accept-current", "--json"], obj={}) + assert json.loads(again.output)["before"] == "IN_SYNC" + + +def test_accept_current_stamps_the_current_tree_not_the_old_baseline(): + """The new baseline must fingerprint the CHANGED code (return 2), not the old.""" + runner = CliRunner() + with runner.isolated_filesystem() as tmp: + base = Path(tmp) + _, _, fp_path = _make_conflict_unit(base) + paths = get_pdd_file_paths(BASENAME, LANGUAGE, prompts_dir="prompts") + current_code_hash = calculate_sha256(Path(paths["code"])) + + result = runner.invoke(resolve, [BASENAME, "--accept-current"], obj={}) + + assert result.exit_code == 0, result.output + # Fingerprint is preserved (never deleted) and now matches the current code. + assert fp_path.exists() + fingerprint = read_fingerprint(BASENAME, LANGUAGE, paths=paths) + assert fingerprint is not None + assert fingerprint.code_hash == current_code_hash + + +def test_accept_current_json_reports_before_and_after(): + runner = CliRunner() + with runner.isolated_filesystem() as tmp: + base = Path(tmp) + _make_conflict_unit(base) + + result = runner.invoke( + resolve, [BASENAME, "--accept-current", "--json"], obj={} + ) + + assert result.exit_code == 0, result.output + payload = json.loads(result.output) + assert payload["resolved"] is True + assert payload["before"] == "CONFLICT" + assert payload["after"] == "IN_SYNC" + assert payload["strategy"] == "accept-current" + + +def test_prompt_wins_is_a_documented_stub_that_exits_nonzero(): + runner = CliRunner() + with runner.isolated_filesystem() as tmp: + _make_conflict_unit(Path(tmp)) + + result = runner.invoke(resolve, [BASENAME, "--prompt-wins"], obj={}) + + assert result.exit_code == 2 + assert "not yet automated" in result.output + assert "pdd sync widget" in result.output + + +def test_code_wins_is_a_documented_stub_that_exits_nonzero(): + runner = CliRunner() + with runner.isolated_filesystem() as tmp: + _make_conflict_unit(Path(tmp)) + + result = runner.invoke(resolve, [BASENAME, "--code-wins"], obj={}) + + assert result.exit_code == 2 + assert "not yet automated" in result.output + # code-wins previews `pdd update ` (pdd update takes a file, + # not a basename — #1969 review pass 2 finding 1). + assert "pdd update " in result.output + assert f"{BASENAME}.py" in result.output + + +def test_stub_previews_do_not_mutate_the_fingerprint(): + """A preview strategy must not change the on-disk baseline.""" + runner = CliRunner() + with runner.isolated_filesystem() as tmp: + base = Path(tmp) + _, _, fp_path = _make_conflict_unit(base) + before = fp_path.read_text(encoding="utf-8") + + runner.invoke(resolve, [BASENAME, "--prompt-wins"], obj={}) + + assert fp_path.read_text(encoding="utf-8") == before + # Still a conflict after a no-op preview (probed via the command itself). + probe = runner.invoke(resolve, [BASENAME, "--accept-current", "--json"], obj={}) + assert json.loads(probe.output)["before"] == "CONFLICT" + + +def test_requires_exactly_one_strategy(): + runner = CliRunner() + with runner.isolated_filesystem() as tmp: + _make_conflict_unit(Path(tmp)) + + none_chosen = runner.invoke(resolve, [BASENAME], obj={}) + assert none_chosen.exit_code == 2 + assert "exactly one" in none_chosen.output + + two_chosen = runner.invoke( + resolve, [BASENAME, "--accept-current", "--prompt-wins"], obj={} + ) + assert two_chosen.exit_code == 2 + assert "exactly one" in two_chosen.output + + +def test_unknown_unit_errors_without_stamping(): + runner = CliRunner() + with runner.isolated_filesystem() as tmp: + _make_conflict_unit(Path(tmp)) + + result = runner.invoke( + resolve, ["does_not_exist", "--accept-current"], obj={} + ) + + assert result.exit_code != 0 + assert "No PDD unit" in result.output diff --git a/tests/test_stamp_fingerprints.py b/tests/test_stamp_fingerprints.py new file mode 100644 index 0000000000..897c52d99c --- /dev/null +++ b/tests/test_stamp_fingerprints.py @@ -0,0 +1,164 @@ +"""Tests for scripts/stamp_fingerprints.py — the .pdd/meta fingerprint stamper. + +The load-bearing guarantee is hash parity with the pdd CLI: a fingerprint the +stamper writes must equal what ``pdd sync`` would compute, or sync sees phantom +drift. ``test_hash_parity_with_pdd`` pins the vendored hashing against pdd's own +functions for representative units (flat, subdir, architecture-overridden, and +ambiguous-leaf). Exhaustive parity across all units is enforced by the stamper's +own ``--check`` on the committed tree (``test_check_passes_on_committed_tree``). +""" +import importlib.util +import json +import logging +import os +from pathlib import Path + +import pytest + +REPO_ROOT = Path(__file__).resolve().parent.parent +STAMPER_PATH = REPO_ROOT / "scripts" / "stamp_fingerprints.py" + + +def _load_stamper(): + spec = importlib.util.spec_from_file_location("stamp_fingerprints", STAMPER_PATH) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + return module + + +stamp = _load_stamper() + + +# Representative units: flat, each subdir context, the two architecture.json +# code-path overrides (checkup_simplify -> pdd/checkup_simplify.py, +# remote_session -> pdd/remote_session.py), and the two ambiguous leaves +# (core/cli, commands/gate) whose example/test stems are disambiguated. +_SAMPLE_UNITS = [ + "sync_orchestration", + "code_generator", + "commands/which", + "server/executor", + "server/routes/auth", + "core/cloud", + "commands/checkup_simplify", + "core/remote_session", + "core/cli", + "commands/gate", +] + + +def test_stamper_delegates_to_continuous_sync(): + """Issue #1927: the stamper shares pdd.continuous_sync's hashing/resolution. + + It previously vendored a stdlib-only copy of pdd's hash functions so it could + run without importing pdd; #1927 removed that second implementation, so the + script now imports the shared core (CI installs the package before the gate). + Guard that the delegation stays in place and no vendored hashing creeps back. + """ + import ast + + tree = ast.parse(STAMPER_PATH.read_text(encoding="utf-8")) + imported = set() + for node in ast.walk(tree): + if isinstance(node, ast.Import): + imported.update(alias.name for alias in node.names) + elif isinstance(node, ast.ImportFrom) and node.module: + imported.add(node.module) + assert any(m == "pdd" or m.startswith("pdd.") for m in imported), imported + # Single implementation: the script must not re-vendor hashlib-based hashing. + assert "hashlib" not in imported + + +def test_write_meta_byte_format(tmp_path): + """Meta JSON is indent=2, no trailing newline, Fingerprint dataclass key order.""" + content = {k: None for k in stamp.FIELD_ORDER} + content["command"] = "fix" + content["test_files"] = {} + content["include_deps"] = {} + out = tmp_path / "x.json" + stamp.write_meta(out, content) + raw = out.read_text(encoding="utf-8") + assert not raw.endswith("\n") # matches pdd's json.dump(indent=2) writers + assert list(json.loads(raw).keys()) == list(stamp.FIELD_ORDER) + assert '\n "timestamp"' in raw # indent=2 + + +@pytest.mark.parametrize("basename", _SAMPLE_UNITS) +def test_hash_parity_with_pdd(basename): + """Stamper hashes must equal pdd's calculate_current_hashes for each unit.""" + logging.disable(logging.CRITICAL) + from pdd.sync_determine_operation import ( + get_pdd_file_paths, + calculate_current_hashes, + ) + + prev = Path.cwd() + os.chdir(REPO_ROOT) # cwd-relative include resolution for both sides + try: + unit = stamp.Unit(basename, REPO_ROOT / "pdd" / "prompts" / f"{basename}_python.prompt") + existing = stamp.read_meta(unit.meta) + stored_deps = None + if existing is not None and isinstance(existing.get("include_deps"), dict): + stored_deps = existing["include_deps"] + mine = stamp.compute_hashes(unit, stored_deps) + paths = get_pdd_file_paths(basename, "python") + theirs = calculate_current_hashes(paths, stored_include_deps=stored_deps) + finally: + os.chdir(prev) + + for field in stamp.HASH_FIELDS: + assert (mine.get(field) or None) == (theirs.get(field) or None), ( + f"{basename}.{field} diverged from pdd" + ) + + +def test_check_passes_on_committed_tree(): + """--check exits 0 on the current tree (acceptance: fresh clone check is green).""" + assert stamp.main(["--check"]) == 0 + + +def test_check_detects_tampered_meta(): + """A hand-edited (tampered) fingerprint must fail --check (acceptance #5).""" + units = stamp.enumerate_units() + stampable, _ig, _wv, _nc = stamp.classify( + units, stamp.load_pddignore(), stamp.load_waivers() + ) + target = next(u for u in stampable if u.meta.exists()) + original = target.meta.read_bytes() + try: + data = json.loads(original) + data["code_hash"] = "0" * 64 # simulate a hand-edit + target.meta.write_text(json.dumps(data, indent=2), encoding="utf-8") + assert stamp.main(["--check"]) == 1 + finally: + target.meta.write_bytes(original) + # tree restored -> check green again + assert stamp.main(["--check"]) == 0 + + +def test_waived_and_ignored_units_excluded(): + """Waived (ambiguous / no-code) and .pddignored units are not required to stamp.""" + units = stamp.enumerate_units() + stampable, ignored, waived, no_code = stamp.classify( + units, stamp.load_pddignore(), stamp.load_waivers() + ) + waived_names = {u.basename for u in waived} + assert {"cli", "gate", "core_dump_smoke", "server/routes/session"} <= waived_names + assert not no_code # every no-code unit is explicitly waived + ignored_names = {u.basename for u in ignored} + # __init__ prompts and architecture_registry are .pddignored + assert "architecture_registry" in ignored_names + assert any(n.endswith("__init__") for n in ignored_names) + # stampable and waived/ignored are disjoint + stampable_names = {u.basename for u in stampable} + assert stampable_names.isdisjoint(waived_names) + + +def test_every_stampable_unit_has_committed_meta(): + """Coverage: every stampable unit has a meta on disk (acceptance #2).""" + units = stamp.enumerate_units() + stampable, _ig, _wv, _nc = stamp.classify( + units, stamp.load_pddignore(), stamp.load_waivers() + ) + missing = [u.basename for u in stampable if not u.meta.exists()] + assert not missing, f"units missing committed meta: {missing}" diff --git a/tests/test_sync_determine_operation.py b/tests/test_sync_determine_operation.py index 58d243f102..f99359de8d 100644 --- a/tests/test_sync_determine_operation.py +++ b/tests/test_sync_determine_operation.py @@ -18,6 +18,7 @@ from sync_determine_operation import ( sync_determine_operation, analyze_conflict_with_llm, + _prompt_derived_conflict_decision, SyncLock, Fingerprint, RunReport, @@ -1123,6 +1124,65 @@ def test_prompt_code_coedit_conflict_with_real_paths_preserves_metadata(pdd_test assert paths["prompt"].read_text(encoding="utf-8") == "Generate a changed function.\n" +def test_conflict_message_is_actionable(pdd_test_environment): + """#1929: the CONFLICT reason names the unit, what moved, and the resolve commands.""" + paths = _write_complete_unit_with_fingerprint(pdd_test_environment) + paths["prompt"].write_text("Generate a changed function.\n", encoding="utf-8") + paths["code"].write_text("def value():\n return 2\n", encoding="utf-8") + + decision = sync_determine_operation( + BASENAME, + LANGUAGE, + TARGET_COVERAGE, + prompts_dir=str(pdd_test_environment / "prompts"), + ) + + assert decision.operation == "fail_and_request_manual_merge" + assert decision.details["classification"] == "CONFLICT" + # The reason is self-actionable: names the unit, what moved, the exact commands. + assert BASENAME in decision.reason + assert "prompt" in decision.reason and "code" in decision.reason + assert f"pdd resolve {BASENAME} --accept-current" in decision.reason + # The message points at the REAL runnable commands, not the preview stubs + # (#1969 review finding 3), with valid command shapes: pdd sync takes a bare + # basename and pdd update takes the code file path (review pass 2, finding 1). + assert f"pdd sync {BASENAME}" in decision.reason + assert f"pdd update {paths['code']}" in decision.reason + assert "--prompt-wins" not in decision.reason + assert "--code-wins" not in decision.reason + # Structured details for machine consumers (CI summaries, drift ledger). + assert decision.details["basename"] == BASENAME + assert decision.details["language"] == LANGUAGE + commands = decision.details["resolution_commands"] + assert commands["accept_current"] == f"pdd resolve {BASENAME} --accept-current" + assert commands["prompt_wins"] == f"pdd sync {BASENAME}" + assert commands["code_wins"] == f"pdd update {paths['code']}" + + +def test_conflict_decision_helper_formats_language_suffix_and_artifacts(): + """Non-Python units: only `pdd resolve` takes --language; sync/update must not + (#1969 review pass 2 finding 1 — sync/update reject --language).""" + decision = _prompt_derived_conflict_decision( + basename="parser", + language="javascript", + changes=["prompt", "code", "test"], + paths={"code": Path("src/parser.js")}, + fingerprint=None, + read_only=True, + ) + commands = decision.details["resolution_commands"] + assert commands["accept_current"] == "pdd resolve parser --language javascript --accept-current" + assert commands["prompt_wins"] == "pdd sync parser" + assert commands["code_wins"] == "pdd update src/parser.js" + # sync/update reject --language; the message must never emit it for them. + assert "--language" not in commands["prompt_wins"] + assert "--language" not in commands["code_wins"] + assert "prompt, code, and test changed" in decision.reason + assert decision.details["read_only"] is True + # Non-destructive contract holds regardless of language. + assert decision.operation == "fail_and_request_manual_merge" + + # --- Part 3: `analyze_conflict_with_llm` --- @patch('sync_determine_operation.get_git_diff', return_value="fake diff") diff --git a/tests/test_sync_main.py b/tests/test_sync_main.py index 3b9dc9d778..1d0f3ea599 100644 --- a/tests/test_sync_main.py +++ b/tests/test_sync_main.py @@ -262,6 +262,59 @@ def test_sync_main_forwards_compress_to_orchestration( assert mock_sync_orchestration.call_args.kwargs["compress"] is True +def test_sync_main_defaults_to_surgical_fresh_false( + mock_project_dir, mock_construct_paths, mock_sync_orchestration +): + """Default sync forwards fresh=False so mature modules regenerate + surgically (edit-shaped) rather than being rebirthed (#1938 Pillar A).""" + (mock_project_dir / "prompts" / "demo_python.prompt").touch() + mock_sync_orchestration.return_value = { + "success": True, + "total_cost": 0.0, + "model_name": "test", + "summary": "ok", + } + ctx = create_mock_context({"quiet": True}) + sync_main( + ctx, + "demo", + max_attempts=1, + budget=5.0, + skip_verify=True, + skip_tests=True, + target_coverage=90.0, + dry_run=False, + ) + assert mock_sync_orchestration.call_args.kwargs["fresh"] is False + + +def test_sync_main_forwards_fresh_true_to_orchestration( + mock_project_dir, mock_construct_paths, mock_sync_orchestration +): + """`pdd sync --fresh` threads fresh=True into sync_orchestration so the + operator opts back into full ('fresh') regeneration (#1938 Pillar A).""" + (mock_project_dir / "prompts" / "demo_python.prompt").touch() + mock_sync_orchestration.return_value = { + "success": True, + "total_cost": 0.0, + "model_name": "test", + "summary": "ok", + } + ctx = create_mock_context({"quiet": True}) + sync_main( + ctx, + "demo", + max_attempts=1, + budget=5.0, + skip_verify=True, + skip_tests=True, + target_coverage=90.0, + dry_run=False, + fresh=True, + ) + assert mock_sync_orchestration.call_args.kwargs["fresh"] is True + + def test_sync_success_multiple_languages(mock_project_dir, mock_construct_paths, mock_sync_orchestration): """Tests a successful sync for multiple languages, checking budget reduction and result aggregation.""" (mock_project_dir / "prompts" / "my_lib_python.prompt").touch() diff --git a/tests/test_sync_orchestration.py b/tests/test_sync_orchestration.py index 16d9c86644..b070e874ea 100644 --- a/tests/test_sync_orchestration.py +++ b/tests/test_sync_orchestration.py @@ -1394,9 +1394,63 @@ def test_dry_run_mode(orchestration_fixture): assert not extra_kwargs, f"unexpected extra kwargs: {extra_kwargs!r}" assert result == mock_log_display.return_value - # Ensure main workflow components were not touched + # Ensure the main workflow (locking, execution) was not touched. orchestration_fixture['SyncLock'].assert_not_called() - orchestration_fixture['sync_determine_operation'].assert_not_called() + # #1929: dry-run now also computes the CURRENT decision read-only so a pending + # CONFLICT surfaces (see test_dry_run_surfaces_conflict). This is analysis + # only — read_only=True / log_mode=True — so no metadata is mutated. + determine_calls = orchestration_fixture['sync_determine_operation'].call_args_list + assert len(determine_calls) == 1 + assert determine_calls[0].kwargs.get('read_only') is True + assert determine_calls[0].kwargs.get('log_mode') is True + +def test_dry_run_surfaces_conflict_without_side_effects(tmp_path, monkeypatch, capsys): + """#1929: `pdd sync --dry-run` reports a pending CONFLICT and mutates nothing. + + Runs the real (unmocked) dry-run path over an on-disk unit whose prompt AND + code both changed since the fingerprint. The current-analysis line must name + the CONFLICT and the `pdd resolve` command, and the fingerprint must be byte + identical afterwards. + """ + from datetime import datetime, timezone + from pdd.sync_determine_operation import calculate_sha256 + + monkeypatch.chdir(tmp_path) + (tmp_path / ".pdd" / "meta").mkdir(parents=True, exist_ok=True) + (tmp_path / "prompts").mkdir(exist_ok=True) + + def _w(path, content): + path = Path(path) + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(content, encoding="utf-8") + return calculate_sha256(path) + + prompt_hash = _w(tmp_path / "prompts" / "widget_python.prompt", "Generate a simple function.\n") + paths = get_pdd_file_paths("widget", "python", prompts_dir="prompts") + code_hash = _w(Path(paths["code"]), "def value():\n return 1\n") + example_hash = _w(Path(paths["example"]), "print(1)\n") + test_hash = _w(Path(paths["test"]), "def test_v():\n assert True\n") + fp_path = tmp_path / ".pdd" / "meta" / "widget_python.json" + fp_path.write_text(json.dumps({ + "pdd_version": "t", "timestamp": datetime.now(timezone.utc).isoformat(), + "command": "fix", "prompt_hash": prompt_hash, "code_hash": code_hash, + "example_hash": example_hash, "test_hash": test_hash, + "test_files": {Path(paths["test"]).name: test_hash}, "include_deps": {}, + }), encoding="utf-8") + # Co-edit prompt + code -> both-changed CONFLICT. + _w(tmp_path / "prompts" / "widget_python.prompt", "Generate a CHANGED function.\n") + _w(Path(paths["code"]), "def value():\n return 2\n") + fp_before = fp_path.read_text(encoding="utf-8") + + result = sync_orchestration(basename="widget", language="python", dry_run=True) + + out = capsys.readouterr().out + assert "CONFLICT" in out + assert "pdd resolve widget --accept-current" in out + # Strictly read-only: the fingerprint is untouched. + assert fp_path.read_text(encoding="utf-8") == fp_before + assert result is not None + def test_skip_verify_flag(orchestration_fixture): """ @@ -1457,6 +1511,33 @@ def test_manual_merge_request(orchestration_fixture): assert 'Manual merge required' in result['errors'][0] assert not result['operations_completed'] + +def test_conflict_reason_surfaces_actionable_resolve_command(orchestration_fixture): + """#1929: the actionable CONFLICT reason reaches the user's error summary. + + Orchestration must not swallow a CONFLICT into a silent success: it surfaces + the decision reason verbatim, including the exact `pdd resolve` command, so a + non-interactive/CI run reports how to resolve rather than healing the unit. + """ + mock_determine = orchestration_fixture['sync_determine_operation'] + mock_determine.return_value = SyncDecision( + operation='fail_and_request_manual_merge', + reason=( + "CONFLICT: 'calculator' — prompt and code changed since the last sync, " + "so pdd will not auto-pick a winner (that would discard your edits). " + "Resolve with `pdd resolve calculator --accept-current` (keep the current " + "files as the new baseline)." + ), + details={'classification': 'CONFLICT'}, + ) + + result = sync_orchestration(basename="calculator", language="python") + + assert result['success'] is False + assert not result['operations_completed'] + assert 'pdd resolve calculator --accept-current' in result['errors'][0] + + def test_unexpected_exception_handling(orchestration_fixture): """ Ensures the finally block runs and cleans up even with unexpected errors. @@ -9500,3 +9581,58 @@ def test_sync_orchestration_skip_handler_for_fix(orchestration_fixture): orchestration_fixture['_save_fingerprint_atomic'].assert_any_call( "calculator", "python", "skip:fix", ANY, 0.0, "skipped" ) + + +# --------------------------------------------------------------------------- +# Issue #1938 (Pillar A): surgical regeneration for mature modules. +# +# For a mature module (existing non-empty code) with a small prompt delta, +# `pdd sync` regeneration must be edit-shaped, not rebirth-shaped, so declared +# symbols are preserved instead of dropped by a full "big change" regen. The +# generate operation therefore drives `code_generator_main` with +# `force_incremental_flag=True` by DEFAULT (surgical/edit-shaped), and only with +# `force_incremental_flag=False` (today's full-regeneration behavior) when the +# operator opts in via `pdd sync --fresh`. The `code_generator_main` layer keeps +# the existing safety nets: a new/empty module or an undeterminable original +# prompt still falls back to full generation, and a repair directive still +# forces full regeneration. +# --------------------------------------------------------------------------- +def _capture_force_incremental(orchestration_fixture): + """Run a single generate op and return the force_incremental_flag kwarg + passed to code_generator_main.""" + mock_determine = orchestration_fixture['sync_determine_operation'] + mock_determine.side_effect = [ + SyncDecision(operation='generate', reason='Prompt changed'), + SyncDecision(operation='all_synced', reason='All artifacts are up to date'), + ] + code_path = orchestration_fixture['get_pdd_file_paths'].return_value['code'] + captured = {} + + def fake_codegen(*_args, **_kwargs): + captured['force_incremental_flag'] = _kwargs.get('force_incremental_flag') + code_path.parent.mkdir(parents=True, exist_ok=True) + code_path.write_text("class Calculator:\n pass\n") + return ("class Calculator:\n pass\n", True, 0.10, "model") + + orchestration_fixture['code_generator_main'].side_effect = fake_codegen + return captured + + +def test_generate_defaults_to_surgical_force_incremental(orchestration_fixture): + """Default sync must request edit-shaped (surgical) generation: + code_generator_main receives force_incremental_flag=True.""" + captured = _capture_force_incremental(orchestration_fixture) + + sync_orchestration(basename="calculator", language="python", budget=1.0) + + assert captured['force_incremental_flag'] is True + + +def test_generate_fresh_flag_forces_full_regeneration(orchestration_fixture): + """`pdd sync --fresh` (fresh=True) must restore full-regeneration behavior: + code_generator_main receives force_incremental_flag=False.""" + captured = _capture_force_incremental(orchestration_fixture) + + sync_orchestration(basename="calculator", language="python", budget=1.0, fresh=True) + + assert captured['force_incremental_flag'] is False