Skip to content

feat: add deprecation warnings for non-create/non-design CLI modes - #1069

Open
osilkin98 wants to merge 3 commits into
mainfrom
factory/run-4fe34848
Open

feat: add deprecation warnings for non-create/non-design CLI modes#1069
osilkin98 wants to merge 3 commits into
mainfrom
factory/run-4fe34848

Conversation

@osilkin98

Copy link
Copy Markdown
Collaborator

Closes the deprecation warning task from the design workflow.

Changes

  • factory/cli/_helpers.py: Added DEPRECATED_MODES frozenset (9 modes: build, improve, research, meta, discover, review, refine, parallel-improve, interactive) and warn_deprecated_mode() function that emits a structlog deprecated_cli_mode event and prints a stderr warning with migration guidance
  • factory/cli/ceo.py: Calls warn_deprecated_mode() in both cmd_ceo() (after interactive→design alias mapping) and cmd_run() (after mode resolution from args), before mode-specific dispatch
  • factory/cli/_main.py: Updated --mode help text for both factory ceo and factory run to note deprecated modes
  • tests/test_deprecation.py: 20 tests covering: exact deprecated set membership, structlog event emission, stderr output, alias note for interactive, and verification that create/design/auto/qa/swebench are NOT deprecated

Excluded from deprecation (per user scope correction):

  • Community modes: qa, deep-qa, swebench, legacybench, featurebench, programbench, terminalbench, tomswe
  • Internal routing: auto, auto-fresh, skill-refine, doc-generate, doc-update, spec-generate, spec-update

RobotSail and others added 2 commits July 27, 2026 19:36
Add DEPRECATED_MODES frozenset and warn_deprecated_mode() to emit
structlog events and stderr warnings when users invoke deprecated
--mode values. Only 9 core factory-shipped modes are deprecated
(build, improve, research, meta, discover, review, refine,
parallel-improve, interactive). Community modes (qa, deep-qa,
swebench, etc.) and internal routing modes (auto, auto-fresh,
skill-refine, etc.) are excluded.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Resolve conflict in factory/cli/_main.py --mode help text: keep our
deprecation-focused wording while incorporating main's 'creating or
updating factory modes' detail for the create mode mention.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

Sentrux Quality Report

Absolute

Scanning ....
[scan] git ls-files: 438 total, 426 kept, 12 dropped (ext:12, meta:0, big:0)
[build_project_map] 426 files, 87 unique dirs, 80 cache misses, 4.0ms
[resolve] 662 resolved, 994 unresolved (of 1656 total specs)
[resolve_imports] project_map 4.1ms, suffix_idx 0.6ms, suffix_resolve 12.0ms, total 16.8ms
[build_graphs] 426 files | maps 1.3ms, imports 16.9ms, calls+inherit 3.6ms, total 21.8ms | 661 import, 5313 call, 0 inherit edges
sentrux check — 3 rules checked

Quality: 4445

✗ [Error] max_cc: 6 function(s) exceed max cyclomatic complexity of 30
    factory/cli/ceo.py:cmd_ceo (cc=104)
    factory/study.py:study_project_local (cc=42)
    factory/workflow/validation.py:validate_workflow (cc=40)
    factory/cli/_wizard.py:_welcome_wizard (cc=39)
    factory/cli/ceo.py:cmd_run (cc=37)
    factory/cli/ceo.py:_build_ceo_task (cc=31)

✗ 1 violation(s) found

Diff (vs base branch)

Scanning ....
[scan] git ls-files: 438 total, 426 kept, 12 dropped (ext:12, meta:0, big:0)
[build_project_map] 426 files, 87 unique dirs, 80 cache misses, 4.1ms
[resolve] 662 resolved, 994 unresolved (of 1656 total specs)
[resolve_imports] project_map 4.2ms, suffix_idx 0.8ms, suffix_resolve 13.0ms, total 18.0ms
[build_graphs] 426 files | maps 1.5ms, imports 18.0ms, calls+inherit 3.7ms, total 23.2ms | 661 import, 5313 call, 0 inherit edges
sentrux gate — structural regression check

Quality:      4445 -> 4445
Coupling:     0.81 → 0.81
Cycles:       5 → 5
God files:    2 → 2

Distance from Main Sequence: 0.34

✓ No degradation detected

@osilkin98
osilkin98 marked this pull request as ready for review July 28, 2026 03:40
@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.36%. Comparing base (6ae4379) to head (c9e3800).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1069      +/-   ##
==========================================
- Coverage   88.36%   88.36%   -0.01%     
==========================================
  Files         127      127              
  Lines       14864    14898      +34     
  Branches     2347     2351       +4     
==========================================
+ Hits        13134    13164      +30     
- Misses       1234     1236       +2     
- Partials      496      498       +2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@osilkin98

Copy link
Copy Markdown
Collaborator Author

@ceo-review

github-actions[bot]
github-actions Bot previously approved these changes Jul 28, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Factory Review: KEEP

Verdict: KEEP
Reason: QA: CLEAN — 3826 tests pass, lint/type-check clean, composite 0.966, code review 7/7 PASS, adversarial QA 10/10 verified. 2 minor observations (unreachable refine in frozenset, misleading run help text) — no blocking issues.

QA Analysis

Adversarial QA Report — PR #1069: Deprecation Warnings for CLI Modes

Date: 2026-07-28
Project type: CLI (Python)
Verdict: PASS


Smoke Test

No project-level smoke test defined in factory.md. Proceeded directly to feature tests.


Test 1: Run existing deprecation test suite

Criterion: All 20 unit tests in tests/test_deprecation.py pass.
Status: VERIFIED

Command:

uv run pytest tests/test_deprecation.py -v

Output:

tests/test_deprecation.py::test_deprecated_modes_exact_set PASSED        [  5%]
tests/test_deprecation.py::test_deprecated_modes_subset_of_known_modes PASSED [ 10%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_deprecated_mode_emits_structlog PASSED [ 15%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_deprecated_mode_prints_stderr PASSED [ 20%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_interactive_has_alias_note PASSED [ 25%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_create_not_deprecated PASSED [ 30%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_design_not_deprecated PASSED [ 35%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_auto_not_deprecated PASSED [ 40%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_swebench_not_deprecated PASSED [ 45%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_qa_not_deprecated PASSED [ 50%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_deep_qa_not_deprecated PASSED [ 55%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_all_deprecated_modes_warn[build] PASSED [ 60%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_all_deprecated_modes_warn[discover] PASSED [ 65%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_all_deprecated_modes_warn[improve] PASSED [ 70%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_all_deprecated_modes_warn[interactive] PASSED [ 75%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_all_deprecated_modes_warn[meta] PASSED [ 80%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_all_deprecated_modes_warn[parallel-improve] PASSED [ 85%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_all_deprecated_modes_warn[refine] PASSED [ 90%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_all_deprecated_modes_warn[research] PASSED [ 95%]
tests/test_deprecation.py::TestWarnDeprecatedMode::test_all_deprecated_modes_warn[review] PASSED [100%]

20 passed in 0.10s

Test 2: All deprecated modes produce stderr warnings

Criterion: Each of the 9 deprecated modes emits a WARNING to stderr with the correct mode name, mentions --mode design, and notes it "remains functional."
Status: VERIFIED

Command:

uv run python -c "
import sys, io
from unittest.mock import patch
from factory.cli._helpers import warn_deprecated_mode, DEPRECATED_MODES
for mode in sorted(DEPRECATED_MODES):
    old_stderr = sys.stderr
    sys.stderr = captured = io.StringIO()
    with patch('factory.cli._helpers.log'):
        warn_deprecated_mode(mode)
    sys.stderr = old_stderr
    output = captured.getvalue()
    has_warning = f'--mode {mode} is deprecated' in output
    has_design = '--mode design instead' in output
    has_functional = 'remains functional' in output
    print(f'  {mode}: WARNING={has_warning}, DESIGN={has_design}, FUNCTIONAL={has_functional}')
"

Output:

  build: WARNING=True, DESIGN=True, FUNCTIONAL=True
  discover: WARNING=True, DESIGN=True, FUNCTIONAL=True
  improve: WARNING=True, DESIGN=True, FUNCTIONAL=True
  interactive: WARNING=True, DESIGN=True, FUNCTIONAL=True
  meta: WARNING=True, DESIGN=True, FUNCTIONAL=True
  parallel-improve: WARNING=True, DESIGN=True, FUNCTIONAL=True
  refine: WARNING=True, DESIGN=True, FUNCTIONAL=True
  research: WARNING=True, DESIGN=True, FUNCTIONAL=True
  review: WARNING=True, DESIGN=True, FUNCTIONAL=True

Test 3: Non-deprecated modes produce NO warning

Criterion: Modes create, design, auto, swebench, qa, deep-qa produce no stderr and no structlog warning.
Status: VERIFIED

Command:

uv run python -c "
import sys, io
from unittest.mock import patch
from factory.cli._helpers import warn_deprecated_mode
for mode in ['create', 'design', 'auto', 'swebench', 'qa', 'deep-qa']:
    old_stderr = sys.stderr
    sys.stderr = captured = io.StringIO()
    with patch('factory.cli._helpers.log') as mock_log:
        warn_deprecated_mode(mode)
    sys.stderr = old_stderr
    output = captured.getvalue()
    log_called = mock_log.warning.called
    print(f'  {mode}: stderr_empty={output == \"\"}, log_silent={not log_called}')
"

Output:

  create: stderr_empty=True, log_silent=True
  design: stderr_empty=True, log_silent=True
  auto: stderr_empty=True, log_silent=True
  swebench: stderr_empty=True, log_silent=True
  qa: stderr_empty=True, log_silent=True
  deep-qa: stderr_empty=True, log_silent=True

Test 4: Interactive mode alias note

Criterion: The interactive mode warning includes the text "alias for 'design'", while other deprecated modes do not.
Status: VERIFIED

Command:

uv run python -c "
import sys, io
from unittest.mock import patch
from factory.cli._helpers import warn_deprecated_mode
# interactive
old_stderr = sys.stderr
sys.stderr = captured = io.StringIO()
with patch('factory.cli._helpers.log'):
    warn_deprecated_mode('interactive')
sys.stderr = old_stderr
print(repr(captured.getvalue()))
# build (should NOT have alias)
old_stderr = sys.stderr
sys.stderr = captured = io.StringIO()
with patch('factory.cli._helpers.log'):
    warn_deprecated_mode('build')
sys.stderr = old_stderr
print(repr(captured.getvalue()))
"

Output:

"WARNING: --mode interactive is deprecated ('interactive' is an alias for 'design'). Use --mode design instead. This mode remains functional but will be removed in a future release.\n"
'WARNING: --mode build is deprecated. Use --mode design instead. This mode remains functional but will be removed in a future release.\n'

Interactive mode has the alias note; build mode does not. Correct.


Test 5: Warning message includes correct mode name and mentions --mode design

Criterion: Each deprecated mode's warning text contains --mode <MODE_NAME> is deprecated and --mode design instead.
Status: VERIFIED

Command:

uv run python -c "
import sys, io
from unittest.mock import patch
from factory.cli._helpers import warn_deprecated_mode
for mode in ['build', 'improve', 'research', 'meta', 'discover', 'review', 'refine', 'parallel-improve', 'interactive']:
    old_stderr = sys.stderr
    sys.stderr = captured = io.StringIO()
    with patch('factory.cli._helpers.log'):
        warn_deprecated_mode(mode)
    sys.stderr = old_stderr
    output = captured.getvalue()
    correct_name = f'--mode {mode} is deprecated' in output
    mentions_design = '--mode design instead' in output
    print(f'  {mode}: correct_name={correct_name}, mentions_design={mentions_design}')
"

Output:

  build: correct_name=True, mentions_design=True
  improve: correct_name=True, mentions_design=True
  research: correct_name=True, mentions_design=True
  meta: correct_name=True, mentions_design=True
  discover: correct_name=True, mentions_design=True
  review: correct_name=True, mentions_design=True
  refine: correct_name=True, mentions_design=True
  parallel-improve: correct_name=True, mentions_design=True
  interactive: correct_name=True, mentions_design=True

Test 6: Edge cases (empty string, None, unknown mode)

Criterion: The function handles edge cases gracefully — no crash, no spurious warnings.
Status: VERIFIED

Command:

uv run python -c "
import sys, io
from unittest.mock import patch
from factory.cli._helpers import warn_deprecated_mode
for label, val in [('empty string', ''), ('None', None), ('unknown', 'nonexistent-mode')]:
    old_stderr = sys.stderr
    sys.stderr = captured = io.StringIO()
    with patch('factory.cli._helpers.log') as mock_log:
        warn_deprecated_mode(val)
    sys.stderr = old_stderr
    output = captured.getvalue()
    log_called = mock_log.warning.called
    print(f'  {label}: stderr_empty={output == \"\"}, log_silent={not log_called}')
"

Output:

  empty string: stderr_empty=True, log_silent=True
  None: stderr_empty=True, log_silent=True
  unknown: stderr_empty=True, log_silent=True

All edge cases handled cleanly — no crash, no output.


Test 7: DEPRECATED_MODES set integrity

Criterion: The set contains exactly 9 modes, is a frozenset (immutable), and create/design are not in it.
Status: VERIFIED

Command:

uv run python -c "
from factory.cli._helpers import DEPRECATED_MODES
print(f'Count: {len(DEPRECATED_MODES)}')
print(f'Type: {type(DEPRECATED_MODES).__name__}')
print(f'Members: {sorted(DEPRECATED_MODES)}')
print(f'create in set: {\"create\" in DEPRECATED_MODES}')
print(f'design in set: {\"design\" in DEPRECATED_MODES}')
try:
    DEPRECATED_MODES.add('test')
    print('Mutable: True (BAD)')
except AttributeError:
    print('Immutable: True (GOOD)')
"

Output:

Count: 9
Type: frozenset
Members: ['build', 'discover', 'improve', 'interactive', 'meta', 'parallel-improve', 'refine', 'research', 'review']
create in set: False
design in set: False
Immutable: True (GOOD)

Test 8: Integration points verified

Criterion: warn_deprecated_mode is called in cmd_ceo and cmd_run before mode dispatch.
Status: VERIFIED

Evidence: grep -n "warn_deprecated_mode" factory/cli/ceo.py shows:

  • Line 33: imported from _helpers
  • Line 71: called with getattr(args, "mode", "auto") in cmd_ceo
  • Line 2172: called with mode in cmd_run

Help text updated at lines 706 and 851 of factory/cli/ceo.py to mention deprecation.


Test 9: No regressions in existing test suite

Criterion: Core test files pass without failures.
Status: VERIFIED

Command:

uv run pytest tests/test_deprecation.py tests/test_models.py tests/test_cli.py tests/test_analysis.py tests/test_state.py -v --tb=short

Output (summary):

403 passed, 3 warnings in 164.69s

All 403 tests pass. The 3 warnings are pre-existing RuntimeWarning about unawaited coroutines in mock objects — unrelated to this PR.


Acceptance Criteria Verification

# Criterion Status
1 DEPRECATED_MODES frozenset with 9 modes VERIFIED
2 warn_deprecated_mode() prints to stderr VERIFIED
3 warn_deprecated_mode() emits structlog event VERIFIED
4 Calls in cmd_ceo() and cmd_run() before mode dispatch VERIFIED
5 Non-deprecated modes (create, design, auto, swebench, qa, deep-qa) produce no warning VERIFIED
6 Interactive mode includes alias note VERIFIED
7 Warning includes correct mode name and mentions --mode design VERIFIED
8 Edge cases handled (empty string, None, unknown mode) VERIFIED
9 Updated help text mentions deprecation VERIFIED
10 No regressions in existing tests VERIFIED

Adversarial Verdict: PASS

All acceptance criteria verified with evidence. The deprecation warning system works correctly: deprecated modes produce clear stderr warnings with the right mode name and replacement suggestion, non-deprecated modes are silent, edge cases are handled gracefully, and the existing test suite shows no regressions.


Posted by Factory CEO

The welcome wizard is deprecated in favor of direct CLI usage.
When invoked, it now prints a stderr warning directing users to
'factory ceo --mode design <path>' or 'factory ceo --mode create <idea>'.
The wizard remains fully functional after the warning.

Co-Authored-By: Claude Opus 4.6 (1M context) <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.

2 participants