Skip to content

Research: Root Project Layout ​

Date: 2026-10-29 Baseline: 25c9763; local files and primary documentation reviewed before design.

1. Preserve the existing distribution and runtime boundaries ​

Decision: Move the project root without changing the camply package architecture or extras.

Evidence: Python metadata defines one distribution with server, distributed, postgres, and sentry extras, and import-linter protects optional dependencies. Standalone launcher already validates installed UI resources and supports an external absolute asset directory. Constitution principle IV still mandates nested roots.

Rationale: The recent package/standalone work already supplies the necessary runtime architecture. A directory/build migration should preserve it.

Alternatives considered: Separate UI distribution, optional downloaded assets, renamed runtime packages, or moving shared scheduler modules. All add scope unrelated to the requested root layout.

2. Build complete source archives before wheels ​

Decision: Keep Hatchling; use a standard-build hook for both targets, explicitly map generated assets into archives, and validate bundled output before source-archive reuse.

Evidence: The current baseline hook only handles standard wheels, looks outside the Python project, can skip missing sources/tooling, and stages output into an ignored source directory. The sdist whitelist omits frontend siblings. python -m build normally creates an sdist and builds its wheel from the extracted archive. PyPA build documentation

Hatch supports build-data forced inclusion and explicit generated artifacts. Default file selection respects version-control ignore rules. Hatch hook interface, Hatch file selection

Rationale: The source archive must be sufficient to build the application independently. A small input/output manifest prevents corrupted or changed archives from silently reusing inconsistent UI assets. Editable paths return before toolchain probing.

Alternatives considered: Wheel-only hooks fail source-first completeness; always rebuilding JavaScript from sdists adds a consumer toolchain requirement; committing compiled UI creates routine source churn; changing build backends is unnecessary.

Metadata nuance: Hatchling exposes metadata-only hooks in supported contexts and builds editable wheels through a distinct version. Some package frontends may fall back to an actual standard wheel build for metadata; that operation must still produce a complete application. Hatchling build frontend hooks

3. Use one Dockerfile with existing runtime images ​

Decision: Share application build stages and retain separate backend/frontend runtime targets and existing image names.

Evidence: Root Compose currently includes two project-relative Compose files; backend/worker/beat share the backend image. The Bake release overlay and release tests assume backend/frontend target identities. Compose entrypoint, release overlay, release tests

Rationale: The user requested definition consolidation, not deployment-topology changes. Explicit root contexts and normalized before/after configuration checks protect mounts and environment semantics.

Alternatives considered: One combined API/Nginx/worker image or a standalone-only stack would change delivery behavior. Retaining nested Dockerfiles/Compose includes conflicts with the accepted plan.

4. Install Gunicorn configuration as an importable module ​

Decision: Move the existing settings into camply.server.gunicorn and use Gunicorn's python: configuration syntax.

Evidence: Current settings live outside the import package and task/container commands reference a file path. Gunicorn accepts python:MODULE_NAME for configuration. Current settings, Gunicorn settings reference

Rationale: Installed packages can resolve configuration regardless of their working directory. Keeping it under server preserves current optional-runtime boundaries.

Alternatives considered: Copying configuration beside site-packages or locating source files at runtime retains deployment-specific filesystem assumptions.

5. Keep Pages Functions and Wrangler with frontend source ​

Decision: Put wrangler.jsonc and functions under src/frontend; run root-installed Wrangler with that working directory. Retain project camply-81r, a config-relative output path, and the already-documented cache-compatible date 2024-11-11.

Evidence: Pages Functions use a functions directory at the Pages project root. Wrangler supports Pages output/date/project configuration, working-directory selection, and local Function bundling. Function placement, Pages configuration, Wrangler Pages commands

The current proxy uses externally supplied CAMPLY_API_ORIGIN, requires HTTPS, rewrites upstream redirects, and forwards cookies. Existing docs identify the project, preview trust policy, and minimum runtime date. Proxy, current operator guide

Rationale: This removes unwanted root folders while retaining framework discovery. The checked-in config must be validated with both preview/production bindings before eventual publication; no live values are inferred.

Alternatives considered: Root functions violates the user's final layout. A static-only upload would omit the cookie-preserving proxy. Migrating to Workers advanced mode would change deployment architecture unnecessarily.

6. Relocate specs through existing feature-path support ​

Decision: New feature 013 is created directly under docs/specs; move old features and update creation/numbering defaults during implementation. Keep .specify at root and feature state checkout-local.

Evidence: The installed version is 1.1.3. Path resolver honors explicit feature directories and .specify/feature.json; creator and Gemini specification command still default to root specs. Spec-Kit's upstream documents the independent project-root and active-feature-directory axes. Core reference

Rationale: Existing plan/task/prerequisite scripts already support the selected location. Creation and next-number scanning require deliberate changes, tested in temporary repositories. Existing duplicate number prefixes remain untouched.

Alternatives considered: A root compatibility symlink retains unwanted structure; a custom global directory setting without tool support would provide false confidence; reinitializing Spec-Kit introduces unrelated changes.

7. Preserve test ownership and current instructions ​

Decision: Move Python suites to root tests, retain colocated React/utility tests, move PNG baselines outside source, and update actual path calculations rather than adding wrapper checkouts.

Evidence: Existing Python live tests calculate roots using fixed parent counts and run pnpm from frontend; release fixtures copy only Python sources; frontend codegen is currently in browser source. CI packaging/release jobs do not consistently install the frontend toolchain. CLAUDE.md is a symlink while GEMINI.md is a diverging copy.

Rationale: Path-sensitive checks and release fixtures must exercise the same complete artifacts that consumers receive. One instruction source and current-versus-historical documentation reduce future drift.

Alternatives considered: Moving all React tests into a central suite or refactoring Python component ownership creates unnecessary churn.

All design questions needed for planning are resolved. Real deployment bindings and publication are operator actions outside this deliverable.