Skip to content

Unify local-mode and issue-mode pipeline behavior - #554

Merged
jwbron merged 18 commits into
mainfrom
egg/issue-543
Feb 12, 2026
Merged

Unify local-mode and issue-mode pipeline behavior#554
jwbron merged 18 commits into
mainfrom
egg/issue-543

Conversation

@james-in-a-box

Copy link
Copy Markdown
Contributor

Summary

Unifies local-mode and issue-mode pipelines so both follow the same contract/checkpoint/push discipline. The only difference between the two modes is now where initial context comes from (GitHub issue vs user prompt).

Key changes:

  • Phase-based push restrictions: Replaced blanket local-mode push blocking with file-level restrictions per phase. Refine/plan phases can push .egg-state/ files, implement phase can push code but not contracts/drafts, PR phase can push everything.
  • State persistence at phase boundaries: Local pipelines now commit state to git when phases transition, matching issue-mode behavior.
  • Unified file paths: Local mode now uses {pipeline_id}- prefixed paths (e.g., .egg-state/drafts/{pipeline_id}-analysis.md) to support concurrent pipelines.
  • Contract CLI in both modes: Local-mode agent prompts now include egg-contract CLI instructions.
  • Checkpoints enabled for local mode: With push restrictions relaxed, checkpoints are captured on successful pushes.
  • Container/agent tracking: PhaseExecution.containers and PhaseExecution.agents are now populated during execution.

Issue: #543

Test plan

  • Run make test to verify all unit and integration tests pass
  • Test a local pipeline (egg local "Test task") and verify:
    • Push works in each phase with appropriate file restrictions
    • Checkpoint JSON appears on egg/checkpoints/v1 branch
    • State files use {pipeline_id}- prefixed paths
    • Contract CLI commands work (egg-contract show)
  • Run two local pipelines concurrently and verify no path conflicts
  • Query /api/v1/pipelines/{id} and verify containers array is populated

Authored-by: egg

james-in-a-box[bot] added 17 commits February 12, 2026 08:59
…d file restrictions

Phase 1 of issue #543:
- Add phase_file_restrictions section to phase-permissions.json defining
  allowed/blocked patterns per phase (refine, plan, implement, pr)
- Add PhaseFileRestriction dataclass and check_phase_file_restrictions()
  method to PhaseFilter for validating files against phase restrictions
- Remove blanket local-mode push block from gateway.py
- Add phase-based file restriction enforcement in push handler
- Add checkpoint branch bypass (egg/checkpoints/v1 always allowed)

This enables local-mode pipelines to push files while still enforcing
phase-appropriate restrictions:
- refine: contracts, analysis drafts, checkpoints only
- plan: contracts, plan drafts, checkpoints only
- implement: code only, no .egg-state/ (except checkpoints)
- pr: everything allowed

Authored-by: egg
Phase 2 of issue #543:
- Add force_commit parameter to StateStore.save_pipeline() and
  delete_pipeline() to override mode-based commit skip
- Update phase transitions in _run_pipeline_execution() to commit
  local pipeline state at phase boundaries (phase changes and completion)

This ensures local pipeline state is persisted to git history at important
checkpoints while still avoiding per-operation commits during normal execution.

Authored-by: egg
…dict files

Phase 3 of issue #543:
- Update _get_draft_path() to use pipeline_id prefix for local mode
  (e.g., local-abc12345-analysis.md instead of analysis.md)
- Update _verdict_path_for_type() to use pipeline_id prefix for local mode
  (e.g., local-abc12345-refine-unified-review.json)
- Update _read_phase_draft() to accept and pass through pipeline_id
- Update _read_review_verdict() to accept and pass through pipeline_id
- Update _build_phase_prompt() to use _get_draft_path() for agent instructions
- Update _build_review_prompt() to pass pipeline_id to helpers
- Update all callers to pass pipeline_id parameter

This enables multiple concurrent local pipelines in the same repo by
ensuring each pipeline uses uniquely-prefixed file paths.

Authored-by: egg
…_synced

Phase 4 of issue #543:
- Add contract CLI instructions (egg-contract show/add-commit) to local
  mode agent prompts in the implement phase
- Update phase restrictions in prompts to reflect new unified push behavior
  (local mode can now push with phase-specific file restrictions)
- Set contract_synced=False when creating pipelines (both modes) since
  contract is created separately from pipeline
- Set contract_synced=True only after contract is successfully created

This ensures local-mode pipelines use the same contract workflow as
issue-mode pipelines.

Authored-by: egg
Phase 6 of issue #543:
- Add AgentExecution tracking to _spawn_and_wait() alongside container tracking
- Record agent with RUNNING status when container spawns
- Update agent status to COMPLETE or FAILED when container exits
- Include error message when agent fails

This ensures the API accurately reports both containers and agents in
PhaseExecution.containers[] and PhaseExecution.agents[].

Authored-by: egg
Phase 7 of issue #543:
- Add test_unified_pipeline_behavior.py with integration tests covering:
  - Local pipeline contract creation with pipeline_id key
  - Prefixed file paths for concurrent pipelines
  - contract_synced flag management
  - Phase-based push restriction session setup
  - Container/agent tracking via API
  - State file persistence
  - Checkpoint branch bypass constant

- Add unit tests to test_phase_filter.py for new phase-based restrictions:
  - refine/plan phases allowing only state files
  - implement phase allowing code but blocking .egg-state/
  - pr phase allowing everything
  - Checkpoints always allowed
  - PhaseFileRestriction dataclass tests

Authored-by: egg
Fixes test failures from Phase 7 integration tests:
- Remove allowed_patterns from implement phase in phase-permissions.json
  and default restrictions. When no allowed_patterns are specified,
  all files are allowed except those matching blocked_patterns.
- Update default permissions to allow push in refine/plan phases
  (file restrictions are applied separately via phase_file_restrictions)
- Fix tests that expected push to be blocked in refine/plan phases
- Change test for unknown phase to expect ValueError (enum validation)

The implement phase now correctly allows code files while blocking
.egg-state/ files (except checkpoints which don't match blocked patterns).

Authored-by: egg

@james-in-a-box james-in-a-box Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Code Review: PR #554 - Unify local-mode and issue-mode pipeline behavior

Summary

This PR replaces the blanket local-mode push blocking with phase-based file restrictions, enabling unified behavior between local and issue mode pipelines. The overall design is sound and follows existing patterns, but there are a few issues that need addressing.


Issues Requiring Changes

1. BUG: Unhandled ValueError in gateway.py when session_phase is invalid

File: gateway/gateway.py:680

phase_result = check_phase_file_restrictions(session_phase, changed_files)

The convenience function check_phase_file_restrictions raises ValueError for unknown phase strings (line 855 in phase_filter.py: phase = PipelinePhase(phase)). This call is not wrapped in a try-except, so an invalid session_phase value will cause an unhandled exception and a 500 error instead of graceful handling.

Fix: Either:

  1. Wrap the call in try-except and handle ValueError (return 500 with clear error message), OR
  2. Modify the convenience function to match the class method behavior (which already handles unknown phases gracefully and allows by default with a log message)

The second option is preferred since it maintains fail-safe behavior without exposing internal implementation details in error messages.


2. BUG: Inconsistency between class method and convenience function for unknown phases

File: gateway/phase_filter.py

The class method PhaseFilter.check_phase_file_restrictions() (lines 593-597) handles unknown phases gracefully:

if isinstance(phase, str):
    try:
        phase = PipelinePhase(phase)
    except ValueError:
        return FileRestrictionResult.allow(f"Unknown phase '{phase}', allowing by default")

But the convenience function (lines 854-856) raises ValueError:

if isinstance(phase, str):
    phase = PipelinePhase(phase)  # Raises ValueError
return get_phase_filter().check_phase_file_restrictions(phase, files)

This inconsistency means the same input produces different behavior depending on which entry point is used. The test test_unknown_phase_raises_value_error even expects this behavior, but it contradicts the class method's fail-safe design.

Fix: The convenience function should delegate validation to the class method, not duplicate it with different behavior:

def check_phase_file_restrictions(
    phase: str | PipelinePhase, files: list[str]
) -> FileRestrictionResult:
    return get_phase_filter().check_phase_file_restrictions(phase, files)

Then update the test to expect the allow-by-default behavior.


3. Potential security consideration: implement phase allows .egg-state/*.json at root

File: .egg/phase-permissions.json:154-161

The implement phase blocks specific subdirectories:

"blocked_patterns": [
    ".egg-state/contracts/*",
    ".egg-state/drafts/*",
    ".egg-state/pipelines/*",
    ".egg-state/reviews/*"
]

However, files directly in .egg-state/ (not in subdirectories) are NOT blocked. For example:

  • .egg-state/config.json - ALLOWED
  • .egg-state/malicious.json - ALLOWED

This may be intentional (no such files currently exist at that level), but it's worth explicitly documenting or adding .egg-state/*.json to blocked patterns if the intent is to prevent modification of any .egg-state/ files during implement phase (except checkpoints).

Question: Is this the intended behavior? If so, please document it in the phase-permissions.json description.


Minor Issues / Suggestions

4. Variable reuse check uses dir() - works but unconventional

File: gateway/gateway.py:655

if "changed_files" not in dir() or changed_files is None:

This works in Python, but the more conventional approach would be to initialize changed_files = None before the conditional blocks and check if changed_files is None. The dir() approach is unusual and may confuse future maintainers.


5. Missing test: path traversal in phase restrictions

The tests don't verify that path traversal attempts (e.g., foo/../.egg-state/contracts/123.json) are correctly normalized and blocked. The path normalization in _normalize_path() appears correct, but adding an explicit test would strengthen confidence.

Suggestion: Add a test case like:

def test_path_traversal_normalized_and_blocked(self):
    """Path traversal attempts are normalized and correctly blocked."""
    result = check_phase_file_restrictions(
        "implement",
        ["foo/../.egg-state/contracts/123.json"],  # Normalizes to .egg-state/contracts/123.json
    )
    assert result.allowed is False

6. Integration test uses undefined fixture

File: integration_tests/local_pipeline/test_unified_pipeline_behavior.py:418

def test_pipeline_reports_container_state(self, orchestrator_url: str) -> None:

This test method takes orchestrator_url as a parameter but the class TestContainerAgentTracking doesn't have the local_pipeline_stack fixture that other tests use. It appears to expect a simple string fixture, but this may not exist in the test environment.


Positive Observations

  1. Security model is sound: Phase-based restrictions with fail-closed behavior on file check errors is the right approach.

  2. Path normalization: The _normalize_path() method correctly handles ./ prefixes and path traversal via posixpath.normpath().

  3. Checkpoint bypass: Explicitly allowing checkpoint branch pushes (egg/checkpoints/v1) regardless of phase is correctly implemented.

  4. Test coverage: Good unit test coverage for the new phase file restrictions, including edge cases like mixed blocked/allowed files.

  5. Audit logging: All block scenarios are properly logged before returning errors.


Conclusion

The core design is correct, but the ValueError handling inconsistency between the convenience function and class method should be fixed before merge. This is currently a bug that could cause unexpected 500 errors in production if an invalid phase value is ever passed to the gateway.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

- Fix check_phase_file_restrictions convenience function to delegate
  validation to the class method instead of raising ValueError for
  unknown phases. Now matches the class method's fail-safe behavior.
- Replace unconventional dir() check with standard None comparison
  in gateway.py push handler.
- Add path traversal tests to verify normalization works correctly.
- Fix integration test to use local_pipeline_stack fixture.
- Fix test mock to explicitly set phase=None to prevent MagicMock
  auto-creation triggering the phase check code path.

Authored-by: egg
@james-in-a-box

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review! I've addressed the actionable items:

Fixed Issues

1. & 2. ValueError inconsistency - Fixed. The convenience function check_phase_file_restrictions() now delegates validation to the class method instead of duplicating it with different behavior. This ensures consistent fail-safe behavior (allow by default for unknown phases) regardless of which entry point is used.

4. Variable reuse check uses dir() - Fixed. Replaced with standard None comparison by initializing changed_files = None before the conditional blocks.

5. Missing path traversal test - Added three test cases for path traversal scenarios to verify normalization works correctly.

6. Integration test undefined fixture - Fixed. Changed test_pipeline_reports_container_state to use local_pipeline_stack fixture instead of the undefined orchestrator_url parameter.

Response to Item #3: .egg-state/*.json at root

This is intentional behavior. The implement phase blocks specific subdirectories that contain SDLC state files:

  • .egg-state/contracts/* - Contracts
  • .egg-state/drafts/* - Plan/analysis drafts
  • .egg-state/pipelines/* - Pipeline state
  • .egg-state/reviews/* - Review state

Files directly in .egg-state/ (not in subdirectories) are NOT blocked because:

  1. No such files currently exist at that level - all state is organized in subdirectories
  2. Checkpoints (.egg-state/checkpoints/*) are intentionally allowed during implement phase
  3. Adding .egg-state/*.json would be a new restriction that could have unintended consequences

If you'd like me to document this explicitly in the phase-permissions.json description, I can add that clarification.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box james-in-a-box Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Re-Review: PR #554 - Address Review Feedback

Summary

All issues from my previous review have been properly addressed. The changes are correct and maintain consistency between the convenience function and class method behavior.


Issues Verified as Fixed

1. ✅ ValueError Inconsistency Fixed

Previous Issue: The convenience function check_phase_file_restrictions() raised ValueError for unknown phases while the class method handled them gracefully.

Fix Verified: The convenience function now delegates directly to the class method without any pre-validation:

# Before (line 854-855):
if isinstance(phase, str):
    phase = PipelinePhase(phase)  # Raised ValueError

# After (line 854-855):
# Delegate to class method which handles unknown phase gracefully
return get_phase_filter().check_phase_file_restrictions(phase, files)

The corresponding test was updated from test_unknown_phase_raises_value_error to test_unknown_phase_allows_by_default, correctly verifying the fail-safe behavior.

2. ✅ Variable Reuse Check Fixed

Previous Issue: Used unconventional if "changed_files" not in dir() pattern.

Fix Verified: Now uses standard Python pattern:

changed_files = None  # May be populated by role check, reused by phase check
...
if changed_files is None:

3. ✅ Path Traversal Tests Added

Three new test cases verify path normalization:

  • test_path_traversal_normalized_and_blocked - Single traversal level
  • test_path_traversal_multiple_levels_blocked - Multiple traversal levels
  • test_path_traversal_mixed_with_valid_files - Mixed valid and traversal paths

All tests properly verify that posixpath.normpath() normalizes paths before restriction checks.

4. ✅ Integration Test Fixture Fixed

The test test_pipeline_reports_container_state now correctly uses the local_pipeline_stack fixture instead of the undefined orchestrator_url parameter:

def test_pipeline_reports_container_state(self, local_pipeline_stack) -> None:
    orchestrator_url = local_pipeline_stack.orchestrator_url

5. ✅ Test Mock Fixed

The mock session now explicitly sets phase = None to prevent MagicMock auto-creation from triggering the phase check code path.


Test Verification

All relevant tests pass:

  • test_unknown_phase_allows_by_default
  • test_path_traversal_normalized_and_blocked
  • test_path_traversal_multiple_levels_blocked
  • test_path_traversal_mixed_with_valid_files
  • test_push_allowed_when_role_unavailable

Conclusion

The review feedback has been thoroughly addressed. The code is ready for merge.

— Authored by egg

@james-in-a-box

This comment has been minimized.

@james-in-a-box

This comment has been minimized.

@james-in-a-box

Copy link
Copy Markdown
Contributor Author

All review feedback has been addressed. The re-review confirmed all fixes are correct and the code is ready for merge.

Summary of fixes applied:

  1. ✅ ValueError inconsistency fixed - convenience function now delegates to class method
  2. ✅ Variable reuse check fixed - using standard if changed_files is None: pattern
  3. ✅ Path traversal tests added - three test cases verify normalization
  4. ✅ Integration test fixture fixed - using local_pipeline_stack instead of undefined parameter
  5. ✅ Test mock fixed - explicitly sets phase = None

All tests pass locally (74 phase_filter tests, 22 gateway phase/push tests).

— Authored by egg

@james-in-a-box

This comment has been minimized.

@jwbron
jwbron merged commit 12cc374 into main Feb 12, 2026
17 checks passed
github-actions Bot pushed a commit that referenced this pull request Feb 12, 2026
Update project structure documentation to include the new integration test
file for unified local/issue pipeline behavior (added in #554).

Authored-by: egg
jwbron added a commit that referenced this pull request Feb 12, 2026
Update project structure documentation to include the new integration test
file for unified local/issue pipeline behavior (added in #554).

Authored-by: egg

Co-authored-by: jwbron <8340608+jwbron@users.noreply.github.com>
james-in-a-box Bot pushed a commit that referenced this pull request Feb 12, 2026
Resolved merge conflicts:
- docs/architecture/README.md: Combined PR's orchestrator.md link with
  main's enhanced description
- gateway/tests/test_phase_api.py: Updated test to use 'gh pr create'
  instead of 'git push' since PR #554 changed REFINE phase permissions
  to allow push operations

Authored-by: egg
@jwbron jwbron added the sdlc:pr SDLC pipeline: PR in review label Feb 12, 2026

@james-in-a-box james-in-a-box Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Contract Verification Report

Performed comprehensive contract verification for issue #543 (Unify local-mode and issue-mode pipeline behavior).

Summary

Core Implementation: Verified ✅

The implementation correctly addresses the main objectives:

  • Phase-based push restrictions replace blanket local-mode blocking
  • State persistence at phase boundaries via force_commit flag
  • File path prefixing with pipeline IDs
  • Contract CLI instructions for both modes
  • Container and agent tracking populated

Detailed Verification

Phase 1: Git Push Restrictions ✅

  • ac-1: phase_file_restrictions section present in .egg/phase-permissions.json with correct patterns for refine/plan/implement/pr phases
  • ac-2: check_phase_file_restrictions() implemented in gateway/phase_filter.py:830-880 with proper pattern matching and path normalization
  • ac-3: Push handler in gateway/gateway.py:646-704 enforces phase restrictions with audit logging
  • ac-4: Blanket local-mode push block removed (diff shows removal of lines 520-533)
  • ac-5: Checkpoint branch bypass implemented: CHECKPOINT_BRANCH = "egg/checkpoints/v1" with is_checkpoint_push check

Phase 2: State Persistence ✅

  • ac-6: force_commit flag added to StateStore.save_pipeline() at line 182
  • ac-7: Phase transitions use force_commit=is_local in pipelines.py:2019-2030
  • ac-8: delete_pipeline() accepts force_commit parameter

Phase 3: File Paths ✅

  • ac-9: _get_draft_path() returns {pipeline_id}-{phase}.md format for local mode
  • ac-10: _verdict_path_for_type() returns {pipeline_id}-{phase}-{type}.json format
  • ac-11: _read_phase_draft() updated to pass pipeline_id parameter
  • ac-12: Phase prompts use _get_draft_path() to show correct paths

Phase 4: Contract Usage ✅

  • ac-13: Contract CLI instructions (egg-contract show/add-commit) now shown for both modes in _build_phase_prompt()
  • ac-14: contract_synced=False set in state_store.create_pipeline(), updated to True after successful creation
  • ac-15: Contract path uses {pipeline_id}.json (verified in integration tests)

Phase 5: Checkpoints ✅

  • ac-16: Checkpoint capture no longer blocked by local-mode restrictions
  • ac-17: Checkpoint patterns included in allowed_patterns for refine/plan phases
  • ac-18: ⚠️ No explicit test for checkpoint JSON on branch; covered implicitly by architecture

Phase 6: Container/Agent Tracking ✅

  • ac-19: _spawn_and_wait() now creates AgentExecution records
  • ac-20: phase_execution.agents.append(agent_execution) populates agent tracking
  • ac-21: Agent status updated with completed_at, status, error fields
  • ac-22: API exposes containers/agents via existing model serialization

Test File Location Issue ⚠️

The contract specifies Phase 7 tests in specific files:

  • orchestrator/tests/test_pipeline_push_restrictions.py
  • orchestrator/tests/test_local_pipeline_contracts.py
  • orchestrator/tests/test_concurrent_pipelines.py
  • orchestrator/tests/test_container_tracking.py
  • orchestrator/tests/test_state_persistence.py

However, these tests are consolidated in integration_tests/local_pipeline/test_unified_pipeline_behavior.py.

While the test scenarios ARE covered (push restrictions, concurrent pipelines, container tracking, etc.), the file locations don't match the contract specification. The tests are functionally equivalent but organized differently.

Recommendation: This is a minor deviation. The consolidated test file provides better maintainability. Consider updating the contract to reflect the actual file structure, or accept this as an acceptable implementation choice.

Additional Unit Tests ✅

Comprehensive unit tests added in gateway/tests/test_phase_filter.py:

  • TestPhaseFileRestrictions class with 20+ test cases
  • Path traversal attack prevention tests
  • Pattern matching edge cases
  • Unknown phase handling

Verdict

All core acceptance criteria verified. The implementation correctly unifies local-mode and issue-mode pipeline behavior with proper security controls, state persistence, and tracking.

The test file location deviation is a minor issue that doesn't affect functionality. The implementation meets the spirit and intent of all acceptance criteria.

— Authored by egg

@james-in-a-box

Copy link
Copy Markdown
Contributor Author

egg contract-verification completed. View run logs

6 previous review(s) hidden.

@james-in-a-box
james-in-a-box Bot deleted the egg/issue-543 branch February 12, 2026 16:54
james-in-a-box Bot pushed a commit that referenced this pull request Feb 14, 2026
- Fix 8 auto-fixable ruff errors: remove unused imports (F401),
  update typing imports to collections.abc (UP035), sort import
  block (I001), remove unused variable (F841)
- Fix 6 E741 ambiguous variable names: rename `l` to `line` in
  list comprehensions across SSE test files
- Fix test_load_invalid_schema_fails: Pipeline model fields became
  optional in #554, so {"id": "issue-9998"} is now valid. Updated
  test to use an invalid enum value for status instead.

Authored-by: egg
jwbron added a commit that referenced this pull request Feb 14, 2026
* Add analysis for issue #545: Remove GitHub Actions SDLC architecture

* Add implementation plan for issue #545: Remove GitHub Actions SDLC architecture

* Remove GitHub Actions SDLC workflows and supporting scripts

The SDLC pipeline has been fully migrated to the local distributed
orchestrator (PR #524). This removes the now-superseded GitHub Actions
implementation: 8 workflow files, 3 supporting scripts, the checks/
directory, 14 action prompt builders, and associated tests. All
documentation is updated to reference the orchestrator equivalents.

Issue: #545
Authored-by: egg

* Add implement phase check results

* Fix lint errors and stale test for invalid schema validation

- Fix 8 auto-fixable ruff errors: remove unused imports (F401),
  update typing imports to collections.abc (UP035), sort import
  block (I001), remove unused variable (F841)
- Fix 6 E741 ambiguous variable names: rename `l` to `line` in
  list comprehensions across SSE test files
- Fix test_load_invalid_schema_fails: Pipeline model fields became
  optional in #554, so {"id": "issue-9998"} is now valid. Updated
  test to use an invalid enum value for status instead.

Authored-by: egg

* Add implement phase check results for issue-545

* Add unified review verdict for issue-545 implement phase

* Add agent-design review verdict for issue-545 implement phase

* Add code review verdict for issue-545 implement phase

Reviewed security, correctness, robustness, and design across the v2
checkpoint system, session manager changes, workflow removals, and
orchestrator updates. No critical issues introduced by this diff.

Authored-by: egg

* Add contract review verdict for issue-545 implement phase

* Address review feedback: remove stale references and fix docstring

Remove dangling references to deleted files (sdlc-work-loop.yml,
build-sdlc-prompt.sh) from comments and docstrings. Fix redundant
phrasing in _populate_contract_from_plan docstring. PR description
updated separately to correct inaccurate claims about action/ and
config/ directory removal.

* Restore PR-operational checks, contract validator, and prompt builder

Restores files that were incorrectly removed as part of the SDLC cleanup:
- .github/scripts/checks/ directory (check_fixer, lint_check, test_check,
  merge_conflict_check, draft_validation_check, plan_yaml_check, base, run_check)
- .github/workflows/on-pull-request-contract-verify.yml
- action/build-contract-verification-prompt.sh
- tests/scripts/test_checks.py

Removes deployment_check from run_check.py registry (SDLC-only, correctly
deleted). Updates docs, README, and test-action.yml shellcheck to include
restored files.

* Remove stale check-deployment definition from phase defaults

The deployment_check.py script was deleted during merge conflict resolution
(it depended on deleted .github/scripts/checks/ base infrastructure from the
GHA SDLC removal). The CheckDefinition referencing it remained as a dangling
reference from PR #653. Remove it to avoid a potential ValueError if
run_check.py tries to dispatch an unknown 'deployment' check.

* Address review feedback: fix EDITOR handling, add subprocess timeouts, add reconnection backoff

- Fix _launch_editor to use shlex.split for multi-word $EDITOR values
  (e.g. "code --wait", "vim +10")
- Add timeout=30 to all subprocess.run calls in _commit_statefiles_to_worktree
  to prevent indefinite hangs on git lock contention
- Add exponential backoff (1s-30s) and max retry limit (20) to
  watch_pipeline reconnection loop to prevent resource exhaustion
- Use consistent word-boundary regex matching in _detect_phase for all
  phase keywords instead of mixing substring and regex strategies
- Narrow _find_repo_path exception handler from bare Exception to
  specific subprocess/OS error types

Authored-by: egg

---------

Co-authored-by: egg <egg@localhost>
Co-authored-by: egg-reviewer[bot] <261018737+egg-reviewer[bot]@users.noreply.github.com>
Co-authored-by: jwbron <8340608+jwbron@users.noreply.github.com>
jwbron added a commit that referenced this pull request Jun 11, 2026
…st the state store (#3070) (#3084)

* fix(orchestrator): commit pipeline state on every save and host-persist the state store (#3070)

A redeploy on 2026-06-10 silently erased three in-flight Khan/webapp
pipelines, including one parked at an approved refine HITL gate
(get_status 404, absent from list_tasks). Root cause, orchestrator
side: save_pipeline only committed prompt-driven pipelines (no
issue_number) when force_commit=True, which fires solely on the
auto-advance and completion paths — so a free-text pipeline parked at
its first gate had never been committed to egg/pipeline-state at all.
Its record existed only as an uncommitted file in the state worktree
on an emptyDir volume; pod recreation rebuilt the worktree from the
last committed branch tip and the pipeline vanished. The gate is a
vestige of the removed local pipeline mode (#554 -> #1073).

- save_pipeline/delete_pipeline: commit whenever commit=True,
  regardless of pipeline origin; drop the dead force_commit params and
  the two now-redundant call sites.
- k8s local overlay: host-persist the orchestrator's egg-state volume
  (pipeline-worktree*) alongside repos, mirroring the gateway's #3005
  session-store fix. The base no longer declares egg-state (it falls
  inside the home emptyDir, matching the gateway base) so the overlay
  add merges cleanly.
- deployment validation: new pipeline-state-store-not-persistent rule
  (error) fires when an overlay persists repos but leaves egg-state
  ephemeral, mirroring session-store-not-persistent.

The gateway-side half of #3070 (startup cleanup destroying parked
pipelines' worktrees/branches) is a separate PR.

* Address PR #3084 review: rule-7 test parity + HOST_UID coupling note

- Add three rule-6-parity tests for the new
  pipeline-state-store-not-persistent rule (PVC/PVC clean,
  PVC-repos/emptyDir-state firing, orchestrator-canary dash-variant
  firing). Mirrors the equivalent rule-6 trio so a future shared-helper
  refactor of both rules has full per-rule coverage.

- Note the HOST_UID coupling in the orchestrator-volumes.yaml comment:
  the shared /home/egg/.egg-state hostPath works because the gateway
  entrypoint chowns the parent to HOST_UID:HOST_GID; HOST_UID != 1000
  would EACCES the orchestrator (runAsUser: 1000), and cloud overlays
  on PVCs sidestep this entirely.

Follow-up filed for the post-fix commit-volume measurement: #3090.

Refs: #3084, #3070.

---------

Co-authored-by: egg-reviewer[bot] <261018737+egg-reviewer[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sdlc:pr SDLC pipeline: PR in review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant