From 02ce4594881e2175dfcfec47ad79f528d7295398 Mon Sep 17 00:00:00 2001 From: David Vujic Date: Sun, 17 May 2026 14:41:23 +0200 Subject: [PATCH 1/6] wip: migrate to polylith skills --- .../skills/polylith/migrate-project/README.md | 53 ++++++ .../skills/polylith/migrate-project/SKILLS.md | 58 ++++++ .../migrate-project/convert-linter/skill.md | 60 ++++++ .../convert-package-manager/skill.md | 76 ++++++++ .../convert-type-checker/skill.md | 58 ++++++ .../polylith/migrate-project/dedupe/skill.md | 62 +++++++ .../definition-of-done/skill.md | 56 ++++++ .../migrate-project/discover/skill.md | 115 ++++++++++++ .../distribute-wiring/skill.md | 57 ++++++ .../extract-standalone-modules/skill.md | 44 +++++ .../migrate-project/extract-to-base/skill.md | 62 +++++++ .../isolate-base-and-big-component/skill.md | 62 +++++++ .../isolate-shared-and-project-logic/skill.md | 66 +++++++ .../migrate-project/orchestrator/skill.md | 175 ++++++++++++++++++ .../migrate-project/prepare-project/skill.md | 61 ++++++ .../migrate-project/refactor-tests/skill.md | 49 +++++ .../split-big-component/skill.md | 166 +++++++++++++++++ .../split-component-internals/skill.md | 45 +++++ 18 files changed, 1325 insertions(+) create mode 100644 .agents/skills/polylith/migrate-project/README.md create mode 100644 .agents/skills/polylith/migrate-project/SKILLS.md create mode 100644 .agents/skills/polylith/migrate-project/convert-linter/skill.md create mode 100644 .agents/skills/polylith/migrate-project/convert-package-manager/skill.md create mode 100644 .agents/skills/polylith/migrate-project/convert-type-checker/skill.md create mode 100644 .agents/skills/polylith/migrate-project/dedupe/skill.md create mode 100644 .agents/skills/polylith/migrate-project/definition-of-done/skill.md create mode 100644 .agents/skills/polylith/migrate-project/discover/skill.md create mode 100644 .agents/skills/polylith/migrate-project/distribute-wiring/skill.md create mode 100644 .agents/skills/polylith/migrate-project/extract-standalone-modules/skill.md create mode 100644 .agents/skills/polylith/migrate-project/extract-to-base/skill.md create mode 100644 .agents/skills/polylith/migrate-project/isolate-base-and-big-component/skill.md create mode 100644 .agents/skills/polylith/migrate-project/isolate-shared-and-project-logic/skill.md create mode 100644 .agents/skills/polylith/migrate-project/orchestrator/skill.md create mode 100644 .agents/skills/polylith/migrate-project/prepare-project/skill.md create mode 100644 .agents/skills/polylith/migrate-project/refactor-tests/skill.md create mode 100644 .agents/skills/polylith/migrate-project/split-big-component/skill.md create mode 100644 .agents/skills/polylith/migrate-project/split-component-internals/skill.md diff --git a/.agents/skills/polylith/migrate-project/README.md b/.agents/skills/polylith/migrate-project/README.md new file mode 100644 index 00000000..8c324521 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/README.md @@ -0,0 +1,53 @@ +# Project Migration Skills + +This directory contains skills for migrating **non-Polylith Python projects** into a Polylith workspace. +These skills are **only** relevant when a human explicitly requests to migrate a specific project (e.g., "Migrate the project `my-app` in `/projects/my-app` to Polylith"). + +--- + +## ⚠️ Important Disclaimer +**These skills are NOT for daily Polylith workflows or already-migrated projects.** +They are **only** intended for use when: +1. A human has **explicitly instructed** to migrate a specific project. +2. The target project has been copied into the `/projects` folder of this Polylith workspace. +3. The goal is to refactor the project into Polylith bricks (bases, components, etc.). + +**Do NOT use these skills for:** +- Automated or unattended migrations. +- Projects that are already structured as Polylith workspaces. +- Daily development tasks in a Polylith workspace. + +--- + +## Purpose +The skills in this directory automate and standardize the process of migrating a Python project to a Polylith workspace. They ensure the migration is performed consistently and efficiently, with minimal manual intervention. + +--- + +## How to Use +1. **Explicit Invocation**: Load the `migrate.orchestrator` skill and specify the project name (e.g., `my-app`). + Example: + ``` + Load the `migrate.orchestrator` skill and provide the project name (e.g., `my-app`). + ``` + +2. **Follow the Workflow**: The orchestrator will guide you through the migration process, ensuring all dependencies are satisfied. + +3. **Verify Completion**: After executing a skill, verify its completion before proceeding to the next step. Each skill includes a "Verify" section to confirm its success. + +--- + +## Key Files +- [`SKILLS.md`](./SKILLS.md): Index of all project migration skills, their descriptions, and dependencies. +- [`orchestrator/skill.md`](./orchestrator/skill.md): Defines the step-by-step migration process and ensures all dependencies are satisfied. + +--- + +## Workflow Overview +The migration process consists of the following phases: +1. **Discovery**: Inspect the project and create `state.md` and `manifest.md`. +2. **Tooling Standardization**: Align the project's tooling (e.g., package manager, linter, type checker) with the workspace's standards. +3. **Extraction**: Extract application code into a temporary migration base and prepare the project for Polylith. +4. **Refactoring**: Split the big component into focused components, extract standalone modules, and isolate shared/project-specific logic. +5. **Testing**: Restructure unit tests to align with the workspace's Polylith theme. +6. **Completion**: Verify that all migration criteria are met. diff --git a/.agents/skills/polylith/migrate-project/SKILLS.md b/.agents/skills/polylith/migrate-project/SKILLS.md new file mode 100644 index 00000000..a0c426ed --- /dev/null +++ b/.agents/skills/polylith/migrate-project/SKILLS.md @@ -0,0 +1,58 @@ +--- +name: migrate-project +description: Skills for migrating a non-Polylith Python project into a Polylith workspace. **Only use when explicitly requested by a human.** +--- + +# Project Migration Skills Index + +This file serves as an index for all **project migration skills** available in the Polylith CLI. These skills are **only** for migrating **non-Polylith Python projects** into a Polylith workspace **when explicitly requested by a human**. + +**⚠️ When to Use These Skills** +- A human has **explicitly instructed** to migrate a specific project (e.g., "Migrate the project `my-app` in `/projects/my-app` to Polylith"). +- The target project has been copied into the `/projects` folder of this Polylith workspace. +- These skills are **not** relevant for daily Polylith workflows or already-migrated projects. +- They are **not** intended for automated or unattended use. + +## Orchestrator +| Skill | Description | Dependencies | +|-------|-------------|--------------| +| [orchestrator](./orchestrator/skill.md) | Orchestrates the migration workflow by defining the order and dependencies of migration skills. | None | + +## Discovery +| Skill | Description | Dependencies | +|-------|-------------|--------------| +| [discover](./discover/skill.md) | Create `migration//state.md` and `migration//manifest.md` by inspecting the existing project under `projects//`. Detect the current linter and type checker, and ask the user if they want to convert them. | None | + +## Extraction +| Skill | Description | Dependencies | +|-------|-------------|--------------| +| [extract-to-base](./extract-to-base/skill.md) | Extract all application code from `projects//` into a temporary migration base. | discover | +| [prepare-project](./prepare-project/skill.md) | Clean up the project subfolder and consolidate dependencies. | extract-to-base | +| [isolate-base-and-big-component](./isolate-base-and-big-component/skill.md) | Shrink the temporary migration base into thin bases and a big component. | prepare-project | + +## Refactoring +| Skill | Description | Dependencies | +|-------|-------------|--------------| +| [split-big-component](./split-big-component/skill.md) | Split the big component into multiple focused components. | isolate-base-and-big-component | +| [extract-standalone-modules](./extract-standalone-modules/skill.md) | Extract foundational modules (e.g., `consts.py`, `exceptions.py`) into standalone components. | split-big-component | +| [isolate-shared-and-project-logic](./isolate-shared-and-project-logic/skill.md) | Identify and isolate shared and project-specific logic in monolithic components (e.g., `models`, `schemas`). | extract-standalone-modules, split-big-component | +| [distribute-wiring](./distribute-wiring/skill.md) | Distribute app-wiring code from the residual component into the appropriate bases and shared components. | isolate-shared-and-project-logic | +| [split-component-internals](./split-component-internals/skill.md) | Split monolithic `core.py` files in generic components (e.g., `models`, `schemas`) into domain-focused modules. | distribute-wiring | +| [refactor-tests](./refactor-tests/skill.md) | Restructure unit tests to align with the workspace's Polylith theme. | split-component-internals | + +## Optional Tooling Conversions +| Skill | Description | Dependencies | +|-------|-------------|--------------| +| [convert-linter](./convert-linter/skill.md) | Replace the project's existing linter and formatter with **ruff** (if the user opts in). | discover | +| [convert-type-checker](./convert-type-checker/skill.md) | Replace the project's existing type checker with **ty** (if the user opts in). | discover | +| [convert-package-manager](./convert-package-manager/skill.md) | Convert the project's `pyproject.toml` to PEP 621/uv format and register it as a workspace member (if the user opts in). | discover | + +## Optional Deduplication +| Skill | Description | Dependencies | +|-------|-------------|--------------| +| [dedupe](./dedupe/skill.md) | Identify and execute controlled deduplication of code during migration (if the user opts in). | split-big-component, extract-standalone-modules | + +## Completion +| Skill | Description | Dependencies | +|-------|-------------|--------------| +| [definition-of-done](./definition-of-done/skill.md) | Define the criteria for completing the migration process. | refactor-tests | diff --git a/.agents/skills/polylith/migrate-project/convert-linter/skill.md b/.agents/skills/polylith/migrate-project/convert-linter/skill.md new file mode 100644 index 00000000..1801b4e6 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/convert-linter/skill.md @@ -0,0 +1,60 @@ +--- +name: convert-linter +description: Remove project-specific linting and formatting configurations and consolidate them into the workspace root. Align the project with the workspace's linting and formatting standards. +--- + +# Skill: convert-linter + +## Goal +Remove project-specific linting and formatting configurations and consolidate them into the workspace root. Align the project with the workspace's linting and formatting standards. + +## When to Skip +Skip this step if the project's `LINTER` and `FORMATTER` in `migration//state.md` match the workspace's tools. + +## Inputs +From `migration//state.md`: +- `PROJECT_DIR` +- `LINTER`, `FORMATTER` +- `RUN_TEST_CMD` (optional: `RUN_LINT_CMD`, `RUN_TYPECHECK_CMD`) + +## Steps + +### 1. Remove Project-Specific Configs and Dependencies +- Remove the following sections from the project's `pyproject.toml`: + - `[tool.black]`, `[tool.isort]`, `[tool.flake8]`, `[tool.pylint.*]`, `[tool.autopep8]`, `[tool.pycodestyle]` +- Remove standalone config files: `.flake8`, `.pylintrc`, `.isort.cfg`, and lint sections in `setup.cfg` or `tox.ini`. +- Remove old linter/formatter dependencies from the project's `pyproject.toml`: + - `flake8`, `flake8-*` plugins, `pylint`, `pylint-*` plugins, `black`, `isort`, `autopep8`, `pyflakes`, `pycodestyle`, `bandit` + +### 2. Assess Workspace Linting Config +- Review the workspace root's linting and formatting configuration. +- Identify any project-specific rules or ignores that differ from the workspace's standards. + +### 3. Merge Project-Specific Rules +- If the project has unique linting rules or ignores, merge them into the workspace root's `[tool.ruff]` configuration. +- For conflicts (e.g., stricter rules in the project), ask the user to provide guidance on whether to: + - Adopt the project's rules in the workspace. + - Suppress the project's rules in favor of the workspace's. + - Defer the decision and document the conflict in `migration//state.md`. + +### 4. Run Workspace Linting and Formatting Tools +- Run the workspace's linting tool to assess violations: + - **Few new violations**: Fix them now. + - **Many new violations**: Ask the user whether to fix, suppress, or defer. +- Run the workspace's formatting tool to reformat the code. + +### 5. Update `state.md` +- Set `LINTER` and `FORMATTER` to the workspace's tool(s). +- Update `RUN_LINT_CMD` to use the workspace's linting and formatting commands. + +## Verify +- The workspace's linting tool passes (or remaining violations are user-approved). +- The workspace's formatting tool passes. +- `RUN_TEST_CMD` succeeds. +- If set, `RUN_TYPECHECK_CMD` succeeds. + +## Done When +- No project-specific linter/formatter config files or dependencies remain. +- Project-specific linting rules are merged into the workspace root's configuration. +- `LINTER` and `FORMATTER` in `migration//state.md` match the workspace's tools. +- Tests pass via the workspace's tooling. diff --git a/.agents/skills/polylith/migrate-project/convert-package-manager/skill.md b/.agents/skills/polylith/migrate-project/convert-package-manager/skill.md new file mode 100644 index 00000000..0fcfd2e2 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/convert-package-manager/skill.md @@ -0,0 +1,76 @@ +--- +name: convert-package-manager +description: Convert the project's `pyproject.toml` to PEP 621/uv format and register it as a workspace member (if the user opts in). +--- + +# Skill: convert-package-manager + +## Goal +Convert the project's `pyproject.toml` to PEP 621/uv format and register it as a workspace member. This step ensures the project shares the workspace's single lock file and virtual environment, preventing version skew. + +## When to Skip +- If `PACKAGE_MANAGER=uv` in `migration//state.md`, skip this step and proceed to the next skill. + +## Inputs +From `migration//state.md`: +- `PROJECT_DIR` +- `PACKAGE_MANAGER` +- `RUN_TEST_CMD` (optional: `RUN_LINT_CMD`, `RUN_TYPECHECK_CMD`) + +## Steps + +### 1. Ask for User Approval +- Ask the user if they want to convert the project's `pyproject.toml` to PEP 621/uv format. +- Record their choice in `state.md`: + ```text + CONVERT_PACKAGE_MANAGER= + ``` + +### 2. Rewrite `pyproject.toml` to PEP 621/uv Format +- Move `[tool.poetry.dependencies]` to `[project] dependencies`. Keep only **runtime** (non-dev, non-test) dependencies in the project, listed **without version constraints**. +- Remove Poetry-specific sections: `[tool.poetry]`, `[tool.poetry.group.*]`, `[[tool.poetry.source]]`, and `[build-system]` with `poetry-core`. +- Add a `[build-system]` with `hatchling` (or the workspace's build backend). +- Preserve `[tool.*]` sections for other tools (e.g., pytest, ruff, mypy). +- Add `[tool.uv]` only if project-level uv configuration is needed. + +### 3. Register as a Workspace Member +- Add the project path to the workspace root `pyproject.toml` under `[tool.uv.workspace] members`. + Example: + ```toml + members = ["projects/example-service-b"] + ``` + +### 4. Consolidate Dependencies +- Add all **third-party runtime dependencies with version constraints** to the workspace root `pyproject.toml` `[project] dependencies`. +- Move all dev/test/tooling dependencies to the workspace root `[dependency-groups]` (e.g., `dev = [...]`, `test = [...]`). +- Ensure the project's `pyproject.toml` lists runtime dependencies **without version numbers**. + +### 5. Lock and Sync +- Run `uv lock` from the workspace root to regenerate `uv.lock` with the new member. +- Run `uv sync` to install all dependencies into the shared `.venv`. +- Resolve any version conflicts that arise during `uv lock`. + +### 6. Delete Old Lock Files +- Remove `poetry.lock`, `Pipfile.lock`, and generated `requirements*.txt` from the project directory. + +### 7. Update Verification Commands +- Replace any `poetry run`, `pipenv run`, or bare commands with `uv run` equivalents in `migration//state.md`. + Example: + ```text + RUN_TEST_CMD=uv run pytest + RUN_LINT_CMD=uv run ruff check + ``` +- Update `PACKAGE_MANAGER=uv` in `state.md`. + +## Verify +- Run the updated `RUN_TEST_CMD` and confirm the same pass/fail counts as before the conversion. +- If set, run `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD`. +- Ensure `uv lock` and `uv sync` succeed from the workspace root. + +## Done When +- The project is listed as a workspace member in the root `pyproject.toml`. +- `uv lock` and `uv sync` succeed from the workspace root. +- Old lock files (`poetry.lock`, `Pipfile.lock`, generated `requirements*.txt`) are deleted from the project directory. +- `PACKAGE_MANAGER=uv` is recorded in `migration//state.md`. +- Verification commands in `state.md` use `uv run`. +- Tests pass via `uv run`. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/convert-type-checker/skill.md b/.agents/skills/polylith/migrate-project/convert-type-checker/skill.md new file mode 100644 index 00000000..dc4b6101 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/convert-type-checker/skill.md @@ -0,0 +1,58 @@ +--- +name: convert-type-checker +description: Replace the project's existing type checker with the workspace's configured type-checking tool to align with the workspace's standards. +--- + +# Skill: convert-type-checker + +## Goal +Replace the project's existing type checker with the workspace's configured type-checking tool to align with the workspace's standards. + +## When to Skip +Skip this step if the project's `TYPE_CHECKER` in `migration//state.md` matches the workspace's type-checking tool. + +## Inputs +From `migration//state.md`: +- `PROJECT_DIR` +- `TYPE_CHECKER` +- `RUN_TEST_CMD` (optional: `RUN_LINT_CMD`, `RUN_TYPECHECK_CMD`) + +## Steps + +### 1. Remove Old Configs and Dependencies +- Remove the following sections from `pyproject.toml`: + - `[tool.mypy]`, `[[tool.mypy.overrides]]`, `[mypy-*]`, `[tool.pyright]`, `[tool.pytype]` +- Remove standalone config files: `mypy.ini`, `.mypy.ini`, `pyrightconfig.json`. +- Remove old type checker dependencies from `pyproject.toml`: + - `mypy`, `mypy-extensions`, `types-*` stub packages, `pyright`, `pytype`, `sqlalchemy-stubs`, `django-stubs`. + +### 2. Clean Up Type Ignore Comments +- Remove `# type: ignore[]` comments that reference mypy-specific error codes. +- Leave comments that suppress real issues and document them in `state.md`. + +### 3. Adopt Workspace Type-Checking Config +- The workspace root `pyproject.toml` may define the type-checking configuration. The project inherits this config. +- Add project-specific overrides if needed. + +### 4. Run the Workspace's Type-Checking Tool +- Run the workspace's type-checking tool to assess errors: + - **Same or fewer errors**: No action needed. + - **New errors**: Ask the user whether to fix, suppress, or adjust the config. + - **Missing stub errors**: Silence with per-module ignores in the workspace's type-checking config. + +### 5. Update `state.md` +- Set `TYPE_CHECKER` to the workspace's type-checking tool. +- Update `RUN_TYPECHECK_CMD` to use the workspace's type-checking command. + +## Verify +- The workspace's type-checking tool runs cleanly (or remaining errors are user-approved). +- `RUN_TEST_CMD` succeeds. +- If set, `RUN_LINT_CMD` succeeds. + +## Done When +- No old type checker config files remain. +- Old type checker dependencies are removed from `pyproject.toml`. +- Stale `# type: ignore` comments referencing tool-specific codes are removed (or documented if intentionally kept). +- `TYPE_CHECKER` in `migration//state.md` matches the workspace's type-checking tool. +- The workspace's type-checking tool runs cleanly or known issues are documented in `state.md`. +- Tests pass via the workspace's tooling. diff --git a/.agents/skills/polylith/migrate-project/dedupe/skill.md b/.agents/skills/polylith/migrate-project/dedupe/skill.md new file mode 100644 index 00000000..209c37be --- /dev/null +++ b/.agents/skills/polylith/migrate-project/dedupe/skill.md @@ -0,0 +1,62 @@ +--- +name: dedupe +description: Identify and execute controlled deduplication of code during migration (if the user opts in). +--- + +# Skill: dedupe + +## Goal +Identify duplication candidates during the migration process and execute controlled deduplication for user-approved candidates. + +## When to Use +- After splitting the big component or extracting standalone modules. +- When potential duplication between components is suspected. + +## Inputs +From `migration//state.md`: +- `TARGET_TOP_NS` +- Verification commands (`RUN_TEST_CMD`, `RUN_LINT_CMD`, `RUN_TYPECHECK_CMD`). + +From `migration//manifest.md`: +- Module map of components. + +## Steps + +### 1. Identify Duplication Candidates +- Use `directory_tree` and `grep` to scan for overlapping logic between components. +- Classify candidates by type: + - **Identical**: Code that is exactly the same. + - **Similar**: Code that serves the same purpose but with minor differences. + - **Coincidental**: Code that looks similar but serves unrelated purposes. + +### 2. Present Candidates to the User +- Provide a list of duplication candidates, including: + - Component names. + - File paths. + - Type of duplication (identical, similar, coincidental). + - Risk assessment (low, medium, high). +- Ask the user to approve or reject each candidate for deduplication. + +### 3. Execute Deduplication for Approved Candidates +- For each approved candidate: + - **Identical Code**: Extract the shared logic into a new component and update imports. + - **Similar Code**: Refactor to use shared logic or parameterize differences. + - **Coincidental Code**: Leave as-is. +- Update `pyproject.toml` to include any new components. +- Run `POLY_CMD_PREFIX sync` to synchronize the workspace. + +### 4. Verify Changes +- Run `RUN_TEST_CMD` to ensure no regressions. +- Run `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` if set. +- Run `POLY_CMD_PREFIX check` to validate the workspace structure. + +## Verify +- All tests pass (`RUN_TEST_CMD`). +- Linting and type-checking pass (if set). +- The workspace structure is valid (`POLY_CMD_PREFIX check`). + +## Done When +- Duplication candidates are identified and presented to the user. +- User-approved candidates are deduplicated. +- All tests and checks pass. +- The workspace structure is valid. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/definition-of-done/skill.md b/.agents/skills/polylith/migrate-project/definition-of-done/skill.md new file mode 100644 index 00000000..70242128 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/definition-of-done/skill.md @@ -0,0 +1,56 @@ +--- +name: definition-of-done +description: Define the criteria for completing the migration process. +--- + +# Skill: definition-of-done + +## Done When + +### Structure +- Temporary migration base is gone or thin. +- Bases contain only entrypoints/wiring. +- All non-entrypoint code lives in components. + +### Source Project +- `projects//` contains only: + - Packaging config (`pyproject.toml`). + - Runner scripts and task runners (`Makefile`, `Justfile`). + - Project-specific config (e.g., `alembic.ini`). +- Project `pyproject.toml` references all required bricks. +- Brick names are meaningful and non-generic. +- Base names are project-prefixed to avoid collisions. + +### Tests +- Tests are moved from `projects//tests/` to workspace level. +- Unit tests are organized according to the Polylith theme in use: + - For the **loose theme**, unit tests mirror the brick structure: `test///`. + - For other themes, follow the workspace's test structure conventions. +- Integration tests live in a shared location (e.g., `test/integration/`). +- Shared fixtures live in `test//conftest.py` or `test/conftest.py`. +- `RUN_TEST_CMD` points to the test root. + +### Infrastructure +- Infrastructure folders are moved to `infra///`. + +### Interfaces +- Each component defines its public API via `__init__.py`. +- Bricks import each other via those APIs. + +### Linting and Type-Checking +- Linting and formatting use the workspace's configured tool(s). +- Type-checking uses the workspace's configured tool(s) (if applicable). +- `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` pass. + +### Dependencies +- Workspace root `pyproject.toml` contains all third-party dependencies with version constraints. +- Project `pyproject.toml` lists runtime dependencies without version numbers. + +### Cleanup +- Migration artifacts (e.g., `migration//state.md`, `migration//manifest.md`) are removed or kept for reference. + +## Verify +- `RUN_TEST_CMD` succeeds. +- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. +- Run `POLY_CMD_PREFIX check` to validate the workspace structure. +- Run `POLY_CMD_PREFIX info` to inspect the workspace and confirm all projects and bricks are correctly registered. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/discover/skill.md b/.agents/skills/polylith/migrate-project/discover/skill.md new file mode 100644 index 00000000..0969ea56 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/discover/skill.md @@ -0,0 +1,115 @@ +--- +name: discover +description: Create `migration//state.md` and `migration//manifest.md` by inspecting the existing project under `projects//`. +--- + +# Skill: discover + +## Goal +Inspect the project and create `state.md` and `manifest.md` to guide the migration process. + +## Steps + +### 1. Record Project Metadata +Record the following in `migration//state.md`: + +| Field | Description | Example | +|-------|-------------|---------| +| `PROJECT_DIR` | Project subfolder path | `projects/api` | +| `ORIG_TOP_NS` | Current import namespace | `myapp` | +| `TARGET_TOP_NS` | Desired Polylith namespace (default: `ORIG_TOP_NS`) | `myapp` | +| `BRICK_NAME` | Derived from `[project.name]` (ask user to confirm) | `example_a` | +| `ALIAS` | Short kebab-case alias for Polylith (ask user to confirm) | `svc-a` | +| `GROUP` | Optional project group (ask user) | `domain-a` | + +#### Derive `BRICK_NAME` from `[project.name]`: +| `[project.name]` | `BRICK_NAME` | +|------------------|--------------| +| `example-service-a` | `example_a` | +| `order-management-api` | `order_management` | +| `payment-worker` | `payment` | + +#### Derive `ALIAS` from `[project.name]`: +| `[project.name]` | `ALIAS` | +|------------------|---------| +| `example-service-a` | `svc-a` | +| `order-management-api` | `order-mgmt` | +| `payment-worker` | `payment` | + +### 2. Detect Tools +Detect the project's package manager, linter, formatter, and type checker by scanning config files: + +| Tool | Detection Criteria | +|------|--------------------| +| **Package Manager** | | +| Poetry | `poetry.lock` or `[tool.poetry]` in `pyproject.toml` | +| Pipenv | `Pipfile` or `Pipfile.lock` | +| Pip | `requirements.txt` (no lock file) | +| UV | `uv.lock` or `[tool.uv]` in `pyproject.toml` | +| Setuptools | `setup.py` or `setup.cfg` only | +| **Linter** | | +| Flake8 | `setup.cfg` or `.flake8` | +| Pylint | `[tool.pylint]` or `.pylintrc` | +| Ruff | `[tool.ruff]` | +| **Formatter** | | +| Black | `[tool.black]` | +| Isort | `[tool.isort]` | +| Ruff | `[tool.ruff]` | +| **Type Checker** | | +| Mypy | `mypy.ini` or `[tool.mypy]` | +| Pyright | `[tool.pyright]` or `pyrightconfig.json` | +| Ty | `[tool.ty]` | + +Record the detected tools in `migration//state.md`: + +```text +PACKAGE_MANAGER= +LINTER= +FORMATTER= +TYPE_CHECKER= +``` + +### 6. Determine `poly` Command Prefix + +Based on the detected package manager, determine the correct command prefix for `poly`: + +| Package Manager | `poly` Command Prefix | Example | +|-----------------|-----------------------|---------| +| Poetry | `poetry poly` | `poetry poly check` | +| Pipenv | `pipenv run poly` | `pipenv run poly sync` | +| Pip | `poly` | `poly info` | +| UV | `uv run poly` | `uv run poly check` | +| Setuptools | `poly` | `poly sync` | + +Record the command prefix in `migration//state.md`: + +```text +POLY_CMD_PREFIX= +``` + +### 3. Ask User for Preferences +- Ask the user if they want to convert the linter to `ruff` and the type checker to `ty`. +- Record their choices in `state.md`: + ```text + CONVERT_LINTER= + CONVERT_TYPE_CHECKER= + ``` + +### 4. Discover Commands +Look inside `projects//` for config files and record the following commands: +- `RUN_TEST_CMD`: Command to run tests. +- `RUN_LINT_CMD`: Command to run linting (optional). +- `RUN_TYPECHECK_CMD`: Command to run type checking (optional). + +### 5. Create `manifest.md` +Record the following in `migration//manifest.md`: +- Directory tree of `projects//`. +- Module map (path + one-line description of role). +- Entrypoints (FastAPI app, CLI, worker, etc.). +- Test directory structure and count. +- Infrastructure files (Dockerfiles, k8s manifests, etc.). + +## Done When +- `state.md` and `manifest.md` are created and populated. +- User confirms `BRICK_NAME`, `ALIAS`, and tooling preferences. +- All detected tools and commands are recorded. diff --git a/.agents/skills/polylith/migrate-project/distribute-wiring/skill.md b/.agents/skills/polylith/migrate-project/distribute-wiring/skill.md new file mode 100644 index 00000000..9aa7ee28 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/distribute-wiring/skill.md @@ -0,0 +1,57 @@ +--- +name: distribute-wiring +description: Distribute app-wiring code from the residual component into the appropriate bases and shared components. +--- + +# Skill: distribute-wiring + +## Goal +Distribute app-wiring code from the residual component into the appropriate bases and shared components. + +## Inputs +From `migration//state.md`: +- `TARGET_TOP_NS` +- `BRICK_NAME` +- Verification commands. +- Base names. + +From `migration//manifest.md`: +- Current module map, including what remains in the residual component. + +## Steps + +### 1. Read the Residual Component +- List every public function remaining in the residual module. +- Confirm it contains only app-wiring code. + +### 2. Identify the Split +- Trace callers of each function (use `grep`). +- Group callers by base or runner script. +- Map each function to its natural base: + +| Function Pattern | Belongs In | Rationale | +|------------------|------------|-----------| +| `init_consumer`, `close_consumer` | Handler/consumer base | Kafka consumer lifecycle is handler-specific | +| `init_job` | Jobs base | Job bootstrap is jobs-specific | +| `init_api` / app factory | API base | HTTP server setup is API-specific | + +### 3. Extract Shared Helpers +- Identify shared init functions (e.g., `init_logging`, `init_db`). +- Move shared helpers to a `bootstrap` component if needed. + +### 4. Move Composite Functions +- Move composite functions to their respective bases. +- Update runner scripts to import from the base directly. + +### 5. Update Tests +- Update integration tests to monkeypatch the correct module. + +### 6. Clean Up +- Delete the residual component directory. +- Update `pyproject.toml` to remove the residual brick. + +## Verify +- `RUN_TEST_CMD` succeeds. +- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. +- Run `POLY_CMD_PREFIX check` to validate the workspace structure. +- Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/extract-standalone-modules/skill.md b/.agents/skills/polylith/migrate-project/extract-standalone-modules/skill.md new file mode 100644 index 00000000..219ff030 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/extract-standalone-modules/skill.md @@ -0,0 +1,44 @@ +--- +name: extract-standalone-modules +description: Extract foundational modules (e.g., `consts.py`, `exceptions.py`) from the residual component into standalone components. +--- + +# Skill: extract-standalone-modules + +## Goal +Extract **foundational modules** (e.g., `consts.py`, `exceptions.py`, `models.py`) from the residual component into standalone components. This skill is for zero-dependency or low-dependency modules that serve as building blocks for other components. + +## Inputs +From `migration//state.md`: +- `TARGET_TOP_NS` +- `BRICK_NAME` +- Verification commands. + +From `migration//manifest.md`: +- Current module map, including what remains in the residual component. + +## Steps + +### 1. Analyze the Residual Component +- Use `directory_tree` and `grep` to list modules remaining in the residual component. +- Classify each module: + - **Zero internal deps**: Modules with only stdlib/third-party imports (e.g., `exceptions.py`, `consts.py`). + - **Low internal deps**: Modules that depend on already extracted or zero-dep modules (e.g., `models.py`). + - **App-wiring**: Modules that compose infrastructure setup. These stay in the residual. + +### 2. Extract Modules in Dependency Order +- Extract zero-dep modules first, followed by modules that depend on them. + +### 3. For Each Extraction +1. Check for naming collisions. +2. Create the component directory with `__init__.py` and `core.py`. +3. Update imports in all consumers. +4. Add the new brick to `pyproject.toml`. +5. Run verification. +6. Delete the original module from the residual component. + +## Verify +- `RUN_TEST_CMD` succeeds. +- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. +- Run `POLY_CMD_PREFIX check` to validate the workspace structure. +- Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/extract-to-base/skill.md b/.agents/skills/polylith/migrate-project/extract-to-base/skill.md new file mode 100644 index 00000000..0c3e2929 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/extract-to-base/skill.md @@ -0,0 +1,62 @@ +--- +name: extract-to-base +description: Extract all application code from `projects//` into a temporary migration base. +--- + +# Skill: extract-to-base + +## Goal +Extract all application code from `projects//` into a temporary migration base (`bases///`). + +## Inputs +From `migration//state.md`: +- `PROJECT_DIR` +- `ORIG_TOP_NS` +- `TARGET_TOP_NS` (default: `ORIG_TOP_NS`) +- `BRICK_NAME` +- `RUN_TEST_CMD` (optional: `RUN_LINT_CMD`, `RUN_TYPECHECK_CMD`) + +From `migration//manifest.md`: +- Directory tree and module map. + +## Steps + +### 1. Create the Base Directory +- Create `bases///`. + +### 2. Move Application Code +- Move application packages/modules from `projects//` to the base: + - For `src/` layout: Move `projects//src//` under the base. + - For flat layout: Move `projects///` under the base. +- Leave non-code files (Dockerfiles, k8s manifests, deploy scripts, `pyproject.toml`) in `projects//`. + +### 3. Update `pyproject.toml` +- Add the base to `[tool.polylith.bricks]`: + ```toml + [tool.polylith.bricks] + "../../bases//" = "/" + ``` + +### 4. Fix Imports +- Update imports minimally to ensure tests and linting pass. + +### 5. Update `manifest.md` +- Reflect the new structure in `migration//manifest.md`. + +### 6. Handle Namespace Changes +If `TARGET_TOP_NS != ORIG_TOP_NS`, choose one of the following options: + +| Option | Description | Risk | +|--------|-------------|------| +| **Compatibility Shim** | Keep `ORIG_TOP_NS` as a shim that re-exports from `TARGET_TOP_NS`. | Lower | +| **Rewrite Imports** | Rewrite all imports to the new namespace in one go. | Higher | + +### 7. Use Shims if Needed +- If imports break, add temporary shims to re-export names from the new brick API. +- Track shims in `migration/shims.md`. + +## Verify +- `RUN_TEST_CMD` succeeds. +- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. +- Run `POLY_CMD_PREFIX check` to validate the workspace structure. +- Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. diff --git a/.agents/skills/polylith/migrate-project/isolate-base-and-big-component/skill.md b/.agents/skills/polylith/migrate-project/isolate-base-and-big-component/skill.md new file mode 100644 index 00000000..90c94dc7 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/isolate-base-and-big-component/skill.md @@ -0,0 +1,62 @@ +--- +name: isolate-base-and-big-component +description: Shrink the temporary migration base into thin base(s) + one big component. Bases contain only entrypoints/wiring, while the big component contains everything else. +--- + +# Skill: isolate-base-and-big-component + +## Goal +Shrink the temporary migration base into thin base(s) + one big component: +- **Bases**: Contain only entrypoints/wiring (e.g., FastAPI endpoints, CLI wiring, consumer wiring). +- **Big Component**: Contains all other code. + +## Inputs +From `migration//state.md`: +- `TARGET_TOP_NS` +- `BRICK_NAME` +- Verification commands. + +From `migration//manifest.md`: +- Entrypoints list. + +## Steps + +### 1. Create the Big Component +- Create `components///`. + +### 2. Move Non-Entrypoint Code +- Move non-entrypoint code from the base(s) to the big component. + +### 3. Define Public API +- Define a minimal public API in `components///__init__.py`. + +### 4. Update Bases +- Update bases to import only from component APIs: + ```python + from . import ... + ``` + +### 5. Update `pyproject.toml` +- Add the new component to `[tool.polylith.bricks]`: + ```toml + [tool.polylith.bricks] + "../../bases//" = "/" + "../../components//" = "/" + ``` + +### 6. Update `manifest.md` +- Reflect the new structure in `migration//manifest.md`. + +### 7. FastAPI Guidance +| Stays in Base | Moves to Big Component | +|---------------|------------------------| +| `app = FastAPI(...)` | Domain/business logic | +| Middleware, router registration | Persistence/repositories | +| Route handlers (endpoints) | External integrations | +| Startup/shutdown/lifespan wiring | Reusable parsing/validation | + +## Verify +- `RUN_TEST_CMD` succeeds. +- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. +- Run `POLY_CMD_PREFIX check` to validate the workspace structure. +- Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. diff --git a/.agents/skills/polylith/migrate-project/isolate-shared-and-project-logic/skill.md b/.agents/skills/polylith/migrate-project/isolate-shared-and-project-logic/skill.md new file mode 100644 index 00000000..acf6c74a --- /dev/null +++ b/.agents/skills/polylith/migrate-project/isolate-shared-and-project-logic/skill.md @@ -0,0 +1,66 @@ +--- +name: isolate-shared-and-project-logic +description: Identify and isolate shared and project-specific logic in monolithic components (e.g., `models`, `schemas`). +--- + +# Skill: isolate-shared-and-project-logic + +## Goal +Identify and isolate shared and project-specific logic in monolithic components (e.g., `models`, `schemas`). Extract shared logic into reusable components and isolate project-specific logic into project-specific components. + +## Inputs +From `migration//state.md`: +- `TARGET_TOP_NS` +- `BRICK_NAME` +- Verification commands (`RUN_TEST_CMD`, `RUN_LINT_CMD`, `RUN_TYPECHECK_CMD`). + +From `migration//manifest.md`: +- Module map of components. + +## Steps + +### 1. Identify Monolithic Components +- Scan the workspace for monolithic components (e.g., `models`, `schemas`). +- Focus on components with large `core.py` files or mixed domain logic. + +### 2. Analyze Usage +- Use `grep` to trace imports of each definition in the component. +- Classify definitions as: + - **Shared**: Used by multiple projects. + - **Project-Specific**: Used by only one or a few projects. + - **Similar**: Definitions that could reuse shared logic (e.g., similar models). + +### 3. Extract Shared Logic +- Create a new shared component (e.g., `models_shared`, `schemas_shared`). +- Move shared definitions into the new component. +- Update imports in all projects to reference the shared component. + +### 4. Isolate Project-Specific Logic +- Create project-specific components (e.g., `models_project_a`, `schemas_project_b`). +- Move project-specific definitions into the appropriate component. +- Update imports in the relevant projects. + +### 5. Refactor Similar Models +- For similar models, extract shared logic into the shared component. +- Update the project-specific models to reuse the shared logic. + +### 6. Update `pyproject.toml` +- Add the new shared and project-specific components to the workspace's `pyproject.toml`. +- Update the project's `pyproject.toml` to reference the new components. + +### 7. Verify Changes +- Run `RUN_TEST_CMD` to ensure no regressions. +- Run `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` if set. +- Run `POLY_CMD_PREFIX check` to validate the workspace structure. + +## Verify +- All tests pass (`RUN_TEST_CMD`). +- Linting and type-checking pass (if set). +- The workspace structure is valid (`POLY_CMD_PREFIX check`). + +## Done When +- Shared logic is extracted into reusable components. +- Project-specific logic is isolated into project-specific components. +- Similar models reuse shared logic where possible. +- All tests and checks pass. +- The workspace structure is valid. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/orchestrator/skill.md b/.agents/skills/polylith/migrate-project/orchestrator/skill.md new file mode 100644 index 00000000..a6b105ab --- /dev/null +++ b/.agents/skills/polylith/migrate-project/orchestrator/skill.md @@ -0,0 +1,175 @@ +--- +name: orchestrator +description: Orchestrates the project migration workflow. **Explicitly invoke this skill to migrate a non-Polylith project in `/projects`.** +--- + +# Skill: orchestrator + +## Goal +Define and execute the workflow for migrating a non-Polylith Python project to a Polylith workspace. +**This skill must be explicitly invoked by a human with the project name/path.** + +## Usage +To migrate a project, load the `migrate.orchestrator` skill and provide the project name (e.g., the subdirectory in `/projects`): +``` +Load the `migrate.orchestrator` skill and specify the project name (e.g., `my-app`). +``` + +## Steps + +### 1. User Confirmation +- Immediately ask the user to confirm the project path and migration intent: + ``` + You are about to migrate `/projects/my-app` to Polylith. This will refactor the project into bases and components. Proceed? (yes/no) + ``` +- If the user declines, abort the migration: + ``` + Migration aborted by user. + ``` + +### 2. Execute Migration Workflow +- Proceed with the migration steps (discover, extract, refactor, etc.) if the user confirms. +- Use the project path provided by the user (e.g., `/projects/my-app`) for all operations. + +## Workflow +The migration process consists of the following steps, executed in this order: + +1. **Discover**: Inspect the project and create `state.md` and `manifest.md`. + - Skill: `discover` + - Dependencies: None + +2. **Extract to Base**: Extract application code into a temporary migration base. + - Skill: `extract-to-base` + - Dependencies: `discover` + +3. **Prepare Project**: Clean up the project subfolder and consolidate dependencies. + - Skill: `prepare-project` + - Dependencies: `extract-to-base` + +4. **Isolate Base and Big Component**: Shrink the temporary migration base into thin bases and a big component. + - Skill: `isolate-base-and-big-component` + - Dependencies: `prepare-project` + +5. **Split Big Component**: Split the big component into multiple focused components. + - Skill: `split-big-component` + - Dependencies: `isolate-base-and-big-component` + +6. **Extract Standalone Modules**: Extract zero-dependency and low-dependency modules into standalone components. + - Skill: `extract-standalone-modules` + - Dependencies: `split-big-component` + +7. **Isolate Shared and Project Logic**: Identify and isolate shared and project-specific logic in monolithic components (e.g., `models`, `schemas`). + - Skill: `isolate-shared-and-project-logic` + - Dependencies: `extract-standalone-modules`, `split-big-component` + +8. **Distribute Wiring**: Distribute app-wiring code from the residual component into the appropriate bases and shared components. + - Skill: `distribute-wiring` + - Dependencies: `isolate-shared-and-project-logic` + +9. **Split Component Internals**: Split monolithic `core.py` files in shared components into domain-focused modules. + - Skill: `split-component-internals` + - Dependencies: `distribute-wiring` + +10. **Refactor Tests**: Restructure unit tests to align with the workspace's Polylith theme. + - Skill: `refactor-tests` + - Dependencies: `split-component-internals` + +11. **Definition of Done**: Verify that all migration criteria are met. + - Skill: `definition-of-done` + - Dependencies: `refactor-tests` + +## Optional Skills + +- **Convert Linter**: Replace the project's linter with `ruff` (if the user opts in during the `discover` step). + - Skill: `convert-linter` + - Dependencies: `discover` + - Trigger: User opts in during `discover` step. + +- **Convert Type Checker**: Replace the project's type checker with `ty` (if the user opts in during the `discover` step). + - Skill: `convert-type-checker` + - Dependencies: `discover` + - Trigger: User opts in during `discover` step. + +- **Convert Package Manager**: Convert the project's `pyproject.toml` to PEP 621/uv format and register it as a workspace member (if the user opts in during the `discover` step). + - Skill: `convert-package-manager` + - Dependencies: `discover` + - Trigger: User opts in during `discover` step. + +- **Dedupe**: Identify and execute controlled deduplication of code during migration (if the user opts in). + - Skill: `dedupe` + - Dependencies: `split-big-component`, `extract-standalone-modules` + +## Workflow +The migration process consists of the following steps, executed in this order: + +1. **Discover**: Inspect the project and create `state.md` and `manifest.md`. + - Skill: `discover` + - Dependencies: None + +2. **Extract to Base**: Extract application code into a temporary migration base. + - Skill: `extract-to-base` + - Dependencies: `discover` + +3. **Prepare Project**: Clean up the project subfolder and consolidate dependencies. + - Skill: `prepare-project` + - Dependencies: `extract-to-base` + +4. **Isolate Base and Big Component**: Shrink the temporary migration base into thin bases and a big component. + - Skill: `isolate-base-and-big-component` + - Dependencies: `prepare-project` + +5. **Split Big Component**: Split the big component into multiple focused components. + - Skill: `split-big-component` + - Dependencies: `isolate-base-and-big-component` + +9. **Extract Standalone Modules**: Extract zero-dependency and low-dependency modules into standalone components. + - Skill: `extract-standalone-modules` + - Dependencies: `split-big-component` + +10. **Isolate Shared and Project Logic**: Identify and isolate shared and project-specific logic in monolithic components (e.g., `models`, `schemas`). + - Skill: `isolate-shared-and-project-logic` + - Dependencies: `extract-standalone-modules`, `split-big-component` + +11. **Split Component Internals**: Split monolithic `core.py` files in shared components into domain-focused modules. + - Skill: `split-component-internals` + - Dependencies: `distribute-wiring` + +12. **Refactor Tests**: Restructure unit tests to align with the workspace's Polylith theme. + - Skill: `refactor-tests` + - Dependencies: `split-component-internals` + +13. **Definition of Done**: Verify that all migration criteria are met. + - Skill: `definition-of-done` + - Dependencies: `refactor-tests` + +## Optional Skills + +- **Convert Linter**: Replace the project's linter with `ruff` (if the user opts in during the `discover` step). + - Skill: `convert-linter` + - Dependencies: `discover` + - Trigger: User opts in during `discover` step (`CONVERT_LINTER=yes` in `state.md`). + +- **Convert Type Checker**: Replace the project's type checker with `ty` (if the user opts in during the `discover` step). + - Skill: `convert-type-checker` + - Dependencies: `discover` + - Trigger: User opts in during `discover` step (`CONVERT_TYPE_CHECKER=yes` in `state.md`). + +- **Convert Package Manager**: Convert the project's `pyproject.toml` to PEP 621/uv format and register it as a workspace member (if the user opts in during the `discover` step). + - Skill: `convert-package-manager` + - Dependencies: `discover` + - Trigger: User opts in during `discover` step (`CONVERT_PACKAGE_MANAGER=yes` in `state.md`). + +- **Dedupe**: Identify and execute controlled deduplication of code during migration (if the user opts in). + - Skill: `dedupe` + - Dependencies: `split-big-component`, `extract-standalone-modules` + +## Execution +To execute the migration workflow: +1. Load the `orchestrator` skill. +2. Follow the steps defined in the workflow, ensuring all dependencies are satisfied before executing each skill. +3. Verify the completion of each step before proceeding to the next. + +## Validation +- Ensure no circular dependencies exist in the workflow. +- Verify that all dependencies are correctly declared and exist in the `SKILLS.md` index. +- Confirm that the workflow is executable in the order defined by the orchestrator skill. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/prepare-project/skill.md b/.agents/skills/polylith/migrate-project/prepare-project/skill.md new file mode 100644 index 00000000..9bda2231 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/prepare-project/skill.md @@ -0,0 +1,61 @@ +--- +name: prepare-project +description: Clean up the project subfolder and consolidate dependencies after extracting application code into bases. +--- + +# Skill: prepare-project + +## Goal +Clean up the project subfolder (`projects//`) and consolidate dependencies. After this step, the project subfolder should contain only: +- Project `pyproject.toml` (with brick references). +- Task runners (`Makefile`, `Justfile`). +- Project-specific config (e.g., `alembic.ini`). + +## Inputs +From `migration//state.md`: +- `PROJECT_DIR` +- `TARGET_TOP_NS` +- `ALIAS` (optional: `GROUP`) +- Verification commands. + +From `migration//manifest.md`: +- Infra files list. + +## Steps + +### 1. Verify Project Subfolder +- Run `directory_tree` on `projects//` to confirm only infra and config files remain. + +### 2. Update `pyproject.toml` +- Add brick references to `[tool.polylith.bricks]`: + ```toml + [tool.polylith.bricks] + "../../bases//" = "/" + ``` +- Register the project alias and group in `workspace.toml` (if provided): + ```toml + [tool.polylith.projects.alias] + = "" + + [tool.polylith.projects.groups] + = [""] + ``` + +### 3. Move Tests to Workspace Level +- Move `tests/` to `test//`. +- Update `RUN_TEST_CMD` in `migration//state.md` to point to the new location. +- Update imports and mock patch strings in test files if paths changed. + +### 4. Move Infrastructure Folders +- Move infra folders (e.g., `helm/`, `k8s/`, `kustomize`, `alembic/`) to `infra///`. + +### 5. Consolidate Dependencies +- Move third-party dependencies with version constraints to the workspace root `pyproject.toml`. +- Move dev/test/tooling dependencies to the workspace root. +- List runtime dependencies without version numbers in the project `pyproject.toml`. + +## Verify +- `RUN_TEST_CMD` succeeds. +- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. +- Run `POLY_CMD_PREFIX check` to validate the workspace structure. +- Run `POLY_CMD_PREFIX info` to inspect the workspace and confirm the project is correctly registered. diff --git a/.agents/skills/polylith/migrate-project/refactor-tests/skill.md b/.agents/skills/polylith/migrate-project/refactor-tests/skill.md new file mode 100644 index 00000000..585598db --- /dev/null +++ b/.agents/skills/polylith/migrate-project/refactor-tests/skill.md @@ -0,0 +1,49 @@ +--- +name: refactor-tests +description: Restructure unit tests to align with the workspace's Polylith theme. +--- + +# Skill: refactor-tests + +## Goal +Restructure unit tests to align with the workspace's Polylith theme. The test directory structure should follow the workspace's conventions for organizing tests. + +## Scope +- **Unit tests only**: Integration tests typically stay in a shared location (e.g., `test/integration/` or `test//integration/`). +- **Structure only**: Reorganize test files and update imports/mocks to align with the workspace's Polylith theme. + +## Inputs +From `migration//state.md`: +- `TARGET_TOP_NS` +- Verification commands. + +From `migration//manifest.md`: +- List of all bricks (bases and components) with their module maps. + +From `test/`: +- Current test directory structure. + +## Steps + +### 1. Classify Test Files +- Scan import statements and mock patch strings to determine which brick each test file primarily tests. +- Produce a classification table (e.g., `test_merchant_handler.py` → `app`). + +### 2. Reorganize Tests +- Create target directories according to the workspace's Polylith theme. +- Move each test file to its brick's test directory. +- Handle `conftest.py` files: + - Move brick-specific fixtures to the brick's test directory. + - Move shared fixtures to `test//conftest.py` or `test/conftest.py`. + +### 3. Update Imports and Mocks +- Update imports in test files if they reference other test modules or fixtures. +- Update mock patch strings if paths changed. + +### 4. Verify +- Update `RUN_TEST_CMD` in `migration//state.md` to point to the new test root. +- Run `RUN_TEST_CMD` and `RUN_LINT_CMD`. + +## Verify +- `RUN_TEST_CMD` succeeds. +- If set, `RUN_LINT_CMD` succeeds. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/split-big-component/skill.md b/.agents/skills/polylith/migrate-project/split-big-component/skill.md new file mode 100644 index 00000000..c497efc2 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/split-big-component/skill.md @@ -0,0 +1,166 @@ +--- +name: split-big-component +description: Split the big component (`components///`) into multiple focused components. +--- + +# Skill: split-big-component + +## Goal +Split the big component (`components///`) into multiple focused components to improve maintainability, clarity, and reusability. + +## Inputs +From `migration//state.md`: +- `TARGET_TOP_NS` +- `BRICK_NAME` +- Verification commands. + +From `migration//manifest.md`: +- Module map of the big component. + +## Steps + +### Phase 1: Plan the Split +1. **Review the Big Component**: Use `directory_tree` and `grep` to analyze the big component's structure and identify natural slices. +2. **Define Component Names**: Name components after the **domain or functionality** they represent (e.g., `domain_a_serializer`, `data_transformations`). Avoid generic names like `utils` or `helpers`. +3. **Create a Split Plan**: Record the following in `migration//split_plan.md`: + - Brick name for each new component. + - Files/modules to move into each component. + - Public API (key functions/classes to export). + - Bricks that will import from the new component. + +**When NOT to Extract**: +- The module is tightly coupled to other modules in the component. +- The module is very small (< 20 lines) and extraction adds more indirection than value. + +**Extraction Order**: +- Extract modules with **zero internal dependencies** first (e.g., `exceptions.py`, `consts.py`). +- Extract modules that depend on already-extracted modules next (e.g., `models.py` that imports `exceptions` and `consts`). + +### Examples of Component Naming +Name components after the **domain or functionality** they represent: + +| Original module name | Content (after inspection) | Component name | +|----------------------|----------------------------|----------------| +| `serializers.py` | Serializes data for ERP | `domain_a_serializer` | +| `transformations/` | Maps data between formats | `data_transformations` | +| `parsers.py` | Parses event payloads | `event_parser` | +| `validators.py` | Validates records | `record_validator` | + +#### Avoiding circular imports when extracting modules + +Extracting a module into a separate component can create circular imports if the new component imports from the parent component and the parent still imports from the new component. This commonly happens when a component's `__init__.py` eagerly imports from many submodules. + +**Diagnosis:** The cycle typically looks like: + +``` +new_component.core → parent.__init__ → parent.submodule → new_component +``` + +Python triggers `parent.__init__` whenever any submodule of `parent` is imported (e.g. `from parent.consts import X` loads `parent/__init__.py` first). + +**Resolution strategies (in order of preference):** + +1. **Extract the circular part into its own component.** A circular dependency often signals that the code involved is isolated enough to be its own component. Extract the module that causes the cycle into a standalone component — this breaks the cycle structurally. A component doesn't have to be a "feature"; it can be a utility, a data definition, a pure technical module, or a single ORM model. If the code has a clear responsibility and can be imported without pulling in the rest of the parent, it belongs in its own brick. + + ``` + # Before (cycle): new_component → parent.consts → parent.__init__ → parent.handlers → new_component + # After (no cycle): new_component → consts_component (standalone, no __init__ chain) + ``` + +2. **Trim `__init__.py` exports.** Remove the problematic import from the parent's `__init__.py` and have callers import the submodule directly. This makes the dependency graph explicit and often eliminates the cycle without creating a new brick. + + ```python + # Before: parent/__init__.py imports everything eagerly + from parent.command_handler import CommandHandler # triggers handler → new_component cycle + + # After: remove from __init__.py, callers import directly + from parent.command_handler import CommandHandler # in the base that needs it + ``` + +3. **Standalone component instead of submodule.** If the new component would be a submodule of an existing package (e.g. `myns.models.example_transaction`), importing it triggers the parent package's `__init__.py` and all its eager imports. Make it a standalone component at the namespace level instead (e.g. `myns.example_transaction`). + +4. **Deferred import (last resort).** Move the import inside the function that uses it. This works but hides the dependency and makes the code harder to reason about. Prefer strategies 1–3 first. + +**Pre-flight check:** Before extracting a module, trace the import chain: +1. The new component imports `parent.submodule_X` → Python loads `parent.__init__` +2. Does `parent.__init__` (directly or transitively) import from the new component? +3. If yes → apply one of the strategies above (extract, trim, or restructure) before proceeding. + +#### Refactoring shared infrastructure components + +When a second project needs a component that already exists (e.g. `myns.logging`, `myns.kafka`), compare the implementations closely. Common refactoring patterns: + +**Pattern: Parameterize the shared component.** When two implementations are 80%+ identical with project-specific extras, refactor the shared component to accept optional parameters rather than duplicating code. + +Example — logging with project-specific loggers: +```python +# Shared component: myns.logging +def init(config, *, extra_loggers=None, cache_logger_on_first_use=False): + loggers = {**_BASE_LOGGERS} + loggers.update(_verbosity_overrides(config.LOG_VERBOSITY_LEVEL)) + if extra_loggers: + loggers.update(extra_loggers) + ... + +# Project A base: +init(config, extra_loggers={"httpx": {...}, "backoff": {...}}, + cache_logger_on_first_use=config.LOG_CACHE_LOGGER_ON_FIRST_USE) + +# Project B base: +init(config, extra_loggers={"confluent_kafka_helpers": {...}}) +``` + +**When to parameterize vs. keep separate:** +- **Parameterize** when the core logic is identical and only data/config differs. +- **Keep separate** when the control flow or structure diverges (different frameworks, different patterns). +- **Extract shared base + project-specific wrappers** when there's a significant shared core but non-trivial project-specific logic around it. + +#### Splitting Component Internals + +Components with generic names like `models`, `schemas`, `exceptions`, or `consts` may start with a single `core.py` file. As the workspace grows, these components can accumulate code from different domains. Splitting `core.py` into multiple domain-focused modules **inside the component** can improve maintainability and clarity. + +**When to Split:** +- If the `core.py` file contains definitions from multiple distinct domains (e.g., `domain_a` and `domain_b`). +- If the file contains helper/utility functions alongside class definitions. +- If preparing for a second project migration that will contribute to the same component. + +**Approach:** +- Group definitions by the domain concept they serve. +- Name each module after the domain or functionality it represents (e.g., `domain_a.py`, `domain_b.py`). +- Ensure the public API remains unchanged to avoid breaking existing imports. + +#### Cross-component duplication analysis + +After drafting the split plan (but before executing any moves), analyze the planned components — and any _already existing_ components in the workspace — for duplication: + +1. **Identify overlap:** For each planned component, check whether an existing component already contains similar logic. Look for: + - Functions/classes with the same or very similar names. + - Modules that operate on the same domain concept (e.g., two different `domain_a_serializer` implementations). + - Copy-pasted utility functions (string helpers, date formatting, retry wrappers, etc.). + +2. **Classify the overlap:** + - **Identical or near-identical:** the code does the same thing with trivial differences (variable names, formatting). → Extract to a shared component. + - **Same purpose, different behavior:** the code solves the same problem but with project-specific logic (e.g., different serialization schemas). → Keep separate, but extract any genuinely shared helpers. + - **Coincidental similarity:** the code looks similar but serves unrelated purposes. → Leave separate. + +3. **Propose shared extractions:** When genuinely duplicated code is found, add a step to the split plan: + - Create a new shared component (or extend an existing one) containing the common logic. + - Have both the existing and the new component depend on the shared one. + - Record this in `/split_plan.md` with a rationale. + +4. **Always confirm with the user** before creating shared components — "is this code genuinely shareable?" is a judgment call that depends on how the projects will evolve. + +### Phase 2: Execute the Split +For each planned component in `split_plan.md`: +1. **Create the Component**: Create the component directory with `__init__.py`. +2. **Move Files/Modules**: Move the relevant files/modules into the new component. +3. **Define the Public API**: Update `__init__.py` to re-export the public API. +4. **Update Callers**: Update all imports to reference the new component. +5. **Update `pyproject.toml`**: Add the new brick to the project's `[tool.polylith.bricks]`. +6. **Run Verification**: Ensure tests, linting, and type-checking pass. + +## Verify +- `RUN_TEST_CMD` succeeds. +- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. +- Run `POLY_CMD_PREFIX check` to validate the workspace structure. +- Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. diff --git a/.agents/skills/polylith/migrate-project/split-component-internals/skill.md b/.agents/skills/polylith/migrate-project/split-component-internals/skill.md new file mode 100644 index 00000000..a1c35820 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/split-component-internals/skill.md @@ -0,0 +1,45 @@ +--- +name: split-component-internals +description: Split monolithic `core.py` files in shared components into domain-focused modules. +--- + +# Skill: split-component-internals + +## Goal +Split monolithic `core.py` files in **shared components** (e.g., `models_shared`, `schemas_shared`) into domain-focused modules. This skill ensures that shared components remain well-organized and maintainable. + +## Inputs +From `migration//state.md`: +- `TARGET_TOP_NS` +- Verification commands. + +From `migration//manifest.md`: +- Current component list and structure. + +## Steps + +### 1. Identify Candidates +- Scan components for large `core.py` files. +- A component is a candidate if: + - The file contains definitions from multiple domains. + - The file contains helper/utility functions alongside class definitions. + - The file exceeds a reasonable size threshold. + +### 2. Group Definitions by Domain +- Group definitions by the domain concept they serve. +- Example domains: ORM models, schema/dataclass clusters, helper functions. + +### 3. Create New Modules +- Create domain-focused modules (e.g., `merchant.py`, `transaction.py`). +- Move relevant definitions from `core.py` to the new modules. + +### 4. Update `__init__.py` +- Re-export public names from the new modules in `__init__.py`. + +### 5. Handle `core.py` +- Delete `core.py` if all definitions have been moved. +- Keep `core.py` if it serves as a composition point. + +## Verify +- `RUN_TEST_CMD` succeeds. +- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. \ No newline at end of file From 85c441135e0d8035bfc0149ee2d6c3937ba8fdc0 Mon Sep 17 00:00:00 2001 From: David Vujic Date: Sun, 17 May 2026 14:48:27 +0200 Subject: [PATCH 2/6] wip: migrate to polylith skills --- .../skills/polylith/migrate-project/SKILLS.md | 2 +- .../migrate-project/convert-linter/skill.md | 2 +- .../convert-package-manager/skill.md | 2 +- .../convert-type-checker/skill.md | 2 +- .../extract-standalone-modules/skill.md | 2 +- .../isolate-shared-and-project-logic/skill.md | 2 +- .../migrate-project/orchestrator/skill.md | 59 +++---------------- .../split-component-internals/skill.md | 2 +- 8 files changed, 15 insertions(+), 58 deletions(-) diff --git a/.agents/skills/polylith/migrate-project/SKILLS.md b/.agents/skills/polylith/migrate-project/SKILLS.md index a0c426ed..4c5e0806 100644 --- a/.agents/skills/polylith/migrate-project/SKILLS.md +++ b/.agents/skills/polylith/migrate-project/SKILLS.md @@ -8,7 +8,7 @@ description: Skills for migrating a non-Polylith Python project into a Polylith This file serves as an index for all **project migration skills** available in the Polylith CLI. These skills are **only** for migrating **non-Polylith Python projects** into a Polylith workspace **when explicitly requested by a human**. **⚠️ When to Use These Skills** -- A human has **explicitly instructed** to migrate a specific project (e.g., "Migrate the project `my-app` in `/projects/my-app` to Polylith"). +- A human has **explicitly instructed** to migrate a specific project (e.g., "Migrate the project `` in `/projects/` to Polylith"). - The target project has been copied into the `/projects` folder of this Polylith workspace. - These skills are **not** relevant for daily Polylith workflows or already-migrated projects. - They are **not** intended for automated or unattended use. diff --git a/.agents/skills/polylith/migrate-project/convert-linter/skill.md b/.agents/skills/polylith/migrate-project/convert-linter/skill.md index 1801b4e6..01c0157d 100644 --- a/.agents/skills/polylith/migrate-project/convert-linter/skill.md +++ b/.agents/skills/polylith/migrate-project/convert-linter/skill.md @@ -31,7 +31,7 @@ From `migration//state.md`: - Identify any project-specific rules or ignores that differ from the workspace's standards. ### 3. Merge Project-Specific Rules -- If the project has unique linting rules or ignores, merge them into the workspace root's `[tool.ruff]` configuration. +- If the project has unique linting rules or ignores, merge them into the workspace root's linting configuration (e.g., `[tool.ruff]`). - For conflicts (e.g., stricter rules in the project), ask the user to provide guidance on whether to: - Adopt the project's rules in the workspace. - Suppress the project's rules in favor of the workspace's. diff --git a/.agents/skills/polylith/migrate-project/convert-package-manager/skill.md b/.agents/skills/polylith/migrate-project/convert-package-manager/skill.md index 0fcfd2e2..6e8fb00a 100644 --- a/.agents/skills/polylith/migrate-project/convert-package-manager/skill.md +++ b/.agents/skills/polylith/migrate-project/convert-package-manager/skill.md @@ -1,6 +1,6 @@ --- name: convert-package-manager -description: Convert the project's `pyproject.toml` to PEP 621/uv format and register it as a workspace member (if the user opts in). +description: Convert the project's `pyproject.toml` to a workspace-standard format (e.g., PEP 621/uv) and register it as a workspace member (if the user opts in). --- # Skill: convert-package-manager diff --git a/.agents/skills/polylith/migrate-project/convert-type-checker/skill.md b/.agents/skills/polylith/migrate-project/convert-type-checker/skill.md index dc4b6101..ba122b85 100644 --- a/.agents/skills/polylith/migrate-project/convert-type-checker/skill.md +++ b/.agents/skills/polylith/migrate-project/convert-type-checker/skill.md @@ -1,6 +1,6 @@ --- name: convert-type-checker -description: Replace the project's existing type checker with the workspace's configured type-checking tool to align with the workspace's standards. +description: Replace the project's existing type checker with a workspace-standard tool (e.g., **ty**) to align with the workspace's standards. --- # Skill: convert-type-checker diff --git a/.agents/skills/polylith/migrate-project/extract-standalone-modules/skill.md b/.agents/skills/polylith/migrate-project/extract-standalone-modules/skill.md index 219ff030..26eed893 100644 --- a/.agents/skills/polylith/migrate-project/extract-standalone-modules/skill.md +++ b/.agents/skills/polylith/migrate-project/extract-standalone-modules/skill.md @@ -1,6 +1,6 @@ --- name: extract-standalone-modules -description: Extract foundational modules (e.g., `consts.py`, `exceptions.py`) from the residual component into standalone components. +description: Extract foundational modules (e.g., `consts.py`, `exceptions.py`, or similar) from the residual component into standalone components. --- # Skill: extract-standalone-modules diff --git a/.agents/skills/polylith/migrate-project/isolate-shared-and-project-logic/skill.md b/.agents/skills/polylith/migrate-project/isolate-shared-and-project-logic/skill.md index acf6c74a..e4c9c251 100644 --- a/.agents/skills/polylith/migrate-project/isolate-shared-and-project-logic/skill.md +++ b/.agents/skills/polylith/migrate-project/isolate-shared-and-project-logic/skill.md @@ -1,6 +1,6 @@ --- name: isolate-shared-and-project-logic -description: Identify and isolate shared and project-specific logic in monolithic components (e.g., `models`, `schemas`). +description: Identify and isolate shared and project-specific logic in monolithic components (e.g., `models`, `schemas`, or similar). --- # Skill: isolate-shared-and-project-logic diff --git a/.agents/skills/polylith/migrate-project/orchestrator/skill.md b/.agents/skills/polylith/migrate-project/orchestrator/skill.md index a6b105ab..67f3a526 100644 --- a/.agents/skills/polylith/migrate-project/orchestrator/skill.md +++ b/.agents/skills/polylith/migrate-project/orchestrator/skill.md @@ -12,7 +12,7 @@ Define and execute the workflow for migrating a non-Polylith Python project to a ## Usage To migrate a project, load the `migrate.orchestrator` skill and provide the project name (e.g., the subdirectory in `/projects`): ``` -Load the `migrate.orchestrator` skill and specify the project name (e.g., `my-app`). +Load the `migrate.orchestrator` skill and specify the project name (e.g., ``). ``` ## Steps @@ -20,7 +20,7 @@ Load the `migrate.orchestrator` skill and specify the project name (e.g., `my-ap ### 1. User Confirmation - Immediately ask the user to confirm the project path and migration intent: ``` - You are about to migrate `/projects/my-app` to Polylith. This will refactor the project into bases and components. Proceed? (yes/no) + You are about to migrate `/projects/` to Polylith. This will refactor the project into bases and components. Proceed? (yes/no) ``` - If the user declines, abort the migration: ``` @@ -99,65 +99,22 @@ The migration process consists of the following steps, executed in this order: - Skill: `dedupe` - Dependencies: `split-big-component`, `extract-standalone-modules` -## Workflow -The migration process consists of the following steps, executed in this order: - -1. **Discover**: Inspect the project and create `state.md` and `manifest.md`. - - Skill: `discover` - - Dependencies: None - -2. **Extract to Base**: Extract application code into a temporary migration base. - - Skill: `extract-to-base` - - Dependencies: `discover` - -3. **Prepare Project**: Clean up the project subfolder and consolidate dependencies. - - Skill: `prepare-project` - - Dependencies: `extract-to-base` - -4. **Isolate Base and Big Component**: Shrink the temporary migration base into thin bases and a big component. - - Skill: `isolate-base-and-big-component` - - Dependencies: `prepare-project` - -5. **Split Big Component**: Split the big component into multiple focused components. - - Skill: `split-big-component` - - Dependencies: `isolate-base-and-big-component` - -9. **Extract Standalone Modules**: Extract zero-dependency and low-dependency modules into standalone components. - - Skill: `extract-standalone-modules` - - Dependencies: `split-big-component` - -10. **Isolate Shared and Project Logic**: Identify and isolate shared and project-specific logic in monolithic components (e.g., `models`, `schemas`). - - Skill: `isolate-shared-and-project-logic` - - Dependencies: `extract-standalone-modules`, `split-big-component` - -11. **Split Component Internals**: Split monolithic `core.py` files in shared components into domain-focused modules. - - Skill: `split-component-internals` - - Dependencies: `distribute-wiring` - -12. **Refactor Tests**: Restructure unit tests to align with the workspace's Polylith theme. - - Skill: `refactor-tests` - - Dependencies: `split-component-internals` - -13. **Definition of Done**: Verify that all migration criteria are met. - - Skill: `definition-of-done` - - Dependencies: `refactor-tests` - ## Optional Skills -- **Convert Linter**: Replace the project's linter with `ruff` (if the user opts in during the `discover` step). +- **Convert Linter**: Replace the project's existing linter and formatter with a workspace-standard tool (e.g., **ruff**) if the user opts in during the `discover` step. - Skill: `convert-linter` - Dependencies: `discover` - - Trigger: User opts in during `discover` step (`CONVERT_LINTER=yes` in `state.md`). + - Trigger: User opts in during `discover` step. -- **Convert Type Checker**: Replace the project's type checker with `ty` (if the user opts in during the `discover` step). +- **Convert Type Checker**: Replace the project's existing type checker with a workspace-standard tool (e.g., **ty**) if the user opts in during the `discover` step. - Skill: `convert-type-checker` - Dependencies: `discover` - - Trigger: User opts in during `discover` step (`CONVERT_TYPE_CHECKER=yes` in `state.md`). + - Trigger: User opts in during `discover` step. -- **Convert Package Manager**: Convert the project's `pyproject.toml` to PEP 621/uv format and register it as a workspace member (if the user opts in during the `discover` step). +- **Convert Package Manager**: Convert the project's `pyproject.toml` to a workspace-standard format (e.g., PEP 621/uv) and register it as a workspace member if the user opts in during the `discover` step. - Skill: `convert-package-manager` - Dependencies: `discover` - - Trigger: User opts in during `discover` step (`CONVERT_PACKAGE_MANAGER=yes` in `state.md`). + - Trigger: User opts in during `discover` step. - **Dedupe**: Identify and execute controlled deduplication of code during migration (if the user opts in). - Skill: `dedupe` diff --git a/.agents/skills/polylith/migrate-project/split-component-internals/skill.md b/.agents/skills/polylith/migrate-project/split-component-internals/skill.md index a1c35820..e4d15c86 100644 --- a/.agents/skills/polylith/migrate-project/split-component-internals/skill.md +++ b/.agents/skills/polylith/migrate-project/split-component-internals/skill.md @@ -6,7 +6,7 @@ description: Split monolithic `core.py` files in shared components into domain-f # Skill: split-component-internals ## Goal -Split monolithic `core.py` files in **shared components** (e.g., `models_shared`, `schemas_shared`) into domain-focused modules. This skill ensures that shared components remain well-organized and maintainable. +Split monolithic `core.py` files in **shared components** (e.g., `models_shared`, `schemas_shared`, or similar) into domain-focused modules. This skill ensures that shared components remain well-organized and maintainable. ## Inputs From `migration//state.md`: From f70251595ac8743edd46281c5938c3768facd339 Mon Sep 17 00:00:00 2001 From: David Vujic Date: Sun, 17 May 2026 20:34:09 +0200 Subject: [PATCH 3/6] wip --- .../skills/polylith/migrate-project/README.md | 122 +++++++++---- .../skills/polylith/migrate-project/SKILLS.md | 58 ------ .../migrate-project/discover/skill.md | 115 ------------ .../distribute-wiring/skill.md | 57 ------ .../migrate-project/extract-to-base/skill.md | 62 ------- .../isolate-base-and-big-component/skill.md | 62 ------- .../SKILL.md} | 15 +- .../SKILL.md} | 18 +- .../SKILL.md} | 15 +- .../skill.md => migrate-dedupe/SKILL.md} | 13 +- .../SKILL.md} | 19 +- .../migrate-project/migrate-discover/SKILL.md | 169 ++++++++++++++++++ .../migrate-distribute-wiring/SKILL.md | 67 +++++++ .../SKILL.md} | 8 +- .../migrate-extract-to-base/SKILL.md | 72 ++++++++ .../SKILL.md | 73 ++++++++ .../SKILL.md} | 15 +- .../migrate-orchestrator/SKILL.md | 104 +++++++++++ .../migrate-prepare-project/SKILL.md | 72 ++++++++ .../migrate-refactor-tests/SKILL.md | 79 ++++++++ .../SKILL.md} | 16 +- .../SKILL.md} | 11 +- .../migrate-project/orchestrator/skill.md | 132 -------------- .../migrate-project/prepare-project/skill.md | 61 ------- .../migrate-project/refactor-tests/skill.md | 49 ----- 25 files changed, 813 insertions(+), 671 deletions(-) delete mode 100644 .agents/skills/polylith/migrate-project/SKILLS.md delete mode 100644 .agents/skills/polylith/migrate-project/discover/skill.md delete mode 100644 .agents/skills/polylith/migrate-project/distribute-wiring/skill.md delete mode 100644 .agents/skills/polylith/migrate-project/extract-to-base/skill.md delete mode 100644 .agents/skills/polylith/migrate-project/isolate-base-and-big-component/skill.md rename .agents/skills/polylith/migrate-project/{convert-linter/skill.md => migrate-convert-linter/SKILL.md} (65%) rename .agents/skills/polylith/migrate-project/{convert-package-manager/skill.md => migrate-convert-package-manager/SKILL.md} (66%) rename .agents/skills/polylith/migrate-project/{convert-type-checker/skill.md => migrate-convert-type-checker/SKILL.md} (63%) rename .agents/skills/polylith/migrate-project/{dedupe/skill.md => migrate-dedupe/SKILL.md} (65%) rename .agents/skills/polylith/migrate-project/{definition-of-done/skill.md => migrate-definition-of-done/SKILL.md} (58%) create mode 100644 .agents/skills/polylith/migrate-project/migrate-discover/SKILL.md create mode 100644 .agents/skills/polylith/migrate-project/migrate-distribute-wiring/SKILL.md rename .agents/skills/polylith/migrate-project/{extract-standalone-modules/skill.md => migrate-extract-standalone-modules/SKILL.md} (79%) create mode 100644 .agents/skills/polylith/migrate-project/migrate-extract-to-base/SKILL.md create mode 100644 .agents/skills/polylith/migrate-project/migrate-isolate-base-and-big-component/SKILL.md rename .agents/skills/polylith/migrate-project/{isolate-shared-and-project-logic/skill.md => migrate-isolate-shared-and-project-logic/SKILL.md} (64%) create mode 100644 .agents/skills/polylith/migrate-project/migrate-orchestrator/SKILL.md create mode 100644 .agents/skills/polylith/migrate-project/migrate-prepare-project/SKILL.md create mode 100644 .agents/skills/polylith/migrate-project/migrate-refactor-tests/SKILL.md rename .agents/skills/polylith/migrate-project/{split-big-component/skill.md => migrate-split-big-component/SKILL.md} (87%) rename .agents/skills/polylith/migrate-project/{split-component-internals/skill.md => migrate-split-component-internals/SKILL.md} (56%) delete mode 100644 .agents/skills/polylith/migrate-project/orchestrator/skill.md delete mode 100644 .agents/skills/polylith/migrate-project/prepare-project/skill.md delete mode 100644 .agents/skills/polylith/migrate-project/refactor-tests/skill.md diff --git a/.agents/skills/polylith/migrate-project/README.md b/.agents/skills/polylith/migrate-project/README.md index 8c324521..3192b08c 100644 --- a/.agents/skills/polylith/migrate-project/README.md +++ b/.agents/skills/polylith/migrate-project/README.md @@ -1,53 +1,111 @@ # Project Migration Skills -This directory contains skills for migrating **non-Polylith Python projects** into a Polylith workspace. -These skills are **only** relevant when a human explicitly requests to migrate a specific project (e.g., "Migrate the project `my-app` in `/projects/my-app` to Polylith"). +> **Note for contributors:** this README is a **human reference** and a skill **index**. Agents load each `migrate-*/SKILL.md` independently via the skill loader; this README is **not** auto-loaded with any skill. Anything an agent must know to act has to live in the relevant `SKILL.md` itself, not here. + +This directory contains skills for migrating **non-Polylith Python projects** into a Polylith workspace. They cooperate via two artifacts under `migration//`: + +- `state.md` — a flat `KEY=value` file. Canonical schema lives in [`migrate-discover/SKILL.md`](./migrate-discover/SKILL.md). +- `manifest.md` — a human-readable structural inventory of the source project. --- -## ⚠️ Important Disclaimer -**These skills are NOT for daily Polylith workflows or already-migrated projects.** -They are **only** intended for use when: -1. A human has **explicitly instructed** to migrate a specific project. -2. The target project has been copied into the `/projects` folder of this Polylith workspace. -3. The goal is to refactor the project into Polylith bricks (bases, components, etc.). +## ⚠️ When to use these skills + +**Only** when: +1. A human has **explicitly instructed** to migrate a specific project (e.g., "migrate `projects/my-app` to Polylith"). +2. The target project lives under `projects//` of this Polylith workspace. +3. The goal is to refactor the project into Polylith bricks (bases and components). -**Do NOT use these skills for:** +**Do not use** for: - Automated or unattended migrations. -- Projects that are already structured as Polylith workspaces. -- Daily development tasks in a Polylith workspace. +- Projects that are already structured as Polylith bricks. +- Daily Polylith development tasks — for those, see the sibling `polylith-*` skills. + +--- + +## How to invoke + +Load the orchestrator and let it drive the rest: + +``` +Load the `migrate-orchestrator` skill and migrate `projects/`. +``` + +The orchestrator will: +1. Ask the user for explicit confirmation. +2. Establish a git safety net (dedicated branch + per-phase commits). +3. Load and execute each phase skill in order, verifying after each. + +> **Why every sub-skill says "do not load directly".** Each `migrate-` skill's `description:` starts with a redirect to `migrate-orchestrator`. This is intentional: it makes the orchestrator the only valid entry point regardless of which sub-skill the agent's fuzzy-match initially favours. The sub-skills depend on state (`migration//state.md`) and a git safety net that only the orchestrator sets up. --- -## Purpose -The skills in this directory automate and standardize the process of migrating a Python project to a Polylith workspace. They ensure the migration is performed consistently and efficiently, with minimal manual intervention. +## Downstream installation + +When this skill set is installed into another Polylith workspace (e.g. via a skills package), the in-skill `[ENTRY POINT]` / `[Internal sub-skill of migrate-orchestrator …]` markers in each `description:` are the primary routing signal — they ship with the package. + +For an extra-strong signal, downstream consumers should add the following snippet to their own repo-level `AGENTS.md` (or equivalent agent-routing file). It is **not** required — the in-skill markers are usually sufficient — but it removes any ambiguity for agents that read `AGENTS.md` before scanning skill descriptions. + +```markdown +## Polylith migration instructions + +When the user asks to migrate a non-Polylith Python project to Polylith +(e.g. "migrate `projects/` to Polylith"), load the +`migrate-orchestrator` skill first and let it drive the workflow. + +Never load `migrate-discover`, `migrate-extract-to-base`, or any other +`migrate-*` sub-skill directly — they are phases the orchestrator +invokes in order, with per-phase verification and git checkpoints +between them. +``` --- -## How to Use -1. **Explicit Invocation**: Load the `migrate.orchestrator` skill and specify the project name (e.g., `my-app`). - Example: - ``` - Load the `migrate.orchestrator` skill and provide the project name (e.g., `my-app`). - ``` +## Workflow at a glance -2. **Follow the Workflow**: The orchestrator will guide you through the migration process, ensuring all dependencies are satisfied. +| # | Phase | Skill | Depends on | +|---|----------------------------------------|------------------------------------------------|---------------------------------------------------------------------| +| — | Orchestration | [`migrate-orchestrator`](./migrate-orchestrator/SKILL.md) | — | +| 1 | Discover | [`migrate-discover`](./migrate-discover/SKILL.md) | — | +| 2 | Extract to base | [`migrate-extract-to-base`](./migrate-extract-to-base/SKILL.md) | `migrate-discover` | +| 3 | Prepare project | [`migrate-prepare-project`](./migrate-prepare-project/SKILL.md) | `migrate-extract-to-base` | +| 4 | Isolate base and big component | [`migrate-isolate-base-and-big-component`](./migrate-isolate-base-and-big-component/SKILL.md) | `migrate-prepare-project` | +| 5 | Split big component | [`migrate-split-big-component`](./migrate-split-big-component/SKILL.md) | `migrate-isolate-base-and-big-component` | +| 6 | Extract standalone modules | [`migrate-extract-standalone-modules`](./migrate-extract-standalone-modules/SKILL.md) | `migrate-split-big-component` | +| 7 | Isolate shared and project logic | [`migrate-isolate-shared-and-project-logic`](./migrate-isolate-shared-and-project-logic/SKILL.md) | `migrate-extract-standalone-modules`, `migrate-split-big-component` | +| 8 | Distribute wiring | [`migrate-distribute-wiring`](./migrate-distribute-wiring/SKILL.md) | `migrate-isolate-shared-and-project-logic` | +| 9 | Split component internals | [`migrate-split-component-internals`](./migrate-split-component-internals/SKILL.md) | `migrate-distribute-wiring` | +|10 | Refactor tests | [`migrate-refactor-tests`](./migrate-refactor-tests/SKILL.md) | `migrate-split-component-internals` | +|11 | Definition of done | [`migrate-definition-of-done`](./migrate-definition-of-done/SKILL.md) | `migrate-refactor-tests` | -3. **Verify Completion**: After executing a skill, verify its completion before proceeding to the next step. Each skill includes a "Verify" section to confirm its success. +### Optional skills (triggered during `migrate-discover`) + +| Skill | Purpose | Trigger / dependency | +|-------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------| +| [`migrate-convert-linter`](./migrate-convert-linter/SKILL.md) | Align the project's linter/formatter with the **workspace's** configured tool. | `CONVERT_LINTER=yes` in `state.md`. Runs between phase 1 and phase 2. | +| [`migrate-convert-type-checker`](./migrate-convert-type-checker/SKILL.md) | Align the project's type checker with the **workspace's** configured tool. | `CONVERT_TYPE_CHECKER=yes` in `state.md`. Runs between phase 1 and phase 2. | +| [`migrate-convert-package-manager`](./migrate-convert-package-manager/SKILL.md) | Convert the project's `pyproject.toml` to uv workspaces. **Opinionated about uv** — only run when the workspace itself uses uv. | `CONVERT_PACKAGE_MANAGER=yes` in `state.md`. Runs between phase 1 and phase 2. | +| [`migrate-dedupe`](./migrate-dedupe/SKILL.md) | Identify and apply controlled deduplication discovered during refactoring. | User approval. Runs after phase 5 or phase 6. | --- -## Key Files -- [`SKILLS.md`](./SKILLS.md): Index of all project migration skills, their descriptions, and dependencies. -- [`orchestrator/skill.md`](./orchestrator/skill.md): Defines the step-by-step migration process and ensures all dependencies are satisfied. +## Scope of the four "splitting" skills + +These skills overlap in vocabulary but address different scopes. Use this matrix to decide which one applies: + +| Skill | Scope | Trigger | +|---------------------------------------------|------------------------------------------------|---------------------------------------------------------------------------| +| `migrate-split-big-component` | Within one project; component → multiple components. | The temporary big component from phase 4 is too large. | +| `migrate-extract-standalone-modules` | Within one project; pulls foundational modules out of the residual. | Residual still contains `consts.py`/`exceptions.py`/`models.py`. | +| `migrate-split-component-internals` | Within one already-extracted shared component; `core.py` → multiple files. | A component's `core.py` mixes multiple domains internally. | +| `migrate-isolate-shared-and-project-logic` | Cross-project; separate shared vs project-specific in components used by ≥ 2 projects. | Migrating a 2nd+ project that overlaps with an already-extracted one. | + +> 💡 In a fresh migration of a single project, you usually run `migrate-split-big-component` → `migrate-extract-standalone-modules` → `migrate-split-component-internals`, and skip `migrate-isolate-shared-and-project-logic` until a second project is migrated. --- -## Workflow Overview -The migration process consists of the following phases: -1. **Discovery**: Inspect the project and create `state.md` and `manifest.md`. -2. **Tooling Standardization**: Align the project's tooling (e.g., package manager, linter, type checker) with the workspace's standards. -3. **Extraction**: Extract application code into a temporary migration base and prepare the project for Polylith. -4. **Refactoring**: Split the big component into focused components, extract standalone modules, and isolate shared/project-specific logic. -5. **Testing**: Restructure unit tests to align with the workspace's Polylith theme. -6. **Completion**: Verify that all migration criteria are met. +## Files in this directory + +- `README.md` — this file (human reference + index). +- `migrate-orchestrator/SKILL.md` — entry point; defines the phase order and the git safety net. +- `migrate-/SKILL.md` — one per phase listed above. diff --git a/.agents/skills/polylith/migrate-project/SKILLS.md b/.agents/skills/polylith/migrate-project/SKILLS.md deleted file mode 100644 index 4c5e0806..00000000 --- a/.agents/skills/polylith/migrate-project/SKILLS.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -name: migrate-project -description: Skills for migrating a non-Polylith Python project into a Polylith workspace. **Only use when explicitly requested by a human.** ---- - -# Project Migration Skills Index - -This file serves as an index for all **project migration skills** available in the Polylith CLI. These skills are **only** for migrating **non-Polylith Python projects** into a Polylith workspace **when explicitly requested by a human**. - -**⚠️ When to Use These Skills** -- A human has **explicitly instructed** to migrate a specific project (e.g., "Migrate the project `` in `/projects/` to Polylith"). -- The target project has been copied into the `/projects` folder of this Polylith workspace. -- These skills are **not** relevant for daily Polylith workflows or already-migrated projects. -- They are **not** intended for automated or unattended use. - -## Orchestrator -| Skill | Description | Dependencies | -|-------|-------------|--------------| -| [orchestrator](./orchestrator/skill.md) | Orchestrates the migration workflow by defining the order and dependencies of migration skills. | None | - -## Discovery -| Skill | Description | Dependencies | -|-------|-------------|--------------| -| [discover](./discover/skill.md) | Create `migration//state.md` and `migration//manifest.md` by inspecting the existing project under `projects//`. Detect the current linter and type checker, and ask the user if they want to convert them. | None | - -## Extraction -| Skill | Description | Dependencies | -|-------|-------------|--------------| -| [extract-to-base](./extract-to-base/skill.md) | Extract all application code from `projects//` into a temporary migration base. | discover | -| [prepare-project](./prepare-project/skill.md) | Clean up the project subfolder and consolidate dependencies. | extract-to-base | -| [isolate-base-and-big-component](./isolate-base-and-big-component/skill.md) | Shrink the temporary migration base into thin bases and a big component. | prepare-project | - -## Refactoring -| Skill | Description | Dependencies | -|-------|-------------|--------------| -| [split-big-component](./split-big-component/skill.md) | Split the big component into multiple focused components. | isolate-base-and-big-component | -| [extract-standalone-modules](./extract-standalone-modules/skill.md) | Extract foundational modules (e.g., `consts.py`, `exceptions.py`) into standalone components. | split-big-component | -| [isolate-shared-and-project-logic](./isolate-shared-and-project-logic/skill.md) | Identify and isolate shared and project-specific logic in monolithic components (e.g., `models`, `schemas`). | extract-standalone-modules, split-big-component | -| [distribute-wiring](./distribute-wiring/skill.md) | Distribute app-wiring code from the residual component into the appropriate bases and shared components. | isolate-shared-and-project-logic | -| [split-component-internals](./split-component-internals/skill.md) | Split monolithic `core.py` files in generic components (e.g., `models`, `schemas`) into domain-focused modules. | distribute-wiring | -| [refactor-tests](./refactor-tests/skill.md) | Restructure unit tests to align with the workspace's Polylith theme. | split-component-internals | - -## Optional Tooling Conversions -| Skill | Description | Dependencies | -|-------|-------------|--------------| -| [convert-linter](./convert-linter/skill.md) | Replace the project's existing linter and formatter with **ruff** (if the user opts in). | discover | -| [convert-type-checker](./convert-type-checker/skill.md) | Replace the project's existing type checker with **ty** (if the user opts in). | discover | -| [convert-package-manager](./convert-package-manager/skill.md) | Convert the project's `pyproject.toml` to PEP 621/uv format and register it as a workspace member (if the user opts in). | discover | - -## Optional Deduplication -| Skill | Description | Dependencies | -|-------|-------------|--------------| -| [dedupe](./dedupe/skill.md) | Identify and execute controlled deduplication of code during migration (if the user opts in). | split-big-component, extract-standalone-modules | - -## Completion -| Skill | Description | Dependencies | -|-------|-------------|--------------| -| [definition-of-done](./definition-of-done/skill.md) | Define the criteria for completing the migration process. | refactor-tests | diff --git a/.agents/skills/polylith/migrate-project/discover/skill.md b/.agents/skills/polylith/migrate-project/discover/skill.md deleted file mode 100644 index 0969ea56..00000000 --- a/.agents/skills/polylith/migrate-project/discover/skill.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -name: discover -description: Create `migration//state.md` and `migration//manifest.md` by inspecting the existing project under `projects//`. ---- - -# Skill: discover - -## Goal -Inspect the project and create `state.md` and `manifest.md` to guide the migration process. - -## Steps - -### 1. Record Project Metadata -Record the following in `migration//state.md`: - -| Field | Description | Example | -|-------|-------------|---------| -| `PROJECT_DIR` | Project subfolder path | `projects/api` | -| `ORIG_TOP_NS` | Current import namespace | `myapp` | -| `TARGET_TOP_NS` | Desired Polylith namespace (default: `ORIG_TOP_NS`) | `myapp` | -| `BRICK_NAME` | Derived from `[project.name]` (ask user to confirm) | `example_a` | -| `ALIAS` | Short kebab-case alias for Polylith (ask user to confirm) | `svc-a` | -| `GROUP` | Optional project group (ask user) | `domain-a` | - -#### Derive `BRICK_NAME` from `[project.name]`: -| `[project.name]` | `BRICK_NAME` | -|------------------|--------------| -| `example-service-a` | `example_a` | -| `order-management-api` | `order_management` | -| `payment-worker` | `payment` | - -#### Derive `ALIAS` from `[project.name]`: -| `[project.name]` | `ALIAS` | -|------------------|---------| -| `example-service-a` | `svc-a` | -| `order-management-api` | `order-mgmt` | -| `payment-worker` | `payment` | - -### 2. Detect Tools -Detect the project's package manager, linter, formatter, and type checker by scanning config files: - -| Tool | Detection Criteria | -|------|--------------------| -| **Package Manager** | | -| Poetry | `poetry.lock` or `[tool.poetry]` in `pyproject.toml` | -| Pipenv | `Pipfile` or `Pipfile.lock` | -| Pip | `requirements.txt` (no lock file) | -| UV | `uv.lock` or `[tool.uv]` in `pyproject.toml` | -| Setuptools | `setup.py` or `setup.cfg` only | -| **Linter** | | -| Flake8 | `setup.cfg` or `.flake8` | -| Pylint | `[tool.pylint]` or `.pylintrc` | -| Ruff | `[tool.ruff]` | -| **Formatter** | | -| Black | `[tool.black]` | -| Isort | `[tool.isort]` | -| Ruff | `[tool.ruff]` | -| **Type Checker** | | -| Mypy | `mypy.ini` or `[tool.mypy]` | -| Pyright | `[tool.pyright]` or `pyrightconfig.json` | -| Ty | `[tool.ty]` | - -Record the detected tools in `migration//state.md`: - -```text -PACKAGE_MANAGER= -LINTER= -FORMATTER= -TYPE_CHECKER= -``` - -### 6. Determine `poly` Command Prefix - -Based on the detected package manager, determine the correct command prefix for `poly`: - -| Package Manager | `poly` Command Prefix | Example | -|-----------------|-----------------------|---------| -| Poetry | `poetry poly` | `poetry poly check` | -| Pipenv | `pipenv run poly` | `pipenv run poly sync` | -| Pip | `poly` | `poly info` | -| UV | `uv run poly` | `uv run poly check` | -| Setuptools | `poly` | `poly sync` | - -Record the command prefix in `migration//state.md`: - -```text -POLY_CMD_PREFIX= -``` - -### 3. Ask User for Preferences -- Ask the user if they want to convert the linter to `ruff` and the type checker to `ty`. -- Record their choices in `state.md`: - ```text - CONVERT_LINTER= - CONVERT_TYPE_CHECKER= - ``` - -### 4. Discover Commands -Look inside `projects//` for config files and record the following commands: -- `RUN_TEST_CMD`: Command to run tests. -- `RUN_LINT_CMD`: Command to run linting (optional). -- `RUN_TYPECHECK_CMD`: Command to run type checking (optional). - -### 5. Create `manifest.md` -Record the following in `migration//manifest.md`: -- Directory tree of `projects//`. -- Module map (path + one-line description of role). -- Entrypoints (FastAPI app, CLI, worker, etc.). -- Test directory structure and count. -- Infrastructure files (Dockerfiles, k8s manifests, etc.). - -## Done When -- `state.md` and `manifest.md` are created and populated. -- User confirms `BRICK_NAME`, `ALIAS`, and tooling preferences. -- All detected tools and commands are recorded. diff --git a/.agents/skills/polylith/migrate-project/distribute-wiring/skill.md b/.agents/skills/polylith/migrate-project/distribute-wiring/skill.md deleted file mode 100644 index 9aa7ee28..00000000 --- a/.agents/skills/polylith/migrate-project/distribute-wiring/skill.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -name: distribute-wiring -description: Distribute app-wiring code from the residual component into the appropriate bases and shared components. ---- - -# Skill: distribute-wiring - -## Goal -Distribute app-wiring code from the residual component into the appropriate bases and shared components. - -## Inputs -From `migration//state.md`: -- `TARGET_TOP_NS` -- `BRICK_NAME` -- Verification commands. -- Base names. - -From `migration//manifest.md`: -- Current module map, including what remains in the residual component. - -## Steps - -### 1. Read the Residual Component -- List every public function remaining in the residual module. -- Confirm it contains only app-wiring code. - -### 2. Identify the Split -- Trace callers of each function (use `grep`). -- Group callers by base or runner script. -- Map each function to its natural base: - -| Function Pattern | Belongs In | Rationale | -|------------------|------------|-----------| -| `init_consumer`, `close_consumer` | Handler/consumer base | Kafka consumer lifecycle is handler-specific | -| `init_job` | Jobs base | Job bootstrap is jobs-specific | -| `init_api` / app factory | API base | HTTP server setup is API-specific | - -### 3. Extract Shared Helpers -- Identify shared init functions (e.g., `init_logging`, `init_db`). -- Move shared helpers to a `bootstrap` component if needed. - -### 4. Move Composite Functions -- Move composite functions to their respective bases. -- Update runner scripts to import from the base directly. - -### 5. Update Tests -- Update integration tests to monkeypatch the correct module. - -### 6. Clean Up -- Delete the residual component directory. -- Update `pyproject.toml` to remove the residual brick. - -## Verify -- `RUN_TEST_CMD` succeeds. -- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. -- Run `POLY_CMD_PREFIX check` to validate the workspace structure. -- Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/extract-to-base/skill.md b/.agents/skills/polylith/migrate-project/extract-to-base/skill.md deleted file mode 100644 index 0c3e2929..00000000 --- a/.agents/skills/polylith/migrate-project/extract-to-base/skill.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -name: extract-to-base -description: Extract all application code from `projects//` into a temporary migration base. ---- - -# Skill: extract-to-base - -## Goal -Extract all application code from `projects//` into a temporary migration base (`bases///`). - -## Inputs -From `migration//state.md`: -- `PROJECT_DIR` -- `ORIG_TOP_NS` -- `TARGET_TOP_NS` (default: `ORIG_TOP_NS`) -- `BRICK_NAME` -- `RUN_TEST_CMD` (optional: `RUN_LINT_CMD`, `RUN_TYPECHECK_CMD`) - -From `migration//manifest.md`: -- Directory tree and module map. - -## Steps - -### 1. Create the Base Directory -- Create `bases///`. - -### 2. Move Application Code -- Move application packages/modules from `projects//` to the base: - - For `src/` layout: Move `projects//src//` under the base. - - For flat layout: Move `projects///` under the base. -- Leave non-code files (Dockerfiles, k8s manifests, deploy scripts, `pyproject.toml`) in `projects//`. - -### 3. Update `pyproject.toml` -- Add the base to `[tool.polylith.bricks]`: - ```toml - [tool.polylith.bricks] - "../../bases//" = "/" - ``` - -### 4. Fix Imports -- Update imports minimally to ensure tests and linting pass. - -### 5. Update `manifest.md` -- Reflect the new structure in `migration//manifest.md`. - -### 6. Handle Namespace Changes -If `TARGET_TOP_NS != ORIG_TOP_NS`, choose one of the following options: - -| Option | Description | Risk | -|--------|-------------|------| -| **Compatibility Shim** | Keep `ORIG_TOP_NS` as a shim that re-exports from `TARGET_TOP_NS`. | Lower | -| **Rewrite Imports** | Rewrite all imports to the new namespace in one go. | Higher | - -### 7. Use Shims if Needed -- If imports break, add temporary shims to re-export names from the new brick API. -- Track shims in `migration/shims.md`. - -## Verify -- `RUN_TEST_CMD` succeeds. -- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. -- Run `POLY_CMD_PREFIX check` to validate the workspace structure. -- Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. diff --git a/.agents/skills/polylith/migrate-project/isolate-base-and-big-component/skill.md b/.agents/skills/polylith/migrate-project/isolate-base-and-big-component/skill.md deleted file mode 100644 index 90c94dc7..00000000 --- a/.agents/skills/polylith/migrate-project/isolate-base-and-big-component/skill.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -name: isolate-base-and-big-component -description: Shrink the temporary migration base into thin base(s) + one big component. Bases contain only entrypoints/wiring, while the big component contains everything else. ---- - -# Skill: isolate-base-and-big-component - -## Goal -Shrink the temporary migration base into thin base(s) + one big component: -- **Bases**: Contain only entrypoints/wiring (e.g., FastAPI endpoints, CLI wiring, consumer wiring). -- **Big Component**: Contains all other code. - -## Inputs -From `migration//state.md`: -- `TARGET_TOP_NS` -- `BRICK_NAME` -- Verification commands. - -From `migration//manifest.md`: -- Entrypoints list. - -## Steps - -### 1. Create the Big Component -- Create `components///`. - -### 2. Move Non-Entrypoint Code -- Move non-entrypoint code from the base(s) to the big component. - -### 3. Define Public API -- Define a minimal public API in `components///__init__.py`. - -### 4. Update Bases -- Update bases to import only from component APIs: - ```python - from . import ... - ``` - -### 5. Update `pyproject.toml` -- Add the new component to `[tool.polylith.bricks]`: - ```toml - [tool.polylith.bricks] - "../../bases//" = "/" - "../../components//" = "/" - ``` - -### 6. Update `manifest.md` -- Reflect the new structure in `migration//manifest.md`. - -### 7. FastAPI Guidance -| Stays in Base | Moves to Big Component | -|---------------|------------------------| -| `app = FastAPI(...)` | Domain/business logic | -| Middleware, router registration | Persistence/repositories | -| Route handlers (endpoints) | External integrations | -| Startup/shutdown/lifespan wiring | Reusable parsing/validation | - -## Verify -- `RUN_TEST_CMD` succeeds. -- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. -- Run `POLY_CMD_PREFIX check` to validate the workspace structure. -- Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. diff --git a/.agents/skills/polylith/migrate-project/convert-linter/skill.md b/.agents/skills/polylith/migrate-project/migrate-convert-linter/SKILL.md similarity index 65% rename from .agents/skills/polylith/migrate-project/convert-linter/skill.md rename to .agents/skills/polylith/migrate-project/migrate-convert-linter/SKILL.md index 01c0157d..92918be6 100644 --- a/.agents/skills/polylith/migrate-project/convert-linter/skill.md +++ b/.agents/skills/polylith/migrate-project/migrate-convert-linter/SKILL.md @@ -1,15 +1,15 @@ --- -name: convert-linter -description: Remove project-specific linting and formatting configurations and consolidate them into the workspace root. Align the project with the workspace's linting and formatting standards. +name: migrate-convert-linter +description: "[Internal sub-skill of `migrate-orchestrator` (optional, runs only when opted in during phase 1). Do not load directly — load `migrate-orchestrator` first.] Align the project's linter and formatter with the **workspace's** configured tool (whatever it is — ruff, black+isort+flake8, pylint, etc.). Removes project-specific config and consolidates rules into the workspace root." --- -# Skill: convert-linter +# Skill: migrate-convert-linter ## Goal -Remove project-specific linting and formatting configurations and consolidate them into the workspace root. Align the project with the workspace's linting and formatting standards. +Align the project with the **workspace's** linter and formatter. This skill is **not** opinionated about ruff — it reads the workspace's configured tool from the root `pyproject.toml` and aligns the project to that. ## When to Skip -Skip this step if the project's `LINTER` and `FORMATTER` in `migration//state.md` match the workspace's tools. +Skip this step if the project's `LINTER` and `FORMATTER` in `migration//state.md` already match the workspace's tools. ## Inputs From `migration//state.md`: @@ -19,6 +19,11 @@ From `migration//state.md`: ## Steps +### 0. Identify the workspace's tool +Open the **workspace root** `pyproject.toml` and identify the configured linter and formatter using the same detection table as `migrate-discover` (`[tool.ruff]` → ruff, `[tool.black]` → black, `[tool.flake8]` or `.flake8` → flake8, etc.). Record what you found — every step below refers to "the workspace's linter/formatter" and means **this** tool, not necessarily ruff. + +If the workspace has no linter configured at all, stop and ask the user how to proceed (introduce ruff? skip linting alignment? abort the conversion?). + ### 1. Remove Project-Specific Configs and Dependencies - Remove the following sections from the project's `pyproject.toml`: - `[tool.black]`, `[tool.isort]`, `[tool.flake8]`, `[tool.pylint.*]`, `[tool.autopep8]`, `[tool.pycodestyle]` diff --git a/.agents/skills/polylith/migrate-project/convert-package-manager/skill.md b/.agents/skills/polylith/migrate-project/migrate-convert-package-manager/SKILL.md similarity index 66% rename from .agents/skills/polylith/migrate-project/convert-package-manager/skill.md rename to .agents/skills/polylith/migrate-project/migrate-convert-package-manager/SKILL.md index 6e8fb00a..9b307c16 100644 --- a/.agents/skills/polylith/migrate-project/convert-package-manager/skill.md +++ b/.agents/skills/polylith/migrate-project/migrate-convert-package-manager/SKILL.md @@ -1,15 +1,23 @@ --- -name: convert-package-manager -description: Convert the project's `pyproject.toml` to a workspace-standard format (e.g., PEP 621/uv) and register it as a workspace member (if the user opts in). +name: migrate-convert-package-manager +description: "[Internal sub-skill of `migrate-orchestrator` (optional, runs only when opted in during phase 1). Do not load directly — load `migrate-orchestrator` first.] Convert the project's `pyproject.toml` to PEP 621/uv-workspaces format and register it as a uv-workspace member. **Opinionated about uv** — only applies when the workspace itself uses uv. Skip otherwise." --- -# Skill: convert-package-manager +# Skill: migrate-convert-package-manager + +> ⚠ **Opinionation gate.** This skill is **explicitly opinionated about uv**. It does not generalize to Poetry, PDM, or Hatch workspaces. If your Polylith workspace uses one of those, **do not run this skill** — align the project to the workspace's manager via a manual step instead, and leave `CONVERT_PACKAGE_MANAGER=no` in `state.md`. ## Goal -Convert the project's `pyproject.toml` to PEP 621/uv format and register it as a workspace member. This step ensures the project shares the workspace's single lock file and virtual environment, preventing version skew. +Convert the project's `pyproject.toml` to PEP 621/uv format and register it as a uv-workspace member. This makes the project share the workspace's single lock file and virtual environment, preventing version skew. ## When to Skip -- If `PACKAGE_MANAGER=uv` in `migration//state.md`, skip this step and proceed to the next skill. +Skip this skill if **any** of the following holds: +- `PACKAGE_MANAGER=uv` already in `migration//state.md` (nothing to convert). +- The workspace root does **not** use uv (this skill does not apply — see the opinionation gate above). +- `CONVERT_PACKAGE_MANAGER=no` in `state.md` (user opted out during `migrate-discover`). + +### Verify the workspace uses uv before proceeding +Open the **workspace root** `pyproject.toml` and look for `[tool.uv.workspace]` and/or a sibling `uv.lock`. If neither is present, **stop**: this skill does not apply. ## Inputs From `migration//state.md`: diff --git a/.agents/skills/polylith/migrate-project/convert-type-checker/skill.md b/.agents/skills/polylith/migrate-project/migrate-convert-type-checker/SKILL.md similarity index 63% rename from .agents/skills/polylith/migrate-project/convert-type-checker/skill.md rename to .agents/skills/polylith/migrate-project/migrate-convert-type-checker/SKILL.md index ba122b85..032eab50 100644 --- a/.agents/skills/polylith/migrate-project/convert-type-checker/skill.md +++ b/.agents/skills/polylith/migrate-project/migrate-convert-type-checker/SKILL.md @@ -1,15 +1,15 @@ --- -name: convert-type-checker -description: Replace the project's existing type checker with a workspace-standard tool (e.g., **ty**) to align with the workspace's standards. +name: migrate-convert-type-checker +description: "[Internal sub-skill of `migrate-orchestrator` (optional, runs only when opted in during phase 1). Do not load directly — load `migrate-orchestrator` first.] Align the project's type checker with the **workspace's** configured tool (whatever it is — mypy, pyright, ty, etc.). Removes project-specific config and consolidates settings into the workspace root." --- -# Skill: convert-type-checker +# Skill: migrate-convert-type-checker ## Goal -Replace the project's existing type checker with the workspace's configured type-checking tool to align with the workspace's standards. +Align the project with the **workspace's** type checker. This skill is **not** opinionated about ty — it reads the workspace's configured tool from the root `pyproject.toml` and aligns the project to that. ## When to Skip -Skip this step if the project's `TYPE_CHECKER` in `migration//state.md` matches the workspace's type-checking tool. +Skip this step if the project's `TYPE_CHECKER` in `migration//state.md` already matches the workspace's type-checking tool. ## Inputs From `migration//state.md`: @@ -19,6 +19,11 @@ From `migration//state.md`: ## Steps +### 0. Identify the workspace's tool +Open the **workspace root** `pyproject.toml` and identify the configured type checker using the same detection table as `migrate-discover` (`[tool.mypy]` or `mypy.ini` → mypy, `[tool.pyright]` or `pyrightconfig.json` → pyright, `[tool.ty]` → ty). Record what you found — every step below refers to "the workspace's type checker" and means **this** tool, not necessarily ty. + +If the workspace has no type checker configured at all, stop and ask the user how to proceed (introduce one? skip type-check alignment? abort the conversion?). + ### 1. Remove Old Configs and Dependencies - Remove the following sections from `pyproject.toml`: - `[tool.mypy]`, `[[tool.mypy.overrides]]`, `[mypy-*]`, `[tool.pyright]`, `[tool.pytype]` diff --git a/.agents/skills/polylith/migrate-project/dedupe/skill.md b/.agents/skills/polylith/migrate-project/migrate-dedupe/SKILL.md similarity index 65% rename from .agents/skills/polylith/migrate-project/dedupe/skill.md rename to .agents/skills/polylith/migrate-project/migrate-dedupe/SKILL.md index 209c37be..e96027b1 100644 --- a/.agents/skills/polylith/migrate-project/dedupe/skill.md +++ b/.agents/skills/polylith/migrate-project/migrate-dedupe/SKILL.md @@ -1,9 +1,16 @@ --- -name: dedupe -description: Identify and execute controlled deduplication of code during migration (if the user opts in). +name: migrate-dedupe +description: "[Internal sub-skill of `migrate-orchestrator` (optional, runs only when opted in during phase 1). Do not load directly — load `migrate-orchestrator` first.] Identify and execute controlled deduplication of code during migration (if the user opts in)." --- -# Skill: dedupe +# Skill: migrate-dedupe + +> 📐 **Scope vs sibling skills.** This skill is **opportunistic deduplication** that may be triggered any time during refactoring when duplication candidates surface. It is **not** the canonical place for the structural decompositions: +> - For "split this big component into smaller ones", use `migrate-split-big-component` (it already includes a dedup-analysis subsection — usually sufficient on a first migration). +> - For "this component's `core.py` mixes domains", use `migrate-split-component-internals`. +> - For "two projects have overlapping code, split shared from project-specific", use `migrate-isolate-shared-and-project-logic`. +> +> Use `migrate-dedupe` when none of the above fits cleanly — e.g., duplication discovered across already-extracted components that don't map to a structural split. ## Goal Identify duplication candidates during the migration process and execute controlled deduplication for user-approved candidates. diff --git a/.agents/skills/polylith/migrate-project/definition-of-done/skill.md b/.agents/skills/polylith/migrate-project/migrate-definition-of-done/SKILL.md similarity index 58% rename from .agents/skills/polylith/migrate-project/definition-of-done/skill.md rename to .agents/skills/polylith/migrate-project/migrate-definition-of-done/SKILL.md index 70242128..4569bee8 100644 --- a/.agents/skills/polylith/migrate-project/definition-of-done/skill.md +++ b/.agents/skills/polylith/migrate-project/migrate-definition-of-done/SKILL.md @@ -1,9 +1,9 @@ --- -name: definition-of-done -description: Define the criteria for completing the migration process. +name: migrate-definition-of-done +description: "[Internal sub-skill of `migrate-orchestrator` (phase 11 of 11). Do not load directly — load `migrate-orchestrator` first, which drives all phases.] Define the criteria for completing the migration process." --- -# Skill: definition-of-done +# Skill: migrate-definition-of-done ## Done When @@ -13,7 +13,7 @@ description: Define the criteria for completing the migration process. - All non-entrypoint code lives in components. ### Source Project -- `projects//` contains only: +- `projects//` contains only: - Packaging config (`pyproject.toml`). - Runner scripts and task runners (`Makefile`, `Justfile`). - Project-specific config (e.g., `alembic.ini`). @@ -22,13 +22,13 @@ description: Define the criteria for completing the migration process. - Base names are project-prefixed to avoid collisions. ### Tests -- Tests are moved from `projects//tests/` to workspace level. +- Tests are moved from `projects//tests/` to workspace level. - Unit tests are organized according to the Polylith theme in use: - - For the **loose theme**, unit tests mirror the brick structure: `test///`. - - For other themes, follow the workspace's test structure conventions. + - **`loose` theme:** unit tests live under `test/bases///` and `test/components///`. + - **`tdd` theme:** unit tests live under `bases//test///` and `components//test///`. - Integration tests live in a shared location (e.g., `test/integration/`). - Shared fixtures live in `test//conftest.py` or `test/conftest.py`. -- `RUN_TEST_CMD` points to the test root. +- `RUN_TEST_CMD` points to the test root **and collects the same number of tests as the pre-migration baseline**. ### Infrastructure - Infrastructure folders are moved to `infra///`. @@ -47,7 +47,8 @@ description: Define the criteria for completing the migration process. - Project `pyproject.toml` lists runtime dependencies without version numbers. ### Cleanup -- Migration artifacts (e.g., `migration//state.md`, `migration//manifest.md`) are removed or kept for reference. +- Migration artifacts (`migration//state.md`, `migration//manifest.md`, and any `migration/shims.md`) are either removed or kept under `migration//` for reference (user's choice). +- The migration branch (`GIT_BRANCH` from `state.md`) is ready to be merged or rebased into the main branch. Per-phase commits remain available for review/bisect. ## Verify - `RUN_TEST_CMD` succeeds. diff --git a/.agents/skills/polylith/migrate-project/migrate-discover/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-discover/SKILL.md new file mode 100644 index 00000000..01be2259 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/migrate-discover/SKILL.md @@ -0,0 +1,169 @@ +--- +name: migrate-discover +description: "[Internal sub-skill of `migrate-orchestrator` (phase 1 of 11). Do not load directly — load `migrate-orchestrator` first, which drives all phases.] Create `migration//state.md` and `migration//manifest.md` by inspecting the existing project under `projects//`." +--- + +# Skill: migrate-discover + +## Goal +Inspect the project and create two artifacts that drive every subsequent migration phase: +- `migration//state.md` — a flat `KEY=value` file (see schema below). +- `migration//manifest.md` — a human-readable structural inventory. + +> 💡 `` is the project subfolder name (e.g., `api` for `projects/api/`). Use that exact string everywhere — paths, filenames, branch names. + +## Canonical `state.md` schema + +All later skills read `state.md` as a flat `KEY=value` file. Use **exactly** this format — no markdown tables, no fenced TOML, no inline comments. One key per line. + +```ini +# migration//state.md +PROJECT_DIR=projects/ +ORIG_TOP_NS= +TARGET_TOP_NS= +INITIAL_BASE_NAME= +ALIAS= +GROUP= + +PACKAGE_MANAGER= +LINTER= +FORMATTER= +TYPE_CHECKER= +POLY_CMD_PREFIX= + +CONVERT_LINTER= +CONVERT_TYPE_CHECKER= +CONVERT_PACKAGE_MANAGER= + +RUN_TEST_CMD= +RUN_LINT_CMD= +RUN_TYPECHECK_CMD= + +GIT_BRANCH= +GIT_BASE_SHA= +``` + +### Field reference + +| Key | Description | Source | +|-----|-------------|--------| +| `PROJECT_DIR` | Project subfolder path. | The orchestrator's ``. | +| `ORIG_TOP_NS` | Current top-level Python package name. | First non-`tests` directory under `projects//src/` or `projects//`. | +| `TARGET_TOP_NS` | Desired Polylith namespace. | `workspace.toml` `[tool.polylith].namespace`, or `ORIG_TOP_NS` if no workspace exists yet. | +| `INITIAL_BASE_NAME` | Name of the **single temporary base** used to hold all code during early phases. Becomes the **default base name** in `migrate-isolate-base-and-big-component`. Final migrations usually contain *several* bases — this is just the starting one. | Derived from `[project.name]`, confirmed by user. | +| `ALIAS` | Short alias shown in `poly info` / `poly deps` tables. Optional. | Derived, confirmed by user. | +| `GROUP` | Polylith project group. Optional. | Asked from user. | +| `PACKAGE_MANAGER` / `LINTER` / `FORMATTER` / `TYPE_CHECKER` | Detected tooling. | Detection table below. | +| `POLY_CMD_PREFIX` | Prefix every `poly …` command in later skills uses. | Derived from `PACKAGE_MANAGER`. | +| `CONVERT_*` | Whether the user opted in to a tooling conversion. | Asked from user. | +| `RUN_TEST_CMD` etc. | The exact shell commands the migration verifies against after each phase. | Derived from project config, confirmed by user if ambiguous. | +| `GIT_BRANCH` / `GIT_BASE_SHA` | Set by the orchestrator's Phase 0; recorded here so later skills know where to roll back to. | Orchestrator. | + +### Deriving `INITIAL_BASE_NAME` and `ALIAS` from `[project.name]` + +| `[project.name]` | `INITIAL_BASE_NAME` (snake) | `ALIAS` (kebab) | +|-------------------------|-----------------------------|-----------------| +| `example-service-a` | `example_a` | `svc-a` | +| `order-management-api` | `order_management` | `order-mgmt` | +| `payment-worker` | `payment` | `payment` | + +## Steps + +> Run these in order. Every step writes to `state.md` or `manifest.md`. Do not skip the confirmation gates (steps 4 and 7). + +### 1. Record project metadata +Read `projects//pyproject.toml` (or `setup.cfg`/`setup.py`) and fill in `PROJECT_DIR`, `ORIG_TOP_NS`, `TARGET_TOP_NS`, and the **derived** `INITIAL_BASE_NAME`, `ALIAS` per the tables above. + +### 2. Detect tooling +Scan project config files and fill in `PACKAGE_MANAGER`, `LINTER`, `FORMATTER`, `TYPE_CHECKER`: + +| Tool | Detection criteria | +|------|--------------------| +| **Package Manager** | | +| Poetry | `poetry.lock` or `[tool.poetry]` in `pyproject.toml` | +| Pipenv | `Pipfile` or `Pipfile.lock` | +| Pip | `requirements.txt` (no lock file) | +| UV | `uv.lock` or `[tool.uv]` in `pyproject.toml` | +| Setuptools | `setup.py` or `setup.cfg` only | +| **Linter** | | +| Flake8 | `setup.cfg`, `tox.ini` `[flake8]` section, or `.flake8` | +| Pylint | `[tool.pylint]` or `.pylintrc` | +| Ruff | `[tool.ruff]` | +| **Formatter** | | +| Black | `[tool.black]` | +| Isort | `[tool.isort]` | +| Ruff | `[tool.ruff.format]` | +| **Type Checker** | | +| Mypy | `mypy.ini`, `.mypy.ini`, or `[tool.mypy]` | +| Pyright | `[tool.pyright]` or `pyrightconfig.json` | +| Ty | `[tool.ty]` | + +### 3. Derive `POLY_CMD_PREFIX` +Map `PACKAGE_MANAGER` to the command prefix: + +| `PACKAGE_MANAGER` | `POLY_CMD_PREFIX` | +|-------------------|-------------------| +| `poetry` | `poetry poly` | +| `pipenv` | `pipenv run poly` | +| `pdm` | `pdm run poly` | +| `hatch` | `hatch run poly` | +| `uv` | `uv run poly` | +| `pip` / `setuptools` / activated venv | `poly` | + +### 4. Discover verification commands +Inspect `Makefile`, `Justfile`, `tox.ini`, `pyproject.toml` `[tool.pytest.ini_options]`, and CI config (`.github/workflows/*.yml`, `.circleci/config.yml`, etc.) to identify the project's existing commands. Fill `RUN_TEST_CMD`, and `RUN_LINT_CMD` / `RUN_TYPECHECK_CMD` when present. If a command can't be found, leave the value empty. + +### 5. Determine tooling-conversion eligibility +Read the **workspace root** `pyproject.toml` to determine the workspace's standard linter, formatter, type checker, and package manager. + +- If the project's `LINTER`/`FORMATTER` already matches the workspace's → set `CONVERT_LINTER=no` (skip). +- If the project's `TYPE_CHECKER` already matches the workspace's → set `CONVERT_TYPE_CHECKER=no` (skip). +- If the project's `PACKAGE_MANAGER` is already `uv` **and** the workspace uses uv → set `CONVERT_PACKAGE_MANAGER=no` (skip). +- If the workspace does **not** use uv, `migrate-convert-package-manager` does not apply at all — set `CONVERT_PACKAGE_MANAGER=no` and skip the question below. + +### 6. Create `manifest.md` +Record the following in `migration//manifest.md`: +- Directory tree of `projects//` (output of a `directory_tree` tool call). +- **Module map** — for each Python module/package: path, one-line description of its role (e.g., "FastAPI route handlers", "Kafka consumer", "domain model"). +- **Entrypoints** — every file that is the start of a deployable (FastAPI `app`, CLI `main`, Lambda `handler`, worker `run`). +- **Tests** — test directory structure, test file count, fixture file locations (`conftest.py`). +- **Infrastructure files** — Dockerfiles, k8s manifests, Helm charts, alembic configs, deploy scripts. + +### 7. Present derived values, then confirm with the user +**Do not proceed past this step without explicit user confirmation.** Present the derived state in one block: + +``` +Derived from projects//: + INITIAL_BASE_NAME = ← initial base name; you will likely add more bases later + ALIAS = ← short alias for poly info/deps tables (optional) + GROUP = ← project group (optional) + +Detected tooling: + PACKAGE_MANAGER = + LINTER = + FORMATTER = + TYPE_CHECKER = + +Verification commands: + RUN_TEST_CMD = + RUN_LINT_CMD = + RUN_TYPECHECK_CMD = + +Optional conversions you can opt into: + - Convert linter/formatter to match workspace standard? (default: no) + - Convert type checker to match workspace standard? (default: no) + - Convert package manager to uv (workspace-uv only)? (default: no) + +Confirm the values above, or correct any of them. +``` + +Wait for the user's response. Update `state.md` with corrections and the `CONVERT_*` answers. + +## Done When +The following artifacts and conditions all hold: + +- [ ] `migration//state.md` exists and contains **every** key in the schema (empty values where N/A, but no missing keys). +- [ ] `migration//manifest.md` exists with all five sections (directory tree, module map, entrypoints, tests, infrastructure). +- [ ] The user has explicitly confirmed `INITIAL_BASE_NAME`, `ALIAS`, `GROUP`, and the three `CONVERT_*` flags. +- [ ] `RUN_TEST_CMD` is set and **runs successfully on the project's current code** (the migration's baseline pass-rate). +- [ ] `GIT_BRANCH` and `GIT_BASE_SHA` are populated (from orchestrator Phase 0). diff --git a/.agents/skills/polylith/migrate-project/migrate-distribute-wiring/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-distribute-wiring/SKILL.md new file mode 100644 index 00000000..c12fb0bb --- /dev/null +++ b/.agents/skills/polylith/migrate-project/migrate-distribute-wiring/SKILL.md @@ -0,0 +1,67 @@ +--- +name: migrate-distribute-wiring +description: "[Internal sub-skill of `migrate-orchestrator` (phase 8 of 11). Do not load directly — load `migrate-orchestrator` first, which drives all phases.] Distribute app-wiring code from the residual component into the appropriate bases and shared components." +--- + +# Skill: migrate-distribute-wiring + +## Goal +Distribute app-wiring code from the residual component into the appropriate bases and shared components. + +## Inputs +From `migration//state.md`: +- `TARGET_TOP_NS` +- `INITIAL_BASE_NAME` +- Verification commands. +- Base names. + +From `migration//manifest.md`: +- Current module map, including what remains in the residual component. + +## Steps + +### 1. Read the Residual Component +- List every public function remaining in the residual module. +- Confirm it contains only app-wiring code. + +### 2. Identify the Split +- Trace callers of each function (use `grep`). +- Group callers by base or runner script. +- Map each function to its natural base: + +| Function Pattern | Belongs In | Rationale | +|------------------|------------|-----------| +| `init_consumer`, `close_consumer` | Handler/consumer base | Kafka consumer lifecycle is handler-specific | +| `init_job` | Jobs base | Job bootstrap is jobs-specific | +| `init_api` / app factory | API base | HTTP server setup is API-specific | + +### 3. Extract Shared Helpers +- Identify shared init functions (e.g., `init_logging`, `init_db`). +- Move shared helpers to a `bootstrap` component if needed. + +### 4. Move Composite Functions +- Move composite functions to their respective bases. +- Update runner scripts to import from the base directly. + +### 5. Update Tests +- Update integration tests to monkeypatch the correct module. + +### 6. Clean Up +- Delete the residual component directory. +- Update `pyproject.toml` to remove the residual brick. + +## Verify +- `RUN_TEST_CMD` succeeds. +- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. +- Run `POLY_CMD_PREFIX check` to validate the workspace structure. +- Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. + +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| Two bases now import the same `init_` function and the residual still exists | Step 6 wasn't reached — the function moved into one base but the residual reference wasn't deleted. | Confirm the residual is gone; if `init_` is genuinely shared by 2+ bases, it belongs in a shared `bootstrap` component (step 3), not in either base. | +| `mock.patch("..init_api")` fails after the move | The test references the old residual path; the function now lives in a base. | Update mock patch strings to the new location, typically `..`. | +| `poly check` says the residual brick is still declared but its files are gone | `pyproject.toml` `[tool.polylith.bricks]` still lists the deleted brick. | Remove the line from `[tool.polylith.bricks]` and re-run `POLY_CMD_PREFIX sync --quiet`. | +| Application starts but a runtime feature is missing (e.g., logging, DB) | A shared init helper was moved into one base only. The other base never calls it. | Promote the helper to a shared `bootstrap` component and call it from every base's startup path. | +| Verification fails and you can't quickly diagnose | Phase commit not yet made. | `git reset --hard HEAD` to roll back to the previous phase's commit and consult the user. | \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/extract-standalone-modules/skill.md b/.agents/skills/polylith/migrate-project/migrate-extract-standalone-modules/SKILL.md similarity index 79% rename from .agents/skills/polylith/migrate-project/extract-standalone-modules/skill.md rename to .agents/skills/polylith/migrate-project/migrate-extract-standalone-modules/SKILL.md index 26eed893..9a289a6d 100644 --- a/.agents/skills/polylith/migrate-project/extract-standalone-modules/skill.md +++ b/.agents/skills/polylith/migrate-project/migrate-extract-standalone-modules/SKILL.md @@ -1,9 +1,9 @@ --- -name: extract-standalone-modules -description: Extract foundational modules (e.g., `consts.py`, `exceptions.py`, or similar) from the residual component into standalone components. +name: migrate-extract-standalone-modules +description: "[Internal sub-skill of `migrate-orchestrator` (phase 6 of 11). Do not load directly — load `migrate-orchestrator` first, which drives all phases.] Extract foundational modules (e.g., `consts.py`, `exceptions.py`, or similar) from the residual component into standalone components." --- -# Skill: extract-standalone-modules +# Skill: migrate-extract-standalone-modules ## Goal Extract **foundational modules** (e.g., `consts.py`, `exceptions.py`, `models.py`) from the residual component into standalone components. This skill is for zero-dependency or low-dependency modules that serve as building blocks for other components. @@ -11,7 +11,7 @@ Extract **foundational modules** (e.g., `consts.py`, `exceptions.py`, `models.py ## Inputs From `migration//state.md`: - `TARGET_TOP_NS` -- `BRICK_NAME` +- `INITIAL_BASE_NAME` - Verification commands. From `migration//manifest.md`: diff --git a/.agents/skills/polylith/migrate-project/migrate-extract-to-base/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-extract-to-base/SKILL.md new file mode 100644 index 00000000..8f9763f8 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/migrate-extract-to-base/SKILL.md @@ -0,0 +1,72 @@ +--- +name: migrate-extract-to-base +description: "[Internal sub-skill of `migrate-orchestrator` (phase 2 of 11). Do not load directly — load `migrate-orchestrator` first, which drives all phases.] Extract all application code from `projects//` into a temporary migration base." +--- + +# Skill: migrate-extract-to-base + +## Goal +Extract all application code from `projects//` into a temporary migration base (`bases///`). + +## Inputs +From `migration//state.md`: +- `PROJECT_DIR` +- `ORIG_TOP_NS` +- `TARGET_TOP_NS` (default: `ORIG_TOP_NS`) +- `INITIAL_BASE_NAME` +- `RUN_TEST_CMD` (optional: `RUN_LINT_CMD`, `RUN_TYPECHECK_CMD`) + +From `migration//manifest.md`: +- Directory tree and module map. + +## Steps + +### 1. Create the Base Directory +- Create `bases///`. + +### 2. Move Application Code +- Move application packages/modules from `projects//` to the base: + - For `src/` layout: Move `projects//src//` under the base. + - For flat layout: Move `projects///` under the base. +- Leave non-code files (Dockerfiles, k8s manifests, deploy scripts, `pyproject.toml`) in `projects//`. + +### 3. Update `pyproject.toml` +- Add the base to `[tool.polylith.bricks]`: + ```toml + [tool.polylith.bricks] + "../../bases//" = "/" + ``` + +### 4. Fix Imports +- Update imports minimally to ensure tests and linting pass. + +### 5. Update `manifest.md` +- Reflect the new structure in `migration//manifest.md`. + +### 6. Handle Namespace Changes +If `TARGET_TOP_NS != ORIG_TOP_NS`, choose one of the following options: + +| Option | Description | Risk | +|--------|-------------|------| +| **Compatibility Shim** | Keep `ORIG_TOP_NS` as a shim that re-exports from `TARGET_TOP_NS`. | Lower | +| **Rewrite Imports** | Rewrite all imports to the new namespace in one go. | Higher | + +### 7. Use Shims if Needed +- If imports break, add temporary shims to re-export names from the new brick API. +- Track shims in `migration/shims.md`. + +## Verify +- `RUN_TEST_CMD` succeeds with the same pass/fail counts as the pre-migration baseline recorded in `migrate-discover`. +- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. +- Run `POLY_CMD_PREFIX check` to validate the workspace structure. +- Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. + +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| `ModuleNotFoundError: No module named '.'` after move | `TARGET_TOP_NS != ORIG_TOP_NS` and imports weren't rewritten or shimmed. | Either rewrite all imports (step 6 option B) or add a shim under `ORIG_TOP_NS` that re-exports from `TARGET_TOP_NS` (step 6 option A). Record the choice in `migration/shims.md`. | +| `RUN_TEST_CMD` collects 0 tests after the move | Test files moved but `pytest` rootdir / `testpaths` still points at the old `projects//tests` location. | Update `pyproject.toml` `[tool.pytest.ini_options].testpaths` or pass explicit dirs in `RUN_TEST_CMD`. (Note: `migrate-prepare-project` moves tests properly — if extract-to-base touched tests at all, consider undoing that part.) | +| `poly check` reports "brick imports another brick that is not in `[tool.polylith.bricks]`" | Step 3 only added the base; imports inside the base may pull in components not yet listed. | Run `POLY_CMD_PREFIX sync --quiet` to populate the rest, then re-run `check`. | +| Editable install / build fails (`error: package directory '' does not exist`) | The base move broke the previous `[tool.setuptools]` or `[tool.hatch.build]` `packages` setting in `projects//pyproject.toml`. | Update `packages` to point at the new `` namespace, or remove the explicit `packages` setting and let Polylith's build hook handle it. | +| Verification fails and you can't quickly diagnose | Phase commit not yet made. | `git reset --hard HEAD` to roll back to the previous phase's commit and consult the user. | diff --git a/.agents/skills/polylith/migrate-project/migrate-isolate-base-and-big-component/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-isolate-base-and-big-component/SKILL.md new file mode 100644 index 00000000..88288019 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/migrate-isolate-base-and-big-component/SKILL.md @@ -0,0 +1,73 @@ +--- +name: migrate-isolate-base-and-big-component +description: "[Internal sub-skill of `migrate-orchestrator` (phase 4 of 11). Do not load directly — load `migrate-orchestrator` first, which drives all phases.] Shrink the temporary migration base into thin base(s) + one big component. Bases contain only entrypoints/wiring, while the big component contains everything else." +--- + +# Skill: migrate-isolate-base-and-big-component + +## Goal +Shrink the temporary migration base into thin base(s) + one big component: +- **Bases**: Contain only entrypoints/wiring (e.g., FastAPI endpoints, CLI wiring, consumer wiring). +- **Big Component**: Contains all other code. + +## Inputs +From `migration//state.md`: +- `TARGET_TOP_NS` +- `INITIAL_BASE_NAME` +- Verification commands. + +From `migration//manifest.md`: +- Entrypoints list. + +## Steps + +### 1. Create the Big Component +- Create `components///`. + +### 2. Move Non-Entrypoint Code +- Move non-entrypoint code from the base(s) to the big component. + +### 3. Define Public API +- Define a minimal public API in `components///__init__.py`. + +### 4. Update Bases +- Update bases to import only from component APIs: + ```python + from . import ... + ``` + +### 5. Update `pyproject.toml` +- Add the new component to `[tool.polylith.bricks]`: + ```toml + [tool.polylith.bricks] + "../../bases//" = "/" + "../../components//" = "/" + ``` + +### 6. Update `manifest.md` +- Reflect the new structure in `migration//manifest.md`. + +### 7. FastAPI Guidance +| Stays in Base | Moves to Big Component | +|---------------|------------------------| +| `app = FastAPI(...)` | Domain/business logic | +| Middleware, router registration | Persistence/repositories | +| Route handlers (endpoints) | External integrations | +| Startup/shutdown/lifespan wiring | Reusable parsing/validation | + +## Verify +- `RUN_TEST_CMD` succeeds. +- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. +- Run `POLY_CMD_PREFIX check` to validate the workspace structure. +- Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. + +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| Circular import: base imports from component, component imports back from base | Some "non-entrypoint" code was moved but still references base-only helpers (e.g. the FastAPI `app` instance). | Move the helper down into the component, or invert the dependency by passing the needed value as a function argument. The base's `app` instance must **never** be imported by a component. | +| `ImportError: cannot import name '' from '.'` | The big component's `__init__.py` doesn't re-export ``. Code that previously reached into submodules now needs the public API. | Add `from .. import ` to the component's `__init__.py`, or have the caller import the submodule directly (and accept the brick-interface violation that `poly deps --interface` will flag). | +| Base file becomes near-empty after the split | Good — that's the goal. But check: is there *any* wiring left, or did you accidentally move the entrypoint itself? | If the entrypoint (e.g., `app = FastAPI(...)`) ended up in the component, move it back to the base. The base must own the entrypoint object. | +| `poly check` flags the component as not used by any project | The base's imports go to the wrong namespace (e.g., `from ...`) so the import graph doesn't reach the component. | Update base imports to `from . import …` and re-run `POLY_CMD_PREFIX sync`. | +| Tests for moved code now fail to find fixtures | `conftest.py` was left in the base or moved to the wrong scope. | Move test fixtures alongside the code they cover; usually that's under `test/components///`. | +| Verification fails and you can't quickly diagnose | Phase commit not yet made. | `git reset --hard HEAD` to roll back to the previous phase's commit and consult the user. | diff --git a/.agents/skills/polylith/migrate-project/isolate-shared-and-project-logic/skill.md b/.agents/skills/polylith/migrate-project/migrate-isolate-shared-and-project-logic/SKILL.md similarity index 64% rename from .agents/skills/polylith/migrate-project/isolate-shared-and-project-logic/skill.md rename to .agents/skills/polylith/migrate-project/migrate-isolate-shared-and-project-logic/SKILL.md index e4c9c251..2c9ab663 100644 --- a/.agents/skills/polylith/migrate-project/isolate-shared-and-project-logic/skill.md +++ b/.agents/skills/polylith/migrate-project/migrate-isolate-shared-and-project-logic/SKILL.md @@ -1,9 +1,16 @@ --- -name: isolate-shared-and-project-logic -description: Identify and isolate shared and project-specific logic in monolithic components (e.g., `models`, `schemas`, or similar). +name: migrate-isolate-shared-and-project-logic +description: "[Internal sub-skill of `migrate-orchestrator` (phase 7 of 11). Do not load directly — load `migrate-orchestrator` first, which drives all phases.] Identify and isolate shared and project-specific logic in monolithic components (e.g., `models`, `schemas`, or similar)." --- -# Skill: isolate-shared-and-project-logic +# Skill: migrate-isolate-shared-and-project-logic + +> 📐 **Scope vs sibling skills.** This skill is **cross-project**: it separates code used by **two or more projects** (shared) from code used by **a single project** (project-specific), potentially creating new shared *and* project-specific components. Don't confuse with: +> - `migrate-split-big-component` — within one project; splits one component into multiple components. +> - `migrate-split-component-internals` — within one component; splits one `core.py` into multiple files. +> - `migrate-dedupe` — opportunistic deduplication, broader than just shared-vs-project-specific. +> +> 💡 **When to skip this skill.** On the **first** project migration there is usually no second project to compare against. Skip this skill until at least one prior project has been migrated. The orchestrator still calls it after `migrate-extract-standalone-modules` — that's the right place when overlap exists. ## Goal Identify and isolate shared and project-specific logic in monolithic components (e.g., `models`, `schemas`). Extract shared logic into reusable components and isolate project-specific logic into project-specific components. @@ -11,7 +18,7 @@ Identify and isolate shared and project-specific logic in monolithic components ## Inputs From `migration//state.md`: - `TARGET_TOP_NS` -- `BRICK_NAME` +- `INITIAL_BASE_NAME` - Verification commands (`RUN_TEST_CMD`, `RUN_LINT_CMD`, `RUN_TYPECHECK_CMD`). From `migration//manifest.md`: diff --git a/.agents/skills/polylith/migrate-project/migrate-orchestrator/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-orchestrator/SKILL.md new file mode 100644 index 00000000..608fcb59 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/migrate-orchestrator/SKILL.md @@ -0,0 +1,104 @@ +--- +name: migrate-orchestrator +description: "[ENTRY POINT] Load this skill first when the user asks to migrate a non-Polylith Python project to Polylith (e.g. \"migrate `projects/` to Polylith\"). Drives all 11 migration phases plus optional tooling conversions; do not load any other `migrate-*` skill directly — they are sub-skills this orchestrator invokes." +--- + +# Skill: migrate-orchestrator + +> 🧭 **You are in the right place.** This is the **entry point** for migrating a non-Polylith Python project into a Polylith workspace. If you arrived here from a fuzzy match on a sub-skill name (e.g., `migrate-discover`, `migrate-extract-to-base`), **stay here** — those sub-skills depend on state and a git safety net that only this orchestrator sets up. Loading them in isolation is undefined behaviour. Execute the phases below in order. + +## Goal +Define and execute the workflow for migrating a non-Polylith Python project to a Polylith workspace. +**This skill must be explicitly invoked by a human with the project name/path.** + +## Usage +To migrate a project, load the `migrate-orchestrator` skill and provide the project name (the subdirectory under `projects/`): + +``` +Load the `migrate-orchestrator` skill and migrate `projects/`. +``` + +> 💡 **How sub-skills are loaded.** Each phase points to another skill named `migrate-` (e.g., `migrate-discover`, `migrate-extract-to-base`). Load each via your skill loader before executing the phase. Do not interleave phases — finish and verify one before starting the next. + +## Pre-flight + +### 0. User Confirmation +Ask the user to confirm the project path and migration intent before doing anything else: + +``` +You are about to migrate `projects/` to Polylith. This will refactor +the project into bases and components and move files. Proceed? (yes/no) +``` + +If the user declines, abort: +``` +Migration aborted by user. +``` + +### Phase 0. Safety Net (git checkpoint) +Migration is destructive — files move, directories are deleted, `pyproject.toml`s are rewritten. **Before loading `migrate-discover`, establish rollback points:** + +1. Confirm the working tree is clean: + ```bash + git status + ``` + If there are uncommitted changes, ask the user to commit/stash before proceeding. Do not start a migration on top of a dirty tree. +2. Create a dedicated migration branch: + ```bash + git checkout -b migrate/ + ``` +3. After **each completed phase below**, commit the current state with a phase-tagged message: + ```bash + git add -A && git commit -m "migrate(): phase " + ``` + This gives the user (and the agent) a discrete, named rollback point per phase. If a later phase fails verification, the agent can `git reset --hard HEAD~1` to back out exactly one phase without losing earlier progress. +4. Record the branch name and starting commit SHA in `migration//state.md` (the `migrate-discover` skill defines that file). + +> ⚠ Never `git reset --hard` past the start of the migration branch without explicit user approval — the user's pre-migration work lives there. + +## Workflow + +Execute the phases in this order. **Verify each phase's `Verify` section succeeds before starting the next.** Commit between phases (see Phase 0 step 3). + +| # | Phase | Skill | Depends on | +|---|----------------------------------------|------------------------------------------------|-----------------------------------------------------| +| 1 | Discover | `migrate-discover` | — | +| 2 | Extract to base | `migrate-extract-to-base` | `migrate-discover` | +| 3 | Prepare project | `migrate-prepare-project` | `migrate-extract-to-base` | +| 4 | Isolate base and big component | `migrate-isolate-base-and-big-component` | `migrate-prepare-project` | +| 5 | Split big component | `migrate-split-big-component` | `migrate-isolate-base-and-big-component` | +| 6 | Extract standalone modules | `migrate-extract-standalone-modules` | `migrate-split-big-component` | +| 7 | Isolate shared and project logic | `migrate-isolate-shared-and-project-logic` | `migrate-extract-standalone-modules`, `migrate-split-big-component` | +| 8 | Distribute wiring | `migrate-distribute-wiring` | `migrate-isolate-shared-and-project-logic` | +| 9 | Split component internals | `migrate-split-component-internals` | `migrate-distribute-wiring` | +| 10| Refactor tests | `migrate-refactor-tests` | `migrate-split-component-internals` | +| 11| Definition of done | `migrate-definition-of-done` | `migrate-refactor-tests` | + +## Optional Skills + +These are not part of the linear flow above. They are triggered when the user opts in during `migrate-discover` (or, for `migrate-dedupe`, when duplication candidates surface). When triggered, **insert them at the indicated point** in the flow. + +| Skill | When to run | Trigger | +|------------------------------------|--------------------------------------------------------------------|-------------------------------------------------| +| `migrate-convert-linter` | After phase 1 (`migrate-discover`), before phase 2. | User opts in during `migrate-discover`. | +| `migrate-convert-type-checker` | After phase 1 (`migrate-discover`), before phase 2. | User opts in during `migrate-discover`. | +| `migrate-convert-package-manager` | After phase 1 (`migrate-discover`), before phase 2. | User opts in during `migrate-discover` **AND** the workspace itself uses uv. The skill is opinionated about uv — see its header for the gating rule. | +| `migrate-dedupe` | After phase 5 (`migrate-split-big-component`) or phase 6 (`migrate-extract-standalone-modules`). | Duplication candidates surfaced and user approves. | + +> ⚠ `migrate-convert-package-manager` only converts **to uv**. If the workspace uses Poetry, PDM, or Hatch as its standard, **skip this skill entirely** — the project should be aligned to the workspace's manager via a manual step instead. + +## Execution checklist + +For each phase: +1. Load the skill (`migrate-`). +2. Execute its `Steps` in order. +3. Run its `Verify` section. **If verification fails, do not commit and do not proceed.** Either fix the issue, or `git reset --hard` to back out the phase and consult the user. +4. On success, commit: + ```bash + git add -A && git commit -m "migrate(): phase " + ``` + +## Validation +- No circular dependencies exist in the phase graph above (verified by the dependency table). +- Every skill referenced above exists as `migrate-/SKILL.md` under `.agents/skills/polylith/migrate-project/`. +- Each phase's `Verify` block uses the commands recorded in `migration//state.md` (`RUN_TEST_CMD`, optionally `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD`, plus `POLY_CMD_PREFIX check`). diff --git a/.agents/skills/polylith/migrate-project/migrate-prepare-project/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-prepare-project/SKILL.md new file mode 100644 index 00000000..10a79c9a --- /dev/null +++ b/.agents/skills/polylith/migrate-project/migrate-prepare-project/SKILL.md @@ -0,0 +1,72 @@ +--- +name: migrate-prepare-project +description: "[Internal sub-skill of `migrate-orchestrator` (phase 3 of 11). Do not load directly — load `migrate-orchestrator` first, which drives all phases.] Clean up the project subfolder and consolidate dependencies after extracting application code into bases." +--- + +# Skill: migrate-prepare-project + +## Goal +Clean up the project subfolder (`projects//`) and consolidate dependencies. After this step, the project subfolder should contain only: +- Project `pyproject.toml` (with brick references). +- Task runners (`Makefile`, `Justfile`). +- Project-specific config (e.g., `alembic.ini`). + +## Inputs +From `migration//state.md`: +- `PROJECT_DIR` +- `TARGET_TOP_NS` +- `ALIAS` (optional: `GROUP`) +- Verification commands. + +From `migration//manifest.md`: +- Infra files list. + +## Steps + +### 1. Verify Project Subfolder +- Run `directory_tree` on `projects//` to confirm only infra and config files remain. + +### 2. Update `pyproject.toml` +- Add brick references to `[tool.polylith.bricks]`: + ```toml + [tool.polylith.bricks] + "../../bases//" = "/" + ``` +- Register the project alias and group in `workspace.toml` (if provided): + ```toml + [tool.polylith.projects.alias] + = "" + + [tool.polylith.projects.groups] + = [""] + ``` + +### 3. Move Tests to Workspace Level +- Move `tests/` to `test//`. +- Update `RUN_TEST_CMD` in `migration//state.md` to point to the new location. +- Update imports and mock patch strings in test files if paths changed. + +### 4. Move Infrastructure Folders +- Move infra folders (e.g., `helm/`, `k8s/`, `kustomize`, `alembic/`) to `infra///`. + +### 5. Consolidate Dependencies +- Move third-party dependencies with version constraints to the workspace root `pyproject.toml`. +- Move dev/test/tooling dependencies to the workspace root. +- List runtime dependencies without version numbers in the project `pyproject.toml`. + +## Verify +- `RUN_TEST_CMD` succeeds against the **new** test location. +- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. +- Run `POLY_CMD_PREFIX check` to validate the workspace structure. +- Run `POLY_CMD_PREFIX info` to inspect the workspace and confirm the project is correctly registered. + +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| `RUN_TEST_CMD` collects 0 tests after step 3 | The command in `state.md` still references the old `projects//tests` path. | Update `RUN_TEST_CMD` to the new `test//` location. Also check `[tool.pytest.ini_options].testpaths` / `rootdir` / `conftest.py` discovery. | +| Tests fail with `ModuleNotFoundError` on internal imports | Tests use `from tests.fixtures import …` and the `tests` package name changed. | Update test imports to the new test root path. If many tests reference the old name, consider keeping `tests` as the leaf directory and only renaming the parent. | +| `mock.patch("")` fails with `AttributeError` | Mock patch strings reference moved modules. | Update patch strings to the new module paths. Use `grep -r 'patch("' test/` to find them all. | +| Infra folder move breaks deploy scripts that hardcoded paths (e.g., `helm//values.yaml`) | Deploy scripts haven't been updated to the new `infra///` location. | Either update the scripts, or symlink the new location from the old one as a transitional step (record the symlink in `migration//state.md`). | +| `poly check` complains about brick references after dependency consolidation | A runtime dependency was moved to the workspace root but the project's `pyproject.toml` doesn't declare it. | Add the dependency name (no version) back to the project's `[project.dependencies]`. The version stays only at the workspace root. | +| Verification fails and you can't quickly diagnose | Phase commit not yet made. | `git reset --hard HEAD` to roll back to the previous phase's commit and consult the user. | diff --git a/.agents/skills/polylith/migrate-project/migrate-refactor-tests/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-refactor-tests/SKILL.md new file mode 100644 index 00000000..157aba19 --- /dev/null +++ b/.agents/skills/polylith/migrate-project/migrate-refactor-tests/SKILL.md @@ -0,0 +1,79 @@ +--- +name: migrate-refactor-tests +description: "[Internal sub-skill of `migrate-orchestrator` (phase 10 of 11). Do not load directly — load `migrate-orchestrator` first, which drives all phases.] Restructure unit tests to align with the workspace's Polylith theme." +--- + +# Skill: migrate-refactor-tests + +## Goal +Restructure unit tests so they live next to the brick they test, following the workspace's Polylith theme (`loose` or `tdd`). + +## Scope +- **Unit tests only.** Integration tests typically stay in a shared location (e.g., `test/integration/` or `test//integration/`). Do not redistribute them across bricks. +- **Structure only.** Reorganize test files and update imports/mock patch strings. Do not rewrite test logic or fixtures unless an import/path change makes it strictly necessary. + +## Inputs +From `migration//state.md`: +- `TARGET_TOP_NS` +- `RUN_TEST_CMD` (will be updated by this skill) +- `RUN_LINT_CMD`, `RUN_TYPECHECK_CMD` (optional) + +From `migration//manifest.md`: +- List of all bricks (bases and components) with their module maps. + +From `workspace.toml`: +- `[tool.polylith.structure].theme` (`loose` or `tdd`). + +From `test/`: +- Current test directory structure. + +## Target layout + +| Theme | Test path for a base | Test path for a component | +|----------|------------------------|-----------------------------| +| `loose` | `test/bases///test_*.py` | `test/components///test_*.py` | +| `tdd` | `bases//test///test_*.py` | `components//test///test_*.py` | + +## Steps + +### 1. Classify each unit test file +For each `test_*.py` under the current test root: +1. Read its `import` statements and `mock.patch("...")` strings. +2. Identify the *primary* brick under test — usually the one most imported or the one mock-patched. Tie-break by file name (`test_user_handler.py` → ``). +3. Record the classification in a table you keep in scratch (do **not** commit a separate file for this — it's transient): + +| Test file | Primary brick | Target path | +|-----------|---------------|-------------| + +If a test exercises 2+ bricks at integration level, classify it as integration and move it to `test/integration/` instead. + +### 2. Move test files +- Create the target directories per the theme matrix above. +- Move each `test_*.py` to its brick's test directory. +- For each `conftest.py`: + - **Brick-scoped fixtures** (referenced only by tests under one brick) → move to that brick's test directory. + - **Workspace-shared fixtures** (cross-brick) → keep one `test//conftest.py` or `test/conftest.py`. + +### 3. Update imports and mock patch strings +- Update any `from tests.` imports that survived to the new layout. +- Update `mock.patch("")` strings. Brick reshuffling earlier in the migration may have changed where the patched symbol now lives — use `grep` to locate the target symbol's new path and align the patch string. Patching a wrong path silently succeeds and the test will pass for the wrong reason — verify by intentionally breaking the target function and checking that the test fails. + +### 4. Update `RUN_TEST_CMD` +- Set `RUN_TEST_CMD` in `state.md` to a command that collects from the **new** test root (typically ` pytest test/` for `loose`, or per-brick collection for `tdd`). +- Verify the test count after the move equals the baseline recorded in `migrate-discover`. **A drop in collected tests means files were lost or pytest discovery is misconfigured.** + +## Verify +- `RUN_TEST_CMD` succeeds and **collects the same number of tests** as the baseline from `migrate-discover`. +- If set, `RUN_LINT_CMD` succeeds. +- `POLY_CMD_PREFIX check` is still green. + +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| `pytest` collects fewer tests than before | The old test root is no longer on `pytest`'s path; or duplicate `conftest.py` files silently shadow each other. | Update `[tool.pytest.ini_options].testpaths` (or pass paths explicitly in `RUN_TEST_CMD`). Run `pytest --collect-only` and diff against baseline collection. | +| `fixture '' not found` | The fixture was in a `conftest.py` you moved into a brick's test dir; tests in another brick can no longer see it. | Either move the fixture up to a higher-scope `conftest.py`, or duplicate it (only if cheap). Don't import fixtures across `conftest.py` files. | +| Tests pass but assert nothing useful (`mock.patch` no longer hits anything) | Patch string still points at the pre-migration module path. | Re-derive the patch path: `...`. Validate by deliberately breaking the patched function and confirming the test fails. | +| `ImportError: cannot import name 'fixture_'` from `conftest.py` | A `conftest.py` `import`s a moved test helper module that didn't follow it. | Move the helper next to the new `conftest.py`, or import it from its new brick path. | +| Tests for moved code suddenly find themselves under a brick name that doesn't match their content | Misclassification in step 1. | Re-read the test's imports — the brick most imported is the one that owns the test. Move and update. | +| Verification fails and you can't quickly diagnose | Phase commit not yet made. | `git reset --hard HEAD` to roll back to the previous phase's commit and consult the user. | diff --git a/.agents/skills/polylith/migrate-project/split-big-component/skill.md b/.agents/skills/polylith/migrate-project/migrate-split-big-component/SKILL.md similarity index 87% rename from .agents/skills/polylith/migrate-project/split-big-component/skill.md rename to .agents/skills/polylith/migrate-project/migrate-split-big-component/SKILL.md index c497efc2..645e7687 100644 --- a/.agents/skills/polylith/migrate-project/split-big-component/skill.md +++ b/.agents/skills/polylith/migrate-project/migrate-split-big-component/SKILL.md @@ -1,17 +1,23 @@ --- -name: split-big-component -description: Split the big component (`components///`) into multiple focused components. +name: migrate-split-big-component +description: "[Internal sub-skill of `migrate-orchestrator` (phase 5 of 11). Do not load directly — load `migrate-orchestrator` first, which drives all phases.] Split the big component (`components///`) into multiple focused components." --- -# Skill: split-big-component +# Skill: migrate-split-big-component + +> 📐 **Scope vs sibling skills.** This skill operates **within one project** and turns *one component into many components*. Don't confuse with: +> - `migrate-extract-standalone-modules` — same scope (one project) but pulls *foundational modules* (`consts.py`, `exceptions.py`) out of the residual big component into their own standalone components. +> - `migrate-split-component-internals` — operates **inside one already-extracted component**, splitting its `core.py` into multiple files. No new components are created. +> - `migrate-isolate-shared-and-project-logic` — **cross-project** scope; identifies shared-vs-project-specific logic when migrating a 2nd+ project. +> - `migrate-dedupe` — opportunistic deduplication; this skill includes a dedup-analysis subsection so for a single project you usually don't need `migrate-dedupe` separately. ## Goal -Split the big component (`components///`) into multiple focused components to improve maintainability, clarity, and reusability. +Split the big component (`components///`) into multiple focused components to improve maintainability, clarity, and reusability. ## Inputs From `migration//state.md`: - `TARGET_TOP_NS` -- `BRICK_NAME` +- `INITIAL_BASE_NAME` - Verification commands. From `migration//manifest.md`: diff --git a/.agents/skills/polylith/migrate-project/split-component-internals/skill.md b/.agents/skills/polylith/migrate-project/migrate-split-component-internals/SKILL.md similarity index 56% rename from .agents/skills/polylith/migrate-project/split-component-internals/skill.md rename to .agents/skills/polylith/migrate-project/migrate-split-component-internals/SKILL.md index e4d15c86..943bf46e 100644 --- a/.agents/skills/polylith/migrate-project/split-component-internals/skill.md +++ b/.agents/skills/polylith/migrate-project/migrate-split-component-internals/SKILL.md @@ -1,9 +1,14 @@ --- -name: split-component-internals -description: Split monolithic `core.py` files in shared components into domain-focused modules. +name: migrate-split-component-internals +description: "[Internal sub-skill of `migrate-orchestrator` (phase 9 of 11). Do not load directly — load `migrate-orchestrator` first, which drives all phases.] Split monolithic `core.py` files in shared components into domain-focused modules." --- -# Skill: split-component-internals +# Skill: migrate-split-component-internals + +> 📐 **Scope vs sibling skills.** This skill operates **inside one already-extracted component**, splitting its `core.py` into multiple sibling Python files. **No new components are created** — only the internal file layout changes. Don't confuse with: +> - `migrate-split-big-component` — splits **one component into multiple components**. Use that when the unit being broken up is a component, not a file. +> - `migrate-isolate-shared-and-project-logic` — separates shared vs project-specific code **across components and projects**; may create new components. +> - `migrate-extract-standalone-modules` — pulls foundational modules out of the residual big component into new standalone components. ## Goal Split monolithic `core.py` files in **shared components** (e.g., `models_shared`, `schemas_shared`, or similar) into domain-focused modules. This skill ensures that shared components remain well-organized and maintainable. diff --git a/.agents/skills/polylith/migrate-project/orchestrator/skill.md b/.agents/skills/polylith/migrate-project/orchestrator/skill.md deleted file mode 100644 index 67f3a526..00000000 --- a/.agents/skills/polylith/migrate-project/orchestrator/skill.md +++ /dev/null @@ -1,132 +0,0 @@ ---- -name: orchestrator -description: Orchestrates the project migration workflow. **Explicitly invoke this skill to migrate a non-Polylith project in `/projects`.** ---- - -# Skill: orchestrator - -## Goal -Define and execute the workflow for migrating a non-Polylith Python project to a Polylith workspace. -**This skill must be explicitly invoked by a human with the project name/path.** - -## Usage -To migrate a project, load the `migrate.orchestrator` skill and provide the project name (e.g., the subdirectory in `/projects`): -``` -Load the `migrate.orchestrator` skill and specify the project name (e.g., ``). -``` - -## Steps - -### 1. User Confirmation -- Immediately ask the user to confirm the project path and migration intent: - ``` - You are about to migrate `/projects/` to Polylith. This will refactor the project into bases and components. Proceed? (yes/no) - ``` -- If the user declines, abort the migration: - ``` - Migration aborted by user. - ``` - -### 2. Execute Migration Workflow -- Proceed with the migration steps (discover, extract, refactor, etc.) if the user confirms. -- Use the project path provided by the user (e.g., `/projects/my-app`) for all operations. - -## Workflow -The migration process consists of the following steps, executed in this order: - -1. **Discover**: Inspect the project and create `state.md` and `manifest.md`. - - Skill: `discover` - - Dependencies: None - -2. **Extract to Base**: Extract application code into a temporary migration base. - - Skill: `extract-to-base` - - Dependencies: `discover` - -3. **Prepare Project**: Clean up the project subfolder and consolidate dependencies. - - Skill: `prepare-project` - - Dependencies: `extract-to-base` - -4. **Isolate Base and Big Component**: Shrink the temporary migration base into thin bases and a big component. - - Skill: `isolate-base-and-big-component` - - Dependencies: `prepare-project` - -5. **Split Big Component**: Split the big component into multiple focused components. - - Skill: `split-big-component` - - Dependencies: `isolate-base-and-big-component` - -6. **Extract Standalone Modules**: Extract zero-dependency and low-dependency modules into standalone components. - - Skill: `extract-standalone-modules` - - Dependencies: `split-big-component` - -7. **Isolate Shared and Project Logic**: Identify and isolate shared and project-specific logic in monolithic components (e.g., `models`, `schemas`). - - Skill: `isolate-shared-and-project-logic` - - Dependencies: `extract-standalone-modules`, `split-big-component` - -8. **Distribute Wiring**: Distribute app-wiring code from the residual component into the appropriate bases and shared components. - - Skill: `distribute-wiring` - - Dependencies: `isolate-shared-and-project-logic` - -9. **Split Component Internals**: Split monolithic `core.py` files in shared components into domain-focused modules. - - Skill: `split-component-internals` - - Dependencies: `distribute-wiring` - -10. **Refactor Tests**: Restructure unit tests to align with the workspace's Polylith theme. - - Skill: `refactor-tests` - - Dependencies: `split-component-internals` - -11. **Definition of Done**: Verify that all migration criteria are met. - - Skill: `definition-of-done` - - Dependencies: `refactor-tests` - -## Optional Skills - -- **Convert Linter**: Replace the project's linter with `ruff` (if the user opts in during the `discover` step). - - Skill: `convert-linter` - - Dependencies: `discover` - - Trigger: User opts in during `discover` step. - -- **Convert Type Checker**: Replace the project's type checker with `ty` (if the user opts in during the `discover` step). - - Skill: `convert-type-checker` - - Dependencies: `discover` - - Trigger: User opts in during `discover` step. - -- **Convert Package Manager**: Convert the project's `pyproject.toml` to PEP 621/uv format and register it as a workspace member (if the user opts in during the `discover` step). - - Skill: `convert-package-manager` - - Dependencies: `discover` - - Trigger: User opts in during `discover` step. - -- **Dedupe**: Identify and execute controlled deduplication of code during migration (if the user opts in). - - Skill: `dedupe` - - Dependencies: `split-big-component`, `extract-standalone-modules` - -## Optional Skills - -- **Convert Linter**: Replace the project's existing linter and formatter with a workspace-standard tool (e.g., **ruff**) if the user opts in during the `discover` step. - - Skill: `convert-linter` - - Dependencies: `discover` - - Trigger: User opts in during `discover` step. - -- **Convert Type Checker**: Replace the project's existing type checker with a workspace-standard tool (e.g., **ty**) if the user opts in during the `discover` step. - - Skill: `convert-type-checker` - - Dependencies: `discover` - - Trigger: User opts in during `discover` step. - -- **Convert Package Manager**: Convert the project's `pyproject.toml` to a workspace-standard format (e.g., PEP 621/uv) and register it as a workspace member if the user opts in during the `discover` step. - - Skill: `convert-package-manager` - - Dependencies: `discover` - - Trigger: User opts in during `discover` step. - -- **Dedupe**: Identify and execute controlled deduplication of code during migration (if the user opts in). - - Skill: `dedupe` - - Dependencies: `split-big-component`, `extract-standalone-modules` - -## Execution -To execute the migration workflow: -1. Load the `orchestrator` skill. -2. Follow the steps defined in the workflow, ensuring all dependencies are satisfied before executing each skill. -3. Verify the completion of each step before proceeding to the next. - -## Validation -- Ensure no circular dependencies exist in the workflow. -- Verify that all dependencies are correctly declared and exist in the `SKILLS.md` index. -- Confirm that the workflow is executable in the order defined by the orchestrator skill. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/prepare-project/skill.md b/.agents/skills/polylith/migrate-project/prepare-project/skill.md deleted file mode 100644 index 9bda2231..00000000 --- a/.agents/skills/polylith/migrate-project/prepare-project/skill.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -name: prepare-project -description: Clean up the project subfolder and consolidate dependencies after extracting application code into bases. ---- - -# Skill: prepare-project - -## Goal -Clean up the project subfolder (`projects//`) and consolidate dependencies. After this step, the project subfolder should contain only: -- Project `pyproject.toml` (with brick references). -- Task runners (`Makefile`, `Justfile`). -- Project-specific config (e.g., `alembic.ini`). - -## Inputs -From `migration//state.md`: -- `PROJECT_DIR` -- `TARGET_TOP_NS` -- `ALIAS` (optional: `GROUP`) -- Verification commands. - -From `migration//manifest.md`: -- Infra files list. - -## Steps - -### 1. Verify Project Subfolder -- Run `directory_tree` on `projects//` to confirm only infra and config files remain. - -### 2. Update `pyproject.toml` -- Add brick references to `[tool.polylith.bricks]`: - ```toml - [tool.polylith.bricks] - "../../bases//" = "/" - ``` -- Register the project alias and group in `workspace.toml` (if provided): - ```toml - [tool.polylith.projects.alias] - = "" - - [tool.polylith.projects.groups] - = [""] - ``` - -### 3. Move Tests to Workspace Level -- Move `tests/` to `test//`. -- Update `RUN_TEST_CMD` in `migration//state.md` to point to the new location. -- Update imports and mock patch strings in test files if paths changed. - -### 4. Move Infrastructure Folders -- Move infra folders (e.g., `helm/`, `k8s/`, `kustomize`, `alembic/`) to `infra///`. - -### 5. Consolidate Dependencies -- Move third-party dependencies with version constraints to the workspace root `pyproject.toml`. -- Move dev/test/tooling dependencies to the workspace root. -- List runtime dependencies without version numbers in the project `pyproject.toml`. - -## Verify -- `RUN_TEST_CMD` succeeds. -- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. -- Run `POLY_CMD_PREFIX check` to validate the workspace structure. -- Run `POLY_CMD_PREFIX info` to inspect the workspace and confirm the project is correctly registered. diff --git a/.agents/skills/polylith/migrate-project/refactor-tests/skill.md b/.agents/skills/polylith/migrate-project/refactor-tests/skill.md deleted file mode 100644 index 585598db..00000000 --- a/.agents/skills/polylith/migrate-project/refactor-tests/skill.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -name: refactor-tests -description: Restructure unit tests to align with the workspace's Polylith theme. ---- - -# Skill: refactor-tests - -## Goal -Restructure unit tests to align with the workspace's Polylith theme. The test directory structure should follow the workspace's conventions for organizing tests. - -## Scope -- **Unit tests only**: Integration tests typically stay in a shared location (e.g., `test/integration/` or `test//integration/`). -- **Structure only**: Reorganize test files and update imports/mocks to align with the workspace's Polylith theme. - -## Inputs -From `migration//state.md`: -- `TARGET_TOP_NS` -- Verification commands. - -From `migration//manifest.md`: -- List of all bricks (bases and components) with their module maps. - -From `test/`: -- Current test directory structure. - -## Steps - -### 1. Classify Test Files -- Scan import statements and mock patch strings to determine which brick each test file primarily tests. -- Produce a classification table (e.g., `test_merchant_handler.py` → `app`). - -### 2. Reorganize Tests -- Create target directories according to the workspace's Polylith theme. -- Move each test file to its brick's test directory. -- Handle `conftest.py` files: - - Move brick-specific fixtures to the brick's test directory. - - Move shared fixtures to `test//conftest.py` or `test/conftest.py`. - -### 3. Update Imports and Mocks -- Update imports in test files if they reference other test modules or fixtures. -- Update mock patch strings if paths changed. - -### 4. Verify -- Update `RUN_TEST_CMD` in `migration//state.md` to point to the new test root. -- Run `RUN_TEST_CMD` and `RUN_LINT_CMD`. - -## Verify -- `RUN_TEST_CMD` succeeds. -- If set, `RUN_LINT_CMD` succeeds. \ No newline at end of file From 082b9878c2a2a2b0d3fc25d590de98f4e6cafe55 Mon Sep 17 00:00:00 2001 From: David Vujic Date: Sun, 17 May 2026 20:38:11 +0200 Subject: [PATCH 4/6] wip --- .agents/skills/polylith/README.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/.agents/skills/polylith/README.md b/.agents/skills/polylith/README.md index 438db3da..98de999d 100644 --- a/.agents/skills/polylith/README.md +++ b/.agents/skills/polylith/README.md @@ -3,7 +3,14 @@ > **Note for contributors:** this README is a **human reference**. The agent loads each `*/SKILL.md` independently via the skill loader; this file is **not** auto-loaded with any skill. Anything an agent must know to act has to live in the relevant `SKILL.md` itself, not here. -## Available Skills +## Skill loading model + +Two kinds of skill live under this directory; the distinction matters when picking an entry point: + +- **Atomic skills (`polylith-*`).** Each maps to one `poly` CLI command (or one focused concept). Safe to load in isolation; individually composable. These cover everyday Polylith workflows — creating bricks, syncing, checking, inspecting, and so on. +- **Orchestrated skill set (`migrate-project/migrate-*`).** A multi-phase workflow with shared state (`migration//state.md`) and a git safety net. **Never load an individual `migrate-*` skill directly** — always load `migrate-orchestrator` and let it drive the phases in order. See [`migrate-project/README.md`](./migrate-project/README.md). This is an advanced, explicit-opt-in workflow used for migrating a **non-Polylith** project into a Polylith workspace, **not** part of daily Polylith use. + +## Available Skills (daily Polylith workflows) | Skill | Command | Purpose | |---------------------------|--------------------|----------------------------------------------------------------------------------------------------------| @@ -15,8 +22,13 @@ | [Sync](./polylith-sync/SKILL.md) | `poly sync` | Update each project's brick list to match actual imports. | | [Workspace Inspection](./polylith-workspace-inspection/SKILL.md) | `poly info` | Show brick × project usage (which projects use which bricks). | | [Dependency Visualization](./polylith-dependency-visualization/SKILL.md) | `poly deps` | Show brick × brick dependencies and interface compliance. | +| [Dependency Management](./polylith-dependency-management/SKILL.md) | — | Add or manage third-party libraries for a brick or project. | | [Testing](./polylith-testing/SKILL.md) | `poly test diff` | List bricks/projects affected by **test-code** changes since a tag. | | [Diff](./polylith-diff/SKILL.md) | `poly diff` | List bricks whose **implementation** changed since a tag. | | [Check](./polylith-check/SKILL.md) | `poly check` | Validate the workspace (CI gate; exits 1 on failure). | | [Libs](./polylith-libs/SKILL.md) | `poly libs` | Inspect third-party libraries per project. | -| [Concepts](./polylith-concepts/SKILL.md) | — | Provides foundational knowledge about Polylith architecture and terminology. | +| [Concepts](./polylith-concepts/SKILL.md) | — | Foundational knowledge about Polylith architecture and terminology. | + +## Advanced workflow + +For migrating an existing **non-Polylith** Python project into a Polylith workspace, see [`migrate-project/README.md`](./migrate-project/README.md). This is a destructive, multi-phase, explicit-opt-in workflow — start with the `migrate-orchestrator` skill, not any individual `migrate-*` sub-skill. From accdad0903b85a771deebfa4b2992f696093ff3c Mon Sep 17 00:00:00 2001 From: David Vujic Date: Sun, 17 May 2026 21:02:55 +0200 Subject: [PATCH 5/6] bump CLI to 1.48.0 --- projects/polylith_cli/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/polylith_cli/pyproject.toml b/projects/polylith_cli/pyproject.toml index 464b1bf7..05af6b8b 100644 --- a/projects/polylith_cli/pyproject.toml +++ b/projects/polylith_cli/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "polylith-cli" -version = "1.47.2" +version = "1.48.0" description = "Python tooling support for the Polylith Architecture" authors = ['David Vujic'] homepage = "https://davidvujic.github.io/python-polylith-docs/" From 759a16099db03b0fec5a1e0976aeda1f86663786 Mon Sep 17 00:00:00 2001 From: David Vujic Date: Tue, 19 May 2026 19:32:25 +0200 Subject: [PATCH 6/6] wip --- .../migrate-convert-linter/SKILL.md | 20 ++++++ .../migrate-convert-package-manager/SKILL.md | 22 +++++- .../migrate-convert-type-checker/SKILL.md | 20 ++++++ .../migrate-project/migrate-dedupe/SKILL.md | 51 +++++++++++++- .../migrate-definition-of-done/SKILL.md | 52 +++++++++++++- .../migrate-project/migrate-discover/SKILL.md | 69 +++++++++++++++++-- .../migrate-distribute-wiring/SKILL.md | 14 +++- .../SKILL.md | 23 ++++++- .../migrate-extract-to-base/SKILL.md | 12 ++++ .../SKILL.md | 12 ++++ .../SKILL.md | 22 +++++- .../migrate-orchestrator/SKILL.md | 26 ++++--- .../migrate-prepare-project/SKILL.md | 12 ++++ .../migrate-refactor-tests/SKILL.md | 12 ++++ .../migrate-split-big-component/SKILL.md | 21 ++++++ .../SKILL.md | 22 +++++- 16 files changed, 389 insertions(+), 21 deletions(-) diff --git a/.agents/skills/polylith/migrate-project/migrate-convert-linter/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-convert-linter/SKILL.md index 92918be6..a990059e 100644 --- a/.agents/skills/polylith/migrate-project/migrate-convert-linter/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-convert-linter/SKILL.md @@ -17,6 +17,8 @@ From `migration//state.md`: - `LINTER`, `FORMATTER` - `RUN_TEST_CMD` (optional: `RUN_LINT_CMD`, `RUN_TYPECHECK_CMD`) +> All inputs from `state.md` are assumed to satisfy the validation rules in `migrate-discover` (`### Validation rules`). Validate before proceeding. + ## Steps ### 0. Identify the workspace's tool @@ -58,8 +60,26 @@ If the workspace has no linter configured at all, stop and ask the user how to p - `RUN_TEST_CMD` succeeds. - If set, `RUN_TYPECHECK_CMD` succeeds. +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| The workspace's linter surfaces hundreds of violations the project's old linter didn't catch | Stricter ruleset; not a "fix everything now" situation. | Ask the user: fix now, suppress via `[tool.]` ignores in the project subsection, or defer in a follow-up branch. Do **not** auto-fix silently — record the decision in `state.md`. | +| Project-specific lint rules are stricter than the workspace's standard | Project had higher discipline; lowering it would regress quality. | Add the project's rule as a per-path override in the workspace's lint config (most linters support per-directory rule overrides). Do not weaken the rule globally. | +| Old linter config file (`.flake8`, `.pylintrc`, etc.) lingers and confuses developers' local editors | Step 1 missed a config file. | Delete it; mention in the commit message so reviewers update their editor configs. | + ## Done When - No project-specific linter/formatter config files or dependencies remain. - Project-specific linting rules are merged into the workspace root's configuration. - `LINTER` and `FORMATTER` in `migration//state.md` match the workspace's tools. - Tests pass via the workspace's tooling. + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase optional — convert-linter" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. diff --git a/.agents/skills/polylith/migrate-project/migrate-convert-package-manager/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-convert-package-manager/SKILL.md index 9b307c16..3abe79e2 100644 --- a/.agents/skills/polylith/migrate-project/migrate-convert-package-manager/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-convert-package-manager/SKILL.md @@ -25,6 +25,8 @@ From `migration//state.md`: - `PACKAGE_MANAGER` - `RUN_TEST_CMD` (optional: `RUN_LINT_CMD`, `RUN_TYPECHECK_CMD`) +> All inputs from `state.md` are assumed to satisfy the validation rules in `migrate-discover` (`### Validation rules`). Validate before proceeding. + ## Steps ### 1. Ask for User Approval @@ -75,10 +77,28 @@ From `migration//state.md`: - If set, run `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD`. - Ensure `uv lock` and `uv sync` succeed from the workspace root. +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| Workspace root does not use uv (no `[tool.uv.workspace]`, no `uv.lock`) | This skill does not apply — the opinionation gate at the top of this file rules it out. | Stop. Set `CONVERT_PACKAGE_MANAGER=no` in `state.md`. Align the project to the workspace's actual manager (Poetry/PDM/Hatch) via a manual step instead. | +| `uv lock` fails with "no version satisfies …" after adding the project as a workspace member | Project's old version constraints conflict with the workspace root's pins. | Relax the workspace root's range, or, if the project legitimately needs a different version, pin it explicitly in the project's `pyproject.toml`. As a last resort, exclude the project from the workspace and use a separate environment. | +| Project depends on a private/internal package that the workspace root doesn't know about | Private index or path-dependency not declared at the root. | Add the dependency (and its source — `[tool.uv.sources]` or `[[tool.uv.index]]`) to the workspace root `pyproject.toml`. | + ## Done When - The project is listed as a workspace member in the root `pyproject.toml`. - `uv lock` and `uv sync` succeed from the workspace root. - Old lock files (`poetry.lock`, `Pipfile.lock`, generated `requirements*.txt`) are deleted from the project directory. - `PACKAGE_MANAGER=uv` is recorded in `migration//state.md`. - Verification commands in `state.md` use `uv run`. -- Tests pass via `uv run`. \ No newline at end of file +- Tests pass via `uv run`. + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase optional — convert-package-manager" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/migrate-convert-type-checker/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-convert-type-checker/SKILL.md index 032eab50..2a0dbcac 100644 --- a/.agents/skills/polylith/migrate-project/migrate-convert-type-checker/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-convert-type-checker/SKILL.md @@ -17,6 +17,8 @@ From `migration//state.md`: - `TYPE_CHECKER` - `RUN_TEST_CMD` (optional: `RUN_LINT_CMD`, `RUN_TYPECHECK_CMD`) +> All inputs from `state.md` are assumed to satisfy the validation rules in `migrate-discover` (`### Validation rules`). Validate before proceeding. + ## Steps ### 0. Identify the workspace's tool @@ -54,6 +56,14 @@ If the workspace has no type checker configured at all, stop and ask the user ho - `RUN_TEST_CMD` succeeds. - If set, `RUN_LINT_CMD` succeeds. +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| The workspace's checker surfaces type errors the old checker didn't | Stricter type rules expose real bugs that were always there. | Fix the bugs now where cheap (small handful). For systemic gaps, suppress per-module in the workspace's checker config and document the debt in `state.md`. Do **not** blanket-ignore at file scope. | +| Stub packages (`types-*`, `django-stubs`, `sqlalchemy-stubs`) were required by the old checker but the new one bundles its own | Dependency carryover. | Remove the stub packages from `pyproject.toml`. Re-run the workspace's checker to confirm it picks up its own stubs. | +| `# type: ignore[]` comments still reference the old checker's error codes | Step 2 missed some. | Strip the `[]` bracket. If the suppression is still needed, leave a bare `# type: ignore` **with a comment explaining why**; otherwise remove the suppression entirely. | + ## Done When - No old type checker config files remain. - Old type checker dependencies are removed from `pyproject.toml`. @@ -61,3 +71,13 @@ If the workspace has no type checker configured at all, stop and ask the user ho - `TYPE_CHECKER` in `migration//state.md` matches the workspace's type-checking tool. - The workspace's type-checking tool runs cleanly or known issues are documented in `state.md`. - Tests pass via the workspace's tooling. + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase optional — convert-type-checker" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. diff --git a/.agents/skills/polylith/migrate-project/migrate-dedupe/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-dedupe/SKILL.md index e96027b1..7ff83aa8 100644 --- a/.agents/skills/polylith/migrate-project/migrate-dedupe/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-dedupe/SKILL.md @@ -19,6 +19,35 @@ Identify duplication candidates during the migration process and execute control - After splitting the big component or extracting standalone modules. - When potential duplication between components is suspected. +## Classification + +Use this table when deciding whether a candidate is a real duplicate: + +| Class | Definition | Action | +|-------|------------|--------| +| **Identical** | Same logic, same control flow, only trivial differences (variable names, formatting, ordering of independent statements). | Extract into a shared component. Both call sites import from it. | +| **Similar** | Same purpose, slightly different behaviour (e.g., different default arguments, project-specific fields on an otherwise shared model). | Extract a **parameterized** shared component. Project-specific behaviour passes in as arguments or subclass hooks. Avoid forcing a one-size-fits-all signature. | +| **Coincidental** | Looks similar (same function name, same shape) but serves unrelated purposes. | Leave alone. Sharing here would couple two domains that should evolve independently. | + +## When to parameterize vs. keep separate + +- **Parameterize** when the core logic is identical and only data/config differs. +- **Keep separate** when control flow or structure diverges (different frameworks, different patterns) — forcing a shared abstraction here creates a brittle "shared core" that grows project-specific flags over time. +- **Extract shared base + per-project wrappers** when there's a significant shared core but non-trivial project-specific logic around it. + +### Worked example — logging + +Two projects each had their own `init_logging`. The core (structlog setup, base log levels, JSON formatter) was identical; the differences were: + +- Project A added loggers for `httpx`, `backoff`. +- Project B added a logger for `confluent_kafka_helpers`. + +The shared component exposed an `init(config, *, extra_loggers=None, cache_logger_on_first_use=False)` function. Each project's base calls `init` with its own `extra_loggers` dict. No coincidental coupling, no version skew, and adding a third project requires only its own dict — not a change to the shared component. + +## Shared-component naming + +When creating a shared component to deduplicate code, name it after the **domain or capability** it represents, never after how it's used. Good: `logging`, `kafka_client`, `merchant_serializer`. Bad: `shared_utils`, `common`, `helpers`, `misc`. Generic-named bricks attract more code over time and become the next thing that needs decomposing. + ## Inputs From `migration//state.md`: - `TARGET_TOP_NS` @@ -27,6 +56,8 @@ From `migration//state.md`: From `migration//manifest.md`: - Module map of components. +> All inputs from `state.md` are assumed to satisfy the validation rules in `migrate-discover` (`### Validation rules`). Validate before proceeding. + ## Steps ### 1. Identify Duplication Candidates @@ -62,8 +93,26 @@ From `migration//manifest.md`: - Linting and type-checking pass (if set). - The workspace structure is valid (`POLY_CMD_PREFIX check`). +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| Two pieces of code look identical but operate on different domains (e.g., both are `validate(...)` but one is for users, the other for transactions) | Coincidental similarity, not real duplication. | Classify as "coincidental"; leave both in place. Resist the urge to share. | +| The candidate shared component would pull in framework-specific dependencies (e.g., a "logging" shared brick that needs both `confluent_kafka_helpers` and `httpx`) | Wrong shared abstraction — you're sharing the union of two project surfaces. | Revert and parameterize instead: keep the shared core minimal and pass project-specific values as arguments. See the "Pattern: Parameterize the shared component" guidance in `migrate-split-big-component`. | +| Tests break after deduplication because `mock.patch("")` no longer hits anything | Patch strings reference the pre-dedup module path. | Update patch strings to the new shared module path. Validate by deliberately breaking the patched function and confirming the test fails. | + ## Done When - Duplication candidates are identified and presented to the user. - User-approved candidates are deduplicated. - All tests and checks pass. -- The workspace structure is valid. \ No newline at end of file +- The workspace structure is valid. + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase optional — dedupe" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/migrate-definition-of-done/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-definition-of-done/SKILL.md index 4569bee8..6991340b 100644 --- a/.agents/skills/polylith/migrate-project/migrate-definition-of-done/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-definition-of-done/SKILL.md @@ -50,8 +50,58 @@ description: "[Internal sub-skill of `migrate-orchestrator` (phase 11 of 11). Do - Migration artifacts (`migration//state.md`, `migration//manifest.md`, and any `migration/shims.md`) are either removed or kept under `migration//` for reference (user's choice). - The migration branch (`GIT_BRANCH` from `state.md`) is ready to be merged or rebased into the main branch. Per-phase commits remain available for review/bisect. +## End-to-end checks + +These checks go beyond per-phase verification and exercise the migrated project as a whole. **All three must pass.** + +### 1. Entrypoint smoke test + +For **each base** in the project, smoke-test its entrypoint: + +- **HTTP API base** → start the server and curl a health endpoint (or any GET that doesn't require auth). Verify it returns 200. +- **CLI base** → run ` --help` and a no-op subcommand. Verify exit code 0. +- **Worker/consumer base** → start the process and observe one cycle of its main loop in the logs. Stop it cleanly. +- **Lambda / Cloud Function base** → invoke the handler with a representative event payload (mocked or recorded). Verify it returns without exception. + +If any base fails to start, the migration is **not done** — return to `migrate-distribute-wiring` and check entrypoint wiring. + +### 2. Baseline test count restored + +Compare collected test count to the baseline recorded by `migrate-discover`: + +```bash + +``` + +The count must **equal** the baseline. A lower count means `pytest` discovery is misconfigured (see `migrate-refactor-tests` failure modes). A higher count means tests were inadvertently duplicated during the move. + +### 3. No undocumented shims remain + +Inspect `migration//shims.md` (if it exists): + +- **Empty or absent** → ✅ proceed. +- **Lists active shims** → either remove them now (rewrite imports to the new namespace and delete the shim modules) or schedule their removal as a follow-up PR and reference that PR in the migration branch description. Do **not** merge with undocumented shims. + ## Verify - `RUN_TEST_CMD` succeeds. - If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. - Run `POLY_CMD_PREFIX check` to validate the workspace structure. -- Run `POLY_CMD_PREFIX info` to inspect the workspace and confirm all projects and bricks are correctly registered. \ No newline at end of file +- Run `POLY_CMD_PREFIX info` to inspect the workspace and confirm all projects and bricks are correctly registered. + +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| `RUN_TEST_CMD` passes but collected test count is lower than the baseline from `migrate-discover` | `pytest` discovery is misconfigured after the test reorganisation. | Update `[tool.pytest.ini_options].testpaths` (or pass paths explicitly in `RUN_TEST_CMD`). Run `pytest --collect-only` and diff against baseline collection. See `migrate-refactor-tests` for details. | +| `poly check` is green and tests pass, but the application doesn't start | Entrypoint wiring regression — a base imports something that no longer exists at the expected path, but no test exercises the boot path. | Smoke-test each base's entrypoint manually (run the FastAPI server, invoke the CLI, send a test event to the consumer). Revisit `migrate-distribute-wiring`. | +| `migration/shims.md` still lists active shims | Shims from `migrate-extract-to-base` (namespace change) were never removed. | Either rewrite imports to the new namespace and delete the shims, or schedule shim removal as a follow-up PR and note it in the migration branch description. Do not merge with undocumented shims. | + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase 11 — definition-of-done" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/migrate-discover/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-discover/SKILL.md index 01be2259..a64c627b 100644 --- a/.agents/skills/polylith/migrate-project/migrate-discover/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-discover/SKILL.md @@ -67,6 +67,19 @@ GIT_BASE_SHA= | `order-management-api` | `order_management` | `order-mgmt` | | `payment-worker` | `payment` | `payment` | +### Validation rules + +When any later phase loads `state.md`, validate before proceeding: + +1. **File exists** at `migration//state.md`. +2. **Format**: every line is one of: blank, `# comment`, or `KEY=value`. No markdown tables, no fenced TOML, no inline comments after a value. +3. **Schema coverage**: every key from the schema above is present. A value may be empty (for optional keys), but the key line must exist. +4. **Enumerations**: `PACKAGE_MANAGER`, `LINTER`, `FORMATTER`, `TYPE_CHECKER`, and the three `CONVERT_*` flags use only the documented values. +5. **Required non-empty**: `PROJECT_DIR`, `ORIG_TOP_NS`, `TARGET_TOP_NS`, `INITIAL_BASE_NAME`, `PACKAGE_MANAGER`, `POLY_CMD_PREFIX`, `RUN_TEST_CMD`, `GIT_BRANCH`, `GIT_BASE_SHA` must all be non-empty. +6. **Consistency**: `POLY_CMD_PREFIX` matches `PACKAGE_MANAGER` per the mapping table. + +If validation fails, abort the phase, surface the offending line(s) to the user, and ask them to fix `state.md` before retrying. Never silently coerce values. + ## Steps > Run these in order. Every step writes to `state.md` or `manifest.md`. Do not skip the confirmation gates (steps 4 and 7). @@ -122,12 +135,38 @@ Read the **workspace root** `pyproject.toml` to determine the workspace's standa - If the workspace does **not** use uv, `migrate-convert-package-manager` does not apply at all — set `CONVERT_PACKAGE_MANAGER=no` and skip the question below. ### 6. Create `manifest.md` -Record the following in `migration//manifest.md`: -- Directory tree of `projects//` (output of a `directory_tree` tool call). -- **Module map** — for each Python module/package: path, one-line description of its role (e.g., "FastAPI route handlers", "Kafka consumer", "domain model"). -- **Entrypoints** — every file that is the start of a deployable (FastAPI `app`, CLI `main`, Lambda `handler`, worker `run`). -- **Tests** — test directory structure, test file count, fixture file locations (`conftest.py`). -- **Infrastructure files** — Dockerfiles, k8s manifests, Helm charts, alembic configs, deploy scripts. + +Write `migration//manifest.md` using **exactly** this template — fixed headings, fixed shapes. Later phases parse this file by heading. + +`````markdown +# migration//manifest.md + +## Directory tree + +/`, fenced as a code block> + +## Module map + +| Path | Role | +|------|------| +| `` | | + +## Entrypoints + +- ``: + +## Tests + +- Root: `` (relative to project) +- File count: +- Fixture files: ``, ``, … + +## Infrastructure + +- ``: +````` + +> ⚠ Keep the five `##` headings literal. Downstream phases reference them by name; renaming a heading silently breaks input discovery. ### 7. Present derived values, then confirm with the user **Do not proceed past this step without explicit user confirmation.** Present the derived state in one block: @@ -159,6 +198,14 @@ Confirm the values above, or correct any of them. Wait for the user's response. Update `state.md` with corrections and the `CONVERT_*` answers. +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| Derived `INITIAL_BASE_NAME` collides with an existing brick under `bases//` or `components//` | Two projects derive the same base name from a generic `[project.name]`. | Append a project-specific suffix (e.g., `payment_api` instead of `payment`) and re-confirm with the user. Check before writing `state.md`. | +| Project has no detectable test command | No `pytest` / `make test` / CI config that reveals a runnable test command. | Ask the user explicitly. If none exists, set `RUN_TEST_CMD=` empty and record that **every later phase loses its primary safety check** — flag the heightened risk and require manual smoke-testing at the entrypoint level. | +| Multiple linters or formatters are configured simultaneously (e.g., black + ruff format both active) | Project history accumulated tools without a cleanup. | Record both in `state.md` (comma-separated values are acceptable in this one case), and flag for resolution during `migrate-convert-linter`. Do **not** silently pick one. | + ## Done When The following artifacts and conditions all hold: @@ -167,3 +214,13 @@ The following artifacts and conditions all hold: - [ ] The user has explicitly confirmed `INITIAL_BASE_NAME`, `ALIAS`, `GROUP`, and the three `CONVERT_*` flags. - [ ] `RUN_TEST_CMD` is set and **runs successfully on the project's current code** (the migration's baseline pass-rate). - [ ] `GIT_BRANCH` and `GIT_BASE_SHA` are populated (from orchestrator Phase 0). + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase 1 — discover" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. diff --git a/.agents/skills/polylith/migrate-project/migrate-distribute-wiring/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-distribute-wiring/SKILL.md index c12fb0bb..25ead820 100644 --- a/.agents/skills/polylith/migrate-project/migrate-distribute-wiring/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-distribute-wiring/SKILL.md @@ -18,6 +18,8 @@ From `migration//state.md`: From `migration//manifest.md`: - Current module map, including what remains in the residual component. +> All inputs from `state.md` are assumed to satisfy the validation rules in `migrate-discover` (`### Validation rules`). Validate before proceeding. + ## Steps ### 1. Read the Residual Component @@ -64,4 +66,14 @@ From `migration//manifest.md`: | `mock.patch("..init_api")` fails after the move | The test references the old residual path; the function now lives in a base. | Update mock patch strings to the new location, typically `..`. | | `poly check` says the residual brick is still declared but its files are gone | `pyproject.toml` `[tool.polylith.bricks]` still lists the deleted brick. | Remove the line from `[tool.polylith.bricks]` and re-run `POLY_CMD_PREFIX sync --quiet`. | | Application starts but a runtime feature is missing (e.g., logging, DB) | A shared init helper was moved into one base only. The other base never calls it. | Promote the helper to a shared `bootstrap` component and call it from every base's startup path. | -| Verification fails and you can't quickly diagnose | Phase commit not yet made. | `git reset --hard HEAD` to roll back to the previous phase's commit and consult the user. | \ No newline at end of file +| Verification fails and you can't quickly diagnose | Phase commit not yet made. | `git reset --hard HEAD` to roll back to the previous phase's commit and consult the user. | + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase 8 — distribute-wiring" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/migrate-extract-standalone-modules/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-extract-standalone-modules/SKILL.md index 9a289a6d..bbb39bad 100644 --- a/.agents/skills/polylith/migrate-project/migrate-extract-standalone-modules/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-extract-standalone-modules/SKILL.md @@ -17,6 +17,8 @@ From `migration//state.md`: From `migration//manifest.md`: - Current module map, including what remains in the residual component. +> All inputs from `state.md` are assumed to satisfy the validation rules in `migrate-discover` (`### Validation rules`). Validate before proceeding. + ## Steps ### 1. Analyze the Residual Component @@ -41,4 +43,23 @@ From `migration//manifest.md`: - `RUN_TEST_CMD` succeeds. - If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. - Run `POLY_CMD_PREFIX check` to validate the workspace structure. -- Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. \ No newline at end of file +- Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. + +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| Module classified as zero-dep actually pulls in a transitive runtime dependency (e.g., reads an env var via the residual's config module) | The classification missed an indirect import. | Reclassify as low-dep, extract the config module first, then retry. | +| Two consumers now import the same constant via different paths (e.g., `from .consts` and `from ..consts`) | The original module wasn't deleted from the residual after extraction. | Delete the original from the residual (step 3.6), pick one canonical import path, and update every caller. Verify with `grep -r '..consts'`. | +| Extracting `exceptions.py` causes `except` clauses elsewhere to stop catching what they used to | Exception classes are identity-based: two definitions are two different classes. | Ensure the new standalone module is the **only** definition. Delete the residual copy and update every `raise`/`except` site. | +| Verification fails and you can't quickly diagnose | Phase commit not yet made. | `git reset --hard HEAD` to roll back to the previous phase's commit and consult the user. | + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase 6 — extract-standalone-modules" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/migrate-extract-to-base/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-extract-to-base/SKILL.md index 8f9763f8..1e2a1acf 100644 --- a/.agents/skills/polylith/migrate-project/migrate-extract-to-base/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-extract-to-base/SKILL.md @@ -19,6 +19,8 @@ From `migration//state.md`: From `migration//manifest.md`: - Directory tree and module map. +> All inputs from `state.md` are assumed to satisfy the validation rules in `migrate-discover` (`### Validation rules`). Validate before proceeding. + ## Steps ### 1. Create the Base Directory @@ -70,3 +72,13 @@ If `TARGET_TOP_NS != ORIG_TOP_NS`, choose one of the following options: | `poly check` reports "brick imports another brick that is not in `[tool.polylith.bricks]`" | Step 3 only added the base; imports inside the base may pull in components not yet listed. | Run `POLY_CMD_PREFIX sync --quiet` to populate the rest, then re-run `check`. | | Editable install / build fails (`error: package directory '' does not exist`) | The base move broke the previous `[tool.setuptools]` or `[tool.hatch.build]` `packages` setting in `projects//pyproject.toml`. | Update `packages` to point at the new `` namespace, or remove the explicit `packages` setting and let Polylith's build hook handle it. | | Verification fails and you can't quickly diagnose | Phase commit not yet made. | `git reset --hard HEAD` to roll back to the previous phase's commit and consult the user. | + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase 2 — extract-to-base" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. diff --git a/.agents/skills/polylith/migrate-project/migrate-isolate-base-and-big-component/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-isolate-base-and-big-component/SKILL.md index 88288019..033b4a27 100644 --- a/.agents/skills/polylith/migrate-project/migrate-isolate-base-and-big-component/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-isolate-base-and-big-component/SKILL.md @@ -19,6 +19,8 @@ From `migration//state.md`: From `migration//manifest.md`: - Entrypoints list. +> All inputs from `state.md` are assumed to satisfy the validation rules in `migrate-discover` (`### Validation rules`). Validate before proceeding. + ## Steps ### 1. Create the Big Component @@ -71,3 +73,13 @@ From `migration//manifest.md`: | `poly check` flags the component as not used by any project | The base's imports go to the wrong namespace (e.g., `from ...`) so the import graph doesn't reach the component. | Update base imports to `from . import …` and re-run `POLY_CMD_PREFIX sync`. | | Tests for moved code now fail to find fixtures | `conftest.py` was left in the base or moved to the wrong scope. | Move test fixtures alongside the code they cover; usually that's under `test/components///`. | | Verification fails and you can't quickly diagnose | Phase commit not yet made. | `git reset --hard HEAD` to roll back to the previous phase's commit and consult the user. | + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase 4 — isolate-base-and-big-component" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. diff --git a/.agents/skills/polylith/migrate-project/migrate-isolate-shared-and-project-logic/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-isolate-shared-and-project-logic/SKILL.md index 2c9ab663..728e17d2 100644 --- a/.agents/skills/polylith/migrate-project/migrate-isolate-shared-and-project-logic/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-isolate-shared-and-project-logic/SKILL.md @@ -24,6 +24,8 @@ From `migration//state.md`: From `migration//manifest.md`: - Module map of components. +> All inputs from `state.md` are assumed to satisfy the validation rules in `migrate-discover` (`### Validation rules`). Validate before proceeding. + ## Steps ### 1. Identify Monolithic Components @@ -65,9 +67,27 @@ From `migration//manifest.md`: - Linting and type-checking pass (if set). - The workspace structure is valid (`POLY_CMD_PREFIX check`). +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| Only one project exists in the workspace, so there's nothing to compare against | Running this skill on the **first** project migration. | Skip the skill entirely. Record `migrate-isolate-shared-and-project-logic: skipped (single-project workspace)` in `migration//state.md` and proceed to `migrate-distribute-wiring`. Revisit when a second project is migrated. | +| A definition looks shared but is actually used by one project via two different bases inside that project | Bases within one project both use it — still single-project usage. | Leave it project-specific. Cross-project sharing requires consumers in **different** projects under `projects/`. | +| Two projects each have a "same" class with subtle field differences (extra fields, different defaults) | Silent merging would change behaviour. | Do **not** merge silently. Either create a shared base class + project-specific subclasses, or keep the implementations separate and accept the duplication. Confirm with the user. | + ## Done When - Shared logic is extracted into reusable components. - Project-specific logic is isolated into project-specific components. - Similar models reuse shared logic where possible. - All tests and checks pass. -- The workspace structure is valid. \ No newline at end of file +- The workspace structure is valid. + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase 7 — isolate-shared-and-project-logic" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. \ No newline at end of file diff --git a/.agents/skills/polylith/migrate-project/migrate-orchestrator/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-orchestrator/SKILL.md index 608fcb59..d7cc4e2d 100644 --- a/.agents/skills/polylith/migrate-project/migrate-orchestrator/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-orchestrator/SKILL.md @@ -47,7 +47,7 @@ Migration is destructive — files move, directories are deleted, `pyproject.tom ```bash git checkout -b migrate/ ``` -3. After **each completed phase below**, commit the current state with a phase-tagged message: +3. After each completed phase, commit per that phase's `## Commit` section. The commit message follows the pattern `migrate(): phase ` so phases can be located in `git log` later. ```bash git add -A && git commit -m "migrate(): phase " ``` @@ -87,16 +87,26 @@ These are not part of the linear flow above. They are triggered when the user op > ⚠ `migrate-convert-package-manager` only converts **to uv**. If the workspace uses Poetry, PDM, or Hatch as its standard, **skip this skill entirely** — the project should be aligned to the workspace's manager via a manual step instead. +### Ordering when multiple converters are opted in + +When the user opts into more than one of the optional `migrate-convert-*` skills during `migrate-discover`, run them in **this order** between phase 1 and phase 2: + +1. `migrate-convert-package-manager` — runs first because it rewrites `pyproject.toml` wholesale; subsequent skills must operate on the final layout. +2. `migrate-convert-linter` — runs second so workspace-level lint config consolidation happens against the final `pyproject.toml`. +3. `migrate-convert-type-checker` — runs last; type-checker config is the most localized of the three. + +`migrate-dedupe` is triggered later (after phase 5 or phase 6 surfaces duplication candidates) and has **no ordering dependency** with the converters. + +Commit between each optional skill the same way the main phases commit (see each skill's `## Commit` section). + ## Execution checklist For each phase: -1. Load the skill (`migrate-`). -2. Execute its `Steps` in order. -3. Run its `Verify` section. **If verification fails, do not commit and do not proceed.** Either fix the issue, or `git reset --hard` to back out the phase and consult the user. -4. On success, commit: - ```bash - git add -A && git commit -m "migrate(): phase " - ``` +1. **Validate `state.md`** against the rules in `migrate-discover` (`### Validation rules`). Abort the phase if validation fails. +2. Load the skill (`migrate-`). +3. Execute its `Steps` in order. +4. Run its `Verify` section. **If verification fails, do not commit and do not proceed.** Either fix the issue, or `git reset --hard` to back out the phase and consult the user. +5. On success, commit per the phase's `## Commit` section. ## Validation - No circular dependencies exist in the phase graph above (verified by the dependency table). diff --git a/.agents/skills/polylith/migrate-project/migrate-prepare-project/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-prepare-project/SKILL.md index 10a79c9a..1e1c9cc4 100644 --- a/.agents/skills/polylith/migrate-project/migrate-prepare-project/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-prepare-project/SKILL.md @@ -21,6 +21,8 @@ From `migration//state.md`: From `migration//manifest.md`: - Infra files list. +> All inputs from `state.md` are assumed to satisfy the validation rules in `migrate-discover` (`### Validation rules`). Validate before proceeding. + ## Steps ### 1. Verify Project Subfolder @@ -70,3 +72,13 @@ From `migration//manifest.md`: | Infra folder move breaks deploy scripts that hardcoded paths (e.g., `helm//values.yaml`) | Deploy scripts haven't been updated to the new `infra///` location. | Either update the scripts, or symlink the new location from the old one as a transitional step (record the symlink in `migration//state.md`). | | `poly check` complains about brick references after dependency consolidation | A runtime dependency was moved to the workspace root but the project's `pyproject.toml` doesn't declare it. | Add the dependency name (no version) back to the project's `[project.dependencies]`. The version stays only at the workspace root. | | Verification fails and you can't quickly diagnose | Phase commit not yet made. | `git reset --hard HEAD` to roll back to the previous phase's commit and consult the user. | + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase 3 — prepare-project" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. diff --git a/.agents/skills/polylith/migrate-project/migrate-refactor-tests/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-refactor-tests/SKILL.md index 157aba19..8de3efec 100644 --- a/.agents/skills/polylith/migrate-project/migrate-refactor-tests/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-refactor-tests/SKILL.md @@ -27,6 +27,8 @@ From `workspace.toml`: From `test/`: - Current test directory structure. +> All inputs from `state.md` are assumed to satisfy the validation rules in `migrate-discover` (`### Validation rules`). Validate before proceeding. + ## Target layout | Theme | Test path for a base | Test path for a component | @@ -77,3 +79,13 @@ If a test exercises 2+ bricks at integration level, classify it as integration a | `ImportError: cannot import name 'fixture_'` from `conftest.py` | A `conftest.py` `import`s a moved test helper module that didn't follow it. | Move the helper next to the new `conftest.py`, or import it from its new brick path. | | Tests for moved code suddenly find themselves under a brick name that doesn't match their content | Misclassification in step 1. | Re-read the test's imports — the brick most imported is the one that owns the test. Move and update. | | Verification fails and you can't quickly diagnose | Phase commit not yet made. | `git reset --hard HEAD` to roll back to the previous phase's commit and consult the user. | + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase 10 — refactor-tests" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. diff --git a/.agents/skills/polylith/migrate-project/migrate-split-big-component/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-split-big-component/SKILL.md index 645e7687..eaaba46d 100644 --- a/.agents/skills/polylith/migrate-project/migrate-split-big-component/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-split-big-component/SKILL.md @@ -23,6 +23,8 @@ From `migration//state.md`: From `migration//manifest.md`: - Module map of the big component. +> All inputs from `state.md` are assumed to satisfy the validation rules in `migrate-discover` (`### Validation rules`). Validate before proceeding. + ## Steps ### Phase 1: Plan the Split @@ -170,3 +172,22 @@ For each planned component in `split_plan.md`: - If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. - Run `POLY_CMD_PREFIX check` to validate the workspace structure. - Run `POLY_CMD_PREFIX sync` to synchronize the `[tool.polylith.bricks]` table with actual imports. + +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| New component is named `utils`, `helpers`, `common`, or `misc` | Naming taken from old module names instead of the domain the code serves. | Rename to a domain-specific name (see the "Examples of Component Naming" table). Generic-named bricks attract more code and become the next big component. | +| Extracted component imports back into the residual via the residual's `__init__.py` | Circular import — see the "Avoiding circular imports" subsection above. | Apply strategies 1–3 from that subsection (extract the cyclic part, trim `__init__.py` exports, or restructure to standalone). Strategy 4 (deferred import) only as last resort. | +| `poly check` flags the newly extracted component as not used by any project | The project's base still imports from the residual path (`..`) instead of the new component. | Update the base's imports to the new component's public API, then `POLY_CMD_PREFIX sync --quiet` and re-run check. | +| Verification fails and you can't quickly diagnose | Phase commit not yet made. | `git reset --hard HEAD` to roll back to the previous phase's commit and consult the user. | + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase 5 — split-big-component" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. diff --git a/.agents/skills/polylith/migrate-project/migrate-split-component-internals/SKILL.md b/.agents/skills/polylith/migrate-project/migrate-split-component-internals/SKILL.md index 943bf46e..df694344 100644 --- a/.agents/skills/polylith/migrate-project/migrate-split-component-internals/SKILL.md +++ b/.agents/skills/polylith/migrate-project/migrate-split-component-internals/SKILL.md @@ -21,6 +21,8 @@ From `migration//state.md`: From `migration//manifest.md`: - Current component list and structure. +> All inputs from `state.md` are assumed to satisfy the validation rules in `migrate-discover` (`### Validation rules`). Validate before proceeding. + ## Steps ### 1. Identify Candidates @@ -47,4 +49,22 @@ From `migration//manifest.md`: ## Verify - `RUN_TEST_CMD` succeeds. -- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. \ No newline at end of file +- If set, `RUN_LINT_CMD` and `RUN_TYPECHECK_CMD` succeed. + +## Common failure modes + +| Symptom | Likely cause | Remediation | +|---------|--------------|-------------| +| `core.py` is small (<100 lines) but mixes two domains | The component itself is too small to warrant an internal split. | Leave it. Splitting adds indirection without benefit at this size; revisit when the file grows. | +| After splitting, an import like `from . import ` fails | `__init__.py` was not updated to re-export the symbol from its new module. | Add `from .. import ` to `__init__.py`. The component's public API must remain stable across the split. | +| New files inside the component now circularly import each other (e.g., `models/user.py` ↔ `models/transaction.py`) | Domain split was too aggressive; the two files genuinely share a concept. | Extract the shared concept into a third file (e.g., `models/_base.py`) and have both depend on it. | + +## Commit + +After verification passes, commit this phase to the migration branch: + +```bash +git add -A && git commit -m "migrate(): phase 9 — split-component-internals" +``` + +Substitute ``, ``, and `` from `state.md` and the orchestrator's phase table. Do not proceed to the next phase without a clean commit — the per-phase commit is the rollback point for the next phase's failure-mode tables. \ No newline at end of file