Test Spec: Core Lifecycle

What to do with this file

You are a coding agent. Your job is to execute this test spec by running commands in a shell, step by step, from Phase 0 through Phase 8. For each step:

  1. Run the command shown in the code block.
  2. Read the output.
  3. Check every Assert listed for that step.
  4. Record the result: PASS or FAIL with the actual output.
  5. When a step says Capture, save the value into the named variable and use it in subsequent steps.

At the end, produce a summary table showing which phases passed and which failed, with the actual vs. expected values for any failures.

If a prerequisite is not met (binary not found, fixture script missing), stop and report that instead of continuing.

What this test is checking

The day-to-day workflow of thinslice: creating state directories, creating worktrees, binding worktrees to state, starting and stopping processes, swapping state between worktrees, verifying port isolation across simultaneous slices, and cleaning everything up.

This test assumes slice setup works correctly (tested separately in test-setup.md). The fixture script handles workspace creation so we can focus on the commands you use after setup.

Important context: Processes defined in the test template are trivial (python3 -m http.server and sleep infinity). They exist to validate that thinslice can start, track, and stop processes — not to test any real application. The httpd process is useful because we can curl it to confirm the process is actually listening on the assigned port.

Prerequisites

The slice binary and slice-test-fixture helper must be on $PATH. Python 3 must be available (for the httpd test process). No external infrastructure is required.

As a quick smoke test, run:

$ slice --version

If this succeeds, proceed. If it fails, stop and report the error.

Fixture: slice-test-fixture workspace

The workspace subcommand creates a ready-to-use thinslice workspace:

  1. Creates a temp directory with a git repo and minimal thinslice.new-state
  2. Runs slice setup to transform it into a workspace
  3. Caches the result on disk — subsequent calls return the cached path instead of rebuilding (safe for parallel test runs)
  4. Prints the absolute path to the workspace

The template defines two processes:

  • process.httpdpython3 -m http.server $HTTPD_PORT -d $SLICE_STATE_DIR/www
  • process.idlesleep infinity

Usage:

$ slice-test-fixture workspace
/tmp/thinslice-workspace-b3e9a0
$ slice-test-fixture destroy /tmp/thinslice-workspace-b3e9a0
destroyed /tmp/thinslice-workspace-b3e9a0

Notation

  • $WS — absolute path to the workspace, captured from fixture output
  • $S1, $S2 — state directory names created during the test
  • $HTTPD_PORT_* — httpd port values captured from various slices
  • $IDLE_PORT_* — idle process port values
  • Lines starting with $ inside code blocks are commands to run

Phase 0: Get workspace

0.1 Create workspace

$ slice-test-fixture workspace

Capture the output as $WS.

0.2 Verify workspace is ready

$ ls -1 $WS

Assert:

  • Contains +clone+, +state+, main, local, pr, tmp
$ test -f $WS/+state+/template-clean/thinslice.toml && echo "ready" || echo "not ready"

Assert:

  • Output is ready

Phase 1: State creation

1.1 Create a state from template

$ cd $WS/main && slice state new dev-alpha --from template-clean

Expect: output confirming the state was created.

1.2 Create a second state

$ cd $WS/main && slice state new dev-beta --from template-clean

Expect: output confirming the state was created.

1.3 List states

$ cd $WS/main && slice state ls

Assert:

  • Output includes template-clean, dev-alpha, and dev-beta
  • All three show status stopped (or equivalent "not running" indicator)
  • None are bound to a worktree

1.4 Verify state directories exist

$ test -f $WS/+state+/dev-alpha/thinslice.toml && echo "found" || echo "missing"
$ test -f $WS/+state+/dev-beta/thinslice.toml && echo "found" || echo "missing"

Assert:

  • Both output found

1.5 Verify ports are distinct

$ cd $WS/main && slice state new __port-check --from template-clean

This is a throwaway state just to verify port allocation.

Capture the HTTPD_PORT values from all three thinslice.toml files:

  • $PORT_ALPHA from $WS/+state+/dev-alpha/thinslice.toml
  • $PORT_BETA from $WS/+state+/dev-beta/thinslice.toml
  • $PORT_CHECK from $WS/+state+/__port-check/thinslice.toml

Assert:

  • $PORT_ALPHA, $PORT_BETA, and $PORT_CHECK are all different — each slice state new allocates fresh ports
$ cd $WS/main && slice state rm __port-check

Expect: cleanup succeeds.


Phase 2: Worktree creation

2.1 Create a local worktree

$ cd $WS && slice local feature-one

Expect: worktree created at local/feature-one.

2.2 Create a tmp worktree

$ cd $WS && slice tmp experiment

Expect: worktree created at tmp/experiment.

2.3 Verify directories exist

$ test -d $WS/local/feature-one && echo "found" || echo "missing"
$ test -d $WS/tmp/experiment && echo "found" || echo "missing"

Assert:

  • Both output found

2.4 Verify git branches were created

$ git -C $WS/+clone+ branch --list

Assert:

  • Branch list includes local/feature-one and tmp/experiment
  • main is also present

2.5 Verify worktrees are git worktrees of +clone+

$ git -C $WS/local/feature-one rev-parse --git-common-dir

Assert:

  • Output points to $WS/+clone+
$ git -C $WS/tmp/experiment rev-parse --git-common-dir

Assert:

  • Output points to $WS/+clone+

Phase 3: Binding and process lifecycle

3.1 Bind feature-one to dev-alpha

$ cd $WS/local/feature-one && slice use dev-alpha

Expect: output confirming binding.

3.2 Verify binding in state ls

$ cd $WS/main && slice state ls

Assert:

  • dev-alpha is bound to local/feature-one
  • dev-beta is not bound to anything

3.3 Start processes

$ cd $WS/local/feature-one && slice up

Expect: processes start without errors.

3.4 Check status

$ cd $WS/main && slice status

Assert:

  • local/feature-one shows state dev-alpha and status running
  • local/feature-one shows port allocations (at least two ports listed)
  • tmp/experiment shows no state binding and is not running

3.5 Check ports

$ cd $WS/local/feature-one && slice ports

Capture:

  • $HTTPD_PORT_ALPHA = the port shown for httpd
  • $IDLE_PORT_ALPHA = the port shown for idle

Assert:

  • Two processes listed: httpd and idle
  • Both ports are integers in valid range (1024–65535)

3.6 Verify httpd is actually listening

$ curl -s -o /dev/null -w "%{http_code}" http://localhost:$HTTPD_PORT_ALPHA/

Assert:

  • HTTP status is 200 — the python http server is running and serving from the state directory's www/ folder

3.7 Check config

$ cd $WS/local/feature-one && slice config

Assert:

  • Output is valid TOML
  • Contains [env] section with HTTPD_PORT = "$HTTPD_PORT_ALPHA"
  • Contains [process.httpd] and [process.idle]

3.8 Stop processes

$ cd $WS/local/feature-one && slice down

Expect: processes stop without errors.

3.9 Verify processes stopped

$ curl -s -o /dev/null -w "%{http_code}" http://localhost:$HTTPD_PORT_ALPHA/ 2>&1 || echo "connection refused"

Assert:

  • Connection is refused — the httpd process is no longer running
$ cd $WS/main && slice status

Assert:

  • local/feature-one shows state dev-alpha but status is no longer running (binding is preserved, processes are stopped)

Phase 4: State swapping

This tests the key scenario: moving state from one worktree to another. The same database, same ports, different code.

4.1 Start feature-one again

$ cd $WS/local/feature-one && slice up

Expect: processes start (feature-one is still bound to dev-alpha from Phase 3).

4.2 Verify it's running

$ curl -s -o /dev/null -w "%{http_code}" http://localhost:$HTTPD_PORT_ALPHA/

Assert:

  • HTTP status is 200

4.3 Swap dev-alpha to the experiment worktree

$ cd $WS/tmp/experiment && slice use dev-alpha

Expect: output confirming binding. This should automatically stop feature-one's processes and unbind it.

4.4 Verify feature-one was stopped and unbound

$ cd $WS/main && slice status

Assert:

  • local/feature-one is NOT bound to any state (or shows )
  • local/feature-one is NOT running
  • tmp/experiment is bound to dev-alpha
$ curl -s -o /dev/null -w "%{http_code}" http://localhost:$HTTPD_PORT_ALPHA/ 2>&1 || echo "connection refused"

Assert:

  • Connection is refused — feature-one's processes were stopped during the swap

4.5 Start experiment with the same state

$ cd $WS/tmp/experiment && slice up

Expect: processes start.

4.6 Verify same ports

$ cd $WS/tmp/experiment && slice ports

Assert:

  • httpd port is $HTTPD_PORT_ALPHA — same port as before, because it's the same state
  • idle port is $IDLE_PORT_ALPHA
$ curl -s -o /dev/null -w "%{http_code}" http://localhost:$HTTPD_PORT_ALPHA/

Assert:

  • HTTP status is 200 — processes are running on the same ports as before, now under the experiment worktree

4.7 Stop experiment

$ cd $WS/tmp/experiment && slice down

Phase 5: Port isolation — two slices running simultaneously

5.1 Bind feature-one to dev-alpha, experiment to dev-beta

$ cd $WS/local/feature-one && slice use dev-alpha
$ cd $WS/tmp/experiment && slice use dev-beta

Expect: both bindings succeed.

5.2 Start both slices

$ cd $WS/local/feature-one && slice up
$ cd $WS/tmp/experiment && slice up

Expect: both start without errors.

5.3 Check status shows both running

$ cd $WS/main && slice status

Assert:

  • local/feature-one shows state dev-alpha and status running
  • tmp/experiment shows state dev-beta and status running

5.4 Verify port isolation

$ cd $WS/tmp/experiment && slice ports

Capture:

  • $HTTPD_PORT_BETA = the port shown for httpd

Assert:

  • $HTTPD_PORT_BETA is different from $HTTPD_PORT_ALPHA — each state has its own port allocation

5.5 Verify both httpds are listening

$ curl -s -o /dev/null -w "%{http_code}" http://localhost:$HTTPD_PORT_ALPHA/

Assert:

  • HTTP status is 200
$ curl -s -o /dev/null -w "%{http_code}" http://localhost:$HTTPD_PORT_BETA/

Assert:

  • HTTP status is 200

Both slices are running simultaneously on different ports, sharing the same git repo but using different state directories.

5.6 Stop both

$ cd $WS/local/feature-one && slice down
$ cd $WS/tmp/experiment && slice down

Expect: both stop cleanly.


Phase 6: Inspection

6.1 Restart feature-one for inspection

$ cd $WS/local/feature-one && slice up

6.2 Check logs

$ cd $WS/local/feature-one && slice logs httpd 2>&1 | head -5

Assert:

  • Output contains log lines from the python http server (e.g., "Serving HTTP on ..." or similar startup message)
  • No error about missing log files

6.3 Check slice edit target

$ cd $WS/local/feature-one && slice config

Assert:

  • Output contains the resolved configuration
  • The SLICE_STATE_DIR or equivalent path points to $WS/+state+/dev-alpha

6.4 Stop

$ cd $WS/local/feature-one && slice down

Phase 7: Cleanup

7.1 Detach feature-one

$ cd $WS/local/feature-one && slice detach

Expect: worktree is unbound from dev-alpha.

$ cd $WS/main && slice state ls

Assert:

  • dev-alpha is not bound to any worktree

7.2 Remove tmp worktree with slice rm

$ cd $WS && slice rm tmp/experiment

Expect: worktree removed, branch deleted.

Assert:

  • $WS/tmp/experiment directory no longer exists
$ git -C $WS/+clone+ branch --list

Assert:

  • tmp/experiment branch no longer exists
  • local/feature-one still exists

7.3 Test slice gc (garbage collection)

Create two throwaway worktrees, leave them stopped, and gc.

$ cd $WS && slice tmp gc-test-1
$ cd $WS && slice tmp gc-test-2
$ test -d $WS/tmp/gc-test-1 && echo "found" || echo "missing"
$ test -d $WS/tmp/gc-test-2 && echo "found" || echo "missing"

Assert:

  • Both output found
$ cd $WS && slice gc

Expect: both tmp worktrees removed (neither is running).

$ test -d $WS/tmp/gc-test-1 && echo "found" || echo "missing"
$ test -d $WS/tmp/gc-test-2 && echo "found" || echo "missing"

Assert:

  • Both output missing

7.4 Remove states

$ cd $WS/main && slice state rm dev-alpha
$ cd $WS/main && slice state rm dev-beta

Expect: both removed without errors (neither is bound or running).

$ cd $WS/main && slice state ls

Assert:

  • Only template-clean remains

7.5 Verify state directories are gone

$ test -d $WS/+state+/dev-alpha && echo "found" || echo "missing"
$ test -d $WS/+state+/dev-beta && echo "found" || echo "missing"

Assert:

  • Both output missing

7.6 Remove the local worktree

$ cd $WS && slice rm local/feature-one

Expect: prompts for confirmation (this is a local/ worktree). Confirm yes.

Assert:

  • $WS/local/feature-one directory no longer exists
$ git -C $WS/+clone+ branch --list

Assert:

  • Only main remains (all test branches cleaned up)

Phase 8: Teardown

8.1 Destroy the workspace

$ slice-test-fixture destroy $WS

Assert:

  • Output confirms destruction
  • $WS directory no longer exists

Summary of Key Assertions

PhaseWhat is testedKey assertion
0FixtureWorkspace is ready with template-clean state
1State creationNew states created with distinct ports
2Worktree creationDirectories and git branches created correctly
3Bind + lifecycleProcesses start on correct ports, curl confirms httpd, clean shutdown
4State swappingOld worktree auto-stopped and unbound, new worktree uses same ports
5Port isolationTwo slices run simultaneously on different ports
6InspectionLogs and config commands work against a running slice
7Cleanuprm, gc, detach, state rm all clean up completely
8TeardownTemp workspace removed, global state cleaned up