From 6f892b9001010de36e2deac7001b01d54a7eb436 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 25 Mar 2026 11:24:13 +0800 Subject: [PATCH 1/2] Add initial AGENTS.md and SpecKit constitution. --- .gitignore | 5 + .specify/memory/constitution.md | 109 ++++++++++++ AGENTS.md | 292 ++++++++++++++++++++++++++++++++ 3 files changed, 406 insertions(+) create mode 100644 .specify/memory/constitution.md create mode 100644 AGENTS.md diff --git a/.gitignore b/.gitignore index 09cb59993..e2d0987cb 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,8 @@ __pycache__/ tests/apps/verify-*/ logs/ + +# AI tooling +.opencode/ +.specify/scripts +.specify/templates diff --git a/.specify/memory/constitution.md b/.specify/memory/constitution.md new file mode 100644 index 000000000..f304e6181 --- /dev/null +++ b/.specify/memory/constitution.md @@ -0,0 +1,109 @@ + + +# Briefcase Constitution + +## Core Principles + +### I. Cross-Platform Compatibility + +Briefcase exists to convert Python projects into standalone native applications across macOS, Windows, Linux, iOS, Android, and Web. + +- Every feature MUST work on all actively supported platforms, or MUST be explicitly scoped to a platform-specific module with clear documentation of platform applicability. +- Platform-specific behavior MUST be isolated in dedicated platform modules (`src/briefcase/platforms//`). Shared logic MUST NOT contain platform conditionals when a platform module can own the behavior instead. +- New platform or format support MUST be added via the entry-point plugin system, never by hard-coding platform names in core logic. +- Regressions on any supported platform are treated as blocking defects regardless of which platform the contributor develops on. + +### II. Plugin Architecture + +Briefcase uses Python entry points to provide an extensible, composable system of platforms, formats, bootstraps, debuggers, and publication channels. + +- All platform backends, output formats, app bootstraps, debuggers, and publication channels MUST be registered as `setuptools` entry points in `pyproject.toml`. +- New extension categories MUST follow the existing entry-point pattern (`briefcase.`) and MUST NOT introduce alternative registration mechanisms. +- Core code MUST discover capabilities through entry-point enumeration; it MUST NOT import platform/format modules directly except via the plugin interface. +- Third-party plugins MUST be a first-class consideration: public APIs consumed by plugins MUST be documented and changes MUST follow the project's deprecation policy. + +### III. Comprehensive Testing (NON-NEGOTIABLE) + +The project enforces 100% code coverage with no exceptions. + +- Test coverage MUST remain at 100% as enforced by `coverage report --fail-under=100` in CI. Any new code that reduces coverage below 100% MUST NOT be merged. +- Tests MUST run across all supported Python versions (currently 3.10 through 3.14) and across macOS, Linux, and Windows via the CI matrix. +- Platform-conditional coverage exclusions (`no-cover-if-*`) are permitted only for code that is genuinely unreachable on a given OS or Python version. Each exclusion MUST have a corresponding rule in `pyproject.toml` under `[tool.coverage.coverage_conditional_plugin.rules]`. +- `pytest` warnings MUST be treated as errors (`filterwarnings = ["error"]`). New warnings MUST be resolved, not suppressed, unless a documented upstream issue justifies a temporary filter. +- Red/Green testing should be used. + +### IV. Code Quality Enforcement + +Automated tooling gates MUST run before code reaches review. + +- Pre-commit hooks (`ruff-format`, `ruff-check`, `codespell`, `docformatter`, `trailing-whitespace`, `end-of-file-fixer`) MUST pass on every commit. Contributors MUST NOT bypass hooks via `--no-verify`. +- Ruff lint rules defined in `pyproject.toml` under `[tool.ruff.lint]` are the project standard. Adding new `ignore` entries requires explicit justification in the PR description. +- Changelog entries MUST be present for *all* changes, managed via `towncrier` fragments in the `changes/` directory. Minor changes or housekeeping updates SHOULD use the `misc` fragment type, as SHOULD any bugfix or change to a feature that has not yet been part of a formal release. Snippet text MUST describe the user- facing impact of a change, not the specifics of the implementation. CI enforces this with `towncrier check`. +- Spelling MUST be verified by `codespell`. Domain-specific terms MUST be added to the ignore list rather than disabling the check. + +### V. Dependency Stewardship + +Briefcase is an end-user tool; dependency choices directly affect every downstream project that uses it. + +- Runtime dependencies MUST specify version ranges: a minimum version satisfying the project's API needs and, for semver packages, an upper bound excluding the next major version. +- Core Python ecosystem toolchain dependencies (e.g., `pip`, `wheel`, `setuptools`, `build`) MUST specify only a minimum version with no upper cap, because the latest version is always preferred. +- Calendar-versioned dependencies MUST NOT have an upper pin, as calendar versions provide no API-stability signal. +- Developer/test dependencies MUST be pinned to exact versions to guarantee reproducible environments. +- New runtime dependencies require justification in the PR description addressing: why the dependency is necessary, license compatibility (BSD-3-Clause), impact on installation size, and project maturity. Dependencies MUST only be added if the upstream project has a history of consistent maintenance and of stable, backward-compatible development. + +## Technology & Dependency Constraints + +- **Language**: Python >= 3.10 (currently 3.10 through 3.14). +- **License**: BSD-3-Clause. All dependencies and contributions MUST be compatible with this license. +- **Build system**: setuptools with setuptools_scm for versioning. Version is derived from git tags; MUST NOT be hard-coded. +- **CLI entry point**: `briefcase` via `briefcase.__main__:main`. +- **Output rendering**: `rich` library for terminal UI. All user- facing output MUST go through Rich's console interface, not raw `print()` (enforced by the `T20` ruff rule). +- **HTTP client**: `httpx` for all network operations. +- **Template engine**: `cookiecutter` for app scaffolding. +- **Platform-specific dependencies**: All platform-specific dependencies (e.g., `dmgbuild` for macOS) MUST use appropriate environment markers (e.g., `sys_platform == 'darwin'`) so they are only installed on the relevant platform. + +## Development Workflow + +- **Branching**: Feature branches off `main`. All changes arrive via pull request. +- **CI gates**: Pre-commit, test suite (all Python versions x platforms), coverage (100%), towncrier check, and docs build MUST pass before merge. +- **Changelog**: Every change MUST include a towncrier fragment categorized as `feature`, `bugfix`, `removal`, `doc`, or `misc`. Fragments MUST describe user-facing impact, not implementation details. Minor or housekeeping changes SHOULD use `misc`, as SHOULD any bugfix or change to a feature that has not yet been part of a formal release. +- **Documentation**: Docs are built using `tox -e docs-all` (MkDocs via beeware-docs-tools, on Python 3.12). API or behavioral changes MUST include corresponding documentation updates. +- **Versioning**: The project uses setuptools_scm (git-tag-derived versions). Release tags MUST follow PEP 440. + +## Governance + +This constitution is the authoritative reference for Briefcase development practices. It supersedes informal conventions and ad-hoc decisions. + +- **Amendment procedure**: Amendments MUST be proposed via pull request modifying this file. The PR description MUST state the rationale and the semantic version bump (MAJOR/MINOR/PATCH). Amendments require maintainer approval before merge. +- **Versioning policy**: This constitution follows semantic versioning. MAJOR for principle removals or incompatible redefinitions; MINOR for new principles or materially expanded guidance; PATCH for clarifications and typo fixes. +- **Compliance review**: All pull requests SHOULD be evaluated against these principles. Deviations MUST be explicitly justified in the PR description and approved by a maintainer. +- **Guidance file**: Runtime development guidance (IDE setup, local workflow tips) belongs in the agent-file or project README, not in this constitution. + +**Version**: 1.0.0 | **Ratified**: 2026-02-26 | **Last Amended**: 2026-02-26 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..467c450a5 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,292 @@ +# Briefcase — Agent Development Guide + +Briefcase converts Python projects into standalone native applications for macOS, Windows, Linux, iOS, Android, and Web. This guide provides the context AI coding agents need to contribute effectively. + +## Quick Reference + +- **Language**: Python >= 3.10 (3.10–3.14 supported) +- **Dev environment**: Python 3.13 virtualenv with `dev` dependency group +- **License**: BSD-3-Clause +- **Entry point**: `briefcase` via `src/briefcase/__main__.py:main()` +- **Test framework**: pytest (100% coverage required, no exceptions) +- **Linting**: ruff (format + check), codespell, docformatter +- **Docs**: MkDocs + +## Development Environment Setup + +All development must use a Python 3.13 virtual environment with the `dev` dependency group installed: + +```bash +python3.13 -m venv venv +source venv/bin/activate # or venv\Scripts\activate on Windows +pip install -e . --group dev +``` + +This installs tox, pre-commit, and other development tooling. All test execution and CI-equivalent checks run through tox. + +## Project Layout + +```text +src/briefcase/ +├── __main__.py # CLI entry point +├── cmdline.py # Command-line parsing and dispatch +├── config.py # AppConfig / GlobalConfig from pyproject.toml +├── console.py # Rich-based Console (ALL user I/O) +├── exceptions.py # BriefcaseError hierarchy +├── constants.py # Reserved words and constants +├── commands/ # Command implementations +│ ├── base.py # BaseCommand ABC +│ ├── create.py, build.py, run.py, update.py, package.py, +│ │ publish.py, dev.py, new.py, open.py, convert.py, upgrade.py +├── platforms/ # Platform plugins (one subdir per OS) +│ ├── __init__.py # get_platforms(), get_output_formats() +│ ├── macOS/, linux/, windows/, android/, iOS/, web/ +├── integrations/ # External tool wrappers +│ ├── base.py # Tool / ManagedTool ABCs, ToolCache +│ └── subprocess.py, android_sdk.py, docker.py, xcode.py, ... +├── bootstraps/ # App template bootstraps +├── channels/ # Publication channels (App Store, Play Store) +└── debuggers/ # Debugger plugins (pdb, debugpy) + +tests/ # Mirrors src/ structure exactly +├── conftest.py # Root fixtures (no_print, dummy_console, configs) +├── utils.py # DummyConsole, PartialMatchString, file helpers +├── commands/ +├── platforms/ +├── integrations/ +└── ... + +docs/en/ # MkDocs documentation (English) +changes/ # Towncrier changelog fragments +automation/ # Separate automation subpackage +debugger/ # Separate debugger subpackage (own pyproject.toml) +``` + +## Critical Rules + +### No `print()` — ever + +All user-facing output MUST go through the `Console` object, never raw `print()`. The `T20` ruff rule bans print statements in source code, and the `no_print` autouse test fixture will **fail any test** where briefcase code calls `print()`. + +Use `self.tools.console.info()`, `.verbose()`, `.debug()`, `.warning()`, or `.error()` instead. + +### 100% test coverage — no exceptions + +Coverage is enforced by `coverage report --fail-under=100` in CI. Every line of new code must be covered. Platform-specific code that is unreachable on certain OSes must use conditional coverage pragmas: + +```python +# pragma: no-cover-if-not-macos +# pragma: no-cover-if-is-windows +# pragma: no-cover-if-lt-py312 +``` + +Each pragma must have a corresponding rule in `pyproject.toml` under `[tool.coverage.coverage_conditional_plugin.rules]`. + +Red/Green TDD should be used when adding features or fixing bugs. + +### Warnings are errors + +pytest is configured with `filterwarnings = ["error"]`. Do not suppress warnings — fix the cause. + +### Modifying AGENTS.md rules + +Do not make changes to AGENTS.md unless specifically directed to do so. + +## Architecture Patterns + +### Command dispatch + +1. `cmdline.parse_cmdline()` resolves platform + format from CLI args +2. Loads the format module via entry points +3. Gets the command class via `getattr(format_module, command_name)` +4. Command classes are composed via **multiple inheritance**: + +```python +class macOSAppBuildCommand( + macOSAppMixin, # format-specific paths/behavior + macOSSigningMixin, # platform signing logic + AppPackagesMergeMixin, # utility mixin + BuildCommand, # base command from commands/ +): + ... +``` + +### Platform module conventions + +Each format file (e.g., `platforms/macOS/app.py`) must export **module-level aliases** matching command names: + +```python +create = macOSAppCreateCommand +update = macOSAppUpdateCommand +build = macOSAppBuildCommand +run = macOSAppRunCommand +package = macOSAppPackageCommand +publish = macOSAppPublishCommand +open = macOSAppOpenCommand +``` + +New platforms/formats MUST register via entry points in `pyproject.toml`, never by hard-coding in core logic. + +### Tool system + +- `Tool` ABC with `verify()` classmethod (calls `verify_host()` then `verify_install()`) +- `ManagedTool(Tool)` adds `exists()`, `install()`, `uninstall()`, `upgrade()` +- Tools register via `__init_subclass__` into `tool_registry` +- Accessed through `ToolCache` on `command.tools` (e.g., `self.tools.subprocess`, `self.tools.java`) +- `ToolCache` wraps stdlib modules (`os`, `platform`, `shutil`, `sys`) to enable test mocking + +### Error handling + +All errors derive from `BriefcaseError(Exception)` with an `error_code` integer. Key subtypes: + +- `BriefcaseCommandError` (200) — general operational errors +- `BriefcaseConfigError` (100) — configuration problems +- `NetworkFailure`, `MissingToolError`, `InvalidDeviceError` — specific failure modes +- `HelpText` — displays help, not an error +- `BriefcaseWarning` — non-fatal (exit code 0) + +## Testing Patterns + +All commands below assume the `dev` dependency group is installed in a Python 3.13 virtual environment (see setup above). + +### Test organization + +Tests mirror the source tree. Within each area, tests are organized by method/behavior: + +```text +tests/commands/create/test_install_app_requirements.py +tests/commands/build/test_call.py +tests/platforms/macOS/app/test_build.py +``` + +### Dummy command pattern + +Tests create concrete subclasses of abstract commands that track method calls in an `actions` list: + +```python +class DummyBuildCommand(BuildCommand): + def build(self, app, **kwargs): + self.actions.append(("build", app.app_name)) + return {} + +# Then assert exact action sequence: +assert build_command.actions == [ + ("verify-host",), + ("verify-tools",), + ("build", "first"), +] +``` + +### Mock conventions + +- Use `MagicMock(spec_set=...)` (not `spec=`) for strict mocking +- External tools: mock via `mock.MagicMock(spec_set=Subprocess)` on `command.tools.subprocess` +- Filesystem layout: use `create_file()` from `tests/utils.py` +- Downloads: use `mock_file_download()`, `mock_zip_download()`, `mock_tgz_download()` side-effect factories from `tests/utils.py` +- `ToolCache` mocks wrap stdlib modules for test isolation + +### Key fixtures (from `tests/conftest.py`) + +- `no_print` (autouse) — fails tests that call `print()` from briefcase code +- `dummy_console` — `DummyConsole` that records prompts and returns programmed values +- `sleep_zero` — replaces `time.sleep` with instant returns +- `first_app_config` / `first_app_unbuilt` / `first_app` — graduated app fixtures (config only / bundle exists / binary exists) + +### Key helpers (from `tests/utils.py`) + +- `DummyConsole` — captures user interaction +- `PartialMatchString` / `NoMatchString` — flexible assertion matching +- `create_file()`, `create_plist_file()`, `create_zip_file()`, `create_tgz_file()` — filesystem helpers +- `create_wheel()`, `create_installed_package()` — fake Python package creation + +## Running Tests and Checks + +All commands below assume the `dev` dependency group is installed in a Python 3.13 virtual environment (see Development Environment Setup above). + +```bash +# Run full test suite with coverage +tox -e py-cov + +# Run tests fast (parallel, no coverage) +tox -e py-fast + +# Run pre-commit hooks +tox -e pre-commit + +# Run just ruff +ruff check src/ tests/ +ruff format --check src/ tests/ + +# Coverage report (must be 100%) +tox -e coverage + +# Lint docs +tox -e docs-lint + +# Build docs +tox -e docs-all + +# Check changelog fragments +tox -e towncrier-check +``` + +## Changelog + +Every change requires a towncrier fragment in `changes/`: + +```text +changes/{issue_number}.{type}.md +``` + +Types: `feature`, `bugfix`, `removal`, `doc`, `misc`. + +Fragment text must describe user-facing impact, not implementation details. Use `misc` for housekeeping, minor changes, or changes to features not yet in a formal release. + +## Documentation Style + +Markdown files in this project (including `AGENTS.md`, `docs/en/`, and `changes/` fragments) must **not** use hard line breaks to enforce an 80-character column limit. Each paragraph or list item must be written as a single unbroken line, regardless of its length. Let the reader's editor or renderer handle wrapping. + +This rule applies to all prose. Code blocks, directory trees, and other pre-formatted blocks are exempt — keep those readable within their own constraints. + +When writing or editing any `.md` file, do not insert newlines mid-sentence or mid-paragraph to stay within 80 columns. + +## Code Style + +- **Class naming**: `{Platform}{Format}{Action}Command` (e.g., `macOSAppBuildCommand`) +- **Mixin naming**: `{Platform}{Feature}Mixin` (e.g., `macOSSigningMixin`) +- **Platform casing**: Preserve original (macOS, iOS, not macos) +- **Docstrings**: Google/Sphinx style with `:param:` / `:returns:` / `:raises:` — formatted by docformatter, using Markdown formatting +- **Imports**: `from __future__ import annotations` used widely for PEP 604 unions +- **Paths**: Always use `pathlib.Path` objects, never raw strings +- **Function call formatting**: When a function call with more than one argument cannot fit on a single line, place each argument on its own line with a trailing comma on the last argument — do **not** use the "multiple arguments on one wrapped line" style that ruff also permits. Prefer: + ```python + my_function( + arg1, + arg2, + arg3, + ) + ``` + over: + ```python + my_function( + arg1, arg2, arg3 + ) + ``` +- **Long string arguments**: When a string argument must be split across lines to satisfy line length requirements, wrap the concatenated string literals in parentheses so it is clear the string is a single argument. Prefer: + ```python + my_function( + ( + "this is a very long string " + "that is wrapped over two lines" + ), + second_argument, + ) + ``` + over: + ```python + my_function( + "this is a very long string " + "that is wrapped over two lines", + second_argument, + ) + ``` From 28cae735c1373f991cbdef6047aaab2298683f8c Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Wed, 25 Mar 2026 11:35:49 +0800 Subject: [PATCH 2/2] Add Changenote. --- changes/2733.feature.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/2733.feature.md diff --git a/changes/2733.feature.md b/changes/2733.feature.md new file mode 100644 index 000000000..905d18c18 --- /dev/null +++ b/changes/2733.feature.md @@ -0,0 +1 @@ +The Briefcase repository now has an `AGENTS.md` file and [Spec Kit](https://github.github.com/spec-kit/) constitution to provide assistance for Generative AI tools.