Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# EditorConfig is awesome: https://EditorConfig.org

# Top-most EditorConfig file
root = true

# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true

# Set default charset
charset = utf-8

# 2 space indentation (default)
indent_style = space
indent_size = 2

# 2 space indentation (JS and JSX and TS files)
[*.{js,jsx,ts}]
indent_size = 2

# Trim trailing whitespace
trim_trailing_whitespace = true

# Max line length
max_line_length = 150
51 changes: 51 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Coverage directory used by tools like istanbul
coverage
*.lcov
.nyc_output

# Dependency directories
node_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# dotenv environment variables file
.env
.env.test

# Build output
dist/

# Turbo
.turbo

# This repository accepts only pnpm — lockfiles from other package managers
# should never be committed
package-lock.json
yarn.lock

# Test reports
reports
1 change: 1 addition & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pnpm exec commitlint --edit ${1}
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pnpm exec pretty-quick --staged
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
engine-strict=true
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v24
8 changes: 8 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
**/dist
node_modules
coverage
reports
pnpm-lock.yaml
CHANGELOG.md
*.html
.turbo
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"@map-colonies/prettier-config"
21 changes: 21 additions & 0 deletions .scratch/monorepo-migration/issues/01-scaffold-monorepo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 01: Scaffold the monorepo

**What to build:** A clone of this repository installs and verifies itself with one command, before any product code exists. Clean clone, one install, then a single entry point that lints, formats, type-checks, builds and tests the whole workspace — all green, because there is nothing in it yet to check. One Node version is declared and nothing in the repository contradicts it. A malformed commit message is rejected locally.

This is the seam every later ticket hangs off: every check in this stack is a turbo task reached through this one entry point, locally and in continuous integration alike.

**Blocked by:** None (can start immediately).

**Branch:** `migration/01-scaffold`, layer 1 of 13, based on `master`, the trunk. Stack conventions: `../stack.md`.

**Status:** ready-for-agent

- [ ] Install succeeds on a clean clone, and the repository accepts only pnpm as its package manager.
- [ ] The task vocabulary the rest of the stack depends on exists and is a no-op: build, container build, lint, lint fix, format, type-check, test, e2e, specification lint, package validation, unused-code detection.
- [ ] Task names are the ones carried over from the source repositories, plus type-check and e2e, which the reference monorepo lacks.
- [ ] Turbo drives every task, and every task is invoked through the single root entry point rather than beside it.
- [ ] Exactly one Node version is declared for the repository and the engine requirement agrees with it. The reference monorepo contradicts itself here — do not inherit that.
- [ ] Commit tooling rejects a message with no conventional type; formatting and editor configuration apply repository-wide.
- [ ] Specification-linting configuration sits at the root, ready for the specification package to point at.
- [ ] No dependency catalog is introduced. A catalog holds one version per entry and the workspaces currently disagree on several; it arrives with the version-alignment work so that adding an entry and aligning a version are the same change.
- [ ] The work sits on `migration/01-scaffold` as layer 1 of the stack, its pull request is based on `master`, the trunk, and that pull request's own diff contains nothing from the layers below it.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 02: Move the three projects in as workspaces

**What to build:** All three Jobnik projects live in this repository and are verified from here. The manager is an application; the SDK is a package; the e2e suite is its own top-level workspace and deliberately **not** an application, so that "application" can mean "deployed service" without exception — the container build matrix relies on that.

Package names are unchanged from the source repositories. This is load-bearing rather than cosmetic: the build matrix later derives the image name from the manager's package name, so keeping the name keeps the image name for free.

Dependency versions are not touched. The workspaces disagree on several today; aligning them is separate work, and keeping it out is what makes this stack reviewable.

**Precondition (human):** the three source repositories are frozen before anything is imported, and the imported commit of each is recorded in this ticket. The e2e suite is the fastest-moving of the three, which is why the freeze must precede the move rather than follow it.

**Blocked by:** 01 (scaffold the monorepo).

**Branch:** `migration/02-move-workspaces`, layer 2 of 13, based on `migration/01-scaffold`. Stack conventions: `../stack.md`.

**Status:** ready-for-agent

- [ ] The three source repositories are frozen, and the commit imported from each is recorded in this ticket.
- [ ] Manager, SDK and e2e suite each install from this repository and run their own lint, type-check and tests through the single entry point.
- [ ] One lockfile for the whole repository; no per-project lockfiles remain.
- [ ] Package names are identical to the source repositories.
- [ ] The e2e suite resolves the SDK through the workspace. Its published-version pin is gone — it was a major behind what the SDK actually ships, so the suite has been testing stale code.
- [ ] The manager's integration tests provision their own database exactly as they do now, unchanged. Their compose file stays inside the manager's workspace and there is **no** compose file at the repository root: the container tool's upward search from the manager's test directory is what makes those tests work, and a file at the root would capture it.
- [ ] No dependency version changed as part of the move.
- [ ] The e2e workspace is not an application.
- [ ] The work sits on `migration/02-move-workspaces` as layer 2 of the stack, its pull request is based on `migration/01-scaffold`, and that pull request's own diff contains nothing from the layers below it.
20 changes: 20 additions & 0 deletions .scratch/monorepo-migration/issues/03-pull-request-ci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 03: Verify every pull request through one workflow

**What to build:** A developer opening a pull request gets everything they touched linted, formatted, type-checked and tested, and nothing they did not touch. One workflow, reaching every check through the single entry point, with change detection scoping the work to what actually moved so that verification is fast enough to trust.

The reference monorepo's pull-request workflow is missing type-checking, a formatting check and chart linting that the manager has today. Those are carried over deliberately rather than inherited away.

**Blocked by:** 02 (move the three projects in as workspaces).

**Branch:** `migration/03-pull-request-ci`, layer 3 of 13, based on `migration/02-move-workspaces`. Stack conventions: `../stack.md`.

**Status:** ready-for-agent

- [ ] A pull request touching only the SDK does not run the manager's tasks.
- [ ] Lint, formatting check, type-check, unit tests, integration tests, specification lint and chart lint all run.
- [ ] Type-checking, the formatting check and chart linting are present — all three are absent from the reference monorepo's workflow.
- [ ] The manager's coverage reporting is preserved.
- [ ] Change detection compares against the pull request's base branch, and the build cache is shared between runs.
- [ ] A deliberately introduced lint error, type error and failing test each fail the run.
- [ ] Node is set up from the repository's single declared version, not from a version pinned in the workflow.
- [ ] The work sits on `migration/03-pull-request-ci` as layer 3 of the stack, its pull request is based on `migration/02-move-workspaces`, and that pull request's own diff contains nothing from the layers below it.
27 changes: 27 additions & 0 deletions .scratch/monorepo-migration/issues/04-specification-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 04: Make the specification exist exactly once

**What to build:** The OpenAPI specification exists in one place, and a half-applied change to it cannot reach the default branch.

A single private, unscoped workspace package owns the specification, its versioned build, its lint task and its generated type declaration, shaped as a real importable module with explicit exports.

The manager takes it as a runtime dependency, imports its request and response types from it, and resolves the specification file through the module system rather than through a copied asset. The manager's own generated type declaration and generation script are deleted.

The SDK takes it as a development-time dependency only, because the SDK is published and this package is not. The SDK keeps its own generation script: that script rewrites the job, stage and task identifier schemas into branded types, so its output is SDK-specific and cannot be shared with the manager. Its generated output stays committed. The SDK continues to copy the specification into its build output, because it parses the specification at runtime and therefore ships it.

A continuous-integration job reruns both generators and fails if anything differs from what is committed. This is the one genuinely new test in the migration.

**Blocked by:** 03 (verify every pull request through one workflow).

**Branch:** `migration/04-openapi-package`, layer 4 of 13, based on `migration/03-pull-request-ci`. Stack conventions: `../stack.md`.

**Status:** ready-for-agent

- [ ] Exactly one copy of the specification exists in the repository. Before this ticket there were two, byte-identical at 2732 lines, hand-maintained across two repositories.
- [ ] The manager serves and validates against the specification resolved through the package, not through a copied file, and its own generated declaration and generation script are gone.
- [ ] The SDK's build output still contains the specification file, and the SDK still parses it at runtime.
- [ ] Job, stage and task identifiers are still branded in the SDK's generated types, so they stay impossible to confuse.
- [ ] Editing a schema without regenerating fails continuous integration; regenerating makes it pass.
- [ ] Specification linting runs from the package.
- [ ] The specification, the manager and the SDK can be changed in a single pull request and reviewed as one unit.
- [ ] The manager's and the SDK's existing suites pass unchanged.
- [ ] The work sits on `migration/04-openapi-package` as layer 4 of the stack, its pull request is based on `migration/03-pull-request-ci`, and that pull request's own diff contains nothing from the layers below it.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# 05: Build the manager image from a pruned workspace

**What to build:** The manager's container image is built from a pruned workspace with a cached dependency store, so builds are faster and the image contains only what the service needs. The deployed image behaves exactly as it does today, including running database migrations.

Follow the reference monorepo's backend image, minus its policy-engine download: prune the workspace to the manager, install from a cached store, build, then deploy production dependencies only.

Every database-tooling quirk is preserved verbatim, including the step whose only apparent purpose is to pull the migration command-line tool into the runtime image. That is not cosmetic — the e2e composition runs migrations through it. The existing runtime base image is kept, because the generated database client already targets its libc.

The image is the artifact, so the test is running it.

**Blocked by:** 04 (make the specification exist exactly once).

**Branch:** `migration/05-container-image`, layer 5 of 13, based on `migration/04-openapi-package`. Stack conventions: `../stack.md`.

**Status:** ready-for-agent

- [ ] The image builds from a clean checkout of the monorepo, installing only what the manager needs.
- [ ] The migration command runs successfully inside the built image against a throwaway database.
- [ ] The container starts and reports liveness, with the same entrypoint and memory settings as today.
- [ ] Only production dependencies are present in the runtime layer.
- [ ] The runtime base image is unchanged, and the migration command-line tool is present in it.
- [ ] The specification the manager resolves through the module system is present in the image.
- [ ] The manager declares which image definition builds it, so a build matrix can find it without anything hardcoded.
- [ ] Rebuilding with no source change reuses the cached dependency store.
- [ ] The work sits on `migration/05-container-image` as layer 5 of the stack, its pull request is based on `migration/04-openapi-package`, and that pull request's own diff contains nothing from the layers below it.
24 changes: 24 additions & 0 deletions .scratch/monorepo-migration/issues/06-e2e-suite-branch-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# 06: Make the e2e suite a gate on unreleased code

**What to build:** A developer learns about a broken interaction before release rather than after. The suite runs against their branch: it builds the manager image from the checkout using the same definition that ships to production, and it exercises the SDK resolved from the workspace, so it can never silently test a stale published version again.

The cross-repository composite action is replaced by a local one. All the machinery that exists purely because the repositories were separate goes away: no checking out other repositories into subdirectories, no packing the SDK, no resolving versions from outside the checkout. Note that the old action checks out the other two repositories at their default branch (or a passed commit) — there is no release-tag resolution to remove; the staleness came from the suite's published-version dependency pin, which ticket 02 replaced.

Both composition services that build the manager — the service itself and the migration runner — switch to the pruned build.

The composition file stays inside the e2e workspace and must never move to the repository root: the manager's integration tests start a database by asking the container tool to resolve a composition file from a directory that has none, relying on the upward search finding the manager's own. A file at the root would capture that search.

**Blocked by:** 05 (build the manager image from a pruned workspace).

**Branch:** `migration/06-e2e-gate`, layer 6 of 13, based on `migration/05-container-image`. Stack conventions: `../stack.md`.

**Status:** ready-for-agent

- [ ] The suite runs in continuous integration against an image built from the pull request's checkout, by the same definition that ships to production.
- [ ] The SDK under test is the workspace SDK. No published version is installed and nothing is packed.
- [ ] No other repository is checked out.
- [ ] Both manager-building composition services use the pruned build.
- [ ] The suite is triggered by a change to any workspace it depends on, and is not scheduled.
- [ ] The composition file is still inside the e2e workspace, and the manager's integration tests are unaffected.
- [ ] The suite is reachable as a task through the single entry point, locally as well as in continuous integration.
- [ ] The work sits on `migration/06-e2e-gate` as layer 6 of the stack, its pull request is based on `migration/05-container-image`, and that pull request's own diff contains nothing from the layers below it.
26 changes: 26 additions & 0 deletions .scratch/monorepo-migration/issues/07-umbrella-chart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# 07: Publish the chart under the product name

**What to build:** The published chart becomes the product name, so it has somewhere to grow when a second deployable component arrives, with the manager as a conditioned subchart. No Kubernetes object changes its name, so the first deployment after the migration is an ordinary rolling update.

The subchart keeps its current name. This is the critical detail: the manager's deployment selector is built from the chart name, and deployment selectors are immutable, so renaming the subchart would delete and recreate the running workload. Renaming only the umbrella leaves every selector untouched.

The database secret stays in the subchart until a second component needs it.

The values live in the deployment-configuration repository, so a matching change there lands with this ticket and with ticket 09: the chart reference and its pinned versions, and the manager's values nested under a parent key to match the umbrella, including the name override that keeps object names stable. The image reference is unchanged.

Verification is a one-time migration check — a recorded comparison against the pre-migration render — not a permanent job. The recurring protection is chart linting, carried over in ticket 03.

**Blocked by:** 02 (move the three projects in as workspaces).

**Branch:** `migration/07-umbrella-chart`, layer 7 of 13, based on `migration/06-e2e-gate`. Stack conventions: `../stack.md`.

**Status:** ready-for-agent

- [ ] Chart dependencies build and the chart lints.
- [ ] The rendered umbrella — manager values nested, name override applied — diffed against the pre-migration render shows no difference beyond chart metadata, and the comparison is recorded in this ticket.
- [ ] The manager's deployment selector is byte-identical to the pre-migration one, and no object changes name.
- [ ] The subchart keeps the manager's current name; only the umbrella is named for the product.
- [ ] The database secret still lives in the subchart.
- [ ] The manager is a conditioned subchart, so it can be disabled independently once a second component exists.
- [ ] The corresponding deployment-configuration change is prepared and referenced from this ticket, ready to land alongside ticket 09.
- [ ] The work sits on `migration/07-umbrella-chart` as layer 7 of the stack, its pull request is based on `migration/06-e2e-gate`, and that pull request's own diff contains nothing from the layers below it.
23 changes: 23 additions & 0 deletions .scratch/monorepo-migration/issues/08-release-configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# 08: Configure two version lines

**What to build:** A deployment is described by a single number, and the SDK is not dragged along by it.

The manager, the specification package and the repository root move together under one version line. The SDK keeps its own, because it is past 1.0 with external consumers: sharing a line would ship it releases containing no SDK changes and force its consumers through a major for a change to a service they do not use.

Neither source repository used component-scoped tags, so a fresh repository has no tags to read. Every component's version is seeded by hand at its current value, so the new repository does not restart versioning from zero.

Changelog sections reflect the commit types the team already uses, so release notes stay readable. Commit scopes are validated against the actual workspace names, with the release and dependency scopes allowed explicitly, so changelog entries land under the right component.

**Blocked by:** 07 (publish the chart under the product name).

**Branch:** `migration/08-release-config`, layer 8 of 13, based on `migration/07-umbrella-chart`. Stack conventions: `../stack.md`.

**Status:** ready-for-agent

- [ ] The manager, the specification package and the repository root share one version; the SDK has its own line and its own tag.
- [ ] Every component's version is seeded at its current value; nothing starts at zero.
- [ ] A release run bumps the chart versions and the specification's declared version along with the code.
- [ ] A commit whose scope is not a workspace name is rejected; workspace scopes plus the release and dependency scopes are accepted.
- [ ] Changelog sections match the commit types the team uses, with the noise-only types hidden.
- [ ] A dry run produces one release pull request for the shared line and a separate one for the SDK.
- [ ] The work sits on `migration/08-release-config` as layer 8 of the stack, its pull request is based on `migration/07-umbrella-chart`, and that pull request's own diff contains nothing from the layers below it.
Loading