diff --git a/.github/workflows/ci-static-checks.yml b/.github/workflows/ci-static-checks.yml index 7f9d1ef..299a51f 100644 --- a/.github/workflows/ci-static-checks.yml +++ b/.github/workflows/ci-static-checks.yml @@ -13,13 +13,6 @@ concurrency: cancel-in-progress: true jobs: - actionlint: - name: actionlint - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: rhysd/actionlint@v1.7.12 - prek: name: prek runs-on: ubuntu-latest diff --git a/.github/workflows/ci-tests.yml b/.github/workflows/ci-tests.yml index 5384799..b5d8d50 100644 --- a/.github/workflows/ci-tests.yml +++ b/.github/workflows/ci-tests.yml @@ -39,91 +39,35 @@ jobs: steps: - uses: actions/checkout@v6 - - uses: actions/checkout@v6 - with: - repository: spore-lang/spore - ref: d96212aa5a4cfe24759c21d8690642adfd357018 - path: _spore + - uses: astral-sh/setup-uv@v6 - uses: dtolnay/rust-toolchain@stable with: - toolchain: stable - - - uses: Swatinem/rust-cache@v2 - with: - workspaces: _spore + toolchain: 1.95 - - name: Build spore compiler - run: cargo build --release --manifest-path _spore/Cargo.toml --bin spore + - name: Install spore CLI + run: uv tool install spore-lang==0.0.3 - name: Format Spore files - run: | - # `src/host.sp` is a legacy compatibility shim; validate package-backed behavior - # via the example and platform modules rather than standalone file-mode checks. - # Standalone file examples - find examples -maxdepth 1 -name '*.sp' -type f | sort | xargs _spore/target/release/spore format - # Project-mode examples (format their src files) - if [ -d examples/hello-app/src ]; then - find examples/hello-app/src -name '*.sp' -type f | sort | xargs _spore/target/release/spore format - fi - # Platform API modules - find src/basic_cli -name '*.sp' -type f | sort | xargs _spore/target/release/spore format - # Check tests if they exist - if [ -d tests ]; then - find tests -name '*.sp' -type f | sort | xargs _spore/target/release/spore format - fi - git diff --exit-code + run: spore format --check - name: Check Spore files run: | - # `src/host.sp` is a legacy compatibility shim; validate package-backed behavior - # via the example and platform modules rather than standalone file-mode checks. - # Standalone file examples - find examples -maxdepth 1 -name '*.sp' -type f | sort | xargs -n 1 _spore/target/release/spore check - # Platform API modules - find src/basic_cli -name '*.sp' -type f | sort | xargs -n 1 _spore/target/release/spore check - # Project-mode examples: validate with spore check - if [ -d examples/hello-app ]; then - echo "✓ Checking project-mode example examples/hello-app/src/main.sp" - _spore/target/release/spore check examples/hello-app/src/main.sp - fi - # Check tests if they exist - if [ -d tests ]; then - find tests -name '*.sp' -type f | sort | xargs -n 1 _spore/target/release/spore check - fi + find src/basic_cli -name '*.sp' -type f | sort | xargs -n 1 spore check + spore check examples/hello-app/src/main.sp - name: Build Spore files run: | - # `src/host.sp` is a legacy compatibility shim; validate package-backed behavior - # via the example and platform modules rather than standalone file-mode builds. - # Standalone file examples - find examples -maxdepth 1 -name '*.sp' -type f | sort | xargs -n 1 _spore/target/release/spore build - # Platform API modules - find src/basic_cli -name '*.sp' -type f | sort | xargs -n 1 _spore/target/release/spore build - # Project-mode examples: validate with spore build - if [ -d examples/hello-app ]; then - echo "✓ Building project-mode example examples/hello-app/src/main.sp" - _spore/target/release/spore build examples/hello-app/src/main.sp - fi - # Check tests if they exist - if [ -d tests ]; then - find tests -name '*.sp' -type f | sort | xargs -n 1 _spore/target/release/spore build - fi - - - name: Run standalone hello example - run: _spore/target/release/spore run examples/hello.sp + find src/basic_cli -name '*.sp' -type f | sort | xargs -n 1 spore build + spore build examples/hello-app/src/main.sp - name: Run project-mode hello-app example run: | - if [ -d examples/hello-app ]; then - echo "✓ Running project-mode example examples/hello-app/src/main.sp" - _spore/target/release/spore run examples/hello-app/src/main.sp - fi + ( + cd examples/hello-app + echo "✓ Running project-mode example (default entry)" + spore run src/main.sp + ) - name: Run Spore spec tests - run: | - if [ ! -d tests ]; then - echo "No Spore spec tests yet; skipping spore test." - exit 0 - fi - find tests -name '*.sp' -type f | sort | xargs -r -n 1 _spore/target/release/spore test + run: spore test diff --git a/.learnings/README.md b/.learnings/README.md new file mode 100644 index 0000000..897322f --- /dev/null +++ b/.learnings/README.md @@ -0,0 +1,4 @@ +# Project Learnings + +## Gotchas +- [Spore CI should target project-mode files explicitly](gotchas/spore-ci-project-mode.md) — spore, ci, project-mode diff --git a/.learnings/gotchas/spore-ci-project-mode.md b/.learnings/gotchas/spore-ci-project-mode.md new file mode 100644 index 0000000..18d89d6 --- /dev/null +++ b/.learnings/gotchas/spore-ci-project-mode.md @@ -0,0 +1,27 @@ +--- +title: "Spore CI should target project-mode files explicitly" +category: gotchas +tags: [spore, ci, project-mode, basic-cli] +created: 2026-04-28 +context: "basic-cli CI needed to validate a package-backed example with the shipped compiler" +--- + +## Problem + +The current `spore` CLI validates package-backed examples reliably when given the explicit entry file, but bare directory invocations and standalone file assumptions can fail depending on current compiler surface and imports. + +## Solution + +Use explicit file targets for project-mode checks: + +```bash +spore check examples/hello-app/src/main.sp +spore build examples/hello-app/src/main.sp +cd examples/hello-app && spore run src/main.sp +``` + +## Key points + +- Keep CI focused on the canonical package-backed entry file. +- Treat standalone `.sp` examples as separate from project-mode validation. +- Re-run `spore format --check`, `spore check`, and `spore build` against explicit files when compiler behavior is in flux. diff --git a/README.md b/README.md index 81f4655..5aefa1a 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # basic-cli -Basic CLI platform for [Spore](https://github.com/spore-lang/spore) — provides file I/O, process execution, environment variables, and standard I/O capabilities. +Basic CLI platform for [Spore](https://github.com/spore-lang/spore) — provides file I/O, process execution, environment variables, and standard I/O effects. ## Overview -In Spore, a **platform** bridges the pure, capability-tracked language with real-world side effects. `basic-cli` is the standard platform for command-line applications. +In Spore, a **platform** bridges the pure, effect-tracked language with real-world side effects. `basic-cli` is the standard platform for command-line applications. ``` -Your Spore App (pure, capability-checked) +Your Spore App (pure, effect-checked) ↕ (effect dispatch) basic-cli Platform API (.sp modules) ↕ (foreign fn) @@ -18,7 +18,7 @@ Operating System ## Modules -| Module | Capabilities | Description | +| Module | Effects | Description | |--------|-------------|-------------| | `basic_cli.stdout` | `uses [Console]` | Standard output | | `basic_cli.stdin` | `uses [Console]` | Standard input | @@ -46,9 +46,6 @@ default-entry = "app" [entries.app] path = "main.sp" -[capabilities] -allow = ["Compute"] - [dependencies] basic-cli = { path = "../.." } ``` @@ -100,19 +97,19 @@ basic-cli/ The current Platform contract MVP is intentionally split across two artifacts: -1. `spore.toml` `[platform]` metadata names the contract module, the startup contract symbol, the adapter function, and the handled capabilities. +1. `spore.toml` `[platform]` metadata names the contract module, the startup contract symbol, the adapter function, and the handled effects. 2. `src/platform_contract.sp` owns the startup contract itself: - a hole-backed `main` function carries the authoritative startup signature - `main_for_host` is the Platform-owned adapter that receives the application startup function -Applications targeting `basic-cli` must implement the same startup function name/signature in their entry module. The current compiler already resolves the package's `[platform]` metadata and `src/platform_contract.sp` to validate that startup shape. Runtime support is currently specialized to package-backed `basic-cli`: imported `basic_cli.*` foreign functions route through the built-in basic-cli host profile, while generic `handles` enforcement and startup `spec` stacking are still follow-up work. +Applications targeting `basic-cli` must implement the same startup function name/signature in their entry module. The current compiler already resolves the package's `[platform]` metadata and `src/platform_contract.sp` to validate that startup shape. Runtime support is currently specialized to package-backed `basic-cli`: imported `basic_cli.*` foreign functions route through the built-in basic-cli host profile, while generic handled-effects enforcement and startup `spec` stacking are still follow-up work. `src/host.sp` remains as a compatibility copy of the adapter for older references; current manifest-backed projects use `src/platform_contract.sp`. ## Tutorial Contract - `examples/hello-app/` is the **canonical project-mode example** — it is formatted, checked, built, and run in CI. -- `examples/hello.sp` is a minimal standalone file for quick experiments (also validated in CI). +- `examples/hello.sp` is a minimal standalone file for quick experiments. - `src/basic_cli/` is the API surface for the platform modules themselves. - `src/platform_contract.sp` is the package-owned startup contract surface. - Only add `tests/` when the repo has real Spore-side regression coverage worth running with `spore test`. @@ -125,9 +122,9 @@ If you want to add a new tutorial/example, treat this as the bar: ## Design Philosophy -Following Spore's [SEP-0003 (Effect Capability System)](https://github.com/spore-lang/spore-evolution/blob/main/seps/SEP-0003-effect-capability-system.md): +Following Spore's [SEP-0003 (Effect System)](https://github.com/spore-lang/spore-evolution/blob/main/seps/SEP-0003-effect-system.md): -- **Capability-gated**: Every I/O function declares its required capabilities via `uses [Cap]` +- **Effect-gated**: Every I/O function declares its required effects via `uses [Effect]` - **Cost-annotated**: Platform functions can carry `cost [c, a, i, p]` budgets where meaningful - **Error-typed**: Functions declare error sets via `! ErrorType` - **Pure by default**: The platform boundary is the only place side effects occur @@ -138,7 +135,7 @@ Following Spore's [SEP-0003 (Effect Capability System)](https://github.com/spore The canonical example is the **package-backed project-mode** `examples/hello-app/` application. It already validates and runs with `[project].platform = "basic-cli"`, an in-repo path dependency, and `import basic_cli.stdout`. -The standalone file example (`examples/hello.sp`) stays around for quick experiments. The main remaining platform gaps are generic `handles` enforcement, startup `spec` stacking, and lifting the runtime from its current explicit `basic-cli` host profile to a more general package-backed mechanism. +The standalone file example (`examples/hello.sp`) stays around for quick experiments. The main remaining platform gaps are generic handled-effects enforcement, startup `spec` stacking, and lifting the runtime from its current explicit `basic-cli` host profile to a more general package-backed mechanism. ## License diff --git a/examples/hello-app/spore.toml b/examples/hello-app/spore.toml index e58d081..b8fcc2c 100644 --- a/examples/hello-app/spore.toml +++ b/examples/hello-app/spore.toml @@ -11,8 +11,5 @@ default-entry = "app" [entries.app] path = "main.sp" -[capabilities] -allow = ["Compute"] - [dependencies] basic-cli = { path = "../.." } diff --git a/examples/hello-app/src/main.sp b/examples/hello-app/src/main.sp index cd90a79..c142ca8 100644 --- a/examples/hello-app/src/main.sp +++ b/examples/hello-app/src/main.sp @@ -1,12 +1,12 @@ -/// Canonical package-backed application example for basic-cli. -/// -/// This mirrors the formatted `spore new` scaffold: import the -/// platform module explicitly and call `println` directly from the -/// imported basic-cli surface. -/// Application entry point matching the platform contract. +// Canonical package-backed application example for basic-cli. +// +// This mirrors the formatted `spore new` scaffold: import the +// platform module explicitly and call `println` from the imported +// basic-cli surface. import basic_cli.stdout as stdout +/// Application entry point matching the platform contract. fn main() -> () uses [Console] { - println("Hello from a project-mode Spore application!") + println("Hello from a project-mode Spore application!"); return } diff --git a/prek.toml b/prek.toml index 19c6d5f..57dbfab 100644 --- a/prek.toml +++ b/prek.toml @@ -22,6 +22,13 @@ hooks = [ { id = "tombi-lint" }, ] +[[repos]] +repo = "https://github.com/rhysd/actionlint" +rev = "v1.7.12" +hooks = [ + { id = "actionlint" }, +] + [[repos]] repo = "https://github.com/crate-ci/typos" rev = "v1.33.1" diff --git a/spore.toml b/spore.toml index a234aa6..7cc1508 100644 --- a/spore.toml +++ b/spore.toml @@ -8,9 +8,6 @@ spore-version = ">=0.1.0" contract-module = "platform_contract" startup-contract = "main" adapter-function = "main_for_host" -handles = ["Console", "FileRead", "FileWrite", "Env", "Spawn", "Exit"] - -[capabilities] -allow = ["Compute"] +handled-effects = ["Console", "FileRead", "FileWrite", "Env", "Spawn", "Exit"] [dependencies] diff --git a/src/basic_cli/cmd.sp b/src/basic_cli/cmd.sp index 9ba9744..ded96b4 100644 --- a/src/basic_cli/cmd.sp +++ b/src/basic_cli/cmd.sp @@ -1,6 +1,6 @@ /// basic-cli platform — Process execution /// -/// Run external commands with capability tracking. +/// Run external commands with effect tracking. /// Run a command and capture its stdout as a string. /// Returns the full stdout output on success. diff --git a/src/basic_cli/file.sp b/src/basic_cli/file.sp index cea3eb8..d36a0ac 100644 --- a/src/basic_cli/file.sp +++ b/src/basic_cli/file.sp @@ -1,6 +1,6 @@ /// basic-cli platform — File system operations /// -/// Provides capability-gated file read and write operations. +/// Provides effect-gated file read and write operations. /// Read the entire contents of a file as a string. pub foreign fn file_read(path: Str) -> Str ! IoError uses [FileRead] diff --git a/src/basic_cli/stdout.sp b/src/basic_cli/stdout.sp index 99308a1..0143446 100644 --- a/src/basic_cli/stdout.sp +++ b/src/basic_cli/stdout.sp @@ -1,6 +1,6 @@ /// basic-cli platform — Standard output operations /// -/// Provides capability-gated access to stdout/stderr. +/// Provides effect-gated access to stdout/stderr. /// Print a string to stdout without trailing newline. pub foreign fn print(s: Str) -> () ! IoError uses [Console] diff --git a/src/host.sp b/src/host.sp index 23aed47..f8de924 100644 --- a/src/host.sp +++ b/src/host.sp @@ -2,6 +2,6 @@ /// Manifest-backed projects resolve `main_for_host` from `platform_contract.sp`; /// keep this shim in sync for older references. pub fn main_for_host(app_main: () -> ()) -> () { - app_main() + app_main(); return } diff --git a/src/platform_contract.sp b/src/platform_contract.sp index 7c99ca9..d465e37 100644 --- a/src/platform_contract.sp +++ b/src/platform_contract.sp @@ -2,12 +2,10 @@ /// Applications targeting this Platform must implement the same `main` /// signature in their entry module. Any `spec` items attached here will stack /// with the application's own `main` spec and both must hold. -pub fn main() -> () { - ?platform_startup_contract -} +pub fn main() -> () { ?platform_startup_contract } /// Platform-owned startup adapter. pub fn main_for_host(app_main: () -> ()) -> () { - app_main() + app_main(); return }