diff --git a/.github/workflows/publish-test.yml b/.github/workflows/publish-test.yml new file mode 100644 index 0000000..115aa7e --- /dev/null +++ b/.github/workflows/publish-test.yml @@ -0,0 +1,50 @@ +# GitHub Actions CI/CD - TestPyPI Publish Workflow +# Publishes package to TestPyPI for testing + +name: Publish to TestPyPI + +on: + push: + tags: + - 'v*-test*' # Trigger on test tags (e.g., v2.2.0-test, v2.2.0-test1) + workflow_dispatch: # Allow manual trigger + +jobs: + build-and-publish-test: + name: Build and Publish to TestPyPI + runs-on: ubuntu-latest + environment: testpypi # Use GitHub environment for Trusted Publishing + permissions: + id-token: write # Required for Trusted Publishing + contents: read + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install build dependencies + run: | + python -m pip install --upgrade pip + pip install build twine + + - name: Build distribution packages + run: | + python -m build + + - name: Check distribution packages + run: | + twine check dist/* + ls -la dist/ + + - name: Publish to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ + skip-existing: true + verbose: true + print-hash: true diff --git a/.wolf/anatomy.md b/.wolf/anatomy.md index 5ec0859..f0ba4f4 100644 --- a/.wolf/anatomy.md +++ b/.wolf/anatomy.md @@ -1,13 +1,13 @@ # anatomy.md -> Auto-maintained by OpenWolf. Last scanned: 2026-04-06T14:36:32.857Z -> Files: 505 tracked | Anatomy hits: 0 | Misses: 0 +> Auto-maintained by OpenWolf. Last scanned: 2026-04-08T02:00:17.107Z +> Files: 506 tracked | Anatomy hits: 0 | Misses: 0 ## ./ - `.coverage` (~14200 tok) - `.gitignore` — Git ignore rules (~175 tok) -- `AGENTS.md` — Agent Instructions for DevAIFlow (~21192 tok) +- `AGENTS.md` — Agent Instructions for DevAIFlow (~21442 tok) - `CHANGELOG.md` — Change log (~4649 tok) - `CLAUDE.md` — OpenWolf (~139 tok) - `config.schema.json` (~9891 tok) @@ -18,7 +18,7 @@ - `pyproject.toml` — Python project configuration (~702 tok) - `QUICKREF.md` — DevAIFlow Quick Reference (~1384 tok) - `README.md` — Project documentation (~6603 tok) -- `RELEASING.md` — Release Management Process (~2707 tok) +- `RELEASING.md` — Release Management Process (~3523 tok) - `requirements-dev.txt` (~28 tok) - `requirements.txt` — Python dependencies (~72 tok) - `SECURITY.md` — Security Policy (~1380 tok) @@ -42,6 +42,7 @@ - `integration-tests.yml` — GitHub Actions CI/CD - Integration Tests Workflow (~387 tok) - `lint.yml` — GitHub Actions CI/CD - Lint Workflow (~534 tok) +- `publish-test.yml` — GitHub Actions CI/CD - TestPyPI Publish Workflow (~370 tok) - `publish.yml` — GitHub Actions CI/CD - PyPI Publish Workflow (~582 tok) - `test.yml` — GitHub Actions CI/CD - Test Workflow (~467 tok) @@ -456,7 +457,7 @@ - `feature-orchestration-architecture.md` — Feature Orchestration Implementation Summary (~3896 tok) - `feature-orchestration.md` — Feature Orchestration Flow (~4079 tok) - `issue-tracker-architecture.md` — Issue Tracker Architecture (~2650 tok) -- `publishing-to-pypi.md` — Publishing DevAIFlow to PyPI (~3596 tok) +- `publishing-to-pypi.md` — Publishing DevAIFlow to PyPI (~4007 tok) - `release-management.md` — Release Management (~4727 tok) ## docs/experimental/ diff --git a/.wolf/buglog.json b/.wolf/buglog.json index 69c0d01..bf99169 100644 --- a/.wolf/buglog.json +++ b/.wolf/buglog.json @@ -36,6 +36,22 @@ "related_bugs": [], "occurrences": 1, "last_seen": "2026-04-06T14:34:09.151Z" + }, + { + "id": "bug-003", + "timestamp": "2026-04-08T01:53:30.693Z", + "error_message": "Missing error handling in unknown", + "file": "RELEASING.md", + "root_cause": "Code path had no error handling — exceptions would propagate uncaught", + "fix": "Added try/catch block", + "tags": [ + "auto-detected", + "error-handling", + "md" + ], + "related_bugs": [], + "occurrences": 1, + "last_seen": "2026-04-08T01:53:30.693Z" } ] } \ No newline at end of file diff --git a/.wolf/hooks/_session.json b/.wolf/hooks/_session.json index 946c0f6..974e97c 100644 --- a/.wolf/hooks/_session.json +++ b/.wolf/hooks/_session.json @@ -1,6 +1,6 @@ { - "session_id": "session-2026-04-06-1049", - "started": "2026-04-06T14:49:18.559Z", + "session_id": "session-2026-04-08-2200", + "started": "2026-04-08T02:00:56.461Z", "files_read": {}, "files_written": [], "edit_counts": {}, diff --git a/.wolf/memory.md b/.wolf/memory.md index ddc2a6e..3e2f624 100644 --- a/.wolf/memory.md +++ b/.wolf/memory.md @@ -72,3 +72,61 @@ | Time | Action | File(s) | Outcome | ~Tokens | |------|--------|---------|---------|--------| + +## Session: 2026-04-06 10:49 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| + +## Session: 2026-04-08 21:17 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| + +## Session: 2026-04-08 21:18 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| + +## Session: 2026-04-08 21:47 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| + +## Session: 2026-04-08 21:52 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| +| 21:53 | Created .github/workflows/publish-test.yml | — | ~370 | +| 21:53 | Edited RELEASING.md | modified workflow() | ~868 | +| 21:53 | Edited RELEASING.md | 7→8 lines | ~83 | +| 21:54 | Edited docs/developer/publishing-to-pypi.md | expanded (+28 lines) | ~441 | +| 21:54 | Edited docs/developer/publishing-to-pypi.md | 5→7 lines | ~88 | +| 21:54 | Edited docs/developer/publishing-to-pypi.md | modified TestPyPI() | ~163 | + +## Session: 2026-04-08 21:57 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| + +## Session: 2026-04-08 21:57 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| + +## Session: 2026-04-08 21:57 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| +| 21:59 | Created .github/workflows/publish-test.yml, Updated RELEASING.md and publishing-to-pypi.md | publish-test.yml, RELEASING.md, docs/developer/publishing-to-pypi.md | TestPyPI workflow ready | ~1300 | +| 22:00 | Edited AGENTS.md | expanded (+6 lines) | ~227 | +| 22:00 | Edited AGENTS.md | 6→7 lines | ~163 | +| 22:00 | Edited AGENTS.md | 19→21 lines | ~321 | +| 22:00 | Edited AGENTS.md | tests() → needed() | ~202 | +| 22:00 | Updated AGENTS.md testing guidelines | AGENTS.md | Added when to run/skip pytest | ~200 | +| 22:00 | Session end: 4 writes across 1 files (AGENTS.md) | 2 reads | ~22170 tok | + +## Session: 2026-04-08 22:00 + +| Time | Action | File(s) | Outcome | ~Tokens | +|------|--------|---------|---------|--------| diff --git a/.wolf/token-ledger.json b/.wolf/token-ledger.json index 3159677..2f1a9c4 100644 --- a/.wolf/token-ledger.json +++ b/.wolf/token-ledger.json @@ -2,14 +2,14 @@ "version": 1, "created_at": "2026-04-06T12:35:01.489Z", "lifetime": { - "total_tokens_estimated": 108651, - "total_reads": 7, - "total_writes": 15, - "total_sessions": 9, - "anatomy_hits": 5, - "anatomy_misses": 1, - "repeated_reads_blocked": 14, - "estimated_savings_vs_bare_cli": 190705 + "total_tokens_estimated": 153928, + "total_reads": 14, + "total_writes": 19, + "total_sessions": 18, + "anatomy_hits": 7, + "anatomy_misses": 5, + "repeated_reads_blocked": 16, + "estimated_savings_vs_bare_cli": 191105 }, "sessions": [ { @@ -145,6 +145,149 @@ "repeated_reads_blocked": 14, "anatomy_lookups": 5 } + }, + { + "id": "session-2026-04-06-1049", + "started": "2026-04-06T14:49:40.616Z", + "ended": "2026-04-06T14:50:24.604Z", + "reads": [ + { + "file": "/Users/dvernier/development/devaiflow/devaiflow/tests/test_investigate_command.py", + "tokens_estimated": 10764, + "was_repeated": false, + "anatomy_had_description": false + }, + { + "file": "/Users/dvernier/development/devaiflow/devaiflow/devflow/cli/commands/investigate_command.py", + "tokens_estimated": 12343, + "was_repeated": false, + "anatomy_had_description": false + } + ], + "writes": [], + "totals": { + "input_tokens_estimated": 23107, + "output_tokens_estimated": 0, + "reads_count": 2, + "writes_count": 0, + "repeated_reads_blocked": 0, + "anatomy_lookups": 1 + } + }, + { + "id": "session-2026-04-08-2157", + "started": "2026-04-08T01:57:55.993Z", + "ended": "2026-04-08T01:58:02.699Z", + "reads": [ + { + "file": "/private/tmp/claude-501/-Users-dvernier-development-devaiflow-devaiflow/b25d0f9a-8d0d-4273-b2e2-3fe114b626aa/tasks/bmqy2pcnp.output", + "tokens_estimated": 0, + "was_repeated": false, + "anatomy_had_description": false + } + ], + "writes": [], + "totals": { + "input_tokens_estimated": 0, + "output_tokens_estimated": 0, + "reads_count": 1, + "writes_count": 0, + "repeated_reads_blocked": 0, + "anatomy_lookups": 0 + } + }, + { + "id": "session-2026-04-08-2157", + "started": "2026-04-08T01:57:55.993Z", + "ended": "2026-04-08T01:58:43.135Z", + "reads": [ + { + "file": "/private/tmp/claude-501/-Users-dvernier-development-devaiflow-devaiflow/b25d0f9a-8d0d-4273-b2e2-3fe114b626aa/tasks/bmqy2pcnp.output", + "tokens_estimated": 0, + "was_repeated": false, + "anatomy_had_description": false + } + ], + "writes": [], + "totals": { + "input_tokens_estimated": 0, + "output_tokens_estimated": 0, + "reads_count": 1, + "writes_count": 0, + "repeated_reads_blocked": 0, + "anatomy_lookups": 0 + } + }, + { + "id": "session-2026-04-08-2157", + "started": "2026-04-08T01:57:55.993Z", + "ended": "2026-04-08T01:59:32.777Z", + "reads": [ + { + "file": "/private/tmp/claude-501/-Users-dvernier-development-devaiflow-devaiflow/b25d0f9a-8d0d-4273-b2e2-3fe114b626aa/tasks/bmqy2pcnp.output", + "tokens_estimated": 0, + "was_repeated": true, + "anatomy_had_description": false + } + ], + "writes": [], + "totals": { + "input_tokens_estimated": 0, + "output_tokens_estimated": 0, + "reads_count": 1, + "writes_count": 0, + "repeated_reads_blocked": 1, + "anatomy_lookups": 0 + } + }, + { + "id": "session-2026-04-08-2157", + "started": "2026-04-08T01:57:55.993Z", + "ended": "2026-04-08T02:00:32.232Z", + "reads": [ + { + "file": "/private/tmp/claude-501/-Users-dvernier-development-devaiflow-devaiflow/b25d0f9a-8d0d-4273-b2e2-3fe114b626aa/tasks/bmqy2pcnp.output", + "tokens_estimated": 0, + "was_repeated": true, + "anatomy_had_description": false + }, + { + "file": "/Users/dvernier/development/devaiflow/devaiflow/AGENTS.md", + "tokens_estimated": 21192, + "was_repeated": false, + "anatomy_had_description": false + } + ], + "writes": [ + { + "file": "/Users/dvernier/development/devaiflow/devaiflow/AGENTS.md", + "tokens_estimated": 243, + "action": "edit" + }, + { + "file": "/Users/dvernier/development/devaiflow/devaiflow/AGENTS.md", + "tokens_estimated": 175, + "action": "edit" + }, + { + "file": "/Users/dvernier/development/devaiflow/devaiflow/AGENTS.md", + "tokens_estimated": 344, + "action": "edit" + }, + { + "file": "/Users/dvernier/development/devaiflow/devaiflow/AGENTS.md", + "tokens_estimated": 216, + "action": "edit" + } + ], + "totals": { + "input_tokens_estimated": 21192, + "output_tokens_estimated": 978, + "reads_count": 2, + "writes_count": 4, + "repeated_reads_blocked": 1, + "anatomy_lookups": 1 + } } ], "daemon_usage": [], diff --git a/AGENTS.md b/AGENTS.md index 5c7a8ca..5a8b448 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -563,6 +563,12 @@ except JiraAuthError as e: ### Testing Guidelines +**When to Run Tests**: +- ✅ **Run pytest** when making code changes (Python files in `devflow/`, `tests/`, or `integration-tests/`) +- ⏭️ **Skip pytest** for documentation-only changes (`.md` files, `docs/`, `CHANGELOG.md`, `README.md`) +- ⏭️ **Skip pytest** for workflow/config-only changes (`.github/workflows/`, `.yml` files without code changes) +- ⚠️ **Always run pytest** if you modified any `.py` file, even if it seems minor + **Integration Tests**: Can be run from inside AI agent sessions using the test runner script `./run_all_integration_tests.sh`, which uses environment isolation (unsets `DEVAIFLOW_IN_SESSION` and `AI_AGENT_SESSION_ID`, sets temporary `DEVAIFLOW_HOME`) to avoid conflicts. Individual test scripts still require running outside AI agent sessions unless you manually set up the isolated environment. **Debug Mode for Integration Tests**: All integration test scripts support a `--debug` flag that enables verbose bash output (`set -x`) for easier troubleshooting: @@ -587,12 +593,13 @@ When `--debug` is used with the test runner, it automatically propagates to all - Use `pytest -x --tb=line -v` to see which test is running and stop on first failure - Check for missing input sequences in `CliRunner().invoke(..., input="...")` calls -**⚠️ CRITICAL TESTING REQUIREMENT**: ALL TESTS MUST BE SUCCESSFUL before marking any task as complete. When tests fail: +**⚠️ CRITICAL TESTING REQUIREMENT**: ALL TESTS MUST BE SUCCESSFUL before marking any task as complete when code changes are made. When tests fail: - **DO NOT** ask the user for permission to continue fixing tests - **DO NOT** stop after fixing some tests - continue fixing ALL failing tests -- **ALWAYS** run the full test suite (`pytest`) after every change -- **ONLY** mark the task as complete when ALL 2000+ tests pass +- **ALWAYS** run the full test suite (`pytest`) after every code change +- **ONLY** mark the task as complete when ALL 3600+ tests pass - If you encounter test failures, continue fixing them systematically until every test passes +- **EXCEPTION**: Documentation-only or workflow-only changes do not require running pytest **⚠️ TEST DATA ANONYMIZATION REQUIREMENT**: ALL test data MUST be anonymized and MUST NOT contain organization-specific information: - **NEVER** use real organization names, project names, product names, or service names in test data @@ -606,7 +613,7 @@ When `--debug` is used with the test runner, it automatically propagates to all - **RATIONALE**: Tests should be portable and not tied to any specific organization's configuration - **ENFORCEMENT**: When writing or modifying tests, search for organization-specific terms and replace with generic equivalents -**IMPORTANT**: These testing requirements must be followed for all code changes: +**IMPORTANT**: These testing requirements must be followed for code changes: 1. **Create Tests for New Methods** - When creating any new method or function, **always create a corresponding test** @@ -617,9 +624,11 @@ When `--debug` is used with the test runner, it automatically propagates to all - Boundary conditions - Integration with other components (using mocks) -2. **Run Full Test Suite After Each Task** - - **After completing each task**, run the complete test suite using `pytest` - - Verify that all tests pass before marking a task as complete +2. **Run Full Test Suite After Code Changes** + - **After completing each task with code changes**, run the complete test suite using `pytest` + - **Documentation-only changes** (markdown files, docs/, CHANGELOG.md) do NOT require running pytest + - **Workflow-only changes** (.github/workflows/ without code changes) do NOT require running pytest + - Verify that all tests pass before marking a code change task as complete - This ensures that new changes don't break existing functionality - If any tests fail: - Fix the failing tests immediately @@ -687,6 +696,7 @@ When `--debug` is used with the test runner, it automatically propagates to all **Example Testing Workflow**: ```bash +# CODE CHANGE EXAMPLE: # 1. Implement new method in devflow/session/manager.py # 2. Create test in tests/test_session_manager.py # 3. Run tests to verify new functionality (can run inside Claude Code) @@ -695,10 +705,15 @@ pytest tests/test_session_manager.py # 4. Run full test suite before completing task (can run inside Claude Code) pytest -# 5. Run integration tests (can run inside Claude Code with test runner) +# 5. Run integration tests if needed (can run inside Claude Code with test runner) cd integration-tests && ./run_all_integration_tests.sh # 6. Only mark task as complete if ALL tests pass (unit + integration) + +# DOCUMENTATION-ONLY CHANGE EXAMPLE: +# 1. Update docs/developer/publishing-to-pypi.md +# 2. Update RELEASING.md +# 3. No pytest needed - mark task complete after verifying changes ``` ### Test Coverage Strategy diff --git a/RELEASING.md b/RELEASING.md index 34a4317..87da660 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -363,6 +363,121 @@ git branch -d hotfix/1.0.1 git push origin --delete hotfix/1.0.1 ``` +## Testing Releases with TestPyPI + +Before publishing to production PyPI, it's recommended to test the release workflow using TestPyPI. This helps catch packaging issues early without affecting the production package. + +### Prerequisites + +1. **TestPyPI Account**: Create account at https://test.pypi.org/account/register/ +2. **Trusted Publishing Setup**: Configure at https://test.pypi.org/manage/account/publishing/ + - PyPI Project Name: `devaiflow` + - Owner: `itdove` + - Repository name: `devaiflow` + - Workflow name: `publish-test.yml` + - Environment name: `testpypi` +3. **GitHub Environment**: Create environment `testpypi` at https://github.com/itdove/devaiflow/settings/environments + +### Tag Naming Convention + +- **TestPyPI**: `v2.2.0-test`, `v2.2.0-test1`, `v2.2.0-test2` +- **Production PyPI**: `v2.2.0`, `v2.2.1`, `v2.3.0` + +The `-test` suffix ensures the workflow only triggers for test releases. + +### Testing Workflow + +#### 1. Create Test Release Branch + +```bash +# Create test release branch from main +git checkout -b release-2.2-test main +``` + +#### 2. Update Version to Test Version + +Update version in **devflow/__init__.py**: +```python +__version__ = "2.2.0-test1" +``` + +Update version in **pyproject.toml**: +```toml +[project] +version = "2.2.0-test1" +``` + +#### 3. Commit and Tag + +```bash +# Commit version changes +git add pyproject.toml devflow/__init__.py +git commit -m "chore: bump version to 2.2.0-test1 for TestPyPI" + +# Create and push test tag +git tag -a v2.2.0-test1 -m "Test release for TestPyPI" +git push origin v2.2.0-test1 +``` + +#### 4. Monitor GitHub Actions + +The `publish-test.yml` workflow will automatically trigger: +1. Go to https://github.com/itdove/devaiflow/actions +2. Find the "Publish to TestPyPI" workflow run +3. Monitor the build and publish steps + +#### 5. Verify Publication on TestPyPI + +Visit https://test.pypi.org/project/devaiflow/ to verify: +- Package uploaded successfully +- Version number is correct +- README renders properly +- Metadata is accurate + +#### 6. Test Installation from TestPyPI + +```bash +# Create test environment +python -m venv /tmp/test-devaiflow +source /tmp/test-devaiflow/bin/activate + +# Install from TestPyPI +pip install --index-url https://test.pypi.org/simple/ \ + --extra-index-url https://pypi.org/simple/ \ + devaiflow==2.2.0-test1 + +# Verify installation +daf --version +daf --help + +# Cleanup +deactivate +rm -rf /tmp/test-devaiflow +``` + +#### 7. Proceed with Production Release + +If testing is successful: +1. Delete the test branch: `git branch -D release-2.2-test` +2. Follow the regular release workflow with production version (v2.2.0) +3. Production workflow (`publish.yml`) will publish to production PyPI + +### Manual Workflow Trigger + +You can also manually trigger the TestPyPI workflow: +1. Go to https://github.com/itdove/devaiflow/actions/workflows/publish-test.yml +2. Click "Run workflow" +3. Select the branch with test version +4. Click "Run workflow" button + +### Automated Workflow Details + +The TestPyPI workflow (`.github/workflows/publish-test.yml`): +- Triggers on tags matching `v*-test*` +- Uses Trusted Publishing (no API tokens needed) +- Publishes to https://test.pypi.org/ +- Supports `skip-existing` for re-running tests + ## Release Checklist Use this checklist for each release: @@ -374,6 +489,7 @@ Use this checklist for each release: - [ ] CHANGELOG.md updated with all changes - [ ] Version bump PR reviewed and approved - [ ] JIRA epic marked as complete +- [ ] (Optional) Test release on TestPyPI with `-test` suffix tag ### Release Branch - [ ] Create release branch (`release/X.Y`) diff --git a/docs/developer/publishing-to-pypi.md b/docs/developer/publishing-to-pypi.md index 314d751..e648dc8 100644 --- a/docs/developer/publishing-to-pypi.md +++ b/docs/developer/publishing-to-pypi.md @@ -2,8 +2,36 @@ This guide documents the complete process for publishing the DevAIFlow package to PyPI (Python Package Index). +## Publishing Methods + +DevAIFlow supports two methods for publishing to PyPI: + +### 1. Automated Publishing via GitHub Actions (Recommended) + +The repository includes GitHub Actions workflows for automated publishing using **Trusted Publishing** (no API tokens needed): + +- **`.github/workflows/publish-test.yml`**: Publishes to TestPyPI when you push tags matching `v*-test*` (e.g., `v2.2.0-test1`) +- **`.github/workflows/publish.yml`**: Publishes to production PyPI when you push tags matching `v*` (e.g., `v2.2.0`) + +**Benefits:** +- ✅ No API tokens to manage or secure +- ✅ Uses OpenID Connect (OIDC) Trusted Publishing +- ✅ Automatic builds on tag push +- ✅ Consistent, repeatable builds +- ✅ Audit trail via GitHub Actions logs + +**See [RELEASING.md](../../RELEASING.md#testing-releases-with-testpypi) for complete automated workflow instructions.** + +### 2. Manual Publishing (Documented Below) + +Traditional manual publishing using local builds and twine. Useful for: +- Testing without creating git tags +- Troubleshooting packaging issues +- One-off manual releases + ## Table of Contents +- [Publishing Methods](#publishing-methods) - [Prerequisites](#prerequisites) - [Pre-Release Checklist](#pre-release-checklist) - [Building the Package](#building-the-package) @@ -15,6 +43,8 @@ This guide documents the complete process for publishing the DevAIFlow package t ## Prerequisites +**Note:** If using the automated GitHub Actions workflow (recommended), you only need TestPyPI and PyPI accounts with Trusted Publishing configured. API tokens and `~/.pypirc` configuration are NOT required for automated publishing. + ### 1. PyPI Accounts You need accounts on both TestPyPI and production PyPI: @@ -31,7 +61,13 @@ You need accounts on both TestPyPI and production PyPI: 1. Register at https://test.pypi.org/account/register/ 2. Verify your email address 3. Enable Two-Factor Authentication (2FA) - **Required** -4. Create an API token at https://test.pypi.org/manage/account/token/ +4. **For Automated Publishing**: Configure Trusted Publishing at https://test.pypi.org/manage/account/publishing/ + - PyPI Project Name: `devaiflow` + - Owner: `itdove` + - Repository name: `devaiflow` + - Workflow name: `publish-test.yml` + - Environment name: `testpypi` +5. **For Manual Publishing**: Create an API token at https://test.pypi.org/manage/account/token/ - Token name: "DevAIFlow Testing" - Scope: "Entire account"