Skip to content

Conversation

@beknobloch
Copy link
Contributor

@beknobloch beknobloch commented Jan 8, 2026

Summary

feat: Modified pdd sync to be steerable. Before taking an action, the TUI will present the user with a list of options, including the default option that pdd sync recommends. If no action is selected after a certain timeout length, the default option will be chosen automatically. Steering may be disabled with a new CLI option --no-steer, and the timeout length may be adjusted with a new option --steer-timeout <FLOAT>.
fix: Modified pdd sync TUI behavior to ignore resizes, fixing a previous issue which caused visuals to become corrupted and unreadable when the terminal window was resized.

Test Results

  • Unit tests: PASS
  • Regression tests: PASS
  • Sync regression: PASS
  • Test coverage: 66%

Manual Testing

Manually tested steering, disabling steering, and window resizing to verify stability and usability.

Fixes #169
Fixes #236

@gltanaka gltanaka requested a review from Copilot January 9, 2026 00:12
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces interactive steering to the PDD sync process via a new --steer flag (disabled by default), and resolves TUI corruption issues caused by terminal resizes by implementing a fixed-width rendering approach during sync runs.

Key changes:

  • Added optional interactive steering allowing users to override sync operation choices at decision points
  • Implemented --steer CLI flag (opt-in) to enable steering without changing default behavior
  • Fixed TUI resize-related visual corruption by freezing animation/layout width at mount time

Reviewed changes

Copilot reviewed 179 out of 184 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
pdd/sync_tui.py Added steering infrastructure (ChoiceScreen, maybe_steer_operation), fixed resize handling with frozen UI width, added on_resize handler
pdd/sync_orchestration.py Integrated steering calls into sync loop, added graceful handling for mock exhaustion in tests
pdd/commands/maintenance.py Added --steer flag with environment variable control
tests/test_sync_tui.py New comprehensive test file with unit tests and Z3 formal verification of steering logic
tests/test_sync_orchestration.py Added missing imports for resize logic testing
tests/test_llm_invoke.py Made model ID matching more robust with fallback candidates and graceful skipping
pdd/fix_code_loop.py Fixed pipe streaming to use readline() instead of single-byte reads, reordered thread joins
pdd/agentic_fix.py Added harvest-first strategy, updated claude binary resolution with shutil.which
README.md Documented --steer flag and interactive steering behavior
Various metadata/backup files Test run results and backup snapshots

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@gltanaka gltanaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please clean out the temp files to make the review easier

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't commit these temp files

@beknobloch beknobloch requested a review from gltanaka January 9, 2026 15:10
Copy link
Contributor

@gltanaka gltanaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your work. Can you also submit your prompt changes to the pdd_cap?

README.md Outdated
- `--skip-tests`: Skip unit test generation and fixing
- `--target-coverage FLOAT`: Desired code coverage percentage (default is 90.0)
- `--dry-run`: Display real-time sync analysis for this basename instead of running sync operations. This performs the same state analysis as a normal sync run but without acquiring exclusive locks or executing any operations, allowing inspection even when another sync process is active.
- `--steer`: Enable interactive steering during the sync process. When enabled, PDD may pause at key decision points (e.g., choosing the next operation) and allow you to select between multiple valid options instead of automatically choosing one.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a countdown timer or does it just stop until action is taken?
I think having it as a countdown timer might be better?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it uses a countdown timer with a default value of 8 seconds. Currently hardcoded, but I'll work on making it configurable via CLI

calculator.py Outdated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should you put this example in examples/ dir instead of root?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the change right now is in the context/ dir with other examples, which seems to be the default. Should I move it elsewhere?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe make steer the default and have no steer if the user doesn't want delays

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, I'll make that change

@@ -54,11 +54,6 @@ You are operating in an isolated git worktree at: {worktree_path}
This worktree is already checked out to branch `fix/issue-{issue_number}`.
Do NOT create a new branch - just stage, commit, and push.

% Files to Stage
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the purpose of this removal?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the LLM did this at some point as part of syncing sync_tui. I didn't know what the intention was, but does it seem like an error to you?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the latest pdd version so that this is hashes vs. timestamps

@beknobloch
Copy link
Contributor Author

Just submitted the prompt changes to pdd_cap as a PR in the branch pr-267-ben!

@beknobloch beknobloch requested a review from gltanaka January 11, 2026 23:34
@gltanaka gltanaka requested a review from Copilot January 12, 2026 17:56
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 14 out of 15 changed files in this pull request and generated 7 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jiaminc-cmu
Copy link
Collaborator

Hi can you resolve the git conflict? I'll test this bug fix.
Thank you!

@gltanaka
Copy link
Contributor

target 1/20

@gltanaka
Copy link
Contributor

@beknobloch It looks like there are still conflicts

@jiaminc-cmu
Copy link
Collaborator

Hi. We refactored agentic_fix.py just yesterday to use the agentic_common.py module so seems like there is git conflict again. I can help with resolving git conflict and testing if you can kindly note why agentic fix was updated and what changes we're expected to keep. Thanks

@beknobloch
Copy link
Contributor Author

Hi! The change to agentic_fix.py was a tentative change I made while debugging sync - it's unconnected with this pull request, so I accepted your refactors and removed the changes I made in my commit. Sorry for the confusion there.

@jiaminc-cmu
Copy link
Collaborator

Hi I am getting a compilation error because there was a missing import. Would you fix it or would you mind if I pushed the fix?
⏺ Confirmed:

The patch for sync_orchestration.py only shows:

  • Import added: from .sync_tui import maybe_steer_operation
  • Usage: steer_timeout: float = DEFAULT_STEER_TIMEOUT_S

Missing: from .sync_tui import DEFAULT_STEER_TIMEOUT_S

This would cause exactly the NameError you saw. The fix I applied to your branch adds the missing import:

from .sync_tui import maybe_steer_operation, DEFAULT_STEER_TIMEOUT_S

You may want to comment on PR #267 about this bug, or the PR author needs to fix it before it can be merged.

To test your fixed local version, reinstall from the local directory:

pip install -e .

@beknobloch
Copy link
Contributor Author

Good catch, I had made an env variable so I didn't notice this bug. You can go ahead and push the fix. Thanks so much!

jiaminc-cmu added a commit that referenced this pull request Jan 24, 2026
The original PR #267 was missing this import, causing a NameError
when the module is loaded.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
jiaminc-cmu added a commit to beknobloch/pdd that referenced this pull request Jan 24, 2026
The original PR promptdriven#267 was missing this import, causing a NameError
when the module is loaded.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@jiaminc-cmu
Copy link
Collaborator

======================== short test summary info =========================
FAILED tests/test_sync_main.py::test_sync_dry_run_mode - AssertionError: 'sync_orchestration' does not contain all of (call('', ('log_test',), {'language': 'python', 'prompts_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/prompts', 'code_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/src', 'examples_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/examples', 'tests_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/tests', 'dry_run': True, 'verbose': True, 'quiet': False, 'context_override': None}), call('', ('log_test',), {'language': 'typescript', 'prompts_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/prompts', 'code_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/src', 'examples_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/examples', 'tests_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/tests', 'dry_run': True, 'verbose': True, 'quiet': False, 'context_override': None})) in its call list, found [call('', ('log_test',), {'language': 'python', 'prompts_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/prompts', 'code_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/src', 'examples_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/examples', 'tests_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/tests', 'dry_run': True, 'verbose': True, 'quiet': False, 'context_override': None, 'no_steer': False, 'steer_timeout': 8.0}), call('', ('log_test',), {'language': 'typescript', 'prompts_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/prompts', 'code_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/src', 'examples_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/examples', 'tests_dir': '/private/var/folders/dx/6jpz4l8545179pngyxzmkhx80000gn/T/pytest-of-caijiamin/pytest-1582/test_sync_dry_run_mode0/test_project/tests', 'dry_run': True, 'verbose': True, 'quiet': False, 'context_override': None, 'no_steer': False, 'steer_timeout': 8.0})] instead
FAILED tests/test_sync_orchestration.py::test_stopiteration_handling - assert False is True
========= 2 failed, 2856 passed, 9 skipped in 1045.64s (0:17:25) =========

@jiaminc-cmu
Copy link
Collaborator

jiaminc-cmu commented Jan 25, 2026

I pushed the fix for missing import. However, I am getting unit tests errors. Does this PR pass all unit tests and regressions?

@gltanaka gltanaka requested a review from Copilot January 25, 2026 20:10
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

tests/test_sync_tui.py:1

  • Use Any from typing instead of lowercase any for type hints.
import pytest

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

beknobloch pushed a commit to beknobloch/pdd that referenced this pull request Jan 26, 2026
The original PR promptdriven#267 was missing this import, causing a NameError
when the module is loaded.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@beknobloch
Copy link
Contributor Author

I pushed the fix for missing import. However, I am getting unit tests errors. Does this PR pass all unit tests and regressions?

I just fixed the remaining issues caused by the merge conflict resolutions, did a full rebase, and reran unit tests and regression tests, and all pass. These issues should be resolved and the PR good to merge.

@gltanaka
Copy link
Contributor

Somehow I am getting conflicts: ```=========================== short test summary info ============================
FAILED tests/test_agentic_fix.py::test_run_agentic_fix_success_via_run_agentic_task
FAILED tests/test_commands_modify.py::test_cli_change_command_csv_validation
FAILED tests/test_agentic_fix.py::test_run_agentic_fix_handles_no_keys - asse...
FAILED tests/test_agentic_fix.py::test_run_agentic_fix_real_call_when_available[anthropic-ANTHROPIC_API_KEY-claude]
FAILED tests/test_agentic_fix.py::test_run_agentic_fix_real_call_when_available[google-GOOGLE_API_KEY-gemini]
FAILED tests/test_agentic_fix.py::test_run_agentic_fix_real_call_when_available[openai-OPENAI_API_KEY-codex]
FAILED tests/test_agentic_fix.py::TestCwdHandling::test_run_agentic_fix_respects_cwd_parameter
FAILED tests/test_agentic_fix.py::TestCwdHandling::test_run_agentic_fix_resolves_paths_against_cwd
FAILED tests/test_agentic_fix.py::TestMtimeChangeDetection::test_detects_new_files
FAILED tests/test_agentic_fix.py::TestMtimeChangeDetection::test_detects_modified_files
FAILED tests/test_agentic_fix.py::TestMtimeChangeDetection::test_detects_deleted_files
FAILED tests/test_fix_code_loop.py::TestCumulativeCostDisplay::test_total_cost_display_includes_prior_cost
FAILED tests/test_unfinished_prompt.py::test_unfinished_prompt_marks_mid_block_tail_without_dangling_as_finished
FAILED tests/test_e2e_issue_364_cumulative_cost.py::TestCumulativeCostDisplayE2E::test_fix_code_loop_displays_cumulative_cost
FAILED tests/test_e2e_issue_364_cumulative_cost.py::TestSyncOrchestrationCostAccumulation::test_sync_accumulated_cost_passed_to_fix_loops
================= 15 failed, 2949 passed in 339.05s (0:05:39) ==================
<<<PYTHON-EXEC-OUTPUT
Finished running tests!

beknobloch and others added 25 commits February 6, 2026 01:03
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
… user to choose how long `sync` waits for user steering before progressing with the default option

chore(steer): changed --steer to --no-steer, making steering the default behavior of `sync`

test(sync): added additional tests to verify the functionality of steering and related edge cases
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
The original PR promptdriven#267 was missing this import, causing a NameError
when the module is loaded.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Add pdd/prompts/*.prompt to .gitignore (local convenience files)
- Add temporary files to .gitignore (error_log.txt, *.backup)
- Fix duplicate --local flag handling in regression.sh
- Add max_output_tokens field to ModelInfo in prompts.py
…dividual path/color references instead of dictionaries. Update _detect_mtime_changes function in agentic_fix.py to handle new, modified, and deleted files more effectively. Introduce run_agentic_task wrapper for better testability. Adjust fix_code_loop to incorporate prior costs. Enhance error handling in SyncLock class for improved robustness.
…M invocations

- Added timeout handling for SSH session simulations in test_e2e_subprocess_issue_399_ssh_url_message.py to skip tests on timeout.
- Improved error handling in test_llm_invoke_integration.py to skip tests when Vertex AI API is unavailable.
- Updated comments for clarity on potential issues during test execution.
- Deleted backup files for agentic_fix and comment_line modules, including code and program files.
- Cleaned up unnecessary temporary files to streamline the project structure.
- Updated sync_orchestration.py to streamline decision handling, replacing StopIteration with a direct all_synced decision for improved workflow completion.
- Introduced a _debug_swallow function in sync_tui.py to better manage non-critical exception logging, enhancing robustness in UI interactions.
- Modified test_sync_orchestration.py to verify the new decision handling and updated test_sync_tui.py for improved environment variable restoration during tests.
- Adjusted .gitignore to include additional staging log files and ensure proper exclusion of temporary files.
- Introduced logging at the beginning of the sync_orchestration function to capture the start of the synchronization process.
- Implemented a try-except block to ensure that the sync proceeds even if logging setup fails, enhancing robustness.
@beknobloch beknobloch requested a review from gltanaka February 6, 2026 06:40
@beknobloch
Copy link
Contributor Author

Okay! I resolved all the issues you noted and also verified the following:

  • Rebased this PR after separating out some unrelated test fixes that I'm also getting on the upstream head.
  • Resolved merge conflicts.
  • Ran all unit and regression tests - all unit tests affected by this PR pass, and all regression tests pass.
  • Did manual verification that sync steering and the resize issue improvements look good.

Let me know if the auto merge succeeds on your end - I'm pretty sure the unit failures are unrelated to this PR (I'm looking into them on another branch) but I'm not certain.

@gltanaka gltanaka merged commit b25313f into promptdriven:main Feb 6, 2026
beknobloch pushed a commit to beknobloch/pdd that referenced this pull request Feb 7, 2026
The original PR promptdriven#267 was missing this import, causing a NameError
when the module is loaded.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
beknobloch pushed a commit to beknobloch/pdd that referenced this pull request Feb 7, 2026
The original PR promptdriven#267 was missing this import, causing a NameError
when the module is loaded.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Terminal UI break when terminal is resized during pdd sync Make sync steerable

4 participants