Skip to content

Commit 7dc693d

Browse files
vpetreskiclaude
andcommitted
setup: full scaffold post-green-light (chapters, apps, specs, ADRs, CI)
Phase 0.5 complete. Ready to open Chapter 1. Chapter notes (docs/chapters/01-*.md through 18-*.md): - 18 chapters, teaching-ladder structure (intuition → formal → tiny example → Python → Kotlin via cpsat-kt → MiniZinc where it helps → exercises → self-check → what-unlocks). - Dual-language parity maintained. Ch. 2 is the only chapter that uses raw com.google.ortools.sat.* in Kotlin (to motivate cpsat-kt). Python (apps/py-cp-sat/ + apps/py-api/): - uv workspace, Python 3.12+, OR-Tools 9.15. - Chapter 2 fully runs (solves 3x+2y=12, x+y≤5, maximize x+y). - Chapters 4–13 have runnable stubs raising NotImplementedError. - FastAPI skeleton with /health, /version, stub /solve; 4/4 tests pass. Kotlin (apps/kt-cp-sat/ + apps/kt-api/): - Gradle composite build, includes cpsat-kt via includeBuild. - Chapter 2 raw-Java demo runs (x=2, y=3, OPTIMAL). - Chapters 4–13 compile as stubs. - Ktor 3 skeleton with /health, /version. Web (apps/web/): - Vite 8 + React 19 + React Router v7 framework mode + TypeScript 5 + Tailwind 4 + shadcn/ui + TanStack Query 5. - Backend toggle (Python / Kotlin) with localStorage. - Build, typecheck, lint, test all green. Shared contract (apps/shared/): - openapi.yaml (OpenAPI 3.1) — /health, /version, /solve, /solution/{id}, /solutions/{id}/stream (SSE). - JSON schemas validated against 2020-12. Specs (specs/nsp-app/): - 10-file skeleton + README + lock procedure. - Fills in Chapter 14; unlocked until then. ADRs (docs/adr/): - 0000 template (Michael Nygard). - 0001 cpsat-kt as first-class artifact. - 0002 stack lock-in v0.2 (JDK 25 / Kotlin 2.1+ / Gradle 9 / Vite+RR7). - 0003 spec-driven NSP app. - 0004 dual-language parity. Data (data/nsp/): - schema.json (JSON Schema 2020-12). - toy-01 (3×7×2) and toy-02 (5×14×3) feasible instances. Tools (tools/): - validate-schedule.py (exits 0 valid / 1 violations / 2 missing deps). - setup-all.sh (auto-runs every setup-*.sh in order). CI (.github/workflows/): - ci.yml — Python (macOS-14), Kotlin (macOS-14, JDK 25 temurin), Web (Ubuntu-24.04, Node 22) parallel jobs. - release.yml — stub for future Maven Central publish on cpsat-kt-v* tags. Benchmarks (benchmarks/): - README with methodology placeholder; results/ + plots/ scaffolded. Alt solvers (apps/alt-solver/): - Timefold and Choco stubs for Phase 8 port. Status docs updated: - README.md, docs/overview.md, docs/plan.md reflect scaffold-complete state. Chapter 1 is open. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 25dfb94 commit 7dc693d

198 files changed

Lines changed: 33550 additions & 14 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
python:
9+
name: Python (py-cp-sat + py-api)
10+
runs-on: macos-14
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v4
14+
15+
- name: Set up uv
16+
uses: astral-sh/setup-uv@v3
17+
with:
18+
enable-cache: true
19+
20+
- name: py-cp-sat — sync + lint + test
21+
working-directory: apps/py-cp-sat
22+
run: |
23+
uv sync
24+
uv run ruff check .
25+
uv run pytest
26+
27+
- name: py-api — sync + test
28+
working-directory: apps/py-api
29+
run: |
30+
uv sync
31+
uv run pytest
32+
33+
kotlin:
34+
name: Kotlin (cpsat-kt + kt-cp-sat + kt-api)
35+
runs-on: macos-14
36+
steps:
37+
- name: Checkout
38+
uses: actions/checkout@v4
39+
40+
- name: Set up JDK 25
41+
uses: actions/setup-java@v4
42+
with:
43+
distribution: temurin
44+
java-version: "25"
45+
# NOTE: if temurin JDK 25 is not yet published on setup-java at the
46+
# time this workflow runs, bump distribution to "zulu" (Azul ships
47+
# JDK 25 early) or temporarily fall back to java-version "21" and
48+
# add a TODO to revisit.
49+
50+
- name: Set up Gradle
51+
uses: gradle/gradle-build-action@v3
52+
53+
- name: cpsat-kt — build + test
54+
working-directory: libs/cpsat-kt
55+
run: ./gradlew build test
56+
57+
- name: kt-cp-sat — build
58+
working-directory: apps/kt-cp-sat
59+
run: ./gradlew build
60+
61+
- name: kt-api — build
62+
working-directory: apps/kt-api
63+
run: ./gradlew build
64+
65+
web:
66+
name: Web (apps/web + apps/shared)
67+
runs-on: ubuntu-24.04
68+
steps:
69+
- name: Checkout
70+
uses: actions/checkout@v4
71+
72+
- name: Set up Node
73+
uses: actions/setup-node@v4
74+
with:
75+
node-version: "22"
76+
cache: npm
77+
cache-dependency-path: |
78+
apps/web/package-lock.json
79+
apps/shared/package-lock.json
80+
81+
- name: apps/web — install + typecheck + build
82+
working-directory: apps/web
83+
run: |
84+
npm ci
85+
npm run typecheck
86+
npm run build
87+
88+
- name: apps/shared — install + validate OpenAPI
89+
working-directory: apps/shared
90+
run: |
91+
npm ci
92+
npm run validate:openapi

.github/workflows/release.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Release cpsat-kt
2+
3+
# STUB — wired up later when we publish cpsat-kt to Maven Central.
4+
# Triggers only on tags like `cpsat-kt-v0.1.0`.
5+
6+
on:
7+
push:
8+
tags:
9+
- "cpsat-kt-v*"
10+
11+
jobs:
12+
publish:
13+
name: Publish cpsat-kt to Maven Central
14+
runs-on: macos-14
15+
steps:
16+
- name: Checkout
17+
uses: actions/checkout@v4
18+
19+
- name: Set up JDK 25
20+
uses: actions/setup-java@v4
21+
with:
22+
distribution: temurin
23+
java-version: "25"
24+
25+
- name: Set up Gradle
26+
uses: gradle/gradle-build-action@v3
27+
28+
# TODO(cpsat-kt publish): wire up the signing + Maven Central flow when
29+
# we actually cut v0.1.0. Needs:
30+
# - GPG key + passphrase in GH Secrets
31+
# (SIGNING_KEY, SIGNING_PASSWORD)
32+
# - Sonatype / Central Portal credentials in GH Secrets
33+
# (OSSRH_USERNAME, OSSRH_PASSWORD)
34+
# - `./gradlew :libs:cpsat-kt:publishToMavenCentral --no-configuration-cache`
35+
# (or the equivalent under the Gradle Nexus Publish plugin)
36+
# - Release notes auto-generated from the tag message.
37+
- name: Placeholder publish step
38+
working-directory: libs/cpsat-kt
39+
run: |
40+
echo "TODO: run ./gradlew publishToMavenCentral once credentials are wired up"
41+
echo "Tag: ${GITHUB_REF_NAME}"

README.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ Follow these links **in order** on your first pass:
3030

3131
Then, **chapter by chapter from Chapter 1**:
3232

33-
- Open `docs/chapters/01-what-is-cp.md` *(created post-green-light)*
34-
- Read the chapter, do the exercises in `apps/py-cp-sat/ch01-*/` and `apps/kt-cp-sat/ch01-*/`
33+
- Open [`docs/chapters/01-what-is-cp.md`](docs/chapters/01-what-is-cp.md)
34+
- Read the chapter, do the exercises in `apps/py-cp-sat/chNN-*/` and `apps/kt-cp-sat/chNN-*/`
3535
- Ask Claude when stuck; Claude updates docs/plan as you progress
3636
- Mark complete in `docs/plan.md`; move to Chapter 2
3737

@@ -168,12 +168,19 @@ Full working agreement: [CLAUDE.md](CLAUDE.md).
168168
- GitHub repo private under `vpetreski/cp-deep-dive`.
169169
- Memory system primed with user/feedback/project context.
170170

171-
**Phase 0.5 — Plan lock-in: ⏳ In progress**
171+
**Phase 0.5 — Full scaffold: ✅ Complete**
172172

173-
- [plan.md](docs/plan.md) v0.2 drafted with Vanja's stack choices folded in.
174-
- Awaiting Vanja's green-light to scaffold the repo (create `libs/cpsat-kt/` skeleton, `specs/nsp-app/`, per-chapter starter apps, CI, etc.).
173+
- `libs/cpsat-kt/` v0.1.0 library with 41/41 tests passing (`./gradlew test` green).
174+
- All 18 chapter MDs drafted in `docs/chapters/` with intuition → formal → Python → Kotlin → MiniZinc → exercises structure.
175+
- Python uv workspace at `apps/py-cp-sat/` (ch02 fully working, ch04–ch13 stubs) + FastAPI skeleton at `apps/py-api/`.
176+
- Kotlin composite build at `apps/kt-cp-sat/` (ch02 raw-Java demo runs) + Ktor 3 skeleton at `apps/kt-api/`.
177+
- Web frontend at `apps/web/` (Vite 8 + React 19 + RR7 framework mode + Tailwind 4 + shadcn/ui) — build/test/lint green.
178+
- `apps/shared/openapi.yaml` (OpenAPI 3.1) + 4 JSON schemas validated against 2020-12.
179+
- `specs/nsp-app/` 10-file skeleton (unlocked, filled in Chapter 14).
180+
- 4 ADRs (`docs/adr/`), GitHub Actions CI (Python + Kotlin + Web parallel jobs), `benchmarks/` + `data/nsp/` (toy-01, toy-02 instances + schema).
181+
- `alt-solver/` Timefold + Choco stubs for Phase 8.
175182

176-
**Phase 1+Learning chapters: 🔒 Locked until green-light**
183+
**Phase 1 — Chapter 1 ready to open:** [`docs/chapters/01-what-is-cp.md`](docs/chapters/01-what-is-cp.md)
177184

178185
---
179186

apps/alt-solver/choco/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Choco Solver — NSP stub
2+
3+
Placeholder for a future port of the Nurse Scheduling Problem to
4+
[Choco Solver](https://choco-solver.org/), an academic / research-oriented
5+
Java CP solver with excellent global constraint coverage.
6+
7+
## Status
8+
9+
Not implemented. Tracked in `docs/plan.md`; will be scheduled after the
10+
CP-SAT NSP chapters (11-13) are complete.
11+
12+
## What to expect here
13+
14+
- Kotlin module using `org.choco-solver:choco-solver`
15+
- Same instance format as `apps/py-cp-sat/` and `apps/kt-cp-sat/ch11..13`
16+
- Side-by-side DSL comparison with `cpsat-kt`
17+
18+
## Why Choco?
19+
20+
- Different internals: classical AC-5 / domain-store propagation (as
21+
opposed to CP-SAT's lazy clause generation + learning).
22+
- Rich library of global constraints (especially scheduling: `diffn`,
23+
`cumulative`, `geost`).
24+
- Good for "explain why this is infeasible" workflows via its tracer.
25+
26+
See `docs/knowledge/ecosystem/` in the parent repo for the full
27+
comparative notes (to be written).

apps/alt-solver/timefold/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Timefold Solver — NSP stub
2+
3+
Placeholder for a future port of the Nurse Scheduling Problem to
4+
[Timefold Solver](https://timefold.ai/) (the OptaPlanner fork), to compare
5+
the constraint-streams / local-search approach against CP-SAT's
6+
constraint-propagation approach.
7+
8+
## Status
9+
10+
Not implemented. Tracked in `docs/plan.md`; will be scheduled after the
11+
CP-SAT NSP chapters (11-13) are complete.
12+
13+
## What to expect here
14+
15+
- Kotlin module using `timefold-solver-core` + `timefold-solver-constreams`
16+
- Same instance format as `apps/py-cp-sat/` and `apps/kt-cp-sat/ch11..13`
17+
- Benchmark harness comparing wall-clock + solution quality vs. CP-SAT
18+
19+
## Why Timefold?
20+
21+
- Different paradigm: local search with construction heuristics, not
22+
complete search. Good for huge instances where CP-SAT runs out of time.
23+
- Java/Kotlin-first API, generates explainable "constraint violation" output.
24+
- Widely used in enterprise scheduling.
25+
26+
See `docs/knowledge/ecosystem/` in the parent repo for the full
27+
comparative notes (to be written).

apps/kt-api/Dockerfile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Multi-stage build for the Ktor NSP backend.
2+
# Stage 1: compile to a distribution tar.
3+
FROM eclipse-temurin:25-jdk AS build
4+
WORKDIR /app
5+
COPY . .
6+
RUN ./gradlew --no-daemon installDist
7+
8+
# Stage 2: minimal runtime.
9+
FROM eclipse-temurin:25-jre
10+
WORKDIR /opt/kt-api
11+
COPY --from=build /app/build/install/kt-api /opt/kt-api
12+
EXPOSE 8080
13+
ENV JAVA_OPTS="-XX:MaxRAMPercentage=75.0 --enable-native-access=ALL-UNNAMED"
14+
ENTRYPOINT ["/opt/kt-api/bin/kt-api"]

apps/kt-api/README.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# kt-api — Ktor 3 backend for the NSP explorer
2+
3+
Skeleton Ktor 3 app that will eventually host the Nurse Scheduling Problem
4+
(NSP) endpoints, backed by the `cpsat-kt` library.
5+
6+
## Endpoints today
7+
8+
| Method | Path | Response |
9+
|--------|-----------|----------|
10+
| GET | /health | `{"status":"ok"}` |
11+
| GET | /version | `{"name":"kt-api","version":"0.1.0","ortools":"9.15.6755"}` |
12+
13+
## Running locally
14+
15+
```bash
16+
./gradlew run
17+
```
18+
19+
The server binds to `0.0.0.0:8080` by default. Override with the `PORT`
20+
environment variable.
21+
22+
```bash
23+
curl http://localhost:8080/health
24+
# {"status":"ok"}
25+
```
26+
27+
## Building a Docker image
28+
29+
```bash
30+
docker build -t kt-api:dev -f Dockerfile ..
31+
```
32+
33+
(The build context is the repo root because the Dockerfile copies the whole
34+
app directory; adjust to your CI.)
35+
36+
## Configuration
37+
38+
Ktor reads `src/main/resources/application.conf` (HOCON). Environment
39+
variables override individual fields — e.g. `PORT=9090 ./gradlew run`.
40+
41+
## Requirements
42+
43+
- JDK 25+
44+
- Gradle 9.x (use bundled wrapper)
45+
- `libs/cpsat-kt` — included via composite build from `../../libs/cpsat-kt`.

apps/kt-api/build.gradle.kts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
plugins {
2+
alias(libs.plugins.kotlin.jvm)
3+
alias(libs.plugins.kotlin.serialization)
4+
application
5+
}
6+
7+
group = "io.vanja"
8+
version = "0.1.0"
9+
10+
java {
11+
toolchain {
12+
languageVersion = JavaLanguageVersion.of(25)
13+
}
14+
}
15+
16+
kotlin {
17+
jvmToolchain(25)
18+
compilerOptions {
19+
jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_25)
20+
}
21+
}
22+
23+
dependencies {
24+
implementation(libs.ktor.server.core)
25+
implementation(libs.ktor.server.netty)
26+
implementation(libs.ktor.server.content.negotiation)
27+
implementation(libs.ktor.serialization.kotlinx.json)
28+
implementation(libs.ktor.server.status.pages)
29+
implementation(libs.ktor.server.call.logging)
30+
implementation(libs.logback.classic)
31+
implementation(libs.cpsat.kt)
32+
}
33+
34+
application {
35+
mainClass.set("io.vanja.nspapi.AppKt")
36+
}

apps/kt-api/gradle.properties

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
kotlin.code.style=official
2+
org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
3+
org.gradle.parallel=true
4+
org.gradle.caching=true
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[versions]
2+
kotlin = "2.3.20"
3+
ktor = "3.0.3"
4+
logback = "1.5.12"
5+
cpsat-kt = "0.1.0"
6+
7+
[libraries]
8+
ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor" }
9+
ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor" }
10+
ktor-server-content-negotiation = { module = "io.ktor:ktor-server-content-negotiation", version.ref = "ktor" }
11+
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
12+
ktor-server-status-pages = { module = "io.ktor:ktor-server-status-pages", version.ref = "ktor" }
13+
ktor-server-call-logging = { module = "io.ktor:ktor-server-call-logging", version.ref = "ktor" }
14+
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" }
15+
cpsat-kt = { module = "io.vanja:cpsat-kt", version.ref = "cpsat-kt" }
16+
17+
[plugins]
18+
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
19+
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }

0 commit comments

Comments
 (0)