Skip to content

Context aware steps#93

Open
azalio wants to merge 5 commits intomainfrom
context-aware-steps
Open

Context aware steps#93
azalio wants to merge 5 commits intomainfrom
context-aware-steps

Conversation

@azalio
Copy link
Copy Markdown
Owner

@azalio azalio commented Mar 31, 2026

No description provided.

azalio added 4 commits March 31, 2026 16:29
Replace full plan injection with two-layer "active window" system:
- Hook layer: goal + subtask title in ≤500 char PreToolUse reminders
- Actor prompt: structured <map_context> block with current subtask details,
  sibling summaries, upstream dependency results, and repo delta
- New StepState fields (subtask_results, last_subtask_commit_sha) for
  per-subtask outcome tracking and differential insight baseline
- New compute_differential_insight() for git-diff-based change tracking
- Add 23 tests for all new functions: compute_differential_insight,
  load_blueprint, get_subtask_from_blueprint, get_upstream_ids,
  build_context_block, StepState.record_subtask_result (867 total)
- Wire pipeline: record subtask_results + last_subtask_commit_sha in
  map-efficient.md after Monitor+tests pass (enables Upstream Results
  and Repo Delta sections)
- Unify goal regex: read_current_goal now matches ## Goal|Overview
  (consistent with hook's load_goal_and_title)
- Fix 500-char truncation: use [:497]+"..." consistently for REQUIRED
- Fix misleading comment in load_goal_and_title docstring
- Replace inline subprocess in build_context_block() with call to
  compute_differential_insight() from repo_insight.py (DRY fix)
- Move subprocess import to module-level in map_step_runner.py
- Fix shell injection: use stdin-based JSON passing instead of
  interpolating FILES_JSON into Python source in map-efficient.md
- Add optional commit_sha param to StepState.record_subtask_result()
- Add 5 tests for Repo Delta path in build_context_block
- Add 7 tests for load_goal_and_title in workflow-context-injector
- Add 7 tests for format_reminder 500-char truncation logic
- Add 2 tests for record_subtask_result commit_sha behavior
…, word truncation

- ARCH-1: Add record_subtask_result + build_context_block to CLI dispatch
  table; replace inline Python in map-efficient.md with single CLI call
- ARCH-2: Resolve CLAUDE_PROJECT_DIR in build_context_block() for correct
  path resolution regardless of CWD
- ARCH-3: Add _sanitize_branch() to build_context_block() for path safety
- QUALITY-1: Extract GOAL_HEADING_RE constant in both map_step_runner.py
  and workflow-context-injector.py with "keep in sync" comments
- QUALITY-2: Guard against empty subtask_id via CLI dispatch validation
- QUALITY-3: Surface deleted_files in Repo Delta context block section
- QUALITY-4: Word-boundary truncation via _truncate_at_word() helper
- TESTS-1: Integration test for record → build_context_block → upstream
- TESTS-2: Edge case tests for empty files_changed/summary
- TESTS-3: Updated truncation test to verify word-boundary behavior
Copilot AI review requested due to automatic review settings March 31, 2026 20:39
Copy link
Copy Markdown
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

Adds “context-aware steps” so MAP workflows inject a focused, current-subtask view instead of dumping full plans/logs—improving token efficiency and keeping the Actor oriented on the active work.

Changes:

  • Extend the PreToolUse hook reminder to include goal + subtask title with a hard ≤500-char cap.
  • Add structured <map_context> assembly (goal, current subtask details, plan overview, upstream results, repo delta) plus new StepState fields to persist subtask outcomes and baseline SHA.
  • Add compute_differential_insight() (git-diff-based) and expand unit tests + documentation/changelog.

Reviewed changes

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

Show a summary per file
File Description
tests/test_workflow_context_injector.py Adds direct-function tests for goal/title loading and 500-char reminder truncation behavior.
tests/test_repo_insight.py Adds tests for compute_differential_insight() success/error paths via subprocess mocking.
tests/test_map_step_runner.py Adds tests for blueprint loading/context block generation and Repo Delta behavior (mocked).
tests/test_map_orchestrator.py Adds StepState tests for new subtask_results and last_subtask_commit_sha fields.
src/mapify_cli/templates/map/scripts/map_step_runner.py Implements context block helpers + CLI dispatch for recording subtask results and building context.
.map/scripts/map_step_runner.py Keeps the runtime .map/scripts version in sync with the template updates.
src/mapify_cli/templates/map/scripts/map_orchestrator.py Extends StepState with subtask results recording + SHA baseline persistence.
.map/scripts/map_orchestrator.py Keeps the runtime .map/scripts version in sync with the template updates.
src/mapify_cli/templates/hooks/workflow-context-injector.py Adds goal/title enrichment to hook reminders and enforces ≤500-char truncation.
.claude/hooks/workflow-context-injector.py Keeps the repo’s hook implementation in sync with the template changes.
src/mapify_cli/templates/commands/map-efficient.md Documents <map_context> structure and adds “record subtask result” snippet after test gate.
.claude/commands/map-efficient.md Keeps the repo command doc in sync with the template changes.
src/mapify_cli/repo_insight.py Adds compute_differential_insight() to compute changed/deleted files between SHAs.
docs/ARCHITECTURE.md Documents the two-layer injection strategy and data sources.
CHANGELOG.md Adds an Unreleased entry describing the new context-aware injection capabilities.

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

Comment on lines +1003 to +1008
branch = _sanitize_branch(branch)
project_dir = Path(os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd()))

blueprint = load_blueprint(branch)
if not blueprint:
return ""
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

build_context_block() uses project_dir = Path(os.environ.get('CLAUDE_PROJECT_DIR', os.getcwd())) for step_state.json and compute_differential_insight(), but load_blueprint() / read_current_goal() still resolve .map/... relative to the current working directory. If CLAUDE_PROJECT_DIR is set and the process CWD differs from the project root, this can mix state from one directory with blueprint/plan from another (or fail to find them). Consider making all .map/<branch>/... reads consistently relative to a single base (e.g., project_dir) or dropping project_dir entirely if CWD is the intended source of truth.

Copilot uses AI. Check for mistakes.
Comment on lines +1003 to +1008
branch = _sanitize_branch(branch)
project_dir = Path(os.environ.get("CLAUDE_PROJECT_DIR", os.getcwd()))

blueprint = load_blueprint(branch)
if not blueprint:
return ""
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

build_context_block() uses project_dir = Path(os.environ.get('CLAUDE_PROJECT_DIR', os.getcwd())) for step_state.json and compute_differential_insight(), but load_blueprint() / read_current_goal() still resolve .map/... relative to the current working directory. If CLAUDE_PROJECT_DIR is set and the process CWD differs from the project root, this can mix state from one directory with blueprint/plan from another (or fail to find them). Consider making all .map/<branch>/... reads consistently relative to a single base (e.g., project_dir) or dropping project_dir entirely if CWD is the intended source of truth.

Copilot uses AI. Check for mistakes.
Comment on lines +219 to +225
Returns:
Dict with changed_files, deleted_files, since_sha, current_sha.
On error: dict with empty lists and error key.
"""
if since_sha is None:
return {"changed_files": [], "deleted_files": [], "note": "no baseline SHA"}

Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

Docstring/API mismatch: the docstring says the return always includes since_sha and current_sha, but the since_sha is None branch returns only changed_files, deleted_files, and note. Either include since_sha/current_sha in this branch as well (e.g., since_sha: None, current_sha: <HEAD or 'unknown'>) or update the docstring to reflect that these keys are optional.

Copilot uses AI. Check for mistakes.
"status": status,
"summary": summary,
}
if commit_sha is not None:
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

record_subtask_result() updates last_subtask_commit_sha whenever commit_sha is not None. This will also overwrite the stored SHA if an empty string is passed (e.g., a failed git rev-parse call that still produces commit_sha=''). Consider only updating the baseline when commit_sha is truthy / non-empty, or normalizing empty strings to None, to avoid unintentionally clearing the baseline for Repo Delta.

Suggested change
if commit_sha is not None:
if commit_sha:

Copilot uses AI. Check for mistakes.
"status": status,
"summary": summary,
}
if commit_sha is not None:
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

record_subtask_result() updates last_subtask_commit_sha whenever commit_sha is not None. This will also overwrite the stored SHA if an empty string is passed (e.g., a failed git rev-parse call that still produces commit_sha=''). Consider only updating the baseline when commit_sha is truthy / non-empty, or normalizing empty strings to None, to avoid unintentionally clearing the baseline for Repo Delta.

Suggested change
if commit_sha is not None:
# Only update baseline when we have a non-empty commit SHA to avoid
# unintentionally clearing last_subtask_commit_sha with an empty string.
if commit_sha:

Copilot uses AI. Check for mistakes.
Comment on lines +487 to +488
CURRENT_SHA=$(git rev-parse HEAD)
echo "{\"files\": ${FILES_JSON}, \"status\": \"valid\", \"summary\": \"Monitor passed + tests passed\", \"commit_sha\": \"${CURRENT_SHA}\"}" | python3 .map/scripts/map_step_runner.py record_subtask_result
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The new snippet records commit_sha via CURRENT_SHA=$(git rev-parse HEAD) and always passes it into record_subtask_result. If git rev-parse fails (e.g., not in a git repo), this can pass an empty SHA and potentially wipe the stored baseline used for Repo Delta. Consider guarding this so commit_sha is only sent when non-empty / command succeeds (or omit the field entirely on failure).

Suggested change
CURRENT_SHA=$(git rev-parse HEAD)
echo "{\"files\": ${FILES_JSON}, \"status\": \"valid\", \"summary\": \"Monitor passed + tests passed\", \"commit_sha\": \"${CURRENT_SHA}\"}" | python3 .map/scripts/map_step_runner.py record_subtask_result
if CURRENT_SHA=$(git rev-parse HEAD 2>/dev/null); then
echo "{\"files\": ${FILES_JSON}, \"status\": \"valid\", \"summary\": \"Monitor passed + tests passed\", \"commit_sha\": \"${CURRENT_SHA}\"}" | python3 .map/scripts/map_step_runner.py record_subtask_result
else
# Git SHA unavailable (e.g., not a git repo); record result without commit_sha to avoid corrupting Repo Delta baseline.
echo "{\"files\": ${FILES_JSON}, \"status\": \"valid\", \"summary\": \"Monitor passed + tests passed\"}" | python3 .map/scripts/map_step_runner.py record_subtask_result
fi

Copilot uses AI. Check for mistakes.
Comment on lines +487 to +488
CURRENT_SHA=$(git rev-parse HEAD)
echo "{\"files\": ${FILES_JSON}, \"status\": \"valid\", \"summary\": \"Monitor passed + tests passed\", \"commit_sha\": \"${CURRENT_SHA}\"}" | python3 .map/scripts/map_step_runner.py record_subtask_result
Copy link

Copilot AI Mar 31, 2026

Choose a reason for hiding this comment

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

The new snippet records commit_sha via CURRENT_SHA=$(git rev-parse HEAD) and always passes it into record_subtask_result. If git rev-parse fails (e.g., not in a git repo), this can pass an empty SHA and potentially wipe the stored baseline used for Repo Delta. Consider guarding this so commit_sha is only sent when non-empty / command succeeds (or omit the field entirely on failure).

Suggested change
CURRENT_SHA=$(git rev-parse HEAD)
echo "{\"files\": ${FILES_JSON}, \"status\": \"valid\", \"summary\": \"Monitor passed + tests passed\", \"commit_sha\": \"${CURRENT_SHA}\"}" | python3 .map/scripts/map_step_runner.py record_subtask_result
CURRENT_SHA=$(git rev-parse HEAD 2>/dev/null || echo "")
if [ -n "$CURRENT_SHA" ]; then
echo "{\"files\": ${FILES_JSON}, \"status\": \"valid\", \"summary\": \"Monitor passed + tests passed\", \"commit_sha\": \"${CURRENT_SHA}\"}" | python3 .map/scripts/map_step_runner.py record_subtask_result
else
echo "{\"files\": ${FILES_JSON}, \"status\": \"valid\", \"summary\": \"Monitor passed + tests passed\"}" | python3 .map/scripts/map_step_runner.py record_subtask_result
fi

Copilot uses AI. Check for mistakes.
…SHA, bash guard

- load_blueprint() accepts optional project_dir; build_context_block()
  passes CLAUDE_PROJECT_DIR consistently to all .map/ file reads
- compute_differential_insight() docstring reflects optional keys
- record_subtask_result uses truthy check (if commit_sha:) to prevent
  empty string overwriting baseline SHA
- map-efficient.md guards git rev-parse HEAD with fallback
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.

2 participants