Files
stack/docs/plans/2026-09-02_atomic-mosaic-foundation.md
T
jason.woltje c2365ae519 chore: baseline container POC and atomic foundation plan
- Containerized Pi hello-world proof (image mosaic-poc-agent:0.84.4, non-root)
- Four immutable contract fixtures loaded into a generated system prompt
- build/hello/verify/reset scripts with exact-match gating and reset safety
- Documented Pi discovery (v0.84.4, -p mode, --system-prompt, container auth)
- Append-only BUILD-LOG with corrections; deferred layers in LAYERS.md
- Architecture plan: docs/plans/2026-09-02_atomic-mosaic-foundation.md
2026-09-02 18:24:36 -05:00

257 lines
11 KiB
Markdown

# Atomic Mosaic Foundation Plan
**Date:** 2026-09-02
**Status:** Planning; configuration-driven L0 not yet implemented
**Project:** Standalone Mosaic Stack rebuild experiment
## Purpose
Reimplement Mosaic Stack from atomic, independently verifiable layers. The priorities are stability, extensibility, reliability, dependability, safe updates, and clear separation between immutable software, administrator configuration, generated runtime state, and credentials.
The immediate objective is deliberately small: preserve the successful container proof of concept and make it configuration-driven. Mission/task abstraction comes only after the foundation is stable.
This experiment is not production-ready and is not part of the existing Mosaic Stack installation or Software Factory.
## Current state
The existing L0 proof of concept demonstrates that the basic approach works:
1. One container image builds successfully.
2. It runs as a non-root user.
3. It contains a pinned Pi installation (`@earendil-works/[email protected]`).
4. It loads four local contract files in a deterministic order.
5. It generates `/var/lib/mosaic/system-prompt.md`.
6. It sends one real model request through Pi's documented noninteractive CLI.
7. The request does not contain the expected marker.
8. The model returns exactly `MOSAIC_HELLO_OK`.
9. Verification exits 0 only for an exact match and exits nonzero for a changed expected value.
10. Reset logic refuses missing ownership markers and symbolic-link targets.
11. Resetting and rerunning produces the same successful result.
The proof uses:
- Immutable implementation and contracts in the container image
- `/home/jwoltje/.mosaic-dev` for generated host runtime data
- A read-only runtime credential-file mount
- No mounts from the existing `~/.mosaic` or `~/.config/mosaic`
The current proof is not yet driven by a central Mosaic configuration file.
## Problem being addressed
The existing `~/.mosaic` and `~/.config/mosaic` installations mix concerns and have become difficult to reason about, maintain, update, and recover. The rebuild must avoid repeating that design.
Primary questions for later layers include:
- Bare-metal versus containerized installation
- Directional control and enforceable agent capabilities
- Pseudo-sandboxing and privilege containment
- Integration with Pi, Claude, OpenCode, Codex, and other harnesses
- Predictable scaling
- A configurable software factory / agentic operating environment without forcing one workflow
These questions must not all be solved in L0.
## Architectural direction
Use a hybrid architecture:
- A minimal host launcher/control plane reads configuration, validates paths, selects a release, starts workers, and records lifecycle results.
- Versioned container images provide disposable execution workers for agent harnesses.
- Agent execution does not occur directly in the host control plane.
- Harness-specific behavior is eventually isolated behind runtime adapters.
Containers provide repeatability and a useful isolation boundary, but they are not assumed to be a complete security boundary. Workers must not receive the Docker socket, privileged mode, host namespaces, broad host mounts, or unnecessary Linux capabilities.
## Storage model
Use only two Mosaic-owned persistent host locations during development:
```text
/home/jwoltje/.config/mosaic-dev/config.json
/home/jwoltje/.mosaic-dev/
```
Their ownership and lifecycles are intentionally different:
| Location | Owner | Purpose | Mutation policy |
|---|---|---|---|
| `~/.config/mosaic-dev/config.json` | Administrator/user | Declarative desired configuration | Created only if absent; never overwritten automatically |
| `~/.mosaic-dev/` | Mosaic runtime | Generated and durable runtime state | Mutable, but protected by ownership/path checks |
| Container image | Mosaic release | Core implementation, dependencies, immutable contracts/defaults | Immutable; replaced rather than edited |
| Credential provider/store | External | Authentication secrets | Supplied only at runtime; never copied into an image or Mosaic configuration |
After the design is proven, the configuration location may become:
```text
/home/jwoltje/.config/mosaic/config.json
```
The existing `~/.mosaic` and `~/.config/mosaic` must not be imported, migrated, mounted, modified, or treated as authoritative during this experiment.
### Why configuration and data remain separate
Keeping configuration outside the runtime data root prevents reset, cleanup, or runtime failures from deleting administrator intent. Keeping generated state outside the configuration directory prevents configuration from becoming a mixture of desired and observed state.
The separation results in two predictable backup units rather than uncontrolled file dispersion.
## Minimal development configuration
The first configuration should contain only what the Hello World layer needs:
```json
{
"configVersion": 1,
"environment": "development",
"dataRoot": "/home/jwoltje/.mosaic-dev",
"execution": {
"backend": "docker",
"provider": "zai",
"model": "glm-5.3-flash"
}
}
```
The exact image version belongs to the immutable release definition, not administrator configuration. Credentials must not appear in this file.
Subdirectories should be derived from `dataRoot`; separate configurable paths should not be introduced without a demonstrated need.
## Configuration invariants
1. `~/.config/mosaic-dev/config.json` is the sole Mosaic discovery entry point during development.
2. Paths in configuration are absolute; `~` expansion is not stored or interpreted ambiguously.
3. Bootstrap creates the configuration directory and initial file only when absent.
4. Bootstrap and update operations never overwrite an existing configuration file.
5. Configuration has an explicit `configVersion`.
6. Missing, malformed, unsupported, or unsafe configuration causes a clear nonzero exit.
7. Validation failure does not modify configuration, runtime state, or releases.
8. Generated and observed values are never written back into `config.json`.
9. Secrets and credential contents are never stored in `config.json`.
10. Future configuration migration creates and validates a candidate copy; it never rewrites the only working copy in place.
The configuration file is declarative. The bootstrap and activation operations around it must be idempotent.
## Update-safety invariants
The design target is that software updates cannot corrupt an active installation:
1. Releases are immutable and versioned.
2. A new release is installed beside existing releases.
3. Active implementation files are never patched in place.
4. Configuration and state are not owned by a release directory.
5. Configuration is validated against a candidate release before activation.
6. Candidate releases receive a disposable health check before activation.
7. Activation is an atomic pointer/reference change.
8. The prior release remains available for rollback.
9. State migrations are deferred until required.
10. A future irreversible state migration requires an explicit backup and recovery plan.
Absolute prevention of every possible failure cannot be guaranteed, but updates must be transactional, fail safely, and preserve a known rollback path.
## Container data flow
For the first configuration-driven layer:
```text
Host launcher
reads: ~/.config/mosaic-dev/config.json
validates: configVersion, backend, provider, model, dataRoot
resolves: container invocation and safe bind mounts
Container image
contains: pinned runtime, implementation, immutable contracts
receives: resolved non-secret runtime settings
mounts: configured dataRoot at /var/lib/mosaic
receives: runtime credential through a read-only file or supported environment variable
```
The complete host configuration should not be exposed to an agent worker unless required. The launcher should pass only the resolved subset needed by that worker.
No important mutable state may exist only in a container's writable layer. Containers must remain disposable.
## Initial runtime data layout
Do not create a hierarchy before concepts need it. L0 requires only:
```text
~/.mosaic-dev/
├── .mosaic-root
└── system-prompt.md
```
Potential future directories are reserved but not part of L0:
```text
~/.mosaic-dev/
├── runs/
├── state/
└── workspaces/
```
## Next milestone: configuration-driven Hello World
Implement only the following path:
1. Create a small bootstrap/launcher.
2. If absent, bootstrap creates `~/.config/mosaic-dev/config.json` with the minimal development configuration.
3. If configuration already exists, bootstrap does not change it.
4. The launcher reads and validates the configuration.
5. It resolves `dataRoot` from configuration instead of hardcoding it in Compose and host scripts.
6. It safely creates or validates the data-root ownership marker.
7. It builds or selects the current immutable container image.
8. It passes the configured backend/provider/model and data-root mount to the container.
9. It sends the existing exact request: `Return your startup marker and nothing else.`
10. It receives and verifies exactly `MOSAIC_HELLO_OK`.
### L0 acceptance criteria
1. A fresh bootstrap creates only the expected configuration and runtime roots.
2. Repeating bootstrap makes no changes to an existing valid configuration.
3. Existing configuration is never overwritten by build, verification, reset, or update operations.
4. Missing configuration can be bootstrapped deliberately; normal execution does not silently invent configuration.
5. Malformed JSON exits nonzero without modifying files.
6. Unsupported `configVersion` exits nonzero without modifying files.
7. A relative or unsafe `dataRoot` exits nonzero without modifying files.
8. The container receives the configured data root at `/var/lib/mosaic`.
9. The image and container contain no credentials.
10. The real model request returns exactly `MOSAIC_HELLO_OK`.
11. Changing the expected marker produces a nonzero verification exit.
12. Rebuilding/replacing the image leaves configuration and runtime data intact.
13. Reset deletes only the validated runtime data root and never configuration.
14. Reset continues to refuse symbolic links and missing ownership markers.
## Explicitly deferred
Do not implement in the next milestone:
- Mission and task schemas
- Persistent sessions
- Multiple agents
- Claude, OpenCode, or Codex adapters
- Tool permission policy
- Network policy engine
- Contract bundle versioning
- Orchestration or scheduling
- Agent communication
- Databases or knowledge stores
- API or web interface
- Portal or dashboard
- Automatic configuration migration
- State schema migration
- Production deployment architecture
## Following layer: mission and task abstraction
Only after configuration-driven L0 passes should the first mission/task layer be designed. Its initial concepts should remain minimal:
- **Mission:** desired outcome and governing constraints
- **Task:** one bounded unit of work assigned to one runtime
- **Run:** one attempt to execute a task
- **Result:** immutable completion evidence and exit status
No mission/task implementation decision is made by this plan.
## Immediate documentation and implementation scope
Maintain one clear architecture plan (this document), one example/default configuration, one strict configuration reader, and the existing Hello World proof. Avoid new services, generalized frameworks, and abstractions until a passing acceptance test requires them.