Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions .deepwork/jobs/deepwork_rules/hooks/capture_prompt_work_tree.sh
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
#!/bin/bash
# capture_prompt_work_tree.sh - Captures the git work tree state at prompt submission
#
# This script creates a snapshot of the current git state by recording
# all files that have been modified, added, or deleted. This baseline
# is used for policies with compare_to: prompt to detect what changed
# during an agent response (between user prompts).
# This script creates a snapshot of ALL tracked files at the time the prompt
# is submitted. This baseline is used for rules with compare_to: prompt and
# created: mode to detect truly NEW files (not modifications to existing ones).
#
# The baseline contains ALL tracked files (not just changed files) so that
# the rules_check hook can determine which files are genuinely new vs which
# files existed before and were just modified.

set -e

# Ensure .deepwork directory exists
mkdir -p .deepwork

# Stage all changes so we can diff against HEAD
git add -A 2>/dev/null || true

# Save the current state of changed files
# Using git diff --name-only HEAD to get the list of all changed files
git diff --name-only HEAD > .deepwork/.last_work_tree 2>/dev/null || true
# Save ALL tracked files (not just changed files)
# This is critical for created: mode rules to distinguish between:
# - Newly created files (not in baseline) -> should trigger created: rules
# - Modified existing files (in baseline) -> should NOT trigger created: rules
git ls-files > .deepwork/.last_work_tree 2>/dev/null || true

# Also include untracked files not yet in the index
# Also include untracked files that exist at prompt time
# These are files the user may have created before submitting the prompt
git ls-files --others --exclude-standard >> .deepwork/.last_work_tree 2>/dev/null || true

# Sort and deduplicate
Expand Down
22 changes: 22 additions & 0 deletions .deepwork/rules/manual-test-created-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
name: "Manual Test: Created Mode"
created: manual_tests/test_created_mode/*.yml
compare_to: prompt
---

# Manual Test: Created Mode (File Creation Trigger)

A new test file was created in the created mode test directory!

**Created:** `{created_files}`

## What to do:

1. Verify the created mode detection is working correctly
2. Acknowledge with `<promise>Manual Test: Created Mode</promise>`

## This tests:

The "created" detection mode where rules only fire for newly created files,
not for modifications to existing files. This is useful for enforcing standards
on new code specifically.
4 changes: 4 additions & 0 deletions manual_tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Each test has two cases: one where the rule SHOULD fire, and one where it should
| **Multi Safety** | Edit `.py` only | Edit `.py` AND any safety file | Manual Test: Multi Safety |
| **Infinite Block Prompt** | Edit `.py` (always blocks) | Provide `<promise>` tag | Manual Test: Infinite Block Prompt |
| **Infinite Block Command** | Edit `.py` (command fails) | Provide `<promise>` tag | Manual Test: Infinite Block Command |
| **Created Mode** | Create NEW `.yml` file | Modify EXISTING `.yml` file | Manual Test: Created Mode |

## Test Results Tracking

Expand All @@ -49,6 +50,7 @@ Each test has two cases: one where the rule SHOULD fire, and one where it should
| Multi Safety | ☐ | ☐ |
| Infinite Block Prompt | ☐ | ☐ |
| Infinite Block Command | ☐ | ☐ |
| Created Mode | ☐ | ☐ |

## Test Folders

Expand All @@ -61,6 +63,7 @@ Each test has two cases: one where the rule SHOULD fire, and one where it should
| `test_multi_safety/` | Multiple Safety | Fires unless ANY of the safety files also edited |
| `test_infinite_block_prompt/` | Infinite Block (Prompt) | Always blocks with prompt; only promise can bypass |
| `test_infinite_block_command/` | Infinite Block (Command) | Command always fails; tests if promise skips command |
| `test_created_mode/` | Created (New Files Only) | Fires ONLY when NEW files are created, not when existing modified |

## Corresponding Rules

Expand All @@ -72,3 +75,4 @@ Rules are defined in `.deepwork/rules/`:
- `manual-test-multi-safety.md`
- `manual-test-infinite-block-prompt.md`
- `manual-test-infinite-block-command.md`
- `manual-test-created-mode.md`
1 change: 1 addition & 0 deletions manual_tests/test_created_mode/existing_file.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This is a modification test
19 changes: 10 additions & 9 deletions src/deepwork/hooks/rules_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,9 +199,16 @@ def get_changed_files_default_tip() -> list[str]:


def get_changed_files_prompt() -> list[str]:
"""Get files changed since prompt was submitted."""
baseline_path = Path(".deepwork/.last_work_tree")
"""Get files changed since prompt was submitted.

Returns ALL files with staged changes (modified, added, deleted).
This is used by trigger/safety, set, and pair mode rules to detect
file modifications during the agent response.

Note: The baseline file (.last_work_tree) is NOT used here - it's only
used by get_created_files_prompt() to detect truly NEW files for
created: mode rules.
"""
try:
subprocess.run(["git", "add", "-A"], capture_output=True, check=False)

Expand All @@ -214,13 +221,7 @@ def get_changed_files_prompt() -> list[str]:
current_files = set(result.stdout.strip().split("\n")) if result.stdout.strip() else set()
current_files = {f for f in current_files if f}

if baseline_path.exists():
baseline_files = set(baseline_path.read_text().strip().split("\n"))
baseline_files = {f for f in baseline_files if f}
new_files = current_files - baseline_files
return sorted(new_files)
else:
return sorted(current_files)
return sorted(current_files)

except (subprocess.CalledProcessError, OSError):
return []
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
#!/bin/bash
# capture_prompt_work_tree.sh - Captures the git work tree state at prompt submission
#
# This script creates a snapshot of the current git state by recording
# all files that have been modified, added, or deleted. This baseline
# is used for policies with compare_to: prompt to detect what changed
# during an agent response (between user prompts).
# This script creates a snapshot of ALL tracked files at the time the prompt
# is submitted. This baseline is used for rules with compare_to: prompt and
# created: mode to detect truly NEW files (not modifications to existing ones).
#
# The baseline contains ALL tracked files (not just changed files) so that
# the rules_check hook can determine which files are genuinely new vs which
# files existed before and were just modified.

set -e

# Ensure .deepwork directory exists
mkdir -p .deepwork

# Stage all changes so we can diff against HEAD
git add -A 2>/dev/null || true

# Save the current state of changed files
# Using git diff --name-only HEAD to get the list of all changed files
git diff --name-only HEAD > .deepwork/.last_work_tree 2>/dev/null || true
# Save ALL tracked files (not just changed files)
# This is critical for created: mode rules to distinguish between:
# - Newly created files (not in baseline) -> should trigger created: rules
# - Modified existing files (in baseline) -> should NOT trigger created: rules
git ls-files > .deepwork/.last_work_tree 2>/dev/null || true

# Also include untracked files not yet in the index
# Also include untracked files that exist at prompt time
# These are files the user may have created before submitting the prompt
git ls-files --others --exclude-standard >> .deepwork/.last_work_tree 2>/dev/null || true

# Sort and deduplicate
Expand Down