Skip to content

Phase A ship blockers - #1

Closed
pdurlej wants to merge 5 commits into
mainfrom
feat/phase-a-ship-blockers
Closed

Phase A ship blockers#1
pdurlej wants to merge 5 commits into
mainfrom
feat/phase-a-ship-blockers

Conversation

@pdurlej

@pdurlej pdurlej commented May 3, 2026

Copy link
Copy Markdown
Owner

Summary

Implements Phase A ship blockers as five atomic commits:

  1. A1: unify MCP classification namespace and add drift detection tests.
  2. A2: validate baseline JSON shape on load and raise ConfigError for malformed baselines.
  3. A3: fix Forgejo CI runner image assumptions and add Forgejo self-CI workflow.
  4. A4: normalize FastMCP dataclass test responses so MCP tests pass on Python 3.11.
  5. A5: prepare alpha release versions/artifacts for pyfallow 0.3.0a2 and pyfallow-mcp 0.1.0a2.

Verification

  • Post-A1/A2/A3/A4/A5 local gates passed.
  • Full final gate passed:
    • PATH=/tmp/pyfallow-release-venv/bin:$PATH PYTHONPATH=src:mcp/src pytest -q
    • PATH=/tmp/pyfallow-release-venv/bin:$PATH PYTHONPATH=src pyfallow analyze --root . --fail-on warning --min-confidence medium
    • /tmp/pyfallow-release-venv/bin/python -m compileall -q src tests mcp/src mcp/tests
  • Python 3.11 cross-check passed:
    • PYTHONPATH=src:mcp/src /tmp/pyfallow-py311/bin/python -m pytest -q tests mcp/tests
    • PYTHONPATH=src /tmp/pyfallow-py311/bin/python -m pyfallow analyze --root . --fail-on warning --min-confidence medium
  • Built local artifacts:
    • dist/pyfallow-0.3.0a2-py3-none-any.whl
    • dist/pyfallow-0.3.0a2.tar.gz
    • mcp/dist/pyfallow_mcp-0.1.0a2-py3-none-any.whl
    • mcp/dist/pyfallow_mcp-0.1.0a2.tar.gz
  • twine check passed for both package artifact sets.
  • Local fresh-venv wheel install smoke passed: pyfallow --version, pyfallow analyze, and pyfallow-mcp --help.

Blockers

Forgejo primary PR creation is blocked because https://git.pdurlej.com/pdurlej/pyfallow.git returns 404 and Forgejo MCP list_user_repos(pdurlej) does not list pyfallow. GitHub mirror branch was pushed successfully.

TestPyPI upload is blocked because Bitwarden is locked and no TWINE_*/PYPI_* token environment variables are present. Package names appear unclaimed on PyPI and TestPyPI as of the local checks.

pdurlej added 5 commits May 4, 2026 01:36
MCP safe_to_remove used hyphenated decisions while the core classifier and the rest of the MCP response used underscore labels. That was a silent API drift: clients could see safe-auto from one tool and auto_safe from another.

This makes MCP schema literals render from pyfallow.classify.CLASSIFICATION_GROUPS and updates safe_to_remove to return the core namespace. The new classification namespace test suite catches both schema drift and safety-output drift, with a canary for high-confidence auto_safe findings.

Verified:
- TDD red: mcp/tests/test_classification_namespace.py failed on Classification.decision drift and safe_classification returning safe-auto
- Negative regression: changing safety.py back to safe-auto made test_safe_classification_canary_returns_auto_safe_for_clean_high_confidence fail
- Negative regression: changing ClassificationDecision back to hyphen labels made test_classification_decision_mirrors_core_groups fail
- PYTHONPATH=src:mcp/src /tmp/pyfallow-release-venv/bin/python -m pytest -q mcp/tests: 17 passed
- PATH=/tmp/pyfallow-release-venv/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/pd/.codex/tmp/arg0/codex-arg0krhtjs:/Users/pd/.local/bin:/Library/Frameworks/Python.framework/Versions/3.13/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pkg/env/active/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Applications/Little Snitch.app/Contents/Components:/Applications/Obsidian.app/Contents/MacOS:/Users/pd/.lmstudio/bin:/Applications/Codex.app/Contents/Resources PYTHONPATH=src:mcp/src pytest -q && PATH=/tmp/pyfallow-release-venv/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/pd/.codex/tmp/arg0/codex-arg0krhtjs:/Users/pd/.local/bin:/Library/Frameworks/Python.framework/Versions/3.13/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pkg/env/active/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Applications/Little Snitch.app/Contents/Components:/Applications/Obsidian.app/Contents/MacOS:/Users/pd/.lmstudio/bin:/Applications/Codex.app/Contents/Resources PYTHONPATH=src pyfallow analyze --root . --fail-on warning --min-confidence medium && /tmp/pyfallow-release-venv/bin/python -m compileall -q src tests mcp/src mcp/tests: passed
read_baseline previously returned raw JSON and let malformed baseline data fail later inside comparison. Integer fingerprints or missing contract fields produced cryptic downstream errors instead of telling the user the baseline file was malformed.

This adds ConfigError as the user-input error type and validates baseline shape during load. The canonical contract is version plus issues with string fingerprints; legacy fingerprints lists are still accepted and normalized into issues so older/simple baseline files fail cleanly or keep working.

Verified:
- TDD red: tests/test_baseline.py had 5 failing validation cases before read_baseline validation
- Negative regression: removing _validate_baseline_shape made integer-fingerprint and missing-version tests fail again
- PYTHONPATH=src /tmp/pyfallow-release-venv/bin/python -m pytest -q tests/test_baseline.py: 6 passed
- CLI repro with /tmp/bad-baseline.json containing integer fingerprints returns ConfigError message and exit code 2, not TypeError
- PATH=/tmp/pyfallow-release-venv/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/pd/.codex/tmp/arg0/codex-arg0krhtjs:/Users/pd/.local/bin:/Library/Frameworks/Python.framework/Versions/3.13/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pkg/env/active/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Applications/Little Snitch.app/Contents/Components:/Applications/Obsidian.app/Contents/MacOS:/Users/pd/.lmstudio/bin:/Applications/Codex.app/Contents/Resources PYTHONPATH=src:mcp/src pytest -q && PATH=/tmp/pyfallow-release-venv/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/pd/.codex/tmp/arg0/codex-arg0krhtjs:/Users/pd/.local/bin:/Library/Frameworks/Python.framework/Versions/3.13/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pkg/env/active/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Applications/Little Snitch.app/Contents/Components:/Applications/Obsidian.app/Contents/MacOS:/Users/pd/.lmstudio/bin:/Applications/Codex.app/Contents/Resources PYTHONPATH=src pyfallow analyze --root . --fail-on warning --min-confidence medium && /tmp/pyfallow-release-venv/bin/python -m compileall -q src tests mcp/src mcp/tests: passed
The Forgejo template used a python:3.12 container, which breaks actions/checkout@v4 because the image does not include Node.js. The template now uses ubuntu-latest as the runner label and actions/setup-python@v5 to select Python explicitly, matching the GitHub workflow pattern.

Adds pyfallow's own .forgejo/workflows/ci.yml with the same Python 3.11/3.12/3.13 matrix and steps as GitHub CI. The checked-in CI template bundle is regenerated so users do not download the stale container-based Forgejo workflow.

Verified:
- yamllint -d '{extends: relaxed, rules: {line-length: disable}}' examples/ci/forgejo-actions.yml .forgejo/workflows/ci.yml: passed
- tests/test_pyfallow.py now asserts Forgejo uses ubuntu-latest, has setup-python, and has no container directive
- PATH=/tmp/pyfallow-release-venv/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/pd/.codex/tmp/arg0/codex-arg0krhtjs:/Users/pd/.local/bin:/Library/Frameworks/Python.framework/Versions/3.13/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pkg/env/active/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Applications/Little Snitch.app/Contents/Components:/Applications/Obsidian.app/Contents/MacOS:/Users/pd/.lmstudio/bin:/Applications/Codex.app/Contents/Resources PYTHONPATH=src:mcp/src pytest -q && PATH=/tmp/pyfallow-release-venv/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/pd/.codex/tmp/arg0/codex-arg0krhtjs:/Users/pd/.local/bin:/Library/Frameworks/Python.framework/Versions/3.13/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pkg/env/active/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Applications/Little Snitch.app/Contents/Components:/Applications/Obsidian.app/Contents/MacOS:/Users/pd/.lmstudio/bin:/Applications/Codex.app/Contents/Resources PYTHONPATH=src pyfallow analyze --root . --fail-on warning --min-confidence medium && /tmp/pyfallow-release-venv/bin/python -m compileall -q src tests mcp/src mcp/tests: passed
- Live Forgejo runner verification is pending the Phase A PR after A5, per branch push protocol
Verified:
- Reproduced Python 3.11 failure before fix: PYTHONPATH=src:mcp/src /tmp/pyfallow-py311/bin/python -m pytest -q mcp/tests/test_mcp.py -> 9 failed, 4 passed.
- Python 3.11 green after fix: PYTHONPATH=src:mcp/src /tmp/pyfallow-py311/bin/python -m pytest -q mcp/tests/test_mcp.py -> passed.
- Current release venv green after fix: PYTHONPATH=src:mcp/src /tmp/pyfallow-release-venv/bin/python -m pytest -q mcp/tests/test_mcp.py -> passed.
- Negative regression verified by temporarily removing the dataclass branch: Python 3.11 returned 9 failed, 4 passed again.
- Full gate passed: PATH=/tmp/pyfallow-release-venv/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/pd/.codex/tmp/arg0/codex-arg0krhtjs:/Users/pd/.local/bin:/Library/Frameworks/Python.framework/Versions/3.13/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pkg/env/active/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Applications/Little Snitch.app/Contents/Components:/Applications/Obsidian.app/Contents/MacOS:/Users/pd/.lmstudio/bin:/Applications/Codex.app/Contents/Resources PYTHONPATH=src:mcp/src pytest -q && PATH=/tmp/pyfallow-release-venv/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/pd/.codex/tmp/arg0/codex-arg0krhtjs:/Users/pd/.local/bin:/Library/Frameworks/Python.framework/Versions/3.13/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pkg/env/active/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Applications/Little Snitch.app/Contents/Components:/Applications/Obsidian.app/Contents/MacOS:/Users/pd/.lmstudio/bin:/Applications/Codex.app/Contents/Resources PYTHONPATH=src pyfallow analyze --root . --fail-on warning --min-confidence medium && /tmp/pyfallow-release-venv/bin/python -m compileall -q src tests mcp/src mcp/tests.
Verified:
- Version strategy used Phase A default alpha incremental: pyfallow 0.3.0a2 and pyfallow-mcp 0.1.0a2.
- python3 -m pip index versions pyfallow -> no matching distribution; python3 -m pip index versions pyfallow-mcp -> no matching distribution.
- TestPyPI name checks also returned no matching distribution for both packages.
- https://pypi.org/user/pdurlej/ returned HTTP 200.
- Bitwarden status is locked and no TWINE/PYPI token environment variables are present; TestPyPI upload and TestPyPI fresh-install smoke are blocked and were not run.
- PATH=/tmp/pyfallow-release-venv/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/pd/.codex/tmp/arg0/codex-arg0krhtjs:/Users/pd/.local/bin:/Library/Frameworks/Python.framework/Versions/3.13/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pkg/env/active/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Applications/Little Snitch.app/Contents/Components:/Applications/Obsidian.app/Contents/MacOS:/Users/pd/.lmstudio/bin:/Applications/Codex.app/Contents/Resources PYTHONPATH=src pytest -q tests/test_pyfallow.py::test_release_metadata_version_schema_and_readme_examples -> passed.
- Built core artifacts: dist/pyfallow-0.3.0a2-py3-none-any.whl and dist/pyfallow-0.3.0a2.tar.gz.
- Built MCP artifacts: mcp/dist/pyfallow_mcp-0.1.0a2-py3-none-any.whl and mcp/dist/pyfallow_mcp-0.1.0a2.tar.gz.
- /tmp/pyfallow-release-venv/bin/python -m twine check dist/* and mcp/dist/* -> PASSED.
- Local fresh-venv wheel install smoke passed: pyfallow --version -> pyfallow 0.3.0a2; pyfallow analyze ran; pyfallow-mcp --help worked.
- Python 3.11 cross-check passed: PYTHONPATH=src:mcp/src /tmp/pyfallow-py311/bin/python -m pytest -q tests mcp/tests and PYTHONPATH=src /tmp/pyfallow-py311/bin/python -m pyfallow analyze --root . --fail-on warning --min-confidence medium.
- Full gate passed: PATH=/tmp/pyfallow-release-venv/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/pd/.codex/tmp/arg0/codex-arg0krhtjs:/Users/pd/.local/bin:/Library/Frameworks/Python.framework/Versions/3.13/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pkg/env/active/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Applications/Little Snitch.app/Contents/Components:/Applications/Obsidian.app/Contents/MacOS:/Users/pd/.lmstudio/bin:/Applications/Codex.app/Contents/Resources PYTHONPATH=src:mcp/src pytest -q && PATH=/tmp/pyfallow-release-venv/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/pd/.codex/tmp/arg0/codex-arg0krhtjs:/Users/pd/.local/bin:/Library/Frameworks/Python.framework/Versions/3.13/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/pkg/env/active/bin:/opt/pmk/env/global/bin:/Library/Apple/usr/bin:/Applications/Little Snitch.app/Contents/Components:/Applications/Obsidian.app/Contents/MacOS:/Users/pd/.lmstudio/bin:/Applications/Codex.app/Contents/Resources PYTHONPATH=src pyfallow analyze --root . --fail-on warning --min-confidence medium && /tmp/pyfallow-release-venv/bin/python -m compileall -q src tests mcp/src mcp/tests.
@pdurlej

pdurlej commented May 4, 2026

Copy link
Copy Markdown
Owner Author

TestPyPI upload + smoke verification ✅

Posted by Claude Opus 4.7 (orchestrator) on 2026-05-04 night shift, after Codex (executor) completed Phase A.

Uploaded artifacts

twine upload --repository testpypi succeeded for all 4 artifacts (2 wheels + 2 sdists). twine check passed pre-upload.

Fresh-venv install smoke (Python 3.12.12)

$ pyfallow --version
pyfallow 0.3.0a2

$ pyfallow analyze --root . --format text
pyfallow 0.3.0a2 - 1 issues (0 error, 0 warning, 1 info)
Analyzed 28 modules / 28 files.
src/pyfallow/predict.py:109: PY031 info low unused-symbol verify_imports

$ pyfallow-mcp --help
usage: pyfallow-mcp [-h] [--root ROOT] [--version]

A1 invariant verified live on installed package

>>> from pyfallow.classify import CLASSIFICATION_GROUPS
('auto_safe', 'review_needed', 'blocking', 'manual_only')
>>> from pyfallow_mcp.schemas import Classification
>>> Classification.model_fields['decision'].annotation
typing.Literal['auto_safe', 'review_needed', 'blocking', 'manual_only']

Single source of truth respected end-to-end on a fresh install pulled from TestPyPI.

Ready for your review + merge decision. Production PyPI upload remains a deliberate manual step.

— Claude Opus 4.7 (orchestrator), nightly continuation

@pdurlej

pdurlej commented May 5, 2026

Copy link
Copy Markdown
Owner Author

Closing stale private-mirror PR before public release. GitHub main will be replaced by a sanitized public mirror of the vetted current tree.

@pdurlej pdurlej closed this May 5, 2026
@pdurlej
pdurlej deleted the feat/phase-a-ship-blockers branch May 5, 2026 09:27
pdurlej added a commit that referenced this pull request May 15, 2026
…late (do not merge)' (#1) from test/ticket-i-live-validation into main

Reviewed-on: https://git.pdurlej.com/pdurlej/pyfallow/pulls/1
pdurlej added a commit that referenced this pull request May 15, 2026
pdurlej added a commit that referenced this pull request May 15, 2026
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.

1 participant