diff --git a/platformcraft.yaml b/platformcraft.yaml
new file mode 100644
index 000000000..b269db16a
--- /dev/null
+++ b/platformcraft.yaml
@@ -0,0 +1,23 @@
+name: my-platform-thingy
+base: ubuntu@25.10
+version: "0"
+
+platforms:
+ mario:
+ build-on: [amd64, arm64]
+ build-for: [arm64]
+ extensions:
+ - player-1
+
+ luigi:
+ build-on: [amd64, riscv64]
+ build-for: [riscv64]
+ extensions:
+ - player-2
+
+
+parts:
+ thing:
+ plugin: nil
+ override-build: |
+ exit 0
diff --git a/platformcraft/__init__.py b/platformcraft/__init__.py
new file mode 100644
index 000000000..a5d4f9dbc
--- /dev/null
+++ b/platformcraft/__init__.py
@@ -0,0 +1,21 @@
+# This file is part of craft_application.
+#
+# Copyright 2025 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see .
+"""Platformcraft.
+
+Platformcraft is a fake craft-application app that enables partitions.
+
+This package contains things that differ between testcraft and platformcraft.
+"""
diff --git a/platformcraft/__main__.py b/platformcraft/__main__.py
new file mode 100644
index 000000000..a16bb2ed9
--- /dev/null
+++ b/platformcraft/__main__.py
@@ -0,0 +1,22 @@
+# This file is part of craft_application.
+#
+# Copyright 2025 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see .
+"""The main entrypoint to testcraft."""
+
+import sys
+
+from platformcraft import cli
+
+sys.exit(cli.create_app().run())
diff --git a/platformcraft/application.py b/platformcraft/application.py
new file mode 100644
index 000000000..733ad5548
--- /dev/null
+++ b/platformcraft/application.py
@@ -0,0 +1,30 @@
+# This file is part of craft_application.
+#
+# Copyright 2025 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see .
+"""Application implementation for partitioncraft."""
+
+import craft_application
+
+from platformcraft.models.project import FancyProject
+
+PLATFORMCRAFT = craft_application.AppMetadata(
+ name="platformcraft",
+ summary="A craft for testing custom platform definitions in craft-application.",
+ docs_url="https://canonical-craft-application.readthedocs-hosted.com",
+ source_ignore_patterns=["*.platformcraft"],
+ project_variables=["version"],
+ mandatory_adoptable_fields=["version"],
+ ProjectClass=FancyProject,
+)
diff --git a/platformcraft/cli.py b/platformcraft/cli.py
new file mode 100644
index 000000000..4dc9906e4
--- /dev/null
+++ b/platformcraft/cli.py
@@ -0,0 +1,43 @@
+# This file is part of craft_application.
+#
+# Copyright 2025 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see .
+"""Main CLI for platformcraft."""
+
+from typing import Any
+
+import craft_application
+import craft_cli
+
+from platformcraft.application import PLATFORMCRAFT
+from platformcraft.services import register_services
+
+
+def create_app() -> craft_application.Application:
+ """Create the application.
+
+ This is used both for running the app and for generating shell completion.
+ This function is where the app should be configured before running it.
+ """
+ register_services()
+ services = craft_application.ServiceFactory(app=PLATFORMCRAFT)
+
+ return craft_application.Application(PLATFORMCRAFT, services=services)
+
+
+def get_completion_data() -> tuple[craft_cli.Dispatcher, dict[str, Any]]:
+ """Get the app info for use with craft-cli's completion module."""
+ app = create_app()
+
+ return app._create_dispatcher(), app.app_config # noqa: SLF001
diff --git a/platformcraft/models/__init__.py b/platformcraft/models/__init__.py
new file mode 100644
index 000000000..0e1c3d033
--- /dev/null
+++ b/platformcraft/models/__init__.py
@@ -0,0 +1 @@
+"""Models for platformcraft."""
diff --git a/platformcraft/models/platform.py b/platformcraft/models/platform.py
new file mode 100644
index 000000000..3d4bebd85
--- /dev/null
+++ b/platformcraft/models/platform.py
@@ -0,0 +1,30 @@
+# noqa: A005
+# This file is part of craft_application.
+#
+# Copyright 2025 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see .
+"""Model for a FANCY platform. 🎩🧐."""
+
+from craft_application.models import platforms
+from craft_application.models.constraints import UniqueList
+
+
+class FancyPlatform(platforms.Platform):
+ """Model for a FANCY platform. 🎩🧐."""
+
+ extensions: UniqueList[str]
+
+
+class FancyPlatformsDict(platforms.GenericPlatformsDict[FancyPlatform]):
+ """Model for a FANCY dictionary of platforms! 🎩🧐."""
diff --git a/platformcraft/models/project.py b/platformcraft/models/project.py
new file mode 100644
index 000000000..07238711b
--- /dev/null
+++ b/platformcraft/models/project.py
@@ -0,0 +1,26 @@
+# This file is part of craft_application.
+#
+# Copyright 2025 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see .
+"""Model for a FANCY project. 🎩🧐."""
+
+from craft_application.models import project
+
+from platformcraft.models.platform import FancyPlatformsDict
+
+
+class FancyProject(project.Project):
+ """Model for a FANCY project. 🎩🧐."""
+
+ platforms: FancyPlatformsDict
diff --git a/platformcraft/services/__init__.py b/platformcraft/services/__init__.py
new file mode 100644
index 000000000..10619dea5
--- /dev/null
+++ b/platformcraft/services/__init__.py
@@ -0,0 +1,34 @@
+# This file is part of craft_application.
+#
+# Copyright 2025 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see .
+"""Services for platformcraft."""
+
+import craft_application
+
+
+def register_services() -> None:
+ """Register Partitioncraft's services.
+
+ This registers with the ServiceFactory all the services that platformcraft
+ adds or overrides.
+ """
+ craft_application.ServiceFactory.register(
+ "package", "PackageService", module="platformcraft.services.package"
+ )
+ craft_application.ServiceFactory.register(
+ "project",
+ "ProjectService",
+ module="platformcraft.services.project",
+ )
diff --git a/platformcraft/services/package.py b/platformcraft/services/package.py
new file mode 100644
index 000000000..c56d3fd65
--- /dev/null
+++ b/platformcraft/services/package.py
@@ -0,0 +1,56 @@
+# This file is part of craft_application.
+#
+# Copyright 2025 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see .
+"""Partitioncraft package service."""
+
+import pathlib
+import tarfile
+from typing import cast
+
+import craft_application
+from craft_application.services import package
+from testcraft.models.metadata import Metadata
+
+from platformcraft.models.project import FancyProject
+
+
+class PackageService(package.PackageService):
+ """Package service for partitioncraft."""
+
+ @property
+ def metadata(self) -> Metadata:
+ """Get the metadata for this model."""
+ project = self._services.get("project").get()
+ if project.version is None:
+ raise ValueError("Unknown version")
+ return Metadata(
+ name=project.name,
+ version=project.version,
+ craft_application_version=craft_application.__version__,
+ )
+
+ def pack(self, prime_dir: pathlib.Path, dest: pathlib.Path) -> list[pathlib.Path]:
+ """Pack a partitioncraft artifact set."""
+ project = cast(FancyProject, self._services.get("project").get())
+ platform = self._services.get("build_plan").plan()[0].platform
+
+ extension = ".".join(project.platforms[platform].extensions)
+
+ tarball_name = (
+ f"{project.name}-{project.version}-{platform}.{extension}.platformcraft"
+ )
+ with tarfile.open(dest / tarball_name, mode="w:xz") as tar:
+ tar.add(prime_dir, arcname=".")
+ return [dest / tarball_name]
diff --git a/platformcraft/services/project.py b/platformcraft/services/project.py
new file mode 100644
index 000000000..a8ab73909
--- /dev/null
+++ b/platformcraft/services/project.py
@@ -0,0 +1,48 @@
+# This file is part of craft_application.
+#
+# Copyright 2025 Canonical Ltd.
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License version 3, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
+# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with this program. If not, see .
+"""Partitioncraft project service.
+
+Needed so we can set partitions.
+"""
+
+import craft_platforms
+import pydantic
+from craft_application.services import project
+from typing_extensions import override
+
+from platformcraft.models.platform import FancyPlatformsDict
+
+
+class ProjectService(project.ProjectService):
+ """Package service for testcraft."""
+
+ @override
+ @classmethod
+ def _preprocess_platforms(
+ cls, platforms: dict[str, craft_platforms.PlatformDict]
+ ) -> dict[str, craft_platforms.PlatformDict]:
+ """Validate that the given platforms value is valid."""
+ if platforms:
+ cls._vectorise_platforms(platforms)
+ platforms_project_adapter = pydantic.TypeAdapter(
+ FancyPlatformsDict,
+ )
+ return platforms_project_adapter.dump_python( # type: ignore[no-any-return]
+ platforms_project_adapter.validate_python(platforms),
+ mode="json",
+ by_alias=True,
+ exclude_defaults=True,
+ )