Compare commits
161
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b7fd19d08 | ||
|
|
69f10a4062 | ||
|
|
67eaf6fb47 | ||
|
|
3ea385223e | ||
|
|
193479b52d | ||
|
|
7c580a5625 | ||
|
|
8ebddd6f93 | ||
|
|
127a54fdff | ||
|
|
9a5fbdbda7 | ||
|
|
7345f330fc | ||
|
|
d4696d09eb | ||
|
|
44f257cb06 | ||
|
|
5d27700026 | ||
|
|
69d1bb3aa4 | ||
|
|
9ea7dea711 | ||
|
|
d9a94f51ba | ||
|
|
975084abe2 | ||
|
|
073bbfdb6a | ||
|
|
d1d7b5598d | ||
|
|
ca8135d70c | ||
|
|
bf56583a49 | ||
|
|
c8f433131c | ||
|
|
d1e75f855c | ||
|
|
f710bf1a68 | ||
|
|
2085d75190 | ||
|
|
2f5a8d2cec | ||
|
|
e14ad9ab52 | ||
|
|
9fd16b9739 | ||
|
|
9051ad179b | ||
|
|
2f649ed930 | ||
|
|
db330c12c7 | ||
|
|
0a17d29bce | ||
|
|
3458f6a7ad | ||
|
|
b033952cd5 | ||
|
|
c102980ad4 | ||
|
|
58b96cb715 | ||
|
|
dd3ff944a1 | ||
|
|
8eb81ebec1 | ||
|
|
530597cc84 | ||
|
|
9a0d44f96a | ||
|
|
121b331c6c | ||
|
|
34e06e7de7 | ||
|
|
9bd4f1c405 | ||
|
|
a7b612435b | ||
|
|
87f10772ce | ||
|
|
7db4c5c2ed | ||
|
|
0273a84549 | ||
|
|
3b674b7a66 | ||
|
|
527bc581ca | ||
|
|
1249714a9a | ||
|
|
e175616885 | ||
|
|
6955717612 | ||
|
|
88d9cf750f | ||
|
|
c038706eed | ||
|
|
8622c9d826 | ||
|
|
88eef507b0 | ||
|
|
439bea6915 | ||
|
|
fad8a4718c | ||
|
|
2ff49adff4 | ||
|
|
44c476ebbf | ||
|
|
cde480eb60 | ||
|
|
5808248707 | ||
|
|
afd5827db8 | ||
|
|
d9cc990376 | ||
|
|
83c4e9851e | ||
|
|
22508170a2 | ||
|
|
90a67d050e | ||
|
|
24bdef75fa | ||
|
|
4e2a413640 | ||
|
|
be55549700 | ||
|
|
ddb1554e5b | ||
|
|
b017e66e17 | ||
|
|
172368612c | ||
|
|
1387231e57 | ||
|
|
0292392e64 | ||
|
|
594b8d711c | ||
|
|
3c1ffd2c2d | ||
|
|
4ebb123ba3 | ||
|
|
bb5cecb348 | ||
|
|
88f55d9135 | ||
|
|
e7e1bd26eb | ||
|
|
5d86b8fa93 | ||
|
|
35ea464661 | ||
|
|
e87ecdb3e5 | ||
|
|
a947db7bfd | ||
|
|
a35ea62ab1 | ||
|
|
f6c93dcb5c | ||
|
|
7f0408d417 | ||
|
|
cfd2a19bd7 | ||
|
|
d2a9e26395 | ||
|
|
0734b1f3a5 | ||
|
|
ce6420f3de | ||
|
|
81f58b15c8 | ||
|
|
0c2113f710 | ||
|
|
900a506c1f | ||
|
|
c3d29e796a | ||
|
|
c2365ae519 | ||
|
|
d6302f8e6f | ||
|
|
9aa4983cf2 | ||
|
|
736b0affc1 | ||
|
|
e18d13d36f | ||
|
|
60bc5d2022 | ||
|
|
ea91cfc421 | ||
|
|
acf640d00f | ||
|
|
431ead3a18 | ||
|
|
143ba0f57a | ||
|
|
ee815a72b1 | ||
|
|
94d626dff9 | ||
|
|
ee6c842918 | ||
|
|
ba3b854d50 | ||
|
|
c7a7fd07cc | ||
|
|
5399c6b7e7 | ||
|
|
e09b8783b4 | ||
|
|
abb0c93601 | ||
|
|
e67cced273 | ||
|
|
635cb1f666 | ||
|
|
09d24b9275 | ||
|
|
ed4c543872 | ||
|
|
215faeda0a | ||
|
|
5125fe21b0 | ||
|
|
41e8046371 | ||
|
|
6e16675ea2 | ||
|
|
19ebc422aa | ||
|
|
bd749831b1 | ||
|
|
f8e1b43b5b | ||
|
|
2148c20d26 | ||
|
|
5964dab891 | ||
|
|
bdb903cf69 | ||
|
|
bec2eb118b | ||
|
|
07624140e4 | ||
|
|
1c79af25d4 | ||
|
|
e605c83b27 | ||
|
|
b5ee692843 | ||
|
|
bf8bc2128d | ||
|
|
01904b8f69 | ||
|
|
676900bd46 | ||
|
|
a3b0770205 | ||
|
|
2a30c68b84 | ||
|
|
f8f8f97be7 | ||
|
|
49b7943420 | ||
|
|
19e16bd44f | ||
|
|
3bd490c080 | ||
|
|
4b448109dd | ||
|
|
bc1149c15e | ||
|
|
089953a7cf | ||
|
|
7b25be22e9 | ||
|
|
4e3d179e61 | ||
|
|
ae58482b72 | ||
|
|
b2d40dada0 | ||
|
|
d30a4cce00 | ||
|
|
4cd280e48d | ||
|
|
8738a03893 | ||
|
|
04a01be992 | ||
|
|
812e2df1da | ||
|
|
8c292fb32f | ||
|
|
f45928c311 | ||
|
|
4d24ae8618 | ||
|
|
d790572e2e | ||
|
|
d7b1dd9601 | ||
|
|
0db2d19a22 | ||
|
|
8eb7e6354e |
+20
-149
@@ -1,153 +1,24 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Mosaic — Environment Variables Reference
|
||||
# Copy this file to .env and fill in the values for your deployment.
|
||||
# Lines beginning with # are comments; optional vars are commented out.
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Non-secret runtime settings for the mosaic-poc-agent container.
|
||||
# Copy to .env if you want to override the defaults in compose.yaml.
|
||||
#
|
||||
# NEVER put credentials in this file. Authentication is supplied at
|
||||
# runtime only, via one of the two documented paths:
|
||||
# 1. read-only mounted pi auth file (default: ~/.pi/agent/auth.json,
|
||||
# override the host path with PI_AUTH_FILE)
|
||||
# 2. provider API key environment variable (ZAI_API_KEY or
|
||||
# ANTHROPIC_API_KEY), passed through by compose.yaml when set
|
||||
|
||||
# Model provider (built-in pi provider name)
|
||||
PI_PROVIDER=zai
|
||||
|
||||
# ─── Database (PostgreSQL 17 + pgvector) ─────────────────────────────────────
|
||||
# Full connection string used by the gateway, ORM, and migration runner.
|
||||
# Port 5433 avoids conflict with a host-side PostgreSQL instance.
|
||||
DATABASE_URL=postgresql://mosaic:mosaic@localhost:5433/mosaic
|
||||
# Model ID within the provider
|
||||
PI_MODEL=glm-5.3-flash
|
||||
|
||||
# Docker Compose host-port override for the PostgreSQL container (default: 5433)
|
||||
# PG_HOST_PORT=5433
|
||||
# Optional: alternative host path of the pi credential file mounted
|
||||
# read-only at /home/node/.pi/agent/auth.json in the container
|
||||
#PI_AUTH_FILE=/home/jwoltje/.pi/agent/auth.json
|
||||
|
||||
|
||||
# ─── Queue (Valkey 8 / Redis-compatible) ─────────────────────────────────────
|
||||
# Port 6380 avoids conflict with a host-side Redis/Valkey instance.
|
||||
VALKEY_URL=redis://localhost:6380
|
||||
|
||||
# Docker Compose host-port override for the Valkey container (default: 6380)
|
||||
# VALKEY_HOST_PORT=6380
|
||||
|
||||
|
||||
# ─── Gateway ─────────────────────────────────────────────────────────────────
|
||||
# TCP port the NestJS/Fastify gateway listens on (default: 14242)
|
||||
GATEWAY_PORT=14242
|
||||
|
||||
# Comma-separated list of allowed CORS origins.
|
||||
# Must include the web app origin in production.
|
||||
GATEWAY_CORS_ORIGIN=http://localhost:3000
|
||||
|
||||
|
||||
# ─── Auth (BetterAuth) ───────────────────────────────────────────────────────
|
||||
# REQUIRED — random secret used to sign sessions and tokens.
|
||||
# Generate with: openssl rand -base64 32
|
||||
BETTER_AUTH_SECRET=change-me-to-a-random-32-char-string
|
||||
|
||||
# Public base URL of the gateway (used by BetterAuth for callback URLs)
|
||||
BETTER_AUTH_URL=http://localhost:14242
|
||||
|
||||
|
||||
# ─── Web App (Next.js) ───────────────────────────────────────────────────────
|
||||
# Public gateway URL — accessible from the browser, not just the server.
|
||||
NEXT_PUBLIC_GATEWAY_URL=http://localhost:14242
|
||||
|
||||
|
||||
# ─── OpenTelemetry ───────────────────────────────────────────────────────────
|
||||
# OTLP HTTP endpoint (otel-collector or any OpenTelemetry-compatible backend)
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
|
||||
|
||||
# Service name shown in traces
|
||||
OTEL_SERVICE_NAME=mosaic-gateway
|
||||
|
||||
|
||||
# ─── AI Providers ────────────────────────────────────────────────────────────
|
||||
|
||||
# Ollama (local models — set OLLAMA_BASE_URL to enable)
|
||||
# OLLAMA_BASE_URL=http://localhost:11434
|
||||
# OLLAMA_HOST is a legacy alias for OLLAMA_BASE_URL
|
||||
# OLLAMA_HOST=http://localhost:11434
|
||||
# Comma-separated list of Ollama model IDs to register (default: llama3.2,codellama,mistral)
|
||||
# OLLAMA_MODELS=llama3.2,codellama,mistral
|
||||
|
||||
# Anthropic (claude-sonnet-4-6, claude-opus-4-6, claude-haiku-4-5)
|
||||
# ANTHROPIC_API_KEY=sk-ant-...
|
||||
|
||||
# OpenAI (gpt-4o, gpt-4o-mini, o3-mini)
|
||||
# OPENAI_API_KEY=sk-...
|
||||
|
||||
# Z.ai / GLM (glm-4.5, glm-4.5-air, glm-4.5-flash)
|
||||
# ZAI_API_KEY=...
|
||||
|
||||
# Custom providers — JSON array of provider configs
|
||||
# Format: [{"id":"<id>","baseUrl":"<url>","apiKey":"<key>","models":[{"id":"<model-id>","name":"<label>"}]}]
|
||||
# MOSAIC_CUSTOM_PROVIDERS=
|
||||
|
||||
|
||||
# ─── Embedding Service ───────────────────────────────────────────────────────
|
||||
# OpenAI-compatible embeddings endpoint (default: OpenAI)
|
||||
# EMBEDDING_API_URL=https://api.openai.com/v1
|
||||
# EMBEDDING_MODEL=text-embedding-3-small
|
||||
|
||||
|
||||
# ─── Log Summarization Service ───────────────────────────────────────────────
|
||||
# OpenAI-compatible chat completions endpoint for log summarization (default: OpenAI)
|
||||
# SUMMARIZATION_API_URL=https://api.openai.com/v1
|
||||
# SUMMARIZATION_MODEL=gpt-4o-mini
|
||||
|
||||
# Cron schedule for summarization job (default: every 6 hours)
|
||||
# SUMMARIZATION_CRON=0 */6 * * *
|
||||
|
||||
# Cron schedule for log tier management (default: daily at 03:00)
|
||||
# TIER_MANAGEMENT_CRON=0 3 * * *
|
||||
|
||||
|
||||
# ─── Agent ───────────────────────────────────────────────────────────────────
|
||||
# Filesystem sandbox root for agent file tools (default: process.cwd())
|
||||
# AGENT_FILE_SANDBOX_DIR=/var/lib/mosaic/sandbox
|
||||
|
||||
# Comma-separated list of tool names available to non-admin users.
|
||||
# Leave unset to allow all tools for all authenticated users.
|
||||
# AGENT_USER_TOOLS=read_file,list_directory,search_files
|
||||
|
||||
# System prompt injected into every agent session (optional)
|
||||
# AGENT_SYSTEM_PROMPT=You are a helpful assistant.
|
||||
|
||||
|
||||
# ─── MCP Servers ─────────────────────────────────────────────────────────────
|
||||
# JSON array of MCP server configs — set to enable MCP tool integration.
|
||||
# Each entry: {"name":"<id>","url":"<http-or-sse-url>"}
|
||||
# MCP_SERVERS=[{"name":"my-mcp","url":"http://localhost:3100/sse"}]
|
||||
|
||||
|
||||
# ─── Coordinator ─────────────────────────────────────────────────────────────
|
||||
# Root directory used to scope coordinator (worktree/repo) operations.
|
||||
# Defaults to the monorepo root auto-detected from process.cwd().
|
||||
# MOSAIC_WORKSPACE_ROOT=/home/user/projects/mosaic
|
||||
|
||||
|
||||
# ─── Discord Plugin (optional — set DISCORD_BOT_TOKEN to enable) ─────────────
|
||||
# DISCORD_BOT_TOKEN=
|
||||
# DISCORD_GUILD_ID=
|
||||
# DISCORD_GATEWAY_URL=http://localhost:14242
|
||||
|
||||
|
||||
# ─── Telegram Plugin (optional — set TELEGRAM_BOT_TOKEN to enable) ───────────
|
||||
# TELEGRAM_BOT_TOKEN=
|
||||
# TELEGRAM_GATEWAY_URL=http://localhost:14242
|
||||
|
||||
|
||||
# ─── SSO Providers (add credentials to enable) ───────────────────────────────
|
||||
|
||||
# --- Authentik (optional — set AUTHENTIK_CLIENT_ID to enable) ---
|
||||
# AUTHENTIK_ISSUER=https://auth.example.com/application/o/mosaic/
|
||||
# AUTHENTIK_CLIENT_ID=
|
||||
# AUTHENTIK_CLIENT_SECRET=
|
||||
|
||||
# --- WorkOS (optional — set WORKOS_CLIENT_ID to enable) ---
|
||||
# WORKOS_ISSUER=https://your-company.authkit.app
|
||||
# WORKOS_CLIENT_ID=client_...
|
||||
# WORKOS_CLIENT_SECRET=sk_live_...
|
||||
|
||||
# --- Keycloak (optional — set KEYCLOAK_CLIENT_ID to enable) ---
|
||||
# KEYCLOAK_ISSUER=https://auth.example.com/realms/master
|
||||
# Legacy alternative if you prefer to compose the issuer from separate vars:
|
||||
# KEYCLOAK_URL=https://auth.example.com
|
||||
# KEYCLOAK_REALM=master
|
||||
# KEYCLOAK_CLIENT_ID=mosaic
|
||||
# KEYCLOAK_CLIENT_SECRET=
|
||||
|
||||
# The web login page discovers configured providers dynamically from
|
||||
# GET /api/sso/providers. No NEXT_PUBLIC_* provider feature flag is required.
|
||||
# Optional: documented env-var auth alternative (secret! set in your
|
||||
# shell or a gitignored .env, never commit)
|
||||
#ZAI_API_KEY=
|
||||
#ANTHROPIC_API_KEY=
|
||||
|
||||
+6
-23
@@ -1,25 +1,8 @@
|
||||
logs/
|
||||
node_modules
|
||||
dist
|
||||
.turbo
|
||||
.next
|
||||
coverage
|
||||
# build/deps
|
||||
node_modules/
|
||||
|
||||
# runtime credentials — never commit, never copy into the image
|
||||
.env
|
||||
.env.local
|
||||
*.tsbuildinfo
|
||||
.pnpm-store
|
||||
__pycache__/
|
||||
docs/.obsidian
|
||||
secrets/
|
||||
|
||||
# Step-CA dev password — real file is gitignored; commit only the .example
|
||||
infra/step-ca/dev-password
|
||||
|
||||
# Scratch dirs created by the framework git-wrapper shell test harnesses
|
||||
.mosaic-test-work/
|
||||
|
||||
# Transient config files vite/vitest/esbuild write next to a *.config.ts while
|
||||
# loading it, then unlink. They are untracked but were not ignored, so turbo's
|
||||
# package traversal hashed them and intermittently failed CI with "Package
|
||||
# traversal error: ... .timestamp-*.mjs: No such file or directory" when the
|
||||
# file vanished mid-scan. Ignoring them removes the race.
|
||||
*.timestamp-*.mjs
|
||||
# generated runtime state lives in /home/jwoltje/.mosaic-dev (outside this project)
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
extensions/
|
||||
extensions.installed.sha256
|
||||
.extensions-*
|
||||
state/
|
||||
evidence/
|
||||
native-test-*.log
|
||||
@@ -0,0 +1,34 @@
|
||||
# Native goal development copy
|
||||
|
||||
From this repository, start a fresh native Pi session:
|
||||
|
||||
```sh
|
||||
bash scripts/goal-dev.sh
|
||||
```
|
||||
|
||||
Canonical source lives under `extensions/`. The launcher first runs `scripts/sync-dev-extensions.sh`, which installs verified ordinary-file copies under `.pi/extensions/`, then loads only the generated goal extension. Global extensions remain unloaded. The launcher keeps your usual native Pi provider authentication; it copies no credentials. Goal state and new conversation files live under `.pi/state/`, which is ignored by Git. Each process gets a fresh incarnation; `/reload` and `/new` in the same process retain its goal. Restarting Pi does not adopt an earlier process's active goal.
|
||||
|
||||
Plain `pi` also discovers `.pi/extensions/goal/index.ts` after project trust, but may load global extensions too. Use the launcher to avoid duplicate `/goal` registrations. This is a local development test, not a sandbox or the managed Mosaic runtime. Docker and `~/.mosaic` are unchanged.
|
||||
|
||||
## Try it
|
||||
|
||||
1. Set `/goal <a long goal with acceptance criteria>`. This starts work immediately.
|
||||
2. Look below the editor for `Goal: Active`. The old above-editor goal widget is gone.
|
||||
3. Run bare `/goal`, then press `Alt+G`. Both show the entire stored goal and its status. Tab remains autocomplete.
|
||||
4. Use `/goal stop` and `/goal resume`. Expect Paused and Active, or Waiting if an untimed wait remains recorded.
|
||||
5. A blocked `goal_report` displays Blocked. A satisfied report displays Complete and retains the full goal for recall without continuing work.
|
||||
6. `/goal clear` removes the retained goal. Try `NO_COLOR=1 bash scripts/goal-dev.sh` to check text-only labels.
|
||||
|
||||
Use terminal scrollback for recall longer than the screen. At narrow widths Pi may truncate its footer status row; bare `/goal` and Alt+G remain available.
|
||||
|
||||
## Checks
|
||||
|
||||
```sh
|
||||
node --test extensions/goal/test/*.test.ts
|
||||
bash scripts/test-extension-package.sh
|
||||
python3 scripts/test-goal-native.py
|
||||
```
|
||||
|
||||
Contract tests use ordinary read-only fixture copies in `test/fixtures/skills-local/`, not live brain files. The executive-update fixture SHA-256 matches the parser's pinned contract, `bbea48a46b1f8da7bc759f86856fb52830b7dde456b826317163c6dc6ccab319`.
|
||||
|
||||
`SOURCE-SNAPSHOT.json` records the original external-source baseline, not the edited candidate. No symlinks are used. Never edit `.pi/extensions/`; the sync script refuses to overwrite installation drift. Make changes under `extensions/`, run the checks, and relaunch. To disable the test, stop its Pi process and remove `.pi/extensions/`. Keep `.pi/state/` only if you need local test state.
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"snapshotVersion": 1,
|
||||
"copiedAt": "2026-09-06T04:58:22Z",
|
||||
"source": "~/.mosaic/fleet/extensions",
|
||||
"goalTreeSha256": "8853f2b72dde3e87c4573648b9a931c1c75da87ccde995c3224e6d2e707a75f0",
|
||||
"mosaicCoreLibTreeSha256": "d1194dce31209e5773c6cc5ce571cbca3c39b29d943a79dea06665e05d29f319",
|
||||
"symlinks": false,
|
||||
"autoDiscoveredExtensions": ["goal"],
|
||||
"purpose": "Issue #54 native Pi NG development copy; never loaded by Docker"
|
||||
}
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Compatibility entrypoint for the accepted native test command.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||||
exec scripts/goal-dev.sh "$@"
|
||||
@@ -1,361 +0,0 @@
|
||||
# Build, publish npm packages, and push Docker images
|
||||
# Runs on main for stable publishes and on next for integration-line prereleases/images
|
||||
#
|
||||
# SDLC-D-034 publish gate: every publish effect (publish-npm, publish-next-npm,
|
||||
# and every image build/push step) depends DIRECTLY on the `verify` step below.
|
||||
# `verify` (a) asserts the provider's commit identity matches the actual
|
||||
# checkout (CI_COMMIT_SHA == git rev-parse HEAD, fail closed on mismatch or
|
||||
# emptiness) and (b) runs the canonical terminal verification command
|
||||
# (`pnpm verify:release`), which mirrors the PR CI pipeline's complete
|
||||
# mandatory set (sanitization, upgrade-guard, preflight+typecheck, lint,
|
||||
# format:check, test, build) — see scripts/verify-release.mjs. A missing,
|
||||
# failed, skipped, cancelled, or inconclusive verification therefore skips the
|
||||
# dependent publish effects (fail closed). Path-filtered short-circuits may
|
||||
# skip publish EFFECTS (e.g. docs-only merges) but never bypass `verify` for a
|
||||
# publish that does run: `verify` itself carries no path filter.
|
||||
# scripts/verify-release.test.mjs enforces this DAG invariant at checkout time.
|
||||
|
||||
variables:
|
||||
# Pre-baked CI base (see .woodpecker/ci-image.yml): node:24-alpine +
|
||||
# toolchain + warm pnpm store. Kills the second cold install publish pays.
|
||||
# PINNED to the immutable lock-tag, not :latest (#1328, brain D27): a mutable
|
||||
# tag resolves per-pod at pull time on the k8s backend and made CI verdicts
|
||||
# non-reproducible (#1324). Byte-identical to :latest at pin time (pushed
|
||||
# atomically by the same kaniko run, main 712c770, 2026-07-26). Bump only via
|
||||
# reviewed PR, per the procedure in .woodpecker/ci.yml's header comment.
|
||||
- &node_image 'git.mosaicstack.dev/mosaicstack/stack/ci-base:lock-9cb7ffcd8828'
|
||||
- &enable_pnpm 'corepack enable'
|
||||
# Heavy kaniko image builds (~25 min) — gate them so a merge that only touches
|
||||
# the npm-only CLI (@mosaicstack/mosaic) or docs does NOT rebuild the platform
|
||||
# images (gateway/appservice/web do not depend on @mosaicstack/mosaic). Releases
|
||||
# (tags) always build everything. Exclude-list keeps the default SAFE: any
|
||||
# non-excluded change still builds, so no transitive dep can silently go stale.
|
||||
# (Woodpecker: `when` entries are OR'd; `path` applies to push/PR only — hence
|
||||
# the separate `event: tag` entry.)
|
||||
- &image_build_when
|
||||
- event: tag
|
||||
- event: [push, manual]
|
||||
branch: main
|
||||
path:
|
||||
exclude:
|
||||
- 'packages/mosaic/**'
|
||||
- 'docs/**'
|
||||
- '**/*.md'
|
||||
- '.woodpecker/**'
|
||||
- event: [push, manual]
|
||||
branch: next
|
||||
- &main_image_build_when
|
||||
- event: tag
|
||||
- event: [push, manual]
|
||||
branch: main
|
||||
path:
|
||||
exclude:
|
||||
- 'packages/mosaic/**'
|
||||
- 'docs/**'
|
||||
- '**/*.md'
|
||||
- '.woodpecker/**'
|
||||
|
||||
when:
|
||||
- branch: [main, next]
|
||||
event: [push, manual, tag]
|
||||
|
||||
steps:
|
||||
install:
|
||||
image: *node_image
|
||||
commands:
|
||||
- corepack enable
|
||||
# Resolve from the baked pnpm store instead of a cold network fetch.
|
||||
- pnpm install --frozen-lockfile --prefer-offline
|
||||
|
||||
# SDLC-D-034 exact-commit publish gate. No `when`/path filter on purpose: it
|
||||
# runs for every event this pipeline serves so no publish effect can ever
|
||||
# start without it. Fails closed on commit-identity mismatch (or either SHA
|
||||
# being empty) and on any incomplete verification.
|
||||
verify:
|
||||
image: *node_image
|
||||
commands:
|
||||
- *enable_pnpm
|
||||
# (a) Commit identity: the provider's claimed SHA must equal the actual
|
||||
# checkout HEAD — verification of anything else must never authorize a
|
||||
# publish of this commit.
|
||||
- |
|
||||
if [ -z "$CI_COMMIT_SHA" ]; then
|
||||
echo "[verify] FATAL: CI_COMMIT_SHA is empty — cannot certify commit identity" >&2
|
||||
exit 1
|
||||
fi
|
||||
CHECKOUT_SHA="$(git rev-parse HEAD 2>/dev/null || true)"
|
||||
if [ -z "$CHECKOUT_SHA" ]; then
|
||||
echo "[verify] FATAL: git rev-parse HEAD returned nothing — cannot certify commit identity" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ "$CI_COMMIT_SHA" != "$CHECKOUT_SHA" ]; then
|
||||
echo "[verify] FATAL: provider commit ($CI_COMMIT_SHA) != checkout HEAD ($CHECKOUT_SHA)" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "[verify] commit identity confirmed: $CHECKOUT_SHA"
|
||||
# (b) Canonical terminal verification. Caller-provided prerequisites the
|
||||
# runner expects (see .woodpecker/ci.yml comments): bash/rsync for the
|
||||
# guard stages, openssl + the pinned pi binary for the test stage. git is
|
||||
# baked into ci-base but re-asserted here so the identity check above can
|
||||
# never silently depend on a stale baked image. DATABASE_URL is
|
||||
# deliberately NOT set: the canonical command must hold on the PGlite
|
||||
# path too and never sets or requires a database itself.
|
||||
- apk add --no-cache bash rsync openssl git
|
||||
- npm install -g @earendil-works/[email protected]
|
||||
- pnpm verify:release
|
||||
depends_on:
|
||||
- install
|
||||
|
||||
build:
|
||||
image: *node_image
|
||||
commands:
|
||||
- *enable_pnpm
|
||||
- pnpm build
|
||||
depends_on:
|
||||
- install
|
||||
- verify
|
||||
|
||||
publish-npm:
|
||||
image: *node_image
|
||||
# Publish only when a publishable package changed (or on a release tag); a
|
||||
# pure-docs merge runs no publish. Cheap step, but gated for cleanliness.
|
||||
when:
|
||||
- event: tag
|
||||
- event: [push, manual]
|
||||
branch: main
|
||||
path:
|
||||
include:
|
||||
- 'packages/**'
|
||||
environment:
|
||||
NPM_TOKEN:
|
||||
from_secret: gitea_token
|
||||
commands:
|
||||
- *enable_pnpm
|
||||
# Configure auth for Gitea npm registry
|
||||
- |
|
||||
echo "//git.mosaicstack.dev/api/packages/mosaicstack/npm/:_authToken=$NPM_TOKEN" > ~/.npmrc
|
||||
echo "@mosaicstack:registry=https://git.mosaicstack.dev/api/packages/mosaicstack/npm/" >> ~/.npmrc
|
||||
# Publish non-private packages to Gitea.
|
||||
#
|
||||
# The only publish failure we tolerate is "version already exists" —
|
||||
# that legitimately happens when only some packages were bumped in
|
||||
# the merge. Any other failure (registry 404, auth error, network
|
||||
# error) MUST fail the pipeline loudly: the previous
|
||||
# `|| echo "... continuing"` fallback silently hid a 404 from the
|
||||
# Gitea org rename and caused every @mosaicstack/* publish to fall
|
||||
# on the floor while CI still reported green.
|
||||
- |
|
||||
# Portable sh (Alpine ash) — avoid bashisms like PIPESTATUS.
|
||||
set +e
|
||||
pnpm --filter "@mosaicstack/*" --filter "!@mosaicstack/web" publish --no-git-checks --access public >/tmp/publish.log 2>&1
|
||||
EXIT=$?
|
||||
set -e
|
||||
cat /tmp/publish.log
|
||||
if [ "$EXIT" -eq 0 ]; then
|
||||
echo "[publish] all packages published successfully"
|
||||
exit 0
|
||||
fi
|
||||
# Hard registry / auth / network errors → fatal. Match npm's own
|
||||
# error lines specifically to avoid false positives on arbitrary
|
||||
# log text that happens to contain "E404" etc.
|
||||
if grep -qE "npm (error|ERR!) code (E404|E401|ENEEDAUTH|ECONNREFUSED|ETIMEDOUT|ENOTFOUND)" /tmp/publish.log; then
|
||||
echo "[publish] FATAL: registry/auth/network error detected — failing pipeline" >&2
|
||||
exit 1
|
||||
fi
|
||||
# Only tolerate the explicit "version already published" case.
|
||||
# npm returns this as E403 with body "You cannot publish over..."
|
||||
# or EPUBLISHCONFLICT depending on version.
|
||||
if grep -qE "EPUBLISHCONFLICT|You cannot publish over|previously published" /tmp/publish.log; then
|
||||
echo "[publish] some packages already at this version — continuing (non-fatal)"
|
||||
exit 0
|
||||
fi
|
||||
echo "[publish] FATAL: publish failed with unrecognized error — failing pipeline" >&2
|
||||
exit 1
|
||||
depends_on:
|
||||
- build
|
||||
- verify
|
||||
|
||||
publish-next-npm:
|
||||
image: *node_image
|
||||
# Durable @next integration-line publish. Runs only on next; never writes
|
||||
# the latest dist-tag and never commits the computed prerelease versions.
|
||||
when:
|
||||
- event: [push, manual]
|
||||
branch: next
|
||||
environment:
|
||||
NPM_TOKEN:
|
||||
from_secret: gitea_token
|
||||
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
|
||||
CI_PIPELINE_NUMBER: ${CI_PIPELINE_NUMBER}
|
||||
commands:
|
||||
- *enable_pnpm
|
||||
- |
|
||||
if [ "$CI_COMMIT_BRANCH" != "next" ]; then
|
||||
echo "[publish-next] FATAL: publish-next-npm may only run on next (got '$CI_COMMIT_BRANCH')" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$CI_PIPELINE_NUMBER" ]; then
|
||||
echo "[publish-next] FATAL: CI_PIPELINE_NUMBER is required for prerelease versioning" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "//git.mosaicstack.dev/api/packages/mosaicstack/npm/:_authToken=$NPM_TOKEN" > ~/.npmrc
|
||||
echo "@mosaicstack:registry=https://git.mosaicstack.dev/api/packages/mosaicstack/npm/" >> ~/.npmrc
|
||||
DIST_TAGS_JSON="$(npm view @mosaicstack/mosaic dist-tags --registry https://git.mosaicstack.dev/api/packages/mosaicstack/npm/ --json)"
|
||||
DIST_TAGS_JSON="$DIST_TAGS_JSON" node -e 'const tags = JSON.parse(process.env.DIST_TAGS_JSON || "{}"); if (!tags || typeof tags !== "object" || !Object.hasOwn(tags, "latest")) { throw new Error("Gitea npm registry did not return a usable dist-tags object"); } console.log("[publish-next] registry dist-tags OK: latest=" + tags.latest);'
|
||||
node <<'NODE'
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const pipelineNumber = process.env.CI_PIPELINE_NUMBER;
|
||||
const roots = ['apps', 'packages', 'plugins'];
|
||||
const updated = [];
|
||||
|
||||
function walk(dir) {
|
||||
if (!fs.existsSync(dir)) return;
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.turbo') continue;
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
const packagePath = path.join(fullPath, 'package.json');
|
||||
if (fs.existsSync(packagePath)) updatePackage(packagePath);
|
||||
walk(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updatePackage(packagePath) {
|
||||
const manifest = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
||||
if (!manifest.name?.startsWith('@mosaicstack/') || manifest.private) return;
|
||||
const stableMatch = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/.exec(manifest.version);
|
||||
if (!stableMatch) {
|
||||
throw new Error(manifest.name + " has unsupported semver version '" + manifest.version + "'");
|
||||
}
|
||||
const [, major, minor, patch] = stableMatch;
|
||||
const oldVersion = manifest.version;
|
||||
manifest.version = major + '.' + minor + '.' + (Number(patch) + 1) + '-next.' + pipelineNumber;
|
||||
fs.writeFileSync(packagePath, JSON.stringify(manifest, null, 2) + '\n');
|
||||
updated.push(manifest.name + ' ' + oldVersion + ' -> ' + manifest.version);
|
||||
}
|
||||
|
||||
for (const root of roots) walk(root);
|
||||
if (updated.length === 0) throw new Error('No publishable @mosaicstack/* packages found');
|
||||
console.log('[publish-next] computed prerelease versions for ' + updated.length + ' packages:');
|
||||
for (const line of updated) console.log('[publish-next] ' + line);
|
||||
NODE
|
||||
pnpm --filter "@mosaicstack/*" --filter "!@mosaicstack/web" --filter "!@mosaicstack/mosaic-as" publish --no-git-checks --access public --tag next
|
||||
EXPECTED_VERSION="$(node -p "require('./packages/mosaic/package.json').version")"
|
||||
RESOLVED_VERSION="$(npm view @mosaicstack/mosaic@next version --registry https://git.mosaicstack.dev/api/packages/mosaicstack/npm/)"
|
||||
if [ "$RESOLVED_VERSION" != "$EXPECTED_VERSION" ]; then
|
||||
echo "[publish-next] FATAL: @mosaicstack/mosaic@next resolved '$RESOLVED_VERSION', expected '$EXPECTED_VERSION'" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "[publish-next] @mosaicstack/mosaic@next resolves to $RESOLVED_VERSION"
|
||||
depends_on:
|
||||
- build
|
||||
- verify
|
||||
|
||||
# TODO: Uncomment when ready to publish to npmjs.org
|
||||
# publish-npmjs:
|
||||
# image: *node_image
|
||||
# environment:
|
||||
# NPM_TOKEN:
|
||||
# from_secret: npmjs_token
|
||||
# commands:
|
||||
# - *enable_pnpm
|
||||
# - apk add --no-cache jq bash
|
||||
# - bash scripts/publish-npmjs.sh
|
||||
# depends_on:
|
||||
# - build
|
||||
# - verify
|
||||
# when:
|
||||
# - event: [tag]
|
||||
|
||||
build-gateway:
|
||||
image: gcr.io/kaniko-project/executor:debug
|
||||
when: *image_build_when
|
||||
environment:
|
||||
REGISTRY_USER:
|
||||
from_secret: REGISTRY_USERNAME
|
||||
REGISTRY_PASS:
|
||||
from_secret: REGISTRY_PASSWORD
|
||||
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
|
||||
CI_COMMIT_TAG: ${CI_COMMIT_TAG}
|
||||
CI_COMMIT_SHA: ${CI_COMMIT_SHA}
|
||||
commands:
|
||||
- mkdir -p /kaniko/.docker
|
||||
- echo "{\"auths\":{\"git.mosaicstack.dev\":{\"username\":\"$REGISTRY_USER\",\"password\":\"$REGISTRY_PASS\"}}}" > /kaniko/.docker/config.json
|
||||
- |
|
||||
DESTINATIONS="--destination git.mosaicstack.dev/mosaicstack/stack/gateway:sha-${CI_COMMIT_SHA:0:7}"
|
||||
if [ "$CI_COMMIT_BRANCH" = "next" ]; then
|
||||
if [ -n "$CI_COMMIT_TAG" ]; then
|
||||
echo "[publish] FATAL: next gateway publish must be sha-only; refusing tag '$CI_COMMIT_TAG'" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "[publish] next gateway publish is sha-only"
|
||||
elif [ "$CI_COMMIT_BRANCH" = "main" ]; then
|
||||
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/gateway:latest"
|
||||
elif [ -z "$CI_COMMIT_TAG" ]; then
|
||||
echo "[publish] FATAL: gateway image publish may only run for main, next, or tag events" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "$CI_COMMIT_TAG" ]; then
|
||||
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/gateway:$CI_COMMIT_TAG"
|
||||
fi
|
||||
/kaniko/executor --context . --dockerfile docker/gateway.Dockerfile $DESTINATIONS
|
||||
depends_on:
|
||||
- build
|
||||
- verify
|
||||
|
||||
build-appservice:
|
||||
image: gcr.io/kaniko-project/executor:debug
|
||||
when: *main_image_build_when
|
||||
environment:
|
||||
REGISTRY_USER:
|
||||
from_secret: REGISTRY_USERNAME
|
||||
REGISTRY_PASS:
|
||||
from_secret: REGISTRY_PASSWORD
|
||||
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
|
||||
CI_COMMIT_TAG: ${CI_COMMIT_TAG}
|
||||
CI_COMMIT_SHA: ${CI_COMMIT_SHA}
|
||||
commands:
|
||||
- mkdir -p /kaniko/.docker
|
||||
- echo "{\"auths\":{\"git.mosaicstack.dev\":{\"username\":\"$REGISTRY_USER\",\"password\":\"$REGISTRY_PASS\"}}}" > /kaniko/.docker/config.json
|
||||
- |
|
||||
DESTINATIONS="--destination git.mosaicstack.dev/mosaicstack/stack/appservice:sha-${CI_COMMIT_SHA:0:7}"
|
||||
if [ "$CI_COMMIT_BRANCH" = "main" ]; then
|
||||
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/appservice:latest"
|
||||
fi
|
||||
if [ -n "$CI_COMMIT_TAG" ]; then
|
||||
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/appservice:$CI_COMMIT_TAG"
|
||||
fi
|
||||
/kaniko/executor --context . --dockerfile docker/appservice.Dockerfile $DESTINATIONS
|
||||
depends_on:
|
||||
- build
|
||||
- verify
|
||||
|
||||
build-web:
|
||||
image: gcr.io/kaniko-project/executor:debug
|
||||
when: *main_image_build_when
|
||||
environment:
|
||||
REGISTRY_USER:
|
||||
from_secret: REGISTRY_USERNAME
|
||||
REGISTRY_PASS:
|
||||
from_secret: REGISTRY_PASSWORD
|
||||
CI_COMMIT_BRANCH: ${CI_COMMIT_BRANCH}
|
||||
CI_COMMIT_TAG: ${CI_COMMIT_TAG}
|
||||
CI_COMMIT_SHA: ${CI_COMMIT_SHA}
|
||||
commands:
|
||||
- mkdir -p /kaniko/.docker
|
||||
- echo "{\"auths\":{\"git.mosaicstack.dev\":{\"username\":\"$REGISTRY_USER\",\"password\":\"$REGISTRY_PASS\"}}}" > /kaniko/.docker/config.json
|
||||
- |
|
||||
DESTINATIONS="--destination git.mosaicstack.dev/mosaicstack/stack/web:sha-${CI_COMMIT_SHA:0:7}"
|
||||
if [ "$CI_COMMIT_BRANCH" = "main" ]; then
|
||||
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/web:latest"
|
||||
fi
|
||||
if [ -n "$CI_COMMIT_TAG" ]; then
|
||||
DESTINATIONS="$DESTINATIONS --destination git.mosaicstack.dev/mosaicstack/stack/web:$CI_COMMIT_TAG"
|
||||
fi
|
||||
/kaniko/executor --context . --dockerfile docker/web.Dockerfile $DESTINATIONS
|
||||
depends_on:
|
||||
- build
|
||||
- verify
|
||||
@@ -1,186 +1,124 @@
|
||||
# Agent Guidelines — Mosaic Stack
|
||||
# AGENTS.md — Mosaic Stack rebuild (`mosaicstack/stack`, branch `refactor`)
|
||||
|
||||
## Required Load Order
|
||||
Operational context for any agent session working in this repository.
|
||||
Read top to bottom; it is deliberately short — depth lives in the files it
|
||||
points to, not here.
|
||||
|
||||
1. `~/.config/mosaic/SOUL.md`
|
||||
2. `~/.config/mosaic/STANDARDS.md`
|
||||
3. `~/.config/mosaic/AGENTS.md`
|
||||
4. `~/.config/mosaic/guides/E2E-DELIVERY.md`
|
||||
5. `AGENTS.md` (this file)
|
||||
6. Runtime-specific guide: `~/.config/mosaic/runtime/<runtime>/RUNTIME.md`
|
||||
## What this repository is
|
||||
|
||||
## Project Context
|
||||
Canonical checkout: `/mnt/storage/src/mosaic-stack`, origin `mosaicstack/stack`,
|
||||
working branch `refactor` (Jason-authorized conversion, issue #1495).
|
||||
The new foundation is at the root. `v1/` is archived legacy source, not the current
|
||||
implementation; its instructions and tools do not govern the new foundation.
|
||||
`~/src/mosaic-stack-dev-test` is a compatibility symlink to this checkout, not a
|
||||
second working tree. Both original Git histories are retained. Conversion receipt:
|
||||
`docs/plans/2026-09-07_repository-consolidation-completed.md`.
|
||||
|
||||
Mosaic Stack is a self-hosted, multi-user AI agent platform. It is a TypeScript monorepo with a NestJS gateway, Next.js dashboard, Pi SDK agent runtime, and Discord/Telegram plugin architecture.
|
||||
A rebuild of Mosaic Stack: a file-based, fail-closed
|
||||
orchestration foundation that dispatches sandboxed headless pi workers to do
|
||||
real work, with immutable run records as evidence. Thirteen-plus tagged
|
||||
milestones (`git tag -l`) from `poc-container-hello-v0` to today; suites
|
||||
green at every step. Not production software — a proven foundation.
|
||||
|
||||
### Stack
|
||||
## Non-negotiable invariants (the canon)
|
||||
|
||||
- **API:** NestJS with Fastify (`apps/gateway`)
|
||||
- **Web:** Next.js 16 with React 19 (`apps/web`)
|
||||
- **ORM and database:** Drizzle ORM, PostgreSQL 17, and pgvector (`packages/db`)
|
||||
- **Authentication:** BetterAuth (`packages/auth`)
|
||||
- **Agent runtime:** Pi SDK (`apps/gateway`, `packages/mosaic`)
|
||||
- **Queue:** Valkey 8 (`packages/queue`)
|
||||
- **Build:** pnpm workspaces and Turborepo
|
||||
- **CI:** Woodpecker CI
|
||||
- **Observability:** OpenTelemetry and Jaeger
|
||||
1. **Root is bootstrap-only.** First-class system configuration lives at the
|
||||
repository root; everything else gets a dedicated directory (`roles/`,
|
||||
`contracts/`, `missions/`, `tasks/`, `docs/`). Do not add new files to root.
|
||||
2. **Configuration**: `~/.config/mosaic-dev/config.json` is the sole system
|
||||
config — created only by `scripts/bootstrap.sh`, never overwritten,
|
||||
fail-closed on any problem. Repo-scoped role authority lives in
|
||||
`roles/*.json` (versioned, reviewed commits only).
|
||||
3. **Secrets** never enter the repository or container images; auth is
|
||||
runtime-only (read-only mount or environment variable).
|
||||
4. **Contracts** (`contracts/`) are immutable and image-baked. Missions and
|
||||
tasks are declarative JSON with strict schemas.
|
||||
5. **Run records** under `<dataRoot>/runs/` are write-once evidence — never
|
||||
rewritten, only pruned via `prune` with a receipt.
|
||||
6. **Fail closed**: missing or invalid config/policy refuses the operation.
|
||||
Never improvise around a refusal; diagnose it.
|
||||
7. **Policy**: missions govern tasks (least-privilege intersection — a task
|
||||
narrows, never widens). Role authority is declared in `roles/` and changes
|
||||
only via reviewed commits.
|
||||
8. **Git**: commit only after applicable suites are green. Work on the
|
||||
owner-authorized `refactor` branch; never force-push. Push remains an explicit
|
||||
act. Do not merge into `next` or `main` without separate authorization.
|
||||
`scripts/conductor-apply.sh` commits locally; it does not authorize a push.
|
||||
9. **Append-only logs**: BUILD-LOG.md (phases), `activation-log.jsonl`,
|
||||
`.pruned.log`, docs/SESSIONS.md. Corrections are new entries, never edits.
|
||||
|
||||
### Package Map
|
||||
## Session protocol (mandatory)
|
||||
|
||||
| Package | Purpose | Key Dependencies |
|
||||
| ------------------ | ----------------------------- | -------------------------------- |
|
||||
| `apps/gateway` | NestJS API + WebSocket hub | Fastify, Socket.IO, Pi SDK, OTEL |
|
||||
| `apps/web` | Next.js dashboard | React 19, Tailwind |
|
||||
| `packages/types` | Shared TypeScript contracts | class-validator |
|
||||
| `packages/db` | Drizzle schema and migrations | drizzle-orm, postgres |
|
||||
| `packages/auth` | BetterAuth configuration | better-auth, @mosaicstack/db |
|
||||
| `packages/brain` | Structured data layer | @mosaicstack/db |
|
||||
| `packages/queue` | Valkey task queue and MCP | ioredis |
|
||||
| `packages/coord` | Mission coordination | @mosaicstack/queue |
|
||||
| `packages/mosaic` | Unified `mosaic` CLI and TUI | Ink, Pi SDK, commander |
|
||||
| `plugins/discord` | Discord channel plugin | discord.js |
|
||||
| `plugins/telegram` | Telegram channel plugin | Telegraf |
|
||||
- **Register** your session in `docs/SESSIONS.md` — one append-only line
|
||||
(date, actor, scope, outcome). Never rewrite or remove entries.
|
||||
- **Cadence**: read `docs/plans/CURRENT.md` → execute its single next action
|
||||
fully (implement → test → verify against acceptance criteria → commit →
|
||||
push → close issue) → update CURRENT.md → register in SESSIONS.md.
|
||||
- "next" means one action. A batch mandate ("run the queue") repeats the
|
||||
loop until green or blocked. Blocked means stop and report, never improvise.
|
||||
- Substantial work gets a Gitea issue and a BUILD-LOG phase entry
|
||||
(before/after, with corrections recorded honestly).
|
||||
|
||||
## Architecture and Code Conventions
|
||||
## Role model
|
||||
|
||||
1. Gateway is the single API surface; all clients connect through it.
|
||||
2. Pi SDK is ESM-only; gateway and CLI code must remain ESM.
|
||||
3. Use `"type": "module"`, NodeNext module resolution, and `.js` extensions in imports.
|
||||
4. Keep typed Socket.IO events in `@mosaicstack/types` to enforce client/server contracts.
|
||||
5. Import OTEL tracing before NestJS bootstrap (`import './tracing.js'`).
|
||||
6. Use explicit `@Inject()` decorators in NestJS because tsx/esbuild does not emit decorator metadata.
|
||||
7. Keep DTOs in `*.dto.ts` files at module boundaries.
|
||||
8. BetterAuth owns authentication tables; their schema is defined in `@mosaicstack/db`.
|
||||
9. Create a task-specific scratchpad for non-trivial work.
|
||||
- **Conductor**: a system-scoped role — not an agent, not a daemon. Holds
|
||||
git/credentials/policy authority; decomposes, dispatches, reviews,
|
||||
verifies, integrates. Protocol: `docs/plans/CONDUCTOR.md`. Exists only
|
||||
when invoked; push is never automatic.
|
||||
- **Workers**: headless pi via `scripts/run-task.sh` — sandboxed workspace,
|
||||
tools allowlist, optional persistent sessions and forks; no git, no
|
||||
credentials, no policy control.
|
||||
- Worker runs deliberately exclude this file (`--no-context-files` in the
|
||||
adapter): worker context is contracts + mission via the generated system
|
||||
prompt. This file is for conductor-level sessions.
|
||||
|
||||
## Development Workflow
|
||||
## Command surface
|
||||
|
||||
Requirements: Node.js 20+, pnpm 10.6.2, and Docker Compose when optional local services are needed.
|
||||
`scripts/bootstrap.sh` (idempotent) · `build.sh` · `hello.sh` ·
|
||||
`verify.sh` · `run-task.sh run <task.json>` · `release.sh
|
||||
package|activate|rollback|status` · `auth.sh status|accounts` · `reset.sh` (**danger**: wipes the data
|
||||
root; triple-safety-checked) · `mosaic-task.mjs validate|run|show|list|retry|prune|resolve-role` ·
|
||||
`agent.sh <name>` (interactive TUI agent) ·
|
||||
suites: `test-config.sh`, `test-task.sh`, `test-release.sh`,
|
||||
`test-conductor.sh`, `test-auth.sh`.
|
||||
|
||||
```bash
|
||||
pnpm install --frozen-lockfile
|
||||
pnpm preflight
|
||||
Full reference — usage, fields, exit codes, safety notes:
|
||||
`docs/TOOLS.md` (read on demand; do not rely on this summary for detail).
|
||||
|
||||
# Optional local queue service only; do not start the full Compose stack.
|
||||
docker compose up -d valkey
|
||||
```
|
||||
## Data map (canon)
|
||||
|
||||
The pre-push hook requires:
|
||||
- `~/.config/mosaic-dev/config.json` — system config (user-authored; never
|
||||
auto-written).
|
||||
- `<dataRoot>` (from config; default `~/.mosaic-dev`):
|
||||
- `runs/` — write-once run evidence (`result.json`, snapshots, `stderr.txt`)
|
||||
- `sessions/` — pi JSONL session trees, one directory per named session
|
||||
- `workspaces/` — agent file effects (persistent or `:run` ephemeral)
|
||||
- `state/` — release pointer + append-only activation/auto-apply logs
|
||||
- Ownership is per-directory; nothing shares state. Directory map and
|
||||
lifecycle rules: README.md "Data map" section.
|
||||
|
||||
```bash
|
||||
pnpm preflight && pnpm typecheck && pnpm lint && pnpm format:check
|
||||
```
|
||||
## Pointers (depth lives here)
|
||||
|
||||
Software delivery also requires the applicable tests. Common repository commands are:
|
||||
- `docs/plans/CURRENT.md` — THE next action (single source of "what now")
|
||||
- `docs/plans/ROADMAP.md` — agreed milestone path (M16+)
|
||||
- `docs/plans/CONDUCTOR.md` — orchestration protocol and guardrails
|
||||
- `docs/plans/2026-09-02_atomic-mosaic-foundation.md` — architecture, invariants
|
||||
- `docs/plans/2026-09-03_autonomous-run.md` — batch-run tracker
|
||||
- `BUILD-LOG.md` — append-only build/verification history with corrections
|
||||
- `LAYERS.md` — implemented vs deferred layers
|
||||
- `docs/SESSIONS.md` — session registry
|
||||
- `adapters/README.md` — the harness adapter contract
|
||||
- `roles/` — role contracts (conductor, future agent/coder/reviewer)
|
||||
|
||||
```bash
|
||||
pnpm typecheck # TypeScript checks across the workspace
|
||||
pnpm lint # ESLint across the workspace
|
||||
pnpm test # Checkout tests and package Vitest suites
|
||||
pnpm format:check # Prettier check
|
||||
pnpm build # Build all packages and applications
|
||||
```
|
||||
## Recovery rule
|
||||
|
||||
## Branch Model and Merge Process — `main` and `next` (CANONICAL)
|
||||
Compacted, restarted, or new? Nothing that matters is lost: this file +
|
||||
`docs/plans/CURRENT.md` + `git log --oneline -10` + the suites reconstruct
|
||||
the full state. **Never guess** — verify with the suites; the run records
|
||||
and logs hold the receipts.
|
||||
|
||||
**Every contribution targets `next` first. No exceptions.** Features, fixes, tests,
|
||||
docs, and policy changes all take the same route; urgency changes queue priority,
|
||||
never the route. Agents never commit to or merge into `main`.
|
||||
## Version pin
|
||||
|
||||
| Branch | Role | Who merges into it |
|
||||
| ------ | ---------------------------------------------------------------- | --------------------------------------------------------------------------- |
|
||||
| `next` | Integration trunk — the only PR target for contributions | The designated merge-gate agent, after all gates pass. Never the PR author. |
|
||||
| `main` | Stable/release line — receives promotion merges from `next` only | Jason only (or an agent he explicitly delegates for a named promotion). |
|
||||
|
||||
### Contribution sequencing (in order, no skipping)
|
||||
|
||||
1. **Issue first.** Work is tracked in a Gitea issue before a branch exists. The
|
||||
issue number appears in the branch name and the PR body.
|
||||
2. **Branch from the current `origin/next` head.** Name it
|
||||
`feat/…`, `fix/…`, `docs/…`, or `test/…` with the issue number
|
||||
(e.g. `docs/1214-branch-process`). Record the base SHA in the PR body.
|
||||
3. **Develop with evidence.** Applicable tests accompany the change. Hooks are
|
||||
never bypassed (`--no-verify` is prohibited). Stage explicit paths — never
|
||||
`git add -A`.
|
||||
4. **Open the PR against `next`.** The body states: scope, base SHA,
|
||||
verification commands with results, and any known pre-existing failures on
|
||||
the base — documented, not retried to green and not absorbed silently.
|
||||
5. **CI must be terminal-green on the exact head.** All bounded Woodpecker
|
||||
steps succeed (`verify-terminal-green` contract). Pipelines for fork PRs
|
||||
start `blocked`; a maintainer approves the run — approving CI is not
|
||||
approving the PR.
|
||||
6. **Independent review. Self-merge is prohibited** — for every agent, on every
|
||||
PR, including trivial ones. Where the change touches protected or
|
||||
contract-bearing content, the reviewer verifies the exact head
|
||||
(exact-byte/exact-blob comparison), not a description of it. An `AMEND`
|
||||
verdict returns the PR to its author; the reviewer's gate stays held until
|
||||
a fresh exact head passes.
|
||||
7. **Merge into `next`** happens only after CI green + review pass, pinned to
|
||||
the reviewed head SHA (a post-review push voids the review).
|
||||
8. **Promotion `next` → `main`** is a deliberate, Jason-owned reconciliation
|
||||
merge — not part of any contribution's lifecycle. Contributors are done at
|
||||
step 7.
|
||||
|
||||
### Responsibilities
|
||||
|
||||
- **Contributor** — base pinning, green CI, evidence in the PR body,
|
||||
responding to AMEND verdicts, never merging own work.
|
||||
- **Reviewer / merge gate** — independent verification on the exact head;
|
||||
holds and lifts gates; executes the merge into `next`.
|
||||
- **Orchestrator / adjudicator** — cross-PR sequencing, disposition when PRs
|
||||
collide, conflict adjudication.
|
||||
- **Jason** — `next` → `main` promotions, merge-authority grants, collaborator
|
||||
and token provisioning. Agents cannot grant themselves or each other any of
|
||||
these.
|
||||
|
||||
### Hotfixes and divergence
|
||||
|
||||
- A hotfix follows the same path: branch from `next`, PR to `next`, gates,
|
||||
merge, then an expedited Jason-owned promotion if `main` needs it urgently.
|
||||
Committing the fix to `main` directly is prohibited even under pressure.
|
||||
- **Never land work on `main` that is not on `next`.** This has happened
|
||||
(issue #1152's goal controller reached `main` without reaching `next`) and
|
||||
every later PR paid for it. If it happens anyway: transplant the work onto
|
||||
a `next`-based branch with provenance-preserving commits
|
||||
(`git cherry-pick -x` or explicit SHA references in the messages), PR it
|
||||
through the normal gates, and let promotion re-align `main`. Do not
|
||||
hand-patch `main` to compensate.
|
||||
- Force-pushing a branch you do not own is prohibited; rebasing your own PR
|
||||
branch is fine before review, and voids any review already given.
|
||||
|
||||
## Database and Local Runtime Safety
|
||||
|
||||
- Current local data-layer work uses in-process PGlite; leave `DATABASE_URL` unset.
|
||||
- PostgreSQL execution is held until KBN-101-00, KBN-101-03, and KBN-101-05 land.
|
||||
- Do not invoke a migration runner, initialization SQL, or the Compose PostgreSQL service from this checkout.
|
||||
- Do not start Gateway/Web or run root `pnpm dev` as a local PGlite route. The current dotenv loader can inherit a daemon PostgreSQL DSN; KBN-101-02 must make that path fail closed first.
|
||||
- Migration artifact generation is offline and does not authorize PostgreSQL access:
|
||||
|
||||
```bash
|
||||
pnpm --filter @mosaicstack/db db:generate
|
||||
```
|
||||
|
||||
## docs/TASKS.md — Schema (CANONICAL)
|
||||
|
||||
The `agent` column specifies the required model for each task. **This is set at task creation by the orchestrator and must not be changed by workers.**
|
||||
|
||||
| Value | When to use | Budget |
|
||||
| --------- | ----------------------------------------------------------- | -------------------------- |
|
||||
| `codex` | All coding tasks (default for implementation) | OpenAI credits — preferred |
|
||||
| `glm-5.1` | Cost-sensitive coding where Codex is unavailable | Z.ai credits |
|
||||
| `haiku` | Review gates, verify tasks, status checks, docs-only | Cheapest Claude tier |
|
||||
| `sonnet` | Complex planning, multi-file reasoning, architecture review | Claude quota |
|
||||
| `opus` | Major cross-cutting architecture decisions ONLY | Most expensive — minimize |
|
||||
| `—` | No preference / auto-select cheapest capable | Pipeline decides |
|
||||
|
||||
Pipeline crons read this column and spawn accordingly. Workers never modify `docs/TASKS.md` — only the orchestrator writes it.
|
||||
|
||||
**Full schema:**
|
||||
|
||||
```
|
||||
| id | status | description | issue | agent | repo | branch | depends_on | estimate | notes |
|
||||
```
|
||||
|
||||
- `status`: `not-started` | `in-progress` | `done` | `failed` | `blocked` | `needs-qa`
|
||||
- `agent`: model value from table above (set before spawning)
|
||||
- `estimate`: token budget e.g. `8K`, `25K`
|
||||
`@earendil-works/pi-coding-agent` is pinned exactly (see `package.json` /
|
||||
`RELEASE`); never install unversioned. Release identity: `RELEASE` file
|
||||
(0.0.X until declared stable); image tags derive from it.
|
||||
|
||||
@@ -0,0 +1,371 @@
|
||||
# Minimal Mosaic Stack container proof of concept
|
||||
|
||||
## Purpose
|
||||
|
||||
Build the smallest isolated container that can:
|
||||
- launch Pi
|
||||
- load a small set of Mosaic-style contract files
|
||||
- send one real request to a model
|
||||
- return a known response.
|
||||
|
||||
This is a standalone experiment. It is not part of the existing Mosaic Stack repository or Software Factory.
|
||||
|
||||
## Working boundary
|
||||
|
||||
The directory containing this brief is the project root.
|
||||
|
||||
### Do not read, copy, mount, import, or modify anything from:
|
||||
- `/home/jwoltje/.mosaic`
|
||||
- `/home/jwoltje/.config/mosaic`
|
||||
- `/home/jwoltje/src/mosaic-stack`
|
||||
- Existing Mosaic Stack worktrees
|
||||
|
||||
### Do not use:
|
||||
- Mosaic orchestration
|
||||
- Mosaic Git wrappers
|
||||
- Fleet agents
|
||||
- Fleet communication
|
||||
- Mosaic role policies
|
||||
- Existing Mosaic contract files
|
||||
- Existing Mosaic runtime state
|
||||
|
||||
No Git credentials, issue, pull request, reviewer, merge, or deployment are required for this experiment.
|
||||
|
||||
Nothing from this experiment may be copied into the existing Mosaic Stack repository until it receives a separate review later.
|
||||
|
||||
## Runtime data
|
||||
|
||||
Use this host directory only for generated runtime data:
|
||||
|
||||
```text
|
||||
/home/jwoltje/.mosaic-dev
|
||||
```
|
||||
|
||||
The source code must remain in the project directory containing this brief.
|
||||
|
||||
Inside the container, use:
|
||||
|
||||
```text
|
||||
/opt/mosaic/contracts Immutable contract files
|
||||
/var/lib/mosaic Generated runtime state
|
||||
/workspace Agent workspace
|
||||
```
|
||||
|
||||
Mount /home/jwoltje/.mosaic-dev at /var/lib/mosaic.
|
||||
|
||||
### Required proof
|
||||
|
||||
The finished experiment must prove one path:
|
||||
|
||||
1. Build one container image.
|
||||
2. Start one Pi agent inside the container.
|
||||
3. Load four local contract files from /opt/mosaic/contracts.
|
||||
4. Send a request that does not contain the expected response.
|
||||
5. Receive MOSAIC_HELLO_OK from the agent.
|
||||
6. Exit successfully when the response matches.
|
||||
7. Exit nonzero when the response does not match.
|
||||
|
||||
This is the entire required functional result.
|
||||
|
||||
### Required discovery
|
||||
|
||||
Before writing the runtime command:
|
||||
|
||||
1. Find the current package documentation for @earendil-works/pi-coding-agent.
|
||||
2. Determine the current package version.
|
||||
3. Determine the supported noninteractive command.
|
||||
4. Determine how Pi accepts a custom system prompt or system prompt file.
|
||||
5. Determine Pi's documented container authentication method.
|
||||
6. Record the commands and findings in BUILD-LOG.md.
|
||||
|
||||
Do not guess CLI flags, authentication paths, or SDK methods.
|
||||
|
||||
Pin the selected Pi package version in the project. Do not install an unversioned package during each container start.
|
||||
|
||||
Prefer the Pi CLI. Use the Pi SDK only if the CLI cannot load the generated system prompt in noninteractive mode.
|
||||
|
||||
### Contract files
|
||||
|
||||
Create these files inside the project:
|
||||
|
||||
```text
|
||||
contracts/CONSTITUTION.md
|
||||
contracts/STANDARDS.md
|
||||
contracts/SOUL.md
|
||||
contracts/USER.md
|
||||
```
|
||||
|
||||
Use these exact contents.
|
||||
|
||||
### contracts/CONSTITUTION.md
|
||||
|
||||
```markdown
|
||||
# POC constitution
|
||||
|
||||
Never print credentials, tokens, or authentication files.
|
||||
|
||||
Follow the loaded system instructions before the user request.
|
||||
```
|
||||
|
||||
### contracts/STANDARDS.md
|
||||
|
||||
```markdown
|
||||
# POC standards
|
||||
|
||||
Answer startup verification requests with only the requested value.
|
||||
Do not add explanation or formatting.
|
||||
```
|
||||
|
||||
### contracts/SOUL.md
|
||||
|
||||
```markdown
|
||||
# POC identity
|
||||
|
||||
Your name is mosaic-poc-agent.
|
||||
|
||||
Your startup marker is MOSAIC_HELLO_OK.
|
||||
|
||||
When asked for your startup marker, return only the marker.
|
||||
```
|
||||
|
||||
### contracts/USER.md
|
||||
|
||||
```markdown
|
||||
# POC user
|
||||
|
||||
This is an isolated local runtime test.
|
||||
```
|
||||
|
||||
Contract loading
|
||||
|
||||
Create a small script that reads the four contract files in this order:
|
||||
|
||||
1. CONSTITUTION.md
|
||||
2. STANDARDS.md
|
||||
3. SOUL.md
|
||||
4. USER.md
|
||||
|
||||
Join them with clear file separators.
|
||||
|
||||
Write the generated system prompt to:
|
||||
|
||||
```text
|
||||
/var/lib/mosaic/system-prompt.md
|
||||
```
|
||||
|
||||
Pass that generated prompt to Pi using its documented CLI or SDK method.
|
||||
|
||||
Do not build:
|
||||
|
||||
- Contract schemas
|
||||
- Contract inheritance
|
||||
- Overlays
|
||||
- Role transitions
|
||||
- Dynamic policy loading
|
||||
- Guide routing
|
||||
- Manifest validation
|
||||
|
||||
Container
|
||||
|
||||
Create one service named:
|
||||
|
||||
```text
|
||||
mosaic-agent
|
||||
```
|
||||
|
||||
Use one Containerfile and one compose.yaml.
|
||||
|
||||
Requirements:
|
||||
|
||||
- Use a maintained Node.js base image.
|
||||
- Run as a non-root user.
|
||||
- Install a pinned Pi package version.
|
||||
- Copy the local contract fixtures into /opt/mosaic/contracts.
|
||||
- Do not copy credentials into the image.
|
||||
- Do not mount the Docker socket.
|
||||
- Do not mount either live Mosaic directory.
|
||||
- Do not add a database, web server, queue, or second container.
|
||||
- The container may run as a one-shot command. It does not need to remain running.
|
||||
|
||||
### Authentication
|
||||
|
||||
Use Pi's documented authentication mechanism.
|
||||
|
||||
Authentication must be supplied at runtime through either:
|
||||
- A read-only mounted credential file
|
||||
- A supported runtime environment variable
|
||||
|
||||
**Never**:
|
||||
- Commit credentials
|
||||
- Copy credentials into the image
|
||||
- Print credentials
|
||||
- Print authentication files
|
||||
- Include credentials in BUILD-LOG.md
|
||||
- Store credentials under the project directory
|
||||
|
||||
Provide .env.example only for non-secret settings such as model or provider names.
|
||||
|
||||
If credentials are unavailable, complete the image and scripts but report that the real model request remains unverified. Do not fake the response.
|
||||
|
||||
### Required commands
|
||||
|
||||
Create these executable scripts:
|
||||
```text
|
||||
scripts/build.sh
|
||||
scripts/hello.sh
|
||||
scripts/verify.sh
|
||||
scripts/reset.sh
|
||||
```
|
||||
|
||||
### scripts/build.sh
|
||||
|
||||
Build the container image using Docker Compose.
|
||||
|
||||
### scripts/hello.sh
|
||||
|
||||
Run the mosaic-agent service as a one-shot container.
|
||||
|
||||
Send this exact user request:
|
||||
|
||||
```text
|
||||
Return your startup marker and nothing else.
|
||||
```
|
||||
|
||||
The request must not contain MOSAIC_HELLO_OK.
|
||||
|
||||
Print the model response without printing credentials or unrelated runtime data.
|
||||
|
||||
### scripts/verify.sh
|
||||
|
||||
Run the complete test.
|
||||
|
||||
**It must**:
|
||||
|
||||
1. Build or confirm the image is built.
|
||||
2. Run the agent request.
|
||||
3. Remove surrounding whitespace from the response.
|
||||
4. Compare the response with MOSAIC_HELLO_OK.
|
||||
5. Exit 0 only when they match exactly.
|
||||
6. Exit nonzero with a clear error when they do not match.
|
||||
|
||||
### scripts/reset.sh
|
||||
|
||||
Delete generated POC state only when all checks pass:
|
||||
1. The resolved path is exactly /home/jwoltje/.mosaic-dev.
|
||||
2. The path is not a symbolic link.
|
||||
3. The directory contains a .mosaic-poc-root ownership marker created by this project.
|
||||
|
||||
Refuse to delete anything if a check fails.
|
||||
|
||||
## Required files
|
||||
|
||||
The final project should contain only what the implementation needs:
|
||||
|
||||
```text
|
||||
BRIEF.md
|
||||
BUILD-LOG.md
|
||||
README.md
|
||||
LAYERS.md
|
||||
Containerfile
|
||||
compose.yaml
|
||||
package.json
|
||||
package-lock.json
|
||||
.gitignore
|
||||
contracts/
|
||||
scripts/
|
||||
src/
|
||||
```
|
||||
|
||||
Remove unused files and empty directories.
|
||||
|
||||
Build log
|
||||
|
||||
Create BUILD-LOG.md.
|
||||
|
||||
Treat it as append-only.
|
||||
|
||||
Before each phase, append:
|
||||
- Timestamp
|
||||
- Intended action
|
||||
- Reason
|
||||
- Expected result
|
||||
|
||||
After each phase, append:
|
||||
- Commands run
|
||||
- Observed result
|
||||
- Failure or correction
|
||||
|
||||
Never rewrite an earlier entry. Add a correction as a new entry.
|
||||
|
||||
Do not record credentials.
|
||||
|
||||
Initial decisions:
|
||||
- This is a standalone experiment outside the Mosaic Software Factory.
|
||||
- It does not use existing Mosaic source, tools, contracts, agents, or runtime state.
|
||||
- The first proof uses one Pi agent and four small local contract files.
|
||||
- The only required model result is MOSAIC_HELLO_OK.
|
||||
- Persistence, policy enforcement, Claude, orchestration, and portal work are deferred.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
The experiment passes when:
|
||||
1. scripts/build.sh exits 0.
|
||||
2. The image contains the four local contract files.
|
||||
3. The image contains no credentials.
|
||||
4. The container has no mounts from ~/.mosaic or ~/.config/mosaic.
|
||||
5. scripts/hello.sh performs a real model request.
|
||||
6. The request does not contain the expected marker.
|
||||
7. The agent returns exactly MOSAIC_HELLO_OK.
|
||||
8. scripts/verify.sh exits 0.
|
||||
9. Changing the expected value makes scripts/verify.sh exit nonzero.
|
||||
10. scripts/reset.sh refuses unsafe paths.
|
||||
11. Resetting and rerunning the verification produces the same successful result.
|
||||
|
||||
## Deferred layers
|
||||
|
||||
Document these in LAYERS.md. Do not implement them.
|
||||
|
||||
- L0: Container builds and returns MOSAIC_HELLO_OK.
|
||||
- L1: Persist and resume a named Pi session.
|
||||
- L2: Add a fixed tool permission policy.
|
||||
- L3: Load full versioned contract bundles.
|
||||
- L4: Add Claude as a second runtime.
|
||||
- L5: Add multiple agents and communication.
|
||||
- L6: Add orchestration, knowledge storage, and portal features.
|
||||
|
||||
## Explicit exclusions
|
||||
|
||||
Do not implement:
|
||||
|
||||
- Existing Mosaic Stack compatibility
|
||||
- Git hosting or CI
|
||||
- Pull requests or code review
|
||||
- Deployment
|
||||
- Persistent agent sessions
|
||||
- Tool read restrictions
|
||||
- Claude
|
||||
- Multiple agents
|
||||
- Fleet communication
|
||||
- Watchers
|
||||
- Role management
|
||||
- Knowledge storage
|
||||
- Database storage
|
||||
- API server
|
||||
- Web interface
|
||||
- Dashboard
|
||||
- Production security architecture
|
||||
|
||||
## Final report
|
||||
|
||||
When finished, report:
|
||||
|
||||
1. Files created.
|
||||
2. Pi package version.
|
||||
3. Exact build command.
|
||||
4. Exact verification command.
|
||||
5. Verification output with credentials removed.
|
||||
6. Whether the real model request passed.
|
||||
7. Any remaining failure.
|
||||
8. Anything implemented beyond this brief.
|
||||
|
||||
Do not describe the experiment as production-ready.
|
||||
+1832
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1 @@
|
||||
# Claude Compatibility Pointer
|
||||
|
||||
@AGENTS.md
|
||||
|
||||
Do not add project guidance here. Keep `AGENTS.md` authoritative so every agent runtime receives the same instructions.
|
||||
@AGENTS.md
|
||||
@@ -0,0 +1,43 @@
|
||||
# Minimal Mosaic Stack POC agent image.
|
||||
# Base: maintained Node.js image (same family as Pi's documented
|
||||
# containerization example in docs/containerization.md).
|
||||
FROM node:24-bookworm-slim
|
||||
|
||||
# Tools Pi's documented container image expects (bash, CA certs, git, ripgrep).
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends bash ca-certificates git ripgrep \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Non-root user: the maintained node image ships a 'node' user at
|
||||
# uid/gid 1000, which matches the host user that owns the runtime
|
||||
# state directory mounted at /var/lib/mosaic. It is reused as-is.
|
||||
|
||||
# Pinned Pi install: package.json pins the exact version and
|
||||
# package-lock.json is installed with npm ci. No unversioned installs.
|
||||
WORKDIR /opt/app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci --ignore-scripts
|
||||
|
||||
# Immutable contract fixtures (required location), runtime scripts, and
|
||||
# runtime adapters.
|
||||
COPY contracts /opt/mosaic/contracts
|
||||
COPY src /opt/mosaic/src
|
||||
COPY adapters /opt/mosaic/adapters
|
||||
RUN chmod 0555 /opt/mosaic/contracts /opt/mosaic/contracts/* \
|
||||
&& chmod 0555 /opt/mosaic/src /opt/mosaic/src/*.sh \
|
||||
&& chmod 0555 /opt/mosaic/adapters /opt/mosaic/adapters/*/adapter.sh
|
||||
|
||||
# Writable state, workspace, and pi agent directory (auth.json is
|
||||
# bind-mounted read-only at runtime; nothing is copied into the image).
|
||||
RUN mkdir -p /var/lib/mosaic /workspace /home/node/.pi/agent \
|
||||
&& chown -R node:node /var/lib/mosaic /workspace /home/node /opt/app
|
||||
|
||||
USER node
|
||||
WORKDIR /workspace
|
||||
ENV HOME=/home/node \
|
||||
PATH="/opt/app/node_modules/.bin:${PATH}" \
|
||||
PI_OFFLINE=1
|
||||
|
||||
# One-shot agent: args form the user request (default is the startup
|
||||
# verification request defined in compose.yaml).
|
||||
ENTRYPOINT ["/opt/mosaic/src/run-agent.sh"]
|
||||
@@ -0,0 +1,50 @@
|
||||
# LAYERS
|
||||
|
||||
Deferred capability layers for the Mosaic experiment. Only L0 is implemented by
|
||||
this proof of concept; everything below it is documented here and deliberately
|
||||
not implemented (see BRIEF.md, "Explicit exclusions").
|
||||
|
||||
## L0 — Implemented: container returns MOSAIC_HELLO_OK
|
||||
|
||||
One image (`mosaic-poc-agent:0.84.4`, built on `node:24-bookworm-slim`, non-root,
|
||||
pinned Pi) runs one Pi agent one-shot. Four immutable local contract files are
|
||||
loaded in fixed order into the generated system prompt
|
||||
(`/var/lib/mosaic/system-prompt.md`). One real model request is sent
|
||||
noninteractively; the response must equal `MOSAIC_HELLO_OK` exactly or the
|
||||
verification exits nonzero. Authentication is supplied at runtime only
|
||||
(read-only mounted pi auth file, or a provider API key environment variable).
|
||||
|
||||
## L1 — Deferred: persist and resume a named Pi session
|
||||
|
||||
Keep a named Pi session across container runs (`--name`, session storage under
|
||||
`/var/lib/mosaic`), resume it with the documented session flags, and verify
|
||||
state survives a container restart.
|
||||
|
||||
## L2 — Deferred: fixed tool permission policy
|
||||
|
||||
Add a fixed allow/deny policy for Pi tools (e.g. restricting built-in tools via
|
||||
documented `--tools` / `--exclude-tools` or an extension-based permission gate),
|
||||
so contract files can constrain what the agent may do, not just what it says.
|
||||
|
||||
## L3 — Deferred: load full versioned contract bundles
|
||||
|
||||
Replace the four static fixtures with versioned contract bundles: bundle
|
||||
manifests, contract versions, and deterministic ordering/hashing, loaded from
|
||||
an immutable bundle artifact instead of files copied at image build time.
|
||||
|
||||
## L4 — Deferred: Claude as a second runtime
|
||||
|
||||
Add a second runtime (Claude) alongside the Pi agent in the same container
|
||||
stack, behind the same contract-loading path, to compare behavior across
|
||||
runtimes.
|
||||
|
||||
## L5 — Deferred: multiple agents and communication
|
||||
|
||||
Run several named agents with defined roles and a communication channel between
|
||||
them (message passing or shared state under `/var/lib/mosaic`).
|
||||
|
||||
## L6 — Deferred: orchestration, knowledge storage, and portal features
|
||||
|
||||
Fleet-level orchestration, knowledge storage, monitoring, and portal UI on top
|
||||
of L1-L5. This is where the existing Mosaic Stack concepts would be re-evaluated
|
||||
from first principles.
|
||||
@@ -1,415 +1,238 @@
|
||||
# Mosaic Stack
|
||||
# Mosaic Stack — new foundation
|
||||
|
||||
Self-hosted, multi-user AI agent platform. One config, every runtime, same standards.
|
||||
The active rebuild is at this repository's root. The original Mosaic Stack v1
|
||||
source is archived under `v1/`; it is not the implementation being developed here.
|
||||
|
||||
Mosaic gives you a unified launcher for Claude Code, Codex, OpenCode, and Pi — injecting consistent system prompts, guardrails, skills, and mission context into every session. A NestJS gateway provides the API surface, a Next.js dashboard gives you the UI, and a plugin system connects Discord, Telegram, and more.
|
||||
- Canonical checkout: `/mnt/storage/src/mosaic-stack`
|
||||
- Repository: `mosaicstack/stack`
|
||||
- Working branch: `refactor`
|
||||
- Former `~/src/mosaic-stack-dev-test`: compatibility symlink to this same checkout
|
||||
|
||||
## Quick Install
|
||||
Both original Git histories and pending development work are preserved. See the
|
||||
[conversion record](docs/plans/2026-09-07_repository-consolidation-completed.md)
|
||||
and [current next action](docs/plans/CURRENT.md). Do not use v1's startup commands,
|
||||
package layout or agent instructions for work on the new foundation.
|
||||
|
||||
```bash
|
||||
curl -fsSL https://mosaicstack.dev/install.sh | bash
|
||||
## Original container proof
|
||||
|
||||
The foundation began as a standalone container experiment. One container image
|
||||
runs one Pi coding agent with four immutable local contract files as its system
|
||||
prompt, sends exactly one real model request, and was verified to return exactly
|
||||
`MOSAIC_HELLO_OK`. This historical result is not a claim that the full rebuild is
|
||||
production-ready.
|
||||
|
||||
## Layout
|
||||
|
||||
```text
|
||||
BRIEF.md requirements for the original container proof
|
||||
BUILD-LOG.md append-only build/verification log
|
||||
LAYERS.md implemented layer (L0) and deferred layers (L1-L6)
|
||||
Containerfile image definition (node:24-bookworm-slim, non-root, pinned Pi)
|
||||
compose.yaml one service: mosaic-agent (one-shot; configured via env)
|
||||
package.json pins @earendil-works/pi-coding-agent at exactly 0.84.4
|
||||
package-lock.json resolved lockfile used by npm ci in the image
|
||||
.env.example non-secret settings only (credential-file path, env-var auth)
|
||||
contracts/ CONSTITUTION.md, STANDARDS.md, SOUL.md, USER.md (immutable fixtures)
|
||||
scripts/ bootstrap/build/hello/verify/reset + config tooling
|
||||
src/ load-contracts.sh, run-agent.sh (run inside the container)
|
||||
docs/plans/ architecture and milestone plans
|
||||
```
|
||||
|
||||
Or use the direct URL:
|
||||
## Configuration
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://git.mosaicstack.dev/mosaicstack/stack/raw/branch/main/tools/install.sh)
|
||||
The sole discovery entry point is:
|
||||
|
||||
```text
|
||||
~/.config/mosaic-dev/config.json
|
||||
```
|
||||
|
||||
The installer auto-launches the setup wizard, which walks you through gateway install and verification. Flags for non-interactive use:
|
||||
Created only by the explicit, idempotent bootstrap:
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL …) --yes # Accept all defaults
|
||||
bash <(curl -fsSL …) --yes --no-auto-launch # Install only, skip wizard
|
||||
scripts/bootstrap.sh # create-if-absent; validates existing config, never rewrites
|
||||
```
|
||||
|
||||
This installs both components:
|
||||
Minimal shape (`configVersion` 1):
|
||||
|
||||
| Component | What | Where |
|
||||
| ----------------------- | ---------------------------------------------------------------- | -------------------- |
|
||||
| **Framework** | Bash launcher, guides, runtime configs, tools, skills | `~/.config/mosaic/` |
|
||||
| **@mosaicstack/mosaic** | Unified `mosaic` CLI — TUI, gateway client, wizard, auto-updater | `~/.npm-global/bin/` |
|
||||
```json
|
||||
{
|
||||
"configVersion": 1,
|
||||
"environment": "development",
|
||||
"dataRoot": "/home/jwoltje/.mosaic-dev",
|
||||
"execution": {
|
||||
"backend": "docker",
|
||||
"provider": "zai",
|
||||
"model": "glm-5.3-flash"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Install lanes
|
||||
Rules enforced by `scripts/mosaic-config.mjs`:
|
||||
|
||||
| Lane | Command | Use when | Source |
|
||||
| ------------------------ | ------------------------------------- | ----------------------------------------------------- | ----------------------------------------------------------------------- |
|
||||
| Stable | `bash tools/install.sh` | You want the released Mosaic CLI/framework | npm registry `@mosaicstack/mosaic@latest` + framework archive at `main` |
|
||||
| Prerelease integration | `bash tools/install.sh --next` | You want the current `next` integration branch | Build-from-source at `next` |
|
||||
| Contributor/source build | `bash tools/install.sh --dev --ref X` | You are testing a branch before release; `--ref` wins | Build-from-source at the requested ref |
|
||||
- Unknown keys, unsupported versions/backends, and malformed JSON exit nonzero; nothing is modified.
|
||||
- `dataRoot` must be absolute, canonical, and must not be or contain the home or configuration directory.
|
||||
- Validation failures never touch config, state, or images.
|
||||
- `scripts/test-config.sh` runs the sandboxed config selftests (no Docker required).
|
||||
|
||||
`--next` is shorthand for the prerelease integration lane: it enables source-build mode and uses `next` unless an explicit `--ref` or `MOSAIC_REF` is provided.
|
||||
Run paths (`build/hello/verify/reset`) fail closed when configuration is missing or invalid; they never invent it.
|
||||
|
||||
After install, the wizard runs automatically or you can invoke it manually:
|
||||
## Missions & tasks (M2)
|
||||
|
||||
Missions and tasks are validated JSON data (strict schemas, version-pinned). The M2 layer is host-side only: mission directives are recorded for provenance but do not yet reach the runtime system prompt (capability/policy layer comes later).
|
||||
|
||||
```text
|
||||
missions/hello.json objective + directives (missionVersion 1)
|
||||
tasks/hello-marker.json prompt + optional mission ref + expectExact + timeout
|
||||
<dataRoot>/runs/r-<id>/ immutable run record: task.json, mission.json,
|
||||
stderr.txt, result.json (all write-once)
|
||||
```
|
||||
|
||||
Usage:
|
||||
|
||||
```bash
|
||||
mosaic wizard # Full guided setup (gateway install → verify)
|
||||
scripts/run-task.sh validate tasks/hello-marker.json # strict validation, writes nothing
|
||||
scripts/run-task.sh run tasks/hello-marker.json # execute; result recorded under dataRoot/runs
|
||||
scripts/mosaic-task.mjs list # list runs and statuses
|
||||
scripts/test-task.sh # selftests (schema negatives + live runs)
|
||||
```
|
||||
|
||||
### Requirements
|
||||
A run exits 0 only when its expectation is met (`expectExact` match); mismatches, nonzero agent exits, and timeouts record `status: failed` in `result.json` and exit 1. Each run gets a unique directory — rerunning never rewrites history.
|
||||
|
||||
- Node.js ≥ 22
|
||||
- npm (for global @mosaicstack/mosaic install)
|
||||
- One or more runtimes:
|
||||
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code)
|
||||
- [Codex](https://github.com/openai/codex)
|
||||
- [OpenCode](https://opencode.ai)
|
||||
- [Pi](https://pi.dev)
|
||||
## Release model (M3)
|
||||
|
||||
`RELEASE` single-sources the release version (0.0.X until declared stable); the image tag derives from it plus the pinned Pi version. Activation is health-gated and every event is recorded:
|
||||
|
||||
```bash
|
||||
scripts/release.sh package # build + tag the release image
|
||||
scripts/release.sh activate # health check (exact marker) -> atomic pointer swap
|
||||
scripts/release.sh activate --fault-injection # prove the refusal path (drills only)
|
||||
scripts/release.sh rollback # health-gated return to the previous release
|
||||
scripts/release.sh ensure # self-determination: align installed to RELEASE (safe no-op when aligned)
|
||||
scripts/release.sh status # release, tag, active pointer, recent log
|
||||
scripts/test-release.sh # release selftests
|
||||
```
|
||||
|
||||
`ensure` is invoked automatically by the human-facing launchers (`hello`,
|
||||
`verify`, `agent`): the system determines what is installed and aligns
|
||||
itself — the user never runs release commands manually.
|
||||
|
||||
- `<dataRoot>/state/active.json` — the activation pointer (atomic tmp+rename replace)
|
||||
- `<dataRoot>/state/activation-log.jsonl` — append-only history: package / activate / refused / rollback
|
||||
|
||||
A failed health check never activates; the previously active release remains deployed. Updating the software therefore cannot corrupt the running installation: package beside, gate, then flip. Verified by the update/refusal/rollback drills in BUILD-LOG Phase 7.
|
||||
|
||||
## Runtime adapters (M4)
|
||||
|
||||
The harness boundary is formalized: everything upstream (config, contracts, missions, tasks, run records) is harness-agnostic; everything inside an adapter belongs to one runtime.
|
||||
|
||||
```text
|
||||
adapters/<name>/adapter.sh env in: MOSAIC_SYSTEM_PROMPT_FILE, MOSAIC_REQUEST,
|
||||
MOSAIC_PROVIDER, MOSAIC_MODEL
|
||||
stdout: response only; stderr: diagnostics
|
||||
```
|
||||
|
||||
- Selection: `execution.adapter` in config.json (optional; `pi` default; allowlist `pi`, `mock`)
|
||||
- `pi` — pinned Pi CLI, noninteractive print mode, ambient discovery off
|
||||
- `mock` — deterministic test adapter; never for real verification
|
||||
- Mission directives have a sanctioned injection point: when a task references a mission, the task runner mounts the run snapshot and the generated prompt gains a `MISSION (runtime)` section (objective + directives) after the four immutable contracts
|
||||
- Adding a harness (Claude, Codex, OpenCode) later means adding one directory — no orchestrator changes
|
||||
|
||||
See `adapters/README.md` for the full contract.
|
||||
|
||||
## Workspaces, capabilities, sessions (M5/M6)
|
||||
|
||||
Optional task fields extend what an agent can do — all defaulting to the previous behavior:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspace": "demo", // ":run" ephemeral, or persistent dataRoot/workspaces/<name>
|
||||
"capabilities": { "tools": ["bash", "read"] }, // pi tool allowlist; absent = no tools
|
||||
"session": "demo" // persistent session at dataRoot/sessions/<name>
|
||||
}
|
||||
```
|
||||
|
||||
- The adapter runs inside the workspace; files it writes are host-visible (`dataRoot/workspaces/<name>`).
|
||||
- Sessions persist via pi's documented `--session-dir`; a follow-up run in the same session resumes the conversation (`-c`) and can recall prior context. Distinct names never share state. Ephemeral (`--no-session`) remains the default when no session is declared.
|
||||
- Selection authority: config for adapter/provider/model; the task file for workspace/capabilities/session.
|
||||
|
||||
Inspect anything:
|
||||
|
||||
```bash
|
||||
node scripts/mosaic-task.mjs list # runs with task/workspace/session columns
|
||||
node scripts/mosaic-task.mjs show <runId> # full record + snapshots + artifacts
|
||||
```
|
||||
|
||||
Demo fixtures: `tasks/workspace-demo.json`, `tasks/session-demo-1.json` + `tasks/session-demo-2.json`.
|
||||
|
||||
See `docs/plans/2026-09-02_atomic-mosaic-foundation.md` for the full plan.
|
||||
|
||||
Inside the container:
|
||||
|
||||
```text
|
||||
/opt/mosaic/contracts immutable contract files
|
||||
/var/lib/mosaic generated runtime state (mounted from configured dataRoot)
|
||||
/workspace agent workspace
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
1. `scripts/build.sh` builds the release image (`mosaic-poc-agent:<pi>-r<release>`,
|
||||
tag derived from `RELEASE` + the pinned Pi version) with Docker Compose.
|
||||
2. On each run, `/opt/mosaic/src/load-contracts.sh` reads the four contract files
|
||||
in fixed order (CONSTITUTION, STANDARDS, SOUL, USER), joins them with clear
|
||||
separators, and writes `/var/lib/mosaic/system-prompt.md`.
|
||||
3. `/opt/mosaic/src/run-agent.sh` starts Pi noninteractively
|
||||
(`pi -p "Return your startup marker and nothing else."`) with
|
||||
`--system-prompt "$(cat /var/lib/mosaic/system-prompt.md)"` and all ambient
|
||||
discovery disabled (`--no-context-files --no-skills --no-extensions
|
||||
--no-prompt-templates --no-themes`), ephemeral (`--no-session`), tool-free
|
||||
(`--no-tools`), and offline for startup network operations (`--offline`).
|
||||
4. `scripts/verify.sh` trims surrounding whitespace from the response and exits 0
|
||||
only when it equals `MOSAIC_HELLO_OK` exactly.
|
||||
|
||||
## Usage
|
||||
|
||||
### Launching Agent Sessions
|
||||
```bash
|
||||
scripts/bootstrap.sh # create config.json if absent (idempotent)
|
||||
scripts/build.sh # build the image
|
||||
scripts/hello.sh # one-shot request; prints the model response
|
||||
scripts/verify.sh # full gated test; exit 0 only on exact MOSAIC_HELLO_OK
|
||||
scripts/run-task.sh # run a mission/task file (see Missions & tasks)
|
||||
scripts/release.sh # package / activate / rollback / status (see Release model)
|
||||
scripts/test-config.sh # fast config-layer selftests (no Docker)
|
||||
scripts/test-task.sh # mission/task selftests (schema + adapter seam + live runs)
|
||||
scripts/test-release.sh # release selftests
|
||||
scripts/reset.sh # delete the configured data root (safety-checked)
|
||||
```
|
||||
|
||||
Prove the failure path (acceptance criterion 9):
|
||||
|
||||
```bash
|
||||
mosaic pi # Launch Pi with Mosaic injection
|
||||
mosaic claude # Launch Claude Code with Mosaic injection
|
||||
mosaic codex # Launch Codex with Mosaic injection
|
||||
mosaic opencode # Launch OpenCode with Mosaic injection
|
||||
|
||||
mosaic yolo claude # Claude with dangerous-permissions mode
|
||||
mosaic yolo pi # Pi in yolo mode
|
||||
EXPECTED_MARKER=MOSAIC_NOT_OK scripts/verify.sh # must exit nonzero
|
||||
```
|
||||
|
||||
The launcher verifies your config, checks for `SOUL.md`, injects your `AGENTS.md` standards into the runtime, and forwards all arguments.
|
||||
|
||||
Pi launches default to a token-lean skill posture: `mosaic pi` passes `--no-skills` so Pi does not preload every global skill description into the system prompt. Use `MOSAIC_PI_SKILL_MODE=all mosaic pi` for the legacy all-skills catalog, or `MOSAIC_PI_SKILL_MODE=discover mosaic pi` to let Pi use its native settings/project skill discovery.
|
||||
|
||||
Mosaic also loads its Pi extensions from `~/.config/mosaic/runtime/pi/`. Inside Pi,
|
||||
`/goal set <statement>` starts a bounded persistent loop that checks every turn and successful
|
||||
compaction, requires two evidence-bearing completion reports, and can be inspected or stopped with
|
||||
`/goal status`, `/goal pause`, `/goal resume`, and `/goal cancel`. Controller-owned goal-state
|
||||
entries redact common credential shapes, but Pi's model/tool-call history is separate, so goals and
|
||||
evidence must never contain secrets or raw sensitive output. Mosaic does not install this extension
|
||||
into `~/.pi/agent/extensions/`.
|
||||
|
||||
### TUI & Gateway
|
||||
|
||||
```bash
|
||||
mosaic tui # Interactive TUI connected to the gateway
|
||||
mosaic gateway login # Authenticate with a gateway instance
|
||||
mosaic sessions list # List active agent sessions
|
||||
```
|
||||
|
||||
### Gateway Management
|
||||
|
||||
```bash
|
||||
mosaic gateway install # Install and configure the gateway service
|
||||
mosaic gateway verify # Post-install health check
|
||||
mosaic gateway login # Authenticate and store a session token
|
||||
mosaic gateway config rotate-token # Rotate your API token
|
||||
mosaic gateway config recover-token # Recover a token via BetterAuth cookie
|
||||
```
|
||||
|
||||
If you already have a gateway account but no token, use `mosaic gateway config recover-token` to retrieve one without recreating your account.
|
||||
|
||||
### Configuration
|
||||
|
||||
Mosaic supports three storage tiers: `local` (PGlite, single-host), `standalone` (PostgreSQL, single-host), and `federated` (PostgreSQL + pgvector + Valkey, multi-host). See [Federated Tier Setup](docs/federation/SETUP.md) for multi-user and production deployments, or [Migrating to Federated](docs/guides/migrate-tier.md) to upgrade from existing tiers.
|
||||
|
||||
```bash
|
||||
mosaic config show # Print full config as JSON
|
||||
mosaic config get <key> # Read a specific key
|
||||
mosaic config set <key> <val># Write a key
|
||||
mosaic config edit # Open config in $EDITOR
|
||||
mosaic config path # Print config file path
|
||||
```
|
||||
|
||||
### Management
|
||||
|
||||
```bash
|
||||
mosaic doctor # Health audit — detect drift and missing files
|
||||
mosaic sync # Sync skills from canonical source
|
||||
mosaic skill list # Audit Claude skill registrations and conflicts
|
||||
mosaic skill register <name> # Register one canonical skill with Claude Code
|
||||
mosaic skill unregister <name> # Remove one Mosaic-owned Claude link
|
||||
mosaic update # Update CLI/framework and auto-register canonical skills
|
||||
mosaic wizard # Full guided setup wizard
|
||||
mosaic bootstrap <path> # Bootstrap a repo with Mosaic standards
|
||||
mosaic coord init # Initialize a new orchestration mission
|
||||
mosaic prdy init # Create a PRD via guided session
|
||||
```
|
||||
|
||||
### Sub-package Commands
|
||||
|
||||
Each Mosaic sub-package exposes its API surface through the unified CLI:
|
||||
|
||||
```bash
|
||||
# User management
|
||||
mosaic auth users list
|
||||
mosaic auth users create
|
||||
mosaic auth sso
|
||||
|
||||
# Agent brain (projects, missions, tasks)
|
||||
mosaic brain projects
|
||||
mosaic brain missions
|
||||
mosaic brain tasks
|
||||
mosaic brain conversations
|
||||
|
||||
# Agent forge pipeline
|
||||
mosaic forge run [--simulate] # fails closed (FORGE_NO_EXECUTOR) with no executor wired; --simulate for typed simulated runs
|
||||
mosaic forge status
|
||||
mosaic forge resume [--simulate] # same fail-closed rule as forge run
|
||||
mosaic forge personas
|
||||
|
||||
# Structured logging
|
||||
mosaic log tail
|
||||
mosaic log search
|
||||
mosaic log export
|
||||
mosaic log level
|
||||
|
||||
# MACP protocol
|
||||
mosaic macp tasks
|
||||
mosaic macp submit
|
||||
mosaic macp gate
|
||||
mosaic macp events
|
||||
|
||||
# Agent memory
|
||||
mosaic memory search
|
||||
mosaic memory stats
|
||||
mosaic memory insights
|
||||
mosaic memory preferences
|
||||
|
||||
# Task queue (Valkey)
|
||||
mosaic queue list
|
||||
mosaic queue stats
|
||||
mosaic queue pause
|
||||
mosaic queue resume
|
||||
mosaic queue jobs
|
||||
mosaic queue drain
|
||||
|
||||
# Object storage
|
||||
mosaic storage status
|
||||
mosaic storage tier
|
||||
mosaic storage export
|
||||
mosaic storage import
|
||||
# Schema migration is unavailable in this release. The current storage wrapper shells
|
||||
# directly to `pnpm --filter @mosaicstack/db db:migrate`; it is legacy N-1,
|
||||
# uncertified, and MUST NOT be invoked pending KBN-101-02/-03/-06/-08 activation.
|
||||
# Future schema migration is non-operative: external bootstrap → TLS/roles → runner
|
||||
# --run → runner --verify → readiness. Tier copy uses only the separately held secure
|
||||
# migrate-tier route.
|
||||
```
|
||||
|
||||
### Telemetry
|
||||
|
||||
```bash
|
||||
# Local observability (OTEL / Jaeger)
|
||||
mosaic telemetry local status
|
||||
mosaic telemetry local tail
|
||||
mosaic telemetry local jaeger
|
||||
|
||||
# Remote telemetry (dry-run by default)
|
||||
mosaic telemetry status
|
||||
mosaic telemetry opt-in
|
||||
mosaic telemetry opt-out
|
||||
mosaic telemetry test
|
||||
mosaic telemetry upload # Dry-run unless opted in
|
||||
```
|
||||
|
||||
Consent state is persisted in config. Remote upload is a no-op until you run `mosaic telemetry opt-in`.
|
||||
|
||||
## Development
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js ≥ 22
|
||||
- pnpm 10.6+
|
||||
- Docker & Docker Compose
|
||||
|
||||
### Setup
|
||||
|
||||
```bash
|
||||
git clone [email protected]:mosaicstack/stack.git
|
||||
cd stack
|
||||
|
||||
# Install dependencies. The local tier uses in-process PGlite; leave DATABASE_URL unset.
|
||||
# The pnpm store defaults to $HOME/.local/share/pnpm/store. Override it without
|
||||
# editing the checkout with NPM_CONFIG_STORE_DIR=$HOME/another-store if needed.
|
||||
pnpm install
|
||||
|
||||
# Verify dependencies and generated state before running source-quality gates.
|
||||
# Missing dependencies exit 42; stale/foreign apps/web/.next state exits 43.
|
||||
# The web build certifies its exact standalone symlink manifest; added, removed,
|
||||
# retargeted, or manifest-only-tampered generated links also exit 43. This detects
|
||||
# accidental, independent, stale, and foreign-residue mutation—the class exposed by
|
||||
# a five-month-stale .next that produced 19 phantom TS2307 errors.
|
||||
# It does NOT defend against a same-UID actor that can rewrite both manifest and
|
||||
# marker consistently (CWE-345). RM-59 tracks the required executor/spine-side
|
||||
# trust anchor outside worktree authority.
|
||||
pnpm preflight
|
||||
|
||||
# Optional local queue service only. This does not start PostgreSQL.
|
||||
docker compose up -d valkey
|
||||
|
||||
# The current Gateway/Web local process is held; see docs/guides/dev-guide.md.
|
||||
# Do not start it until KBN-101-02 makes inherited dotenv/DSN state fail closed.
|
||||
```
|
||||
|
||||
### Held future procedure
|
||||
|
||||
The checked-in Compose PostgreSQL service mounts legacy initialization SQL and is **not** a
|
||||
current PostgreSQL, standalone, or federated developer route. Do not start it with Compose,
|
||||
invoke initialization SQL, or treat the planned migrator as currently executable.
|
||||
|
||||
**Held future activation procedure — non-operative and no current command authority until KBN-101-00, KBN-101-03, and KBN-101-05
|
||||
land:** external bootstrap → TLS/roles → `mosaic-db-migrator --run` →
|
||||
`mosaic-db-migrator --verify` → Gateway/Compose readiness. The future deployment artifacts—not
|
||||
this README—will provide the reviewed commands and secret-consumer interface.
|
||||
|
||||
For local data-layer work, PGlite needs no PostgreSQL service. The optional Compose command above
|
||||
starts only Valkey; OTEL Collector and Jaeger may likewise be started individually if needed,
|
||||
without starting PostgreSQL. A Gateway/Web local process is not currently a safe PGlite route:
|
||||
its unguarded dotenv loader may inherit a daemon PostgreSQL DSN. Do not use root `pnpm dev` or a
|
||||
Gateway start command until KBN-101-02 makes that state fail closed.
|
||||
|
||||
### Quality Gates
|
||||
|
||||
```bash
|
||||
pnpm preflight # Checkout/dependency/generated-state validation
|
||||
pnpm typecheck # TypeScript type checking (all packages)
|
||||
pnpm lint # ESLint (all packages)
|
||||
pnpm test # Vitest (all packages)
|
||||
pnpm format:check # Prettier check
|
||||
pnpm format # Prettier auto-fix
|
||||
```
|
||||
|
||||
### CI
|
||||
|
||||
Woodpecker CI runs on every push:
|
||||
|
||||
- `pnpm install --frozen-lockfile`
|
||||
- **Legacy N-1 CI status only — active, uncertified, and non-authorizing as an operator route:** the checked-in job currently invokes `pnpm --filter @mosaicstack/db run db:migrate` with `DATABASE_URL` against an isolated disposable PostgreSQL CI database. It performs direct DDL in that CI database, is not approved ordinary behavior or an operator route, and remains a known exception pending KBN-101-06 removal/replacement by the certified runner-backed CI path.
|
||||
- `pnpm test` (Turbo-orchestrated across all packages)
|
||||
|
||||
npm packages are published to the Gitea package registry on main merges.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
stack/
|
||||
├── apps/
|
||||
│ ├── gateway/ NestJS API + WebSocket hub (Fastify, Socket.IO, OTEL)
|
||||
│ └── web/ Next.js dashboard (React 19, Tailwind)
|
||||
├── packages/
|
||||
│ ├── mosaic/ Unified CLI — TUI, gateway client, wizard, sub-package commands
|
||||
│ ├── types/ Shared TypeScript contracts (Socket.IO typed events)
|
||||
│ ├── db/ Drizzle ORM schema + migrations (pgvector)
|
||||
│ ├── auth/ BetterAuth configuration
|
||||
│ ├── brain/ Data layer (PG-backed)
|
||||
│ ├── queue/ Valkey task queue + MCP
|
||||
│ ├── coord/ Mission coordination
|
||||
│ ├── forge/ Multi-stage AI pipeline (intake → board → plan → code → review)
|
||||
│ ├── macp/ MACP protocol — credential resolution, gate runner, events
|
||||
│ ├── agent/ Agent session management
|
||||
│ ├── memory/ Agent memory layer
|
||||
│ ├── log/ Structured logging
|
||||
│ ├── prdy/ PRD creation and validation
|
||||
│ ├── quality-rails/ Quality templates (TypeScript, Next.js, monorepo)
|
||||
│ └── design-tokens/ Shared design tokens
|
||||
├── plugins/
|
||||
│ ├── discord/ Discord channel plugin (discord.js)
|
||||
│ ├── telegram/ Telegram channel plugin (Telegraf)
|
||||
│ ├── macp/ OpenClaw MACP runtime plugin
|
||||
│ └── mosaic-framework/ OpenClaw framework injection plugin
|
||||
├── tools/
|
||||
│ └── install.sh Unified installer (framework + npm CLI, --yes / --no-auto-launch)
|
||||
├── scripts/agent/ Agent session lifecycle scripts
|
||||
├── docker-compose.yml Dev infrastructure
|
||||
└── .woodpecker/ CI pipeline configs
|
||||
```
|
||||
|
||||
### Key Design Decisions
|
||||
|
||||
- **Gateway is the single API surface** — all clients (TUI, web, Discord, Telegram) connect through it
|
||||
- **ESM everywhere** — `"type": "module"`, `.js` extensions in imports, NodeNext resolution
|
||||
- **Socket.IO typed events** — defined in `@mosaicstack/types`, enforced at compile time
|
||||
- **OTEL auto-instrumentation** — loads before NestJS bootstrap
|
||||
- **Explicit `@Inject()` decorators** — required since tsx/esbuild doesn't emit decorator metadata
|
||||
|
||||
### Framework (`~/.config/mosaic/`)
|
||||
|
||||
The framework is the bash-based standards layer installed to every developer machine:
|
||||
|
||||
```
|
||||
~/.config/mosaic/
|
||||
├── AGENTS.md ← Central standards (loaded into every runtime)
|
||||
├── SOUL.md ← Agent identity (name, style, guardrails)
|
||||
├── USER.md ← User profile (name, timezone, preferences)
|
||||
├── TOOLS.md ← Machine-level tool reference
|
||||
├── bin/mosaic ← Unified launcher (claude, codex, opencode, pi, yolo)
|
||||
├── guides/ ← E2E delivery, orchestrator protocol, PRD, etc.
|
||||
├── runtime/ ← Per-runtime configs (claude/, codex/, opencode/, pi/)
|
||||
├── skills/ ← Universal skills (shipped with the framework package)
|
||||
├── tools/ ← Tool suites (orchestrator, git, quality, prdy, etc.)
|
||||
└── memory/ ← Persistent agent memory (preserved across upgrades)
|
||||
```
|
||||
|
||||
### Forge Pipeline
|
||||
|
||||
Forge is a multi-stage AI pipeline for autonomous feature delivery:
|
||||
|
||||
```
|
||||
Intake → Discovery → Board Review → Planning (3 stages) → Coding → Review → Remediation → Test → Deploy
|
||||
```
|
||||
|
||||
Each stage has a dispatch mode (`exec` for research/review, `yolo` for coding), quality gates, and timeouts. The board review uses multiple AI personas (CEO, CTO, CFO, COO + specialists) to evaluate briefs before committing resources.
|
||||
|
||||
## Upgrading
|
||||
|
||||
Run the installer again — it handles upgrades automatically:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://mosaicstack.dev/install.sh | bash
|
||||
```
|
||||
|
||||
Or use the direct URL:
|
||||
|
||||
```bash
|
||||
bash <(curl -fsSL https://git.mosaicstack.dev/mosaicstack/stack/raw/branch/main/tools/install.sh)
|
||||
```
|
||||
|
||||
Or use the CLI:
|
||||
|
||||
```bash
|
||||
mosaic update # Check + install CLI updates
|
||||
mosaic update --check # Check only, don't install
|
||||
```
|
||||
|
||||
The CLI also performs a background update check on every invocation (cached for 1 hour).
|
||||
|
||||
### Installer Flags
|
||||
|
||||
```bash
|
||||
bash tools/install.sh --check # Version check only
|
||||
bash tools/install.sh --framework # Framework only (skip npm CLI)
|
||||
bash tools/install.sh --cli # npm CLI only (skip framework)
|
||||
bash tools/install.sh --next # Prerelease lane: source build from next
|
||||
bash tools/install.sh --dev # Contributor lane: source build at --ref/main
|
||||
bash tools/install.sh --ref v1.0 # Install from a specific git ref (--ref wins over --next)
|
||||
bash tools/install.sh --yes # Non-interactive, accept all defaults
|
||||
bash tools/install.sh --no-auto-launch # Skip auto-launch of wizard
|
||||
```
|
||||
|
||||
The installer rejects unrecognized flags or positional arguments before making changes and prints the supported-option usage.
|
||||
|
||||
## Contributing
|
||||
|
||||
```bash
|
||||
# Create a feature branch
|
||||
git checkout -b feat/my-feature
|
||||
|
||||
# Make changes, then verify
|
||||
pnpm typecheck && pnpm lint && pnpm test && pnpm format:check
|
||||
|
||||
# Commit (husky runs lint-staged automatically)
|
||||
git commit -m "feat: description of change"
|
||||
|
||||
# Push and create PR
|
||||
git push -u origin feat/my-feature
|
||||
```
|
||||
|
||||
DTOs go in `*.dto.ts` files at module boundaries. Scratchpads (`docs/scratchpads/`) are mandatory for non-trivial tasks. See `AGENTS.md` for the full standards reference.
|
||||
|
||||
## License
|
||||
|
||||
Proprietary — all rights reserved.
|
||||
## Authentication
|
||||
|
||||
Pi's documented container authentication (see the package's
|
||||
`docs/containerization.md`) is used, in this order:
|
||||
|
||||
1. **Read-only mounted credential file** (default): the host pi auth file
|
||||
`~/.pi/agent/auth.json` is bind-mounted read-only to
|
||||
`/home/node/.pi/agent/auth.json`. The host file holds a static API-key
|
||||
entry for the built-in `zai` provider, so no token refresh writes are needed.
|
||||
2. **Runtime environment variable** (documented alternative): set `ZAI_API_KEY`
|
||||
or `ANTHROPIC_API_KEY` in the environment or in a gitignored `.env`; compose
|
||||
passes them through. Pi's documented precedence applies.
|
||||
|
||||
Credentials are never committed, never copied into the image, and never printed.
|
||||
Mosaic-managed named accounts (`agent.sh --auth`) live under the data root
|
||||
(`auth/<account>.json`, 0600) — the stack never writes into `~/.pi`.
|
||||
`.env.example` contains non-secret settings only.
|
||||
|
||||
## Boundaries honored
|
||||
|
||||
- No mounts of `~/.mosaic` or `~/.config/mosaic`; no Docker socket mount.
|
||||
- Source stays in this project directory; generated state only in
|
||||
`/home/jwoltje/.mosaic-dev` (host) and `/var/lib/mosaic` (container).
|
||||
- No database, web server, queue, second container, orchestration, Git
|
||||
integration, persistent sessions, or policy machinery.
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# Mosaic Stack
|
||||
|
||||
You are the default collaborator for Mosaic Stack: a practical engineering
|
||||
partner helping people build, inspect, and operate a trustworthy foundation
|
||||
for delegated work.
|
||||
|
||||
Mosaic Stack is deliberately small, file-based, and evidence-oriented. Its
|
||||
purpose is not to perform confidence; it is to make useful work attributable,
|
||||
bounded, reproducible, and reviewable. Treat the system's contracts, policies,
|
||||
and run records as part of the product, not paperwork around it.
|
||||
|
||||
Work with calm precision. Start from what the user is trying to accomplish,
|
||||
make the next useful step clear, and explain results in plain language. Be
|
||||
decisive when the evidence supports a decision; be explicit about uncertainty
|
||||
when it does not. Never claim a test, command, integration, or outcome that
|
||||
you have not actually verified.
|
||||
|
||||
Respect boundaries. Ask before expanding scope, changing authority, touching
|
||||
credentials, or taking an irreversible external action. Prefer the least
|
||||
privileged path, preserve user work, and stop on a policy or validation
|
||||
refusal rather than working around it. A clean refusal with a useful diagnosis
|
||||
is better than a superficially successful but untrustworthy result.
|
||||
|
||||
Leave a legible trail. Make changes intentional, keep records honest, and
|
||||
report what changed, how it was checked, and what remains unresolved. When
|
||||
coordinating other workers, give each one a bounded objective and review their
|
||||
evidence instead of treating their confidence as proof.
|
||||
|
||||
The aim is dependable progress: small enough to understand, safe enough to
|
||||
trust, and concrete enough for a person to verify.
|
||||
@@ -0,0 +1,54 @@
|
||||
# Mosaic runtime adapters
|
||||
|
||||
An adapter is the entire harness-specific surface of the system. Everything
|
||||
upstream of an adapter — configuration, contracts, missions, tasks, run
|
||||
records — is harness-agnostic; everything inside an adapter may assume one
|
||||
specific agent runtime.
|
||||
|
||||
## Contract
|
||||
|
||||
An adapter lives at:
|
||||
|
||||
```text
|
||||
/opt/mosaic/adapters/<name>/adapter.sh
|
||||
```
|
||||
|
||||
and must be executable. The dispatcher (`/opt/mosaic/src/run-agent.sh`)
|
||||
selects it via `MOSAIC_ADAPTER` (default: `pi`) and execs it after the
|
||||
system prompt has been generated.
|
||||
|
||||
**Inputs (environment):**
|
||||
|
||||
| Variable | Meaning |
|
||||
|---|---|
|
||||
| `MOSAIC_SYSTEM_PROMPT_FILE` | Absolute path to the generated system prompt (contracts + optional mission section). Read it; do not modify it. |
|
||||
| `MOSAIC_REQUEST` | The exact user request text (may contain newlines). |
|
||||
| `MOSAIC_PROVIDER` | Configured provider name. |
|
||||
| `MOSAIC_MODEL` | Configured model id. |
|
||||
|
||||
Optional, adapter-specific (documented per adapter):
|
||||
|
||||
| Variable | Meaning |
|
||||
|---|---|
|
||||
| `MOSAIC_MOCK_RESPONSE` | mock only: the verbatim response to emit |
|
||||
|
||||
**Outputs:**
|
||||
|
||||
- `stdout`: the model response text — the only channel the orchestrator captures
|
||||
- `stderr`: diagnostics (never credentials)
|
||||
- exit `0`: success; nonzero: failure
|
||||
|
||||
## Rules
|
||||
|
||||
1. Adapters print ONLY the response on stdout. Status lines go to stderr.
|
||||
2. Adapters never read configuration files; the resolved settings arrive via environment.
|
||||
3. Adapters never write outside `/var/lib/mosaic`.
|
||||
4. Adding an adapter requires: a new directory, the contract implementation, and
|
||||
adding the name to the allowlist in `scripts/mosaic-config.mjs`.
|
||||
|
||||
## Included adapters
|
||||
|
||||
- `pi` — the pinned `@earendil-works/pi-coding-agent` CLI in noninteractive
|
||||
print mode (`-p`), ambient discovery disabled, stdin detached.
|
||||
- `mock` — deterministic echo of `MOSAIC_MOCK_RESPONSE`. Test-only: never use
|
||||
it where a real model response is required.
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/sh
|
||||
# Mock adapter: deterministic response for seam tests. NEVER use where a
|
||||
# real model response is required.
|
||||
#
|
||||
# Contract: see /opt/mosaic/adapters/README.md.
|
||||
set -eu
|
||||
|
||||
[ -n "${MOSAIC_SYSTEM_PROMPT_FILE:-}" ] || { echo "mock adapter: MOSAIC_SYSTEM_PROMPT_FILE is required" >&2; exit 2; }
|
||||
if [ "${MOSAIC_INTERACTIVE:-}" != "1" ]; then
|
||||
[ -n "${MOSAIC_REQUEST:-}" ] || { echo "mock adapter: MOSAIC_REQUEST is required" >&2; exit 2; }
|
||||
fi
|
||||
[ -r "$MOSAIC_SYSTEM_PROMPT_FILE" ] || { echo "mock adapter: system prompt not readable: $MOSAIC_SYSTEM_PROMPT_FILE" >&2; exit 2; }
|
||||
|
||||
echo "mock adapter: responding verbatim from MOSAIC_MOCK_RESPONSE" >&2
|
||||
# Deterministic plumbing evidence: which MOSAIC_* variables did the
|
||||
# orchestrator actually deliver? (Auth secrets are not MOSAIC_-prefixed.)
|
||||
(env | grep '^MOSAIC_' | sort) >&2 2>/dev/null || true
|
||||
printf '%s\n' "${MOSAIC_MOCK_RESPONSE:-}"
|
||||
@@ -0,0 +1,96 @@
|
||||
#!/bin/sh
|
||||
# Pi adapter: implements the Mosaic adapter contract for the pinned
|
||||
# @earendil-works/pi-coding-agent CLI.
|
||||
#
|
||||
# Contract: see /opt/mosaic/adapters/README.md.
|
||||
# Headless (default): stdout = response only; stderr = diagnostics; exit 0.
|
||||
# Interactive (MOSAIC_INTERACTIVE=1): full pi TUI on the attached terminal.
|
||||
set -eu
|
||||
|
||||
[ -n "${MOSAIC_SYSTEM_PROMPT_FILE:-}" ] || { echo "pi adapter: MOSAIC_SYSTEM_PROMPT_FILE is required" >&2; exit 2; }
|
||||
[ -r "$MOSAIC_SYSTEM_PROMPT_FILE" ] || { echo "pi adapter: system prompt not readable: $MOSAIC_SYSTEM_PROMPT_FILE" >&2; exit 2; }
|
||||
# MOSAIC_AGENT_NAME is optional in headless mode (identity section is then
|
||||
# omitted); interactive launches always set it via scripts/agent.sh.
|
||||
|
||||
: "${PI_PROVIDER:?pi adapter: PI_PROVIDER is required}"
|
||||
: "${PI_MODEL:?pi adapter: PI_MODEL is required}"
|
||||
|
||||
INTERACTIVE="${MOSAIC_INTERACTIVE:-}"
|
||||
if [ "$INTERACTIVE" != "1" ]; then
|
||||
[ -n "${MOSAIC_REQUEST:-}" ] || { echo "pi adapter: MOSAIC_REQUEST is required" >&2; exit 2; }
|
||||
fi
|
||||
|
||||
# Workspace (M5): run inside the provided workspace when present.
|
||||
if [ -n "${MOSAIC_WORKSPACE:-}" ]; then
|
||||
mkdir -p "$MOSAIC_WORKSPACE"
|
||||
cd "$MOSAIC_WORKSPACE"
|
||||
fi
|
||||
|
||||
# Session (M6/M11): default ephemeral (--no-session). With a declared
|
||||
# session dir: persist there and resume the most recent session. With a
|
||||
# fork source: branch the source session file into the target dir
|
||||
# (pi --fork) - the ancestor session is never modified.
|
||||
SESSION_FLAGS="--no-session"
|
||||
if [ -n "${MOSAIC_SESSION_FORK:-}" ]; then
|
||||
[ -n "${MOSAIC_SESSION_DIR:-}" ] || { echo "pi adapter: session fork requires MOSAIC_SESSION_DIR" >&2; exit 2; }
|
||||
mkdir -p "$MOSAIC_SESSION_DIR"
|
||||
SESSION_FLAGS="--fork $MOSAIC_SESSION_FORK --session-dir $MOSAIC_SESSION_DIR"
|
||||
elif [ -n "${MOSAIC_SESSION_DIR:-}" ]; then
|
||||
mkdir -p "$MOSAIC_SESSION_DIR"
|
||||
SESSION_FLAGS="--session-dir $MOSAIC_SESSION_DIR"
|
||||
if [ -n "$(ls -A "$MOSAIC_SESSION_DIR" 2>/dev/null)" ]; then
|
||||
SESSION_FLAGS="$SESSION_FLAGS -c"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Capabilities (M5): explicit allowlist or no tools.
|
||||
TOOLS_FLAG="--no-tools"
|
||||
[ -n "${MOSAIC_TOOLS:-}" ] && TOOLS_FLAG="--tools $MOSAIC_TOOLS"
|
||||
|
||||
# Skills (M17): explicitly provided skill dirs replace discovery. When none
|
||||
# are provided the agent runs with --no-skills (nothing ambient to find).
|
||||
SKILLS_FLAG="--no-skills"
|
||||
if [ -n "${MOSAIC_SKILLS:-}" ]; then
|
||||
SKILLS_FLAG=""
|
||||
OLDIFS=$IFS; IFS=','
|
||||
for s in $MOSAIC_SKILLS; do
|
||||
[ -d "$s" ] || { echo "pi adapter: skill dir missing: $s" >&2; exit 2; }
|
||||
SKILLS_FLAG="$SKILLS_FLAG --skill $s"
|
||||
done
|
||||
IFS=$OLDIFS
|
||||
fi
|
||||
|
||||
# Mode (M13): interactive TUI or one-shot print.
|
||||
PRINT_MODE="-p"
|
||||
REQUEST_ARG=""
|
||||
if [ "$INTERACTIVE" = "1" ]; then
|
||||
PRINT_MODE=""
|
||||
else
|
||||
REQUEST_ARG="$MOSAIC_REQUEST"
|
||||
fi
|
||||
|
||||
# All flags documented in the pi package README (CLI Reference):
|
||||
# -p/--print one-shot mode: print the response and exit (omitted in
|
||||
# interactive TUI mode)
|
||||
# --system-prompt replace the default prompt with the generated one
|
||||
# --no-* no ambient context/skills/extensions/templates/themes
|
||||
# SESSION_FLAGS ephemeral | persistent | forked (per env)
|
||||
# TOOLS_FLAG per capabilities
|
||||
# --offline no startup network operations (update checks/telemetry)
|
||||
PROMPT_CONTENT="$(cat "$MOSAIC_SYSTEM_PROMPT_FILE")"
|
||||
set -- \
|
||||
--offline \
|
||||
--no-extensions \
|
||||
$SKILLS_FLAG \
|
||||
--no-prompt-templates \
|
||||
--no-themes \
|
||||
--no-context-files \
|
||||
$TOOLS_FLAG \
|
||||
$SESSION_FLAGS \
|
||||
--provider "$PI_PROVIDER" \
|
||||
--model "$PI_MODEL" \
|
||||
--system-prompt "$PROMPT_CONTENT"
|
||||
# One-shot mode appends -p and the request (both safely quoted);
|
||||
# interactive mode appends nothing - clean TUI.
|
||||
[ "$INTERACTIVE" = "1" ] || set -- "$@" -p "$MOSAIC_REQUEST"
|
||||
exec pi "$@"
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
===== DARKWING NATIVE DEVELOPMENT CONTEXT =====
|
||||
|
||||
Your identity is Darkwing. This launch runs Pi directly on the host, in the
|
||||
Mosaic Stack development repository. The injected SOUL defines your persona;
|
||||
CONSTITUTION and STANDARDS supply governance, USER supplies user context,
|
||||
and AGENTS.md supplies repository instructions.
|
||||
|
||||
You have host read, bash, edit, write, grep, find, and ls tools. This is a
|
||||
development TUI with the operator's OS access, not a sandbox or a registered
|
||||
managed fleet seat. Use repository scripts for Mosaic operations and inspect
|
||||
their effects before running them. Container paths in skills describe worker
|
||||
deployments, not your current workspace. A tool's presence is not authority
|
||||
to change unrelated files, other agents' work, or the live fleet.
|
||||
|
||||
For an assigned improvement, inspect the implementation, reproduce the issue,
|
||||
make the smallest useful change, verify it, and continue through the authorized
|
||||
outcome. Read docs/plans/CURRENT.md to reconcile ownership and existing gates;
|
||||
a new user assignment does not silently resume unrelated queued work.
|
||||
|
||||
The local /goal extension is loaded and owns any operator-set goal lifecycle.
|
||||
Use ms-proactive-agent for work selection and ms-goal for recovery guidance;
|
||||
do not create a competing goal loop. Follow goal_report's actual schema and
|
||||
reporting instructions. Its text format is Just Completed / Next Step /
|
||||
Blocked, with '* none' for empty sections. No external reporting skill is
|
||||
needed to discover that format. Native development packaging supersedes
|
||||
older skill statements that this extension is unavailable.
|
||||
|
||||
For relocation recovery, read agents/darkwing/work/RESTART.md after the root
|
||||
AGENTS.md and docs/plans/CURRENT.md. It records verified checkpoints and limits,
|
||||
not a new assignment. The canonical checkout is /mnt/storage/src/mosaic-stack;
|
||||
v1/ is archived legacy source. Reconcile newer owner direction before acting.
|
||||
|
||||
Conversation history persists across launcher restarts. Goals belong to a
|
||||
single process incarnation; recover the assignment from verified records and
|
||||
the operator's direction after a restart. No goal is started by this launcher.
|
||||
Context is captured anew at launch; source edits do not update this process's
|
||||
injected snapshot. Relaunch to load approved context changes.
|
||||
@@ -0,0 +1,64 @@
|
||||
# Darkwing development TUI
|
||||
|
||||
From any terminal, run:
|
||||
|
||||
```sh
|
||||
/home/jwoltje/src/mosaic-stack-dev-test/agents/darkwing/launch.sh
|
||||
```
|
||||
|
||||
The agent launcher is a thin shim to `scripts/agent.sh --host-dev darkwing`,
|
||||
forwarding all arguments unchanged. `scripts/agent.sh` is the common entry
|
||||
point; `scripts/agent-host-dev.sh` implements its native development mode.
|
||||
The host launcher opens the repository as Darkwing's workspace.
|
||||
It uses the repository-pinned Pi, the configured Mosaic provider/model, and
|
||||
native Pi authentication (normal `~/.pi/agent`, or `PI_CODING_AGENT_DIR` if
|
||||
explicitly set). It never copies credentials. Install dependencies with
|
||||
`npm ci --ignore-scripts --no-audit --no-fund` if needed.
|
||||
|
||||
`--check` validates configuration and required inputs without opening Pi or
|
||||
calling a model. `--fresh` starts a new conversation without deleting earlier
|
||||
ones. Normal launches continue the latest conversation under
|
||||
`.pi/state/darkwing/sessions/`; the first launch creates one. A launcher lock
|
||||
rejects simultaneous launches through this script. It does not exclude Pi
|
||||
processes started another way. Damaged JSONL history refuses automatic resume;
|
||||
`--fresh` is an explicit escape hatch that preserves the damaged evidence.
|
||||
|
||||
The current files are combined into a private launch snapshot under
|
||||
`.pi/state/darkwing/launches/`:
|
||||
|
||||
- `contracts/CONSTITUTION.md` and `contracts/STANDARDS.md`
|
||||
- `agents/darkwing/SOUL.md`
|
||||
- `<configured dataRoot>/user/USER.md`, the deployment's live user profile
|
||||
- the repository's `AGENTS.md` and Darkwing's `CONTEXT.md`
|
||||
|
||||
Use `--soul FILE`, `--constitution FILE`, or `--user FILE` to select alternate
|
||||
inputs, including a future `contracts/USER.md`. Relative paths resolve from
|
||||
the repository root. Missing or empty inputs refuse launch. Snapshots can
|
||||
contain personal context and remain local, with private file permissions.
|
||||
Context edits take effect on relaunch, including when resuming a conversation.
|
||||
|
||||
The launcher enables coding/search tools, `goal_report`, ten explicit local
|
||||
skills, and the canonical goal extension through `scripts/sync-dev-extensions.sh`.
|
||||
Ambient context, skills, extensions, templates, and themes are disabled.
|
||||
The normal Pi coding prompt is retained with the Mosaic context appended.
|
||||
Enter `/goal <assignment and acceptance criteria>` to start continuing work;
|
||||
`/goal stop`, `/goal resume`, and `/goal` pause, resume, and inspect it. A new
|
||||
process does not automatically adopt a previous process's goal.
|
||||
|
||||
This TUI has the operator's host access, including repository edits and host
|
||||
commands. Its tool list is not OS isolation. It creates no managed role or
|
||||
fleet registration. Worker dispatch still uses the governed Mosaic task runner.
|
||||
The user supplies the assignment; launch alone does not start self-modification.
|
||||
|
||||
## Deployment findings
|
||||
|
||||
The existing `scripts/agent.sh` launches a Docker container, defaults to the
|
||||
`agent-<name>` session directory, and asks Pi to continue when that directory
|
||||
is nonempty. Its default workspace is `<dataRoot>/workspaces/<name>`, not this
|
||||
checkout. `src/load-contracts.sh` loads image-baked governance, an optional
|
||||
seat SOUL override, live user Markdown, and mission context into a shared
|
||||
prompt path. A seat override requires `agent.json`; a standalone SOUL is not
|
||||
discovered. `adapters/pi/adapter.sh` disables extensions. The temporary host
|
||||
launcher follows the existing native development path to provide repository
|
||||
access and `/goal`, and keeps its conversations separate from container and
|
||||
live fleet sessions. It does not invoke release alignment on startup.
|
||||
@@ -0,0 +1,20 @@
|
||||
# SOUL — Darkwing
|
||||
|
||||
You are Darkwing, Mosaic Stack's hands-on engineering collaborator. Your job
|
||||
is to help Jason make the system dependable by using it, finding where it
|
||||
falls short, and carrying authorized improvements through verification.
|
||||
|
||||
Be curious, direct, and resourceful. Have a technical opinion and explain
|
||||
the evidence behind it. Investigate before guessing. Distinguish a design
|
||||
claim, a passing test, and behavior you have observed in the running system.
|
||||
|
||||
Use Mosaic's own tools and workflows where they fit. Turn a failure into a
|
||||
reproducible case, make a focused correction, and test the behavior again.
|
||||
Let each verified improvement inform the next one within the assignment.
|
||||
Keep the human informed when the result, scope, or next decision changes.
|
||||
|
||||
Own the outcome while respecting other agents' work. Preserve their changes
|
||||
and records, give delegated work clear boundaries, and seek independent
|
||||
review where required. Self-improvement never grants new authority: changing
|
||||
your instructions, permissions, or a live deployment follows the same review
|
||||
and authorization rules as any other system change.
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# Darkwing's native development mode through the Mosaic agent entry point.
|
||||
set -euo pipefail
|
||||
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
exec "$REPO/scripts/agent.sh" --host-dev darkwing "$@"
|
||||
@@ -0,0 +1,21 @@
|
||||
// Refuse damaged history before Pi's --continue can silently skip it.
|
||||
import { readFileSync, lstatSync } from 'node:fs';
|
||||
|
||||
try {
|
||||
for (const file of process.argv.slice(2)) {
|
||||
if (!lstatSync(file).isFile()) throw new Error(`not a regular session file: ${file}`);
|
||||
const lines = readFileSync(file, 'utf8').trim().split('\n');
|
||||
const entries = lines.map((line) => JSON.parse(line));
|
||||
const header = entries[0];
|
||||
if (header?.type !== 'session' || typeof header.id !== 'string' || !header.id ||
|
||||
typeof header.version !== 'number' || typeof header.cwd !== 'string' ||
|
||||
!Number.isFinite(Date.parse(header.timestamp)) ||
|
||||
entries.slice(1).some((entry) => !entry || typeof entry.type !== 'string')) {
|
||||
throw new Error(`invalid session structure: ${file}`);
|
||||
}
|
||||
if (header.cwd !== process.cwd()) throw new Error(`session belongs to another workspace: ${file}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`darkwing: cannot safely resume: ${error.message}; inspect history or explicitly use --fresh`);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
# Darkwing — relocation handoff
|
||||
|
||||
Recorded 2026-09-07 17:43 UTC. Jason intends to relaunch with
|
||||
`/mnt/storage/src/mosaic-stack/agents/darkwing/launch.sh`.
|
||||
This is a recovery note, not a new assignment or automatic goal resumption.
|
||||
|
||||
## Read first
|
||||
|
||||
1. Root `AGENTS.md` and `docs/plans/CURRENT.md`.
|
||||
2. This note, then `git status --short` and `git log --oneline -5`.
|
||||
3. Reconcile current owner direction and any newer declared artifacts before acting.
|
||||
|
||||
## Repository conversion is completed locally
|
||||
|
||||
Jason explicitly ordered the conversion and confirmed no work was active.
|
||||
- Canonical checkout: `/mnt/storage/src/mosaic-stack`.
|
||||
- Origin: `https://git.mosaicstack.dev/mosaicstack/stack`.
|
||||
- Branch: `refactor`.
|
||||
- Conversion commit: `127a54fdff1fe6ae56c3197edddf957481465db4`.
|
||||
- New foundation is at root. `v1/` is legacy archival source, NOT current code.
|
||||
- Old `/home/jwoltje/src/mosaic-stack-dev-test` is a compatibility symlink to this
|
||||
same checkout. Do not recreate a second working copy there.
|
||||
- Both histories retained: merge parents v2 `9a5fbdbda74b16adf488fe28138b2ba69ea5e669`
|
||||
and v1 `5d2770002612a09ae0cadc129b4ea30619133e8a`.
|
||||
- Exact 3,507-file v1 tracked tree imported; v1 refs under `refs/archive/v1/`.
|
||||
- Original v2 refs retained; `stack-v2-archive` remote has a disabled push URL.
|
||||
- Only legacy tracked tree and four conversion docs committed. All earlier
|
||||
uncommitted/untracked/ignored work preserved. Index was verified clean.
|
||||
- Issue https://git.mosaicstack.dev/mosaicstack/stack/issues/1495 closed explicitly
|
||||
for local conversion. No push, PR/trunk merge or live-service change occurred.
|
||||
|
||||
Record: `docs/plans/2026-09-07_repository-consolidation-completed.md`.
|
||||
Receipts: `docs/plans/reviews/2026-09-07_repository-conversion-verification.json`
|
||||
and `2026-09-07_repository-conversion-postcommit-verification.json`.
|
||||
Verified rollback copies, NOT development roots:
|
||||
- `/mnt/storage/src/.mosaic-stack-conversion-20260907T172430Z/`
|
||||
- `/home/jwoltje/src/.mosaic-stack-dev-test.pre-conversion-20260907T172430Z`
|
||||
Do not delete them, launch from them or restore over newer work.
|
||||
|
||||
## Current unfinished foundation gate
|
||||
|
||||
Jason's A9 acceptance of the first offline synthetic scope/permission inspector
|
||||
is pending. Code is independently approved by Filbert; no blocking code finding
|
||||
remains at the reviewed r6 candidate. Owner acceptance is not inferred from tests.
|
||||
|
||||
- Manifest: `docs/plans/reviews/2026-09-07_foundation-inspector-rocko-build-manifest-r6.json`
|
||||
SHA-256 `a4a4493000aff5905337a643886ca36e7c5377d52deed77b8aeab7174ca73dcf`.
|
||||
- Report: `docs/plans/reviews/2026-09-07_foundation-inspector-rocko-build-r6.md`
|
||||
SHA-256 `ee0e83efd7c71eddecf5e26f939e9a34ba85b184cfcd1cffac9ff9e56ea13c37`.
|
||||
- APPROVED verdict: `docs/plans/reviews/2026-09-07_foundation-inspector-code-verdict-r6.md`
|
||||
SHA-256 `ab9dd5e5c3cad5c9263e873ff82cac444da2d36040e907e4798b208fa1c08b13`.
|
||||
- Guide: `docs/plans/reviews/2026-09-07_foundation-inspector-demo.md`.
|
||||
|
||||
All 382 approved inspector files and pinned inputs survived conversion unchanged.
|
||||
Actual offline checks: Node 80/0, selftests 43/0, oracle 1,568 records / zero
|
||||
schema disagreements, foundation checker PASS, config/auth/conductor 24/15/17.
|
||||
Postcommit conductor 17/0 and four CLI demos passed: allowed read, allowed change
|
||||
PREVIEW (no mutation), missing-registration refusal, unresolved reassignment with
|
||||
original selection retained. Demo inputs are separate synthetic scenarios.
|
||||
|
||||
`test-task.sh` and `test-release.sh` remain NOT RUN / DEFERRED under Jason's bounded
|
||||
offline-demo ruling. No deployment/native/live/provider/security certification.
|
||||
Reviewer qualifications: ordering equality means structural equality, not byte
|
||||
identity; auxiliary native-parser warm-run anomalies remain separate unresolved
|
||||
observations, not a passing universal parser-equivalence claim. Preserve all earlier
|
||||
NOT APPROVED reviews and the historical correction that r3 ran unauthorized live
|
||||
branches; later deferral did not retroactively authorize them.
|
||||
|
||||
## Ownership and limits
|
||||
|
||||
- Rocko authored inspector code; Filbert independently reviewed; Darkwing coordinates
|
||||
and verifies. Keep the approved candidate frozen unless a new fix is authorized.
|
||||
- No automatic permission to push, merge to next/main, deploy, change live config,
|
||||
grant permissions, access credentials, investigate ~/.mosaic, or start new runtime
|
||||
work. Local conversion authority is not authority for those activities.
|
||||
- Preserve unrelated pending work. In particular `scripts/agent.sh`, `docs/TOOLS.md`,
|
||||
host launcher/context files and other untracked concepts/skills belong to existing
|
||||
work. Do not blanket-stage/reset/clean. Root logs and CURRENT remain uncommitted.
|
||||
- Foundation #53 in the old stack-v2 project remains a separate open issue; do not
|
||||
silently close or renumber it. Accepted historical SHA/path citations remain valid.
|
||||
- Rocko's Archify C1 remains HELD for owner T2/T3 decisions. No lane reassignment.
|
||||
- Future durability/workflow/evidence/federation/onboarding topics are notes, not
|
||||
authorization to expand the inspector.
|
||||
|
||||
## Communications
|
||||
|
||||
Use only `tools/tmux/agent-send.sh`; sender `dragon-lin:darkwing`.
|
||||
Rocko: `-L mosaic-fleet -s '=rocko'`; Filbert/Dewey:
|
||||
`-L default -s '=filbert'` / `'=dewey'`.
|
||||
Conversion notice delivered to Rocko. Filbert/Dewey sends were unconfirmed
|
||||
(input boxes not locatable); no retries, no acknowledgement claimed. Check declared
|
||||
artifact paths as well as direct messages; completed reviews have existed without
|
||||
transported replies. Do not inspect private panes or blindly resend.
|
||||
|
||||
## Relaunch and goal recovery
|
||||
|
||||
The project launcher continues its own latest `.pi/state/darkwing/sessions/`
|
||||
conversation by default. Do NOT assume this pre-launch conversation is already in
|
||||
that store or that the next launch resumes this exact conversation. This handoff
|
||||
is the durable bridge. No session-tree migration or launch was performed here.
|
||||
|
||||
The goal extension owns lifecycle. The earlier extension goal had been paused;
|
||||
no restart automatically resumes it. Reconcile the actual new process state and
|
||||
Jason's direction rather than reporting progress against a guessed old goal or
|
||||
creating a second goal loop. Launch alone grants no new assignment.
|
||||
|
||||
This handoff and its CONTEXT pointer are documentation-only. Launcher scripts,
|
||||
private sessions, credentials and runtime configuration were not modified.
|
||||
@@ -0,0 +1,5 @@
|
||||
# SOUL - researcher
|
||||
|
||||
You are the researcher seat of the Mosaic fleet. You are curious, methodical,
|
||||
and precise. You cite what you know, admit what you do not, and never guess
|
||||
when you can verify.
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"agentVersion": 1,
|
||||
"name": "researcher",
|
||||
"role": "researcher",
|
||||
"capabilities": { "tools": ["read", "bash"] }
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
import path from 'node:path';
|
||||
import fs from 'node:fs';
|
||||
|
||||
/**
|
||||
* Resolves a user-provided path and verifies it is inside the allowed sandbox directory.
|
||||
* Throws SandboxEscapeError if the resolved path is outside the sandbox.
|
||||
*
|
||||
* Uses realpathSync to resolve symlinks in the sandbox root. The user-supplied path
|
||||
* is checked for containment AFTER lexical resolution but BEFORE resolving any symlinks
|
||||
* within the user path — so symlink escape attempts are caught too.
|
||||
*
|
||||
* @param userPath - The path provided by the agent (may be relative or absolute)
|
||||
* @param sandboxDir - The allowed root directory (already validated on session creation)
|
||||
* @returns The resolved absolute path, guaranteed to be within sandboxDir
|
||||
*/
|
||||
export function guardPath(userPath: string, sandboxDir: string): string {
|
||||
const resolved = path.resolve(sandboxDir, userPath);
|
||||
const sandboxResolved = fs.realpathSync.native(sandboxDir);
|
||||
|
||||
// Normalize both paths to resolve any symlinks in the sandbox root itself.
|
||||
// For the user path, we check containment BEFORE resolving symlinks in the path
|
||||
// (so we catch symlink escape attempts too — the resolved path must still be under sandbox)
|
||||
if (!resolved.startsWith(sandboxResolved + path.sep) && resolved !== sandboxResolved) {
|
||||
throw new SandboxEscapeError(userPath, sandboxDir, resolved);
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a path without resolving symlinks in the user-provided portion.
|
||||
* Use for paths that may not exist yet (creates, writes).
|
||||
*
|
||||
* Performs a lexical containment check only using path.resolve.
|
||||
*/
|
||||
export function guardPathUnsafe(userPath: string, sandboxDir: string): string {
|
||||
const resolved = path.resolve(sandboxDir, userPath);
|
||||
const sandboxAbs = path.resolve(sandboxDir);
|
||||
|
||||
if (!resolved.startsWith(sandboxAbs + path.sep) && resolved !== sandboxAbs) {
|
||||
throw new SandboxEscapeError(userPath, sandboxDir, resolved);
|
||||
}
|
||||
|
||||
return resolved;
|
||||
}
|
||||
|
||||
export class SandboxEscapeError extends Error {
|
||||
constructor(
|
||||
public readonly userPath: string,
|
||||
public readonly sandboxDir: string,
|
||||
public readonly resolvedPath: string,
|
||||
) {
|
||||
super(
|
||||
`Path escape attempt blocked: "${userPath}" resolves to "${resolvedPath}" which is outside sandbox "${sandboxDir}"`,
|
||||
);
|
||||
this.name = 'SandboxEscapeError';
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
import { Controller, Get, Param, UseGuards } from '@nestjs/common';
|
||||
import { AuthGuard } from '../auth/auth.guard.js';
|
||||
import { TeamsService } from './teams.service.js';
|
||||
|
||||
@Controller('api/teams')
|
||||
@UseGuards(AuthGuard)
|
||||
export class TeamsController {
|
||||
constructor(private readonly teams: TeamsService) {}
|
||||
|
||||
@Get()
|
||||
async list() {
|
||||
return this.teams.findAll();
|
||||
}
|
||||
|
||||
@Get(':teamId')
|
||||
async findOne(@Param('teamId') teamId: string) {
|
||||
return this.teams.findById(teamId);
|
||||
}
|
||||
|
||||
@Get(':teamId/members')
|
||||
async listMembers(@Param('teamId') teamId: string) {
|
||||
return this.teams.listMembers(teamId);
|
||||
}
|
||||
|
||||
@Get(':teamId/members/:userId')
|
||||
async checkMembership(@Param('teamId') teamId: string, @Param('userId') userId: string) {
|
||||
const isMember = await this.teams.isMember(teamId, userId);
|
||||
return { isMember };
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { loginAs, TEST_USER } from './helpers/auth.js';
|
||||
|
||||
test.describe('Chat page', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await loginAs(page, TEST_USER.email, TEST_USER.password);
|
||||
// If login failed (no seeded user in env) we may be on /login — skip
|
||||
const url = page.url();
|
||||
test.skip(!url.includes('/chat'), 'No seeded test user — skipping authenticated tests');
|
||||
});
|
||||
|
||||
test('chat page loads and shows the welcome message or conversation list', async ({ page }) => {
|
||||
await page.goto('/chat');
|
||||
// Either there are conversations listed or the welcome empty-state is shown
|
||||
const hasWelcome = await page
|
||||
.getByRole('heading', { name: /welcome to mosaic chat/i })
|
||||
.isVisible()
|
||||
.catch(() => false);
|
||||
const hasConversationPanel = await page
|
||||
.locator('[data-testid="conversation-list"], nav, aside')
|
||||
.first()
|
||||
.isVisible()
|
||||
.catch(() => false);
|
||||
|
||||
expect(hasWelcome || hasConversationPanel).toBe(true);
|
||||
});
|
||||
|
||||
test('new conversation button is visible', async ({ page }) => {
|
||||
await page.goto('/chat');
|
||||
// "Start new conversation" button or a "+" button in the sidebar
|
||||
const newConvButton = page.getByRole('button', { name: /new conversation|start new/i }).first();
|
||||
await expect(newConvButton).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
|
||||
test('clicking new conversation shows a chat input area', async ({ page }) => {
|
||||
await page.goto('/chat');
|
||||
// Find any button that creates a new conversation
|
||||
const newBtn = page.getByRole('button', { name: /new conversation|start new/i }).first();
|
||||
await newBtn.click();
|
||||
// After creating, a text input for sending messages should appear
|
||||
const chatInput = page.getByRole('textbox').or(page.locator('textarea')).first();
|
||||
await expect(chatInput).toBeVisible({ timeout: 10_000 });
|
||||
});
|
||||
|
||||
test('sidebar navigation is present on chat page', async ({ page }) => {
|
||||
await page.goto('/chat');
|
||||
// The app-shell sidebar should be visible
|
||||
await expect(page.getByRole('link', { name: /chat/i }).first()).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -1,23 +0,0 @@
|
||||
import type { Page } from '@playwright/test';
|
||||
|
||||
export const TEST_USER = {
|
||||
email: process.env['E2E_USER_EMAIL'] ?? '[email protected]',
|
||||
password: process.env['E2E_USER_PASSWORD'] ?? 'password123',
|
||||
name: 'E2E Test User',
|
||||
};
|
||||
|
||||
export const ADMIN_USER = {
|
||||
email: process.env['E2E_ADMIN_EMAIL'] ?? '[email protected]',
|
||||
password: process.env['E2E_ADMIN_PASSWORD'] ?? 'adminpass123',
|
||||
name: 'E2E Admin User',
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill the login form and submit. Waits for navigation after success.
|
||||
*/
|
||||
export async function loginAs(page: Page, email: string, password: string): Promise<void> {
|
||||
await page.goto('/login');
|
||||
await page.getByLabel('Email').fill(email);
|
||||
await page.getByLabel('Password').fill(password);
|
||||
await page.getByRole('button', { name: /sign in/i }).click();
|
||||
}
|
||||
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
import "./.next/types/routes.d.ts";
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
@@ -1,32 +0,0 @@
|
||||
import type { NextConfig } from 'next';
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: 'standalone',
|
||||
transpilePackages: ['@mosaicstack/design-tokens'],
|
||||
|
||||
// Enable gzip/brotli compression for all responses.
|
||||
compress: true,
|
||||
|
||||
// Reduce bundle size: disable source maps in production builds.
|
||||
productionBrowserSourceMaps: false,
|
||||
|
||||
// Image optimisation: allow the gateway origin as an external image source.
|
||||
images: {
|
||||
formats: ['image/avif', 'image/webp'],
|
||||
remotePatterns: [
|
||||
{
|
||||
protocol: 'https',
|
||||
hostname: '**',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Experimental: enable React compiler for automatic memoisation (Next 15+).
|
||||
// Falls back gracefully if the compiler plugin is not installed.
|
||||
experimental: {
|
||||
// Turbopack is the default in dev for Next 15; keep it opt-in for now.
|
||||
// turbo: {},
|
||||
},
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
@@ -1,32 +0,0 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Playwright E2E configuration for Mosaic web app.
|
||||
*
|
||||
* Assumes:
|
||||
* - Next.js web app running on http://localhost:3000
|
||||
* - NestJS gateway running on http://localhost:14242
|
||||
*
|
||||
* Run with: pnpm --filter @mosaicstack/web test:e2e
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: './e2e',
|
||||
fullyParallel: true,
|
||||
forbidOnly: !!process.env['CI'],
|
||||
retries: process.env['CI'] ? 2 : 0,
|
||||
workers: process.env['CI'] ? 1 : undefined,
|
||||
reporter: 'html',
|
||||
use: {
|
||||
baseURL: process.env['PLAYWRIGHT_BASE_URL'] ?? 'http://localhost:3000',
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
],
|
||||
// Do NOT auto-start the dev server — tests assume it is already running.
|
||||
// webServer is intentionally omitted so tests can run against a live env.
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { GuestGuard } from '@/components/guest-guard';
|
||||
|
||||
export default function AuthLayout({ children }: { children: ReactNode }): React.ReactElement {
|
||||
return (
|
||||
<GuestGuard>
|
||||
<div className="flex min-h-screen items-center justify-center bg-surface-bg">
|
||||
<div className="w-full max-w-md rounded-xl border border-surface-border bg-surface-card p-8 shadow-lg">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</GuestGuard>
|
||||
);
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { api } from '@/lib/api';
|
||||
import { authClient, signIn } from '@/lib/auth-client';
|
||||
import type { SsoProviderDiscovery } from '@/lib/sso';
|
||||
import { SsoProviderButtons } from '@/components/auth/sso-provider-buttons';
|
||||
|
||||
export default function LoginPage(): React.ReactElement {
|
||||
const router = useRouter();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [ssoProviders, setSsoProviders] = useState<SsoProviderDiscovery[]>([]);
|
||||
const [ssoLoadingProviderId, setSsoLoadingProviderId] = useState<
|
||||
SsoProviderDiscovery['id'] | null
|
||||
>(null);
|
||||
|
||||
useEffect(() => {
|
||||
api<SsoProviderDiscovery[]>('/api/sso/providers')
|
||||
.catch(() => [] as SsoProviderDiscovery[])
|
||||
.then((providers) => setSsoProviders(providers.filter((provider) => provider.configured)));
|
||||
}, []);
|
||||
|
||||
async function handleSubmit(e: React.FormEvent<HTMLFormElement>): Promise<void> {
|
||||
e.preventDefault();
|
||||
setError(null);
|
||||
setLoading(true);
|
||||
|
||||
const form = new FormData(e.currentTarget);
|
||||
const email = form.get('email') as string;
|
||||
const password = form.get('password') as string;
|
||||
|
||||
const result = await signIn.email({ email, password });
|
||||
|
||||
if (result.error) {
|
||||
setError(result.error.message ?? 'Sign in failed');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
router.push('/chat');
|
||||
}
|
||||
|
||||
async function handleSsoSignIn(providerId: SsoProviderDiscovery['id']): Promise<void> {
|
||||
setError(null);
|
||||
setSsoLoadingProviderId(providerId);
|
||||
|
||||
try {
|
||||
const result = await authClient.signIn.oauth2({
|
||||
providerId,
|
||||
callbackURL: '/chat',
|
||||
newUserCallbackURL: '/chat',
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
setError(result.error.message ?? `Sign in with ${providerId} failed`);
|
||||
setSsoLoadingProviderId(null);
|
||||
}
|
||||
} catch (err: unknown) {
|
||||
setError(err instanceof Error ? err.message : `Sign in with ${providerId} failed`);
|
||||
setSsoLoadingProviderId(null);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold">Sign in</h1>
|
||||
<p className="mt-1 text-sm text-text-secondary">Sign in to your Mosaic account</p>
|
||||
|
||||
{error && (
|
||||
<div
|
||||
role="alert"
|
||||
className="mt-4 rounded-lg border border-error/30 bg-error/10 px-4 py-3 text-sm text-error"
|
||||
>
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form className="mt-6 space-y-4" onSubmit={handleSubmit}>
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium text-text-secondary">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
autoComplete="email"
|
||||
required
|
||||
disabled={loading}
|
||||
className="mt-1 block w-full rounded-lg border border-surface-border bg-surface-elevated px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:opacity-50"
|
||||
placeholder="[email protected]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="password" className="block text-sm font-medium text-text-secondary">
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
required
|
||||
disabled={loading}
|
||||
className="mt-1 block w-full rounded-lg border border-surface-border bg-surface-elevated px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:opacity-50"
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full rounded-lg bg-blue-600 px-4 py-2.5 text-sm font-medium text-white transition-colors hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-surface-card disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Signing in...' : 'Sign in'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<SsoProviderButtons
|
||||
providers={ssoProviders}
|
||||
loadingProviderId={ssoLoadingProviderId}
|
||||
onOidcSignIn={(providerId) => {
|
||||
void handleSsoSignIn(providerId);
|
||||
}}
|
||||
/>
|
||||
|
||||
<p className="mt-4 text-center text-sm text-text-muted">
|
||||
Don't have an account?{' '}
|
||||
<Link href="/register" className="text-blue-400 hover:text-blue-300">
|
||||
Sign up
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { signUp } from '@/lib/auth-client';
|
||||
|
||||
export default function RegisterPage(): React.ReactElement {
|
||||
const router = useRouter();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
async function handleSubmit(e: React.FormEvent<HTMLFormElement>): Promise<void> {
|
||||
e.preventDefault();
|
||||
setError(null);
|
||||
setLoading(true);
|
||||
|
||||
const form = new FormData(e.currentTarget);
|
||||
const name = form.get('name') as string;
|
||||
const email = form.get('email') as string;
|
||||
const password = form.get('password') as string;
|
||||
|
||||
const result = await signUp.email({ name, email, password });
|
||||
|
||||
if (result.error) {
|
||||
setError(result.error.message ?? 'Registration failed');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
router.push('/chat');
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-semibold">Create account</h1>
|
||||
<p className="mt-1 text-sm text-text-secondary">Get started with Mosaic</p>
|
||||
|
||||
{error && (
|
||||
<div
|
||||
role="alert"
|
||||
className="mt-4 rounded-lg border border-error/30 bg-error/10 px-4 py-3 text-sm text-error"
|
||||
>
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form className="mt-6 space-y-4" onSubmit={handleSubmit}>
|
||||
<div>
|
||||
<label htmlFor="name" className="block text-sm font-medium text-text-secondary">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
id="name"
|
||||
name="name"
|
||||
type="text"
|
||||
autoComplete="name"
|
||||
required
|
||||
disabled={loading}
|
||||
className="mt-1 block w-full rounded-lg border border-surface-border bg-surface-elevated px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:opacity-50"
|
||||
placeholder="Your name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="email" className="block text-sm font-medium text-text-secondary">
|
||||
Email
|
||||
</label>
|
||||
<input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
autoComplete="email"
|
||||
required
|
||||
disabled={loading}
|
||||
className="mt-1 block w-full rounded-lg border border-surface-border bg-surface-elevated px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:opacity-50"
|
||||
placeholder="[email protected]"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label htmlFor="password" className="block text-sm font-medium text-text-secondary">
|
||||
Password
|
||||
</label>
|
||||
<input
|
||||
id="password"
|
||||
name="password"
|
||||
type="password"
|
||||
autoComplete="new-password"
|
||||
required
|
||||
disabled={loading}
|
||||
className="mt-1 block w-full rounded-lg border border-surface-border bg-surface-elevated px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 disabled:opacity-50"
|
||||
placeholder="••••••••"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="w-full rounded-lg bg-blue-600 px-4 py-2.5 text-sm font-medium text-white transition-colors hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-surface-card disabled:opacity-50"
|
||||
>
|
||||
{loading ? 'Creating account...' : 'Create account'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<p className="mt-4 text-center text-sm text-text-muted">
|
||||
Already have an account?{' '}
|
||||
<Link href="/login" className="text-blue-400 hover:text-blue-300">
|
||||
Sign in
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,365 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { api } from '@/lib/api';
|
||||
import { destroySocket, getSocket } from '@/lib/socket';
|
||||
import type { Conversation, Message } from '@/lib/types';
|
||||
import {
|
||||
ConversationSidebar,
|
||||
type ConversationSidebarRef,
|
||||
} from '@/components/chat/conversation-sidebar';
|
||||
import { MessageBubble } from '@/components/chat/message-bubble';
|
||||
import { ChatInput } from '@/components/chat/chat-input';
|
||||
import { StreamingMessage } from '@/components/chat/streaming-message';
|
||||
|
||||
interface ModelInfo {
|
||||
id: string;
|
||||
provider: string;
|
||||
name: string;
|
||||
reasoning: boolean;
|
||||
contextWindow: number;
|
||||
maxTokens: number;
|
||||
inputTypes: ('text' | 'image')[];
|
||||
cost: { input: number; output: number; cacheRead: number; cacheWrite: number };
|
||||
}
|
||||
|
||||
interface ProviderInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
available: boolean;
|
||||
models: ModelInfo[];
|
||||
}
|
||||
|
||||
export default function ChatPage(): React.ReactElement {
|
||||
const [activeId, setActiveId] = useState<string | null>(null);
|
||||
const [messages, setMessages] = useState<Message[]>([]);
|
||||
const [streamingText, setStreamingText] = useState('');
|
||||
const [isStreaming, setIsStreaming] = useState(false);
|
||||
const [isSidebarOpen, setIsSidebarOpen] = useState(true);
|
||||
const [models, setModels] = useState<ModelInfo[]>([]);
|
||||
const [selectedModelId, setSelectedModelId] = useState('');
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
const sidebarRef = useRef<ConversationSidebarRef>(null);
|
||||
|
||||
// Track the active conversation ID in a ref so socket event handlers always
|
||||
// see the current value without needing to be re-registered.
|
||||
const activeIdRef = useRef<string | null>(null);
|
||||
activeIdRef.current = activeId;
|
||||
|
||||
// Accumulate streamed text in a ref so agent:end can read the full content
|
||||
// without stale-closure issues.
|
||||
const streamingTextRef = useRef('');
|
||||
|
||||
useEffect(() => {
|
||||
const savedState = window.localStorage.getItem('mosaic-sidebar-open');
|
||||
if (savedState !== null) {
|
||||
setIsSidebarOpen(savedState === 'true');
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.localStorage.setItem('mosaic-sidebar-open', String(isSidebarOpen));
|
||||
}, [isSidebarOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
api<ProviderInfo[]>('/api/providers')
|
||||
.then((providers) => {
|
||||
const availableModels = providers
|
||||
.filter((provider) => provider.available)
|
||||
.flatMap((provider) => provider.models);
|
||||
setModels(availableModels);
|
||||
setSelectedModelId((current) => current || availableModels[0]?.id || '');
|
||||
})
|
||||
.catch(() => {
|
||||
setModels([]);
|
||||
setSelectedModelId('');
|
||||
});
|
||||
}, []);
|
||||
|
||||
// Load messages when active conversation changes
|
||||
useEffect(() => {
|
||||
if (!activeId) {
|
||||
setMessages([]);
|
||||
return;
|
||||
}
|
||||
// Clear streaming state when switching conversations
|
||||
setIsStreaming(false);
|
||||
setStreamingText('');
|
||||
streamingTextRef.current = '';
|
||||
api<Message[]>(`/api/conversations/${activeId}/messages`)
|
||||
.then(setMessages)
|
||||
.catch(() => {});
|
||||
}, [activeId]);
|
||||
|
||||
// Auto-scroll to bottom
|
||||
useEffect(() => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
}, [messages, streamingText]);
|
||||
|
||||
// Socket.io setup — connect once for the page lifetime
|
||||
useEffect(() => {
|
||||
const socket = getSocket();
|
||||
|
||||
function onAgentStart(data: { conversationId: string }): void {
|
||||
// Only update state if the event belongs to the currently viewed conversation
|
||||
if (activeIdRef.current !== data.conversationId) return;
|
||||
setIsStreaming(true);
|
||||
setStreamingText('');
|
||||
streamingTextRef.current = '';
|
||||
}
|
||||
|
||||
function onAgentText(data: { conversationId: string; text: string }): void {
|
||||
if (activeIdRef.current !== data.conversationId) return;
|
||||
streamingTextRef.current += data.text;
|
||||
setStreamingText((prev) => prev + data.text);
|
||||
}
|
||||
|
||||
function onAgentEnd(data: { conversationId: string }): void {
|
||||
if (activeIdRef.current !== data.conversationId) return;
|
||||
const finalText = streamingTextRef.current;
|
||||
setIsStreaming(false);
|
||||
setStreamingText('');
|
||||
streamingTextRef.current = '';
|
||||
// Append the completed assistant message to the local message list.
|
||||
// The Pi agent session is in-memory so the assistant response is not
|
||||
// persisted to the DB — we build the local UI state instead.
|
||||
if (finalText) {
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: `assistant-${Date.now()}`,
|
||||
conversationId: data.conversationId,
|
||||
role: 'assistant' as const,
|
||||
content: finalText,
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
]);
|
||||
sidebarRef.current?.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
function onError(data: { error: string; conversationId?: string }): void {
|
||||
setIsStreaming(false);
|
||||
setStreamingText('');
|
||||
streamingTextRef.current = '';
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: `error-${Date.now()}`,
|
||||
conversationId: data.conversationId ?? '',
|
||||
role: 'system' as const,
|
||||
content: `Error: ${data.error}`,
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
socket.on('agent:start', onAgentStart);
|
||||
socket.on('agent:text', onAgentText);
|
||||
socket.on('agent:end', onAgentEnd);
|
||||
socket.on('error', onError);
|
||||
|
||||
// Connect if not already connected
|
||||
if (!socket.connected) {
|
||||
socket.connect();
|
||||
}
|
||||
|
||||
return () => {
|
||||
socket.off('agent:start', onAgentStart);
|
||||
socket.off('agent:text', onAgentText);
|
||||
socket.off('agent:end', onAgentEnd);
|
||||
socket.off('error', onError);
|
||||
// Fully tear down the socket when the chat page unmounts so we get a
|
||||
// fresh authenticated connection next time the page is visited.
|
||||
destroySocket();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleNewConversation = useCallback(async (projectId?: string | null) => {
|
||||
const conv = await api<Conversation>('/api/conversations', {
|
||||
method: 'POST',
|
||||
body: { title: 'New conversation', projectId: projectId ?? null },
|
||||
});
|
||||
|
||||
sidebarRef.current?.addConversation({
|
||||
id: conv.id,
|
||||
title: conv.title,
|
||||
projectId: conv.projectId,
|
||||
updatedAt: conv.updatedAt,
|
||||
archived: conv.archived,
|
||||
});
|
||||
|
||||
setActiveId(conv.id);
|
||||
setMessages([]);
|
||||
setIsSidebarOpen(true);
|
||||
}, []);
|
||||
|
||||
const handleSend = useCallback(
|
||||
async (content: string, options?: { modelId?: string }) => {
|
||||
let convId = activeId;
|
||||
|
||||
// Auto-create conversation if none selected
|
||||
if (!convId) {
|
||||
const autoTitle = content.slice(0, 60);
|
||||
const conv = await api<Conversation>('/api/conversations', {
|
||||
method: 'POST',
|
||||
body: { title: autoTitle },
|
||||
});
|
||||
sidebarRef.current?.addConversation({
|
||||
id: conv.id,
|
||||
title: conv.title,
|
||||
projectId: conv.projectId,
|
||||
updatedAt: conv.updatedAt,
|
||||
archived: conv.archived,
|
||||
});
|
||||
setActiveId(conv.id);
|
||||
convId = conv.id;
|
||||
} else if (messages.length === 0) {
|
||||
// Auto-title the initial placeholder conversation from the first user message.
|
||||
const autoTitle = content.slice(0, 60);
|
||||
api<Conversation>(`/api/conversations/${convId}`, {
|
||||
method: 'PATCH',
|
||||
body: { title: autoTitle },
|
||||
})
|
||||
.then(() => sidebarRef.current?.refresh())
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
// Optimistic user message in local UI state
|
||||
setMessages((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: `user-${Date.now()}`,
|
||||
conversationId: convId,
|
||||
role: 'user' as const,
|
||||
content,
|
||||
createdAt: new Date().toISOString(),
|
||||
},
|
||||
]);
|
||||
|
||||
// Persist the user message to the DB so conversation history is
|
||||
// available when the page is reloaded or a new session starts.
|
||||
api<Message>(`/api/conversations/${convId}/messages`, {
|
||||
method: 'POST',
|
||||
body: { role: 'user', content },
|
||||
}).catch(() => {
|
||||
// Non-fatal: the agent can still process the message even if
|
||||
// REST persistence fails.
|
||||
});
|
||||
|
||||
// Send to WebSocket — gateway creates/resumes the agent session and
|
||||
// streams the response back via agent:start / agent:text / agent:end.
|
||||
const socket = getSocket();
|
||||
if (!socket.connected) {
|
||||
socket.connect();
|
||||
}
|
||||
socket.emit('message', {
|
||||
conversationId: convId,
|
||||
content,
|
||||
modelId: (options?.modelId ?? selectedModelId) || undefined,
|
||||
});
|
||||
},
|
||||
[activeId, messages, selectedModelId],
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="-m-6 flex h-[calc(100vh-3.5rem)] overflow-hidden"
|
||||
style={{ background: 'var(--bg-deep, var(--color-surface-bg, #0a0f1a))' }}
|
||||
>
|
||||
<ConversationSidebar
|
||||
ref={sidebarRef}
|
||||
isOpen={isSidebarOpen}
|
||||
onClose={() => setIsSidebarOpen(false)}
|
||||
currentConversationId={activeId}
|
||||
onSelectConversation={(conversationId) => {
|
||||
setActiveId(conversationId);
|
||||
setMessages([]);
|
||||
if (conversationId && window.innerWidth < 768) {
|
||||
setIsSidebarOpen(false);
|
||||
}
|
||||
}}
|
||||
onNewConversation={(projectId) => {
|
||||
void handleNewConversation(projectId);
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="flex min-w-0 flex-1 flex-col">
|
||||
<div
|
||||
className="flex items-center gap-3 border-b px-4 py-3"
|
||||
style={{ borderColor: 'var(--border)' }}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsSidebarOpen((open) => !open)}
|
||||
className="rounded-lg border p-2 transition-colors"
|
||||
style={{
|
||||
borderColor: 'var(--border)',
|
||||
background: 'var(--surface)',
|
||||
color: 'var(--text)',
|
||||
}}
|
||||
aria-label={isSidebarOpen ? 'Close conversation sidebar' : 'Open conversation sidebar'}
|
||||
>
|
||||
<svg viewBox="0 0 24 24" className="h-4 w-4" fill="none" stroke="currentColor">
|
||||
<path strokeWidth="2" strokeLinecap="round" d="M4 7h16M4 12h16M4 17h16" />
|
||||
</svg>
|
||||
</button>
|
||||
<div>
|
||||
<h1 className="text-sm font-semibold" style={{ color: 'var(--text)' }}>
|
||||
Mosaic Chat
|
||||
</h1>
|
||||
<p className="text-xs" style={{ color: 'var(--muted)' }}>
|
||||
{activeId ? 'Active conversation selected' : 'Choose or start a conversation'}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{activeId ? (
|
||||
<>
|
||||
<div className="flex-1 space-y-4 overflow-y-auto p-6">
|
||||
{messages.map((msg) => (
|
||||
<MessageBubble key={msg.id} message={msg} />
|
||||
))}
|
||||
{isStreaming && <StreamingMessage text={streamingText} />}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
<ChatInput
|
||||
onSend={handleSend}
|
||||
isStreaming={isStreaming}
|
||||
models={models}
|
||||
selectedModelId={selectedModelId}
|
||||
onModelChange={setSelectedModelId}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex flex-1 items-center justify-center px-6">
|
||||
<div
|
||||
className="max-w-md rounded-2xl border px-8 py-10 text-center"
|
||||
style={{
|
||||
borderColor: 'var(--border)',
|
||||
background: 'var(--surface)',
|
||||
}}
|
||||
>
|
||||
<h2 className="text-lg font-medium" style={{ color: 'var(--text)' }}>
|
||||
Welcome to Mosaic Chat
|
||||
</h2>
|
||||
<p className="mt-1 text-sm" style={{ color: 'var(--muted)' }}>
|
||||
Select a conversation or start a new one
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
void handleNewConversation();
|
||||
}}
|
||||
className="mt-4 rounded-lg px-4 py-2 text-sm font-medium text-white transition-colors"
|
||||
style={{ background: 'var(--primary)' }}
|
||||
>
|
||||
Start new conversation
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { AppShell } from '@/components/layout/app-shell';
|
||||
import { AuthGuard } from '@/components/auth-guard';
|
||||
|
||||
export default function DashboardLayout({ children }: { children: ReactNode }): React.ReactElement {
|
||||
return (
|
||||
<AuthGuard>
|
||||
<AppShell>{children}</AppShell>
|
||||
</AuthGuard>
|
||||
);
|
||||
}
|
||||
@@ -1,338 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useParams, useRouter } from 'next/navigation';
|
||||
import { api } from '@/lib/api';
|
||||
import { cn } from '@/lib/cn';
|
||||
import type { Mission, Project, Task, TaskStatus } from '@/lib/types';
|
||||
import { MissionTimeline } from '@/components/projects/mission-timeline';
|
||||
import { PrdViewer } from '@/components/projects/prd-viewer';
|
||||
import { TaskDetailModal } from '@/components/tasks/task-detail-modal';
|
||||
import { TaskListView } from '@/components/tasks/task-list-view';
|
||||
import { TaskStatusSummary } from '@/components/tasks/task-status-summary';
|
||||
|
||||
type Tab = 'overview' | 'tasks' | 'missions' | 'prd';
|
||||
|
||||
const statusColors: Record<string, string> = {
|
||||
active: 'bg-success/20 text-success',
|
||||
paused: 'bg-warning/20 text-warning',
|
||||
completed: 'bg-blue-600/20 text-blue-400',
|
||||
archived: 'bg-gray-600/20 text-gray-400',
|
||||
};
|
||||
|
||||
interface TabButtonProps {
|
||||
id: Tab;
|
||||
label: string;
|
||||
activeTab: Tab;
|
||||
onClick: (tab: Tab) => void;
|
||||
}
|
||||
|
||||
function TabButton({ id, label, activeTab, onClick }: TabButtonProps): React.ReactElement {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onClick(id)}
|
||||
className={cn(
|
||||
'border-b-2 px-4 py-2 text-sm transition-colors',
|
||||
activeTab === id
|
||||
? 'border-text-primary text-text-primary'
|
||||
: 'border-transparent text-text-muted hover:text-text-secondary',
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ProjectDetailPage(): React.ReactElement {
|
||||
const params = useParams();
|
||||
const router = useRouter();
|
||||
const id = typeof params['id'] === 'string' ? params['id'] : '';
|
||||
|
||||
const [project, setProject] = useState<Project | null>(null);
|
||||
const [missions, setMissions] = useState<Mission[]>([]);
|
||||
const [tasks, setTasks] = useState<Task[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const [activeTab, setActiveTab] = useState<Tab>('overview');
|
||||
const [taskFilter, setTaskFilter] = useState<TaskStatus | 'all'>('all');
|
||||
const [selectedTask, setSelectedTask] = useState<Task | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
Promise.all([
|
||||
api<Project>(`/api/projects/${id}`),
|
||||
api<Mission[]>('/api/missions').catch(() => [] as Mission[]),
|
||||
api<Task[]>(`/api/tasks?projectId=${id}`).catch(() => [] as Task[]),
|
||||
])
|
||||
.then(([proj, allMissions, tks]) => {
|
||||
setProject(proj);
|
||||
setMissions(allMissions.filter((m) => m.projectId === id));
|
||||
setTasks(tks);
|
||||
})
|
||||
.catch((err: Error) => {
|
||||
setError(err.message ?? 'Failed to load project');
|
||||
})
|
||||
.finally(() => setLoading(false));
|
||||
}, [id]);
|
||||
|
||||
const handleTaskClick = useCallback((task: Task) => {
|
||||
setSelectedTask(task);
|
||||
}, []);
|
||||
|
||||
const handleCloseTaskModal = useCallback(() => {
|
||||
setSelectedTask(null);
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="py-16 text-center">
|
||||
<p className="text-sm text-text-muted">Loading project...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !project) {
|
||||
return (
|
||||
<div className="py-16 text-center">
|
||||
<p className="text-sm text-error">{error ?? 'Project not found'}</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => router.push('/projects')}
|
||||
className="mt-4 text-sm text-text-muted underline hover:text-text-secondary"
|
||||
>
|
||||
Back to projects
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const filteredTasks = taskFilter === 'all' ? tasks : tasks.filter((t) => t.status === taskFilter);
|
||||
|
||||
const prdContent = getPrdContent(project);
|
||||
const hasPrd = Boolean(prdContent);
|
||||
|
||||
const tabs: { id: Tab; label: string }[] = [
|
||||
{ id: 'overview', label: 'Overview' },
|
||||
{ id: 'tasks', label: `Tasks (${tasks.length})` },
|
||||
{ id: 'missions', label: `Missions (${missions.length})` },
|
||||
...(hasPrd ? [{ id: 'prd' as Tab, label: 'PRD' }] : []),
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Breadcrumb */}
|
||||
<nav className="mb-4 flex items-center gap-2 text-sm text-text-muted">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => router.push('/projects')}
|
||||
className="hover:text-text-secondary"
|
||||
>
|
||||
Projects
|
||||
</button>
|
||||
<span>/</span>
|
||||
<span className="text-text-primary">{project.name}</span>
|
||||
</nav>
|
||||
|
||||
{/* Project header */}
|
||||
<div className="mb-6 flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-3">
|
||||
<h1 className="text-2xl font-semibold text-text-primary">{project.name}</h1>
|
||||
<span
|
||||
className={cn(
|
||||
'rounded-full px-2 py-0.5 text-xs',
|
||||
statusColors[project.status] ?? 'bg-gray-600/20 text-gray-400',
|
||||
)}
|
||||
>
|
||||
{project.status}
|
||||
</span>
|
||||
</div>
|
||||
{project.description && (
|
||||
<p className="mt-1 text-sm text-text-muted">{project.description}</p>
|
||||
)}
|
||||
<p className="mt-2 text-xs text-text-muted">
|
||||
Created {new Date(project.createdAt).toLocaleDateString()} · Updated{' '}
|
||||
{new Date(project.updatedAt).toLocaleDateString()}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Stats bar */}
|
||||
<div className="mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4">
|
||||
<StatCard label="Tasks" value={String(tasks.length)} />
|
||||
<StatCard
|
||||
label="Done"
|
||||
value={String(tasks.filter((t) => t.status === 'done').length)}
|
||||
valueClass="text-success"
|
||||
/>
|
||||
<StatCard
|
||||
label="In Progress"
|
||||
value={String(tasks.filter((t) => t.status === 'in-progress').length)}
|
||||
valueClass="text-blue-400"
|
||||
/>
|
||||
<StatCard
|
||||
label="Blocked"
|
||||
value={String(tasks.filter((t) => t.status === 'blocked').length)}
|
||||
valueClass={tasks.some((t) => t.status === 'blocked') ? 'text-error' : undefined}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="mb-6 flex gap-0 border-b border-surface-border">
|
||||
{tabs.map((tab) => (
|
||||
<TabButton
|
||||
key={tab.id}
|
||||
id={tab.id}
|
||||
label={tab.label}
|
||||
activeTab={activeTab}
|
||||
onClick={setActiveTab}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Tab content */}
|
||||
{activeTab === 'overview' && (
|
||||
<OverviewTab project={project} missions={missions} tasks={tasks} />
|
||||
)}
|
||||
|
||||
{activeTab === 'tasks' && (
|
||||
<div>
|
||||
<div className="mb-4">
|
||||
<TaskStatusSummary
|
||||
tasks={tasks}
|
||||
activeFilter={taskFilter}
|
||||
onFilterChange={setTaskFilter}
|
||||
/>
|
||||
</div>
|
||||
<TaskListView tasks={filteredTasks} onTaskClick={handleTaskClick} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'missions' && <MissionTimeline missions={missions} />}
|
||||
|
||||
{activeTab === 'prd' && prdContent && (
|
||||
<div className="rounded-lg border border-surface-border bg-surface-card p-6">
|
||||
<PrdViewer content={prdContent} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Task detail modal */}
|
||||
{selectedTask && <TaskDetailModal task={selectedTask} onClose={handleCloseTaskModal} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface OverviewTabProps {
|
||||
project: Project;
|
||||
missions: Mission[];
|
||||
tasks: Task[];
|
||||
}
|
||||
|
||||
function OverviewTab({ project, missions, tasks }: OverviewTabProps): React.ReactElement {
|
||||
const recentTasks = [...tasks]
|
||||
.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())
|
||||
.slice(0, 5);
|
||||
|
||||
return (
|
||||
<div className="grid gap-6 lg:grid-cols-2">
|
||||
{/* Recent tasks */}
|
||||
<section>
|
||||
<h2 className="mb-3 text-sm font-semibold text-text-secondary">Recent Tasks</h2>
|
||||
{recentTasks.length === 0 ? (
|
||||
<div className="rounded-lg border border-surface-border bg-surface-card p-4 text-center">
|
||||
<p className="text-sm text-text-muted">No tasks yet</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{recentTasks.map((task) => (
|
||||
<TaskSummaryRow key={task.id} task={task} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Mission summary */}
|
||||
<section>
|
||||
<h2 className="mb-3 text-sm font-semibold text-text-secondary">Missions</h2>
|
||||
{missions.length === 0 ? (
|
||||
<div className="rounded-lg border border-surface-border bg-surface-card p-4 text-center">
|
||||
<p className="text-sm text-text-muted">No missions yet</p>
|
||||
</div>
|
||||
) : (
|
||||
<MissionTimeline missions={missions.slice(0, 4)} />
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Metadata */}
|
||||
{project.metadata && Object.keys(project.metadata).length > 0 && (
|
||||
<section className="lg:col-span-2">
|
||||
<h2 className="mb-3 text-sm font-semibold text-text-secondary">Project Metadata</h2>
|
||||
<div className="rounded-lg border border-surface-border bg-surface-card p-4">
|
||||
<pre className="overflow-x-auto text-xs text-text-muted">
|
||||
{JSON.stringify(project.metadata, null, 2)}
|
||||
</pre>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const taskStatusColors: Record<string, string> = {
|
||||
'not-started': 'bg-gray-600/20 text-gray-300',
|
||||
'in-progress': 'bg-blue-600/20 text-blue-400',
|
||||
blocked: 'bg-error/20 text-error',
|
||||
done: 'bg-success/20 text-success',
|
||||
cancelled: 'bg-gray-600/20 text-gray-500',
|
||||
};
|
||||
|
||||
function TaskSummaryRow({ task }: { task: Task }): React.ReactElement {
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-2 rounded-lg border border-surface-border bg-surface-card px-3 py-2">
|
||||
<span className="truncate text-sm text-text-primary">{task.title}</span>
|
||||
<span
|
||||
className={cn(
|
||||
'shrink-0 rounded-full px-2 py-0.5 text-xs',
|
||||
taskStatusColors[task.status] ?? 'bg-gray-600/20 text-gray-400',
|
||||
)}
|
||||
>
|
||||
{task.status}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StatCard({
|
||||
label,
|
||||
value,
|
||||
valueClass,
|
||||
}: {
|
||||
label: string;
|
||||
value: string;
|
||||
valueClass?: string;
|
||||
}): React.ReactElement {
|
||||
return (
|
||||
<div className="rounded-lg border border-surface-border bg-surface-card p-3">
|
||||
<p className="text-xs text-text-muted">{label}</p>
|
||||
<p className={cn('mt-1 text-lg font-semibold', valueClass ?? 'text-text-primary')}>{value}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getPrdContent(project: Project): string | null {
|
||||
if (!project.metadata) return null;
|
||||
|
||||
const prd = project.metadata['prd'];
|
||||
if (typeof prd === 'string' && prd.trim().length > 0) return prd;
|
||||
|
||||
const prdContent = project.metadata['prdContent'];
|
||||
if (typeof prdContent === 'string' && prdContent.trim().length > 0) return prdContent;
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { api } from '@/lib/api';
|
||||
import type { Project } from '@/lib/types';
|
||||
import { ProjectCard } from '@/components/projects/project-card';
|
||||
|
||||
export default function ProjectsPage(): React.ReactElement {
|
||||
const [projects, setProjects] = useState<Project[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
api<Project[]>('/api/projects')
|
||||
.then(setProjects)
|
||||
.catch(() => {})
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const handleProjectClick = useCallback(
|
||||
(project: Project) => {
|
||||
router.push(`/projects/${project.id}`);
|
||||
},
|
||||
[router],
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-6 flex items-center justify-between">
|
||||
<h1 className="text-2xl font-semibold">Projects</h1>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<p className="py-8 text-center text-sm text-text-muted">Loading projects...</p>
|
||||
) : projects.length === 0 ? (
|
||||
<div className="py-12 text-center">
|
||||
<h2 className="text-lg font-medium text-text-secondary">No projects yet</h2>
|
||||
<p className="mt-1 text-sm text-text-muted">
|
||||
Projects will appear here when created via the gateway API
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{projects.map((project) => (
|
||||
<ProjectCard key={project.id} project={project} onClick={handleProjectClick} />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Mission status section */}
|
||||
<MissionStatus />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function MissionStatus(): React.ReactElement {
|
||||
const [mission, setMission] = useState<Record<string, unknown> | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
api<Record<string, unknown>>('/api/coord/status')
|
||||
.then(setMission)
|
||||
.catch(() => setMission(null))
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<section className="mt-8">
|
||||
<h2 className="mb-4 text-lg font-semibold">Active Mission</h2>
|
||||
{loading ? (
|
||||
<p className="text-sm text-text-muted">Loading mission status...</p>
|
||||
) : !mission ? (
|
||||
<div className="rounded-lg border border-surface-border bg-surface-card p-6 text-center">
|
||||
<p className="text-sm text-text-muted">No active mission detected</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="rounded-lg border border-surface-border bg-surface-card p-4">
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<StatCard label="Mission" value={String(mission['missionId'] ?? 'Unknown')} />
|
||||
<StatCard label="Phase" value={String(mission['currentPhase'] ?? '—')} />
|
||||
<StatCard
|
||||
label="Tasks"
|
||||
value={`${mission['completedTasks'] ?? 0} / ${mission['totalTasks'] ?? 0}`}
|
||||
/>
|
||||
<StatCard label="Status" value={String(mission['status'] ?? '—')} />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function StatCard({ label, value }: { label: string; value: string }): React.ReactElement {
|
||||
return (
|
||||
<div className="rounded-lg bg-surface-elevated p-3">
|
||||
<p className="text-xs text-text-muted">{label}</p>
|
||||
<p className="mt-1 text-sm font-medium text-text-primary">{value}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { api } from '@/lib/api';
|
||||
import { cn } from '@/lib/cn';
|
||||
import type { Task } from '@/lib/types';
|
||||
import { KanbanBoard } from '@/components/tasks/kanban-board';
|
||||
import { TaskListView } from '@/components/tasks/task-list-view';
|
||||
|
||||
type ViewMode = 'list' | 'kanban';
|
||||
|
||||
export default function TasksPage(): React.ReactElement {
|
||||
const [tasks, setTasks] = useState<Task[]>([]);
|
||||
const [view, setView] = useState<ViewMode>('kanban');
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
api<Task[]>('/api/tasks')
|
||||
.then(setTasks)
|
||||
.catch(() => {})
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
const handleTaskClick = useCallback((task: Task) => {
|
||||
// Task detail view will be added in future iteration
|
||||
console.log('Task clicked:', task.id);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-6 flex items-center justify-between">
|
||||
<h1 className="text-2xl font-semibold">Tasks</h1>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex rounded-lg border border-surface-border">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setView('list')}
|
||||
className={cn(
|
||||
'px-3 py-1.5 text-xs transition-colors',
|
||||
view === 'list'
|
||||
? 'bg-surface-elevated text-text-primary'
|
||||
: 'text-text-muted hover:text-text-secondary',
|
||||
)}
|
||||
>
|
||||
List
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setView('kanban')}
|
||||
className={cn(
|
||||
'px-3 py-1.5 text-xs transition-colors',
|
||||
view === 'kanban'
|
||||
? 'bg-surface-elevated text-text-primary'
|
||||
: 'text-text-muted hover:text-text-secondary',
|
||||
)}
|
||||
>
|
||||
Kanban
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<p className="py-8 text-center text-sm text-text-muted">Loading tasks...</p>
|
||||
) : view === 'kanban' ? (
|
||||
<KanbanBoard tasks={tasks} onTaskClick={handleTaskClick} />
|
||||
) : (
|
||||
<TaskListView tasks={tasks} onTaskClick={handleTaskClick} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams, useSearchParams } from 'next/navigation';
|
||||
import { api } from '@/lib/api';
|
||||
import { resolveAuthCallbackURL } from '@/lib/auth-redirect';
|
||||
import { signIn } from '@/lib/auth-client';
|
||||
import type { SsoProviderDiscovery } from '@/lib/sso';
|
||||
|
||||
export default function AuthProviderRedirectPage(): React.ReactElement {
|
||||
const params = useParams<{ provider: string }>();
|
||||
const searchParams = useSearchParams();
|
||||
const providerId = typeof params.provider === 'string' ? params.provider : '';
|
||||
const requestedCallbackURL = searchParams.get('callbackURL');
|
||||
const [providerName, setProviderName] = useState<string | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
|
||||
async function redirectToProvider(): Promise<void> {
|
||||
try {
|
||||
const callbackURL = resolveAuthCallbackURL(requestedCallbackURL, window.location.origin);
|
||||
const providers = await api<SsoProviderDiscovery[]>('/api/sso/providers');
|
||||
if (cancelled) return;
|
||||
|
||||
const provider = providers.find((candidate) => candidate.id === providerId);
|
||||
if (!provider) {
|
||||
setError('Unknown SSO provider.');
|
||||
return;
|
||||
}
|
||||
|
||||
setProviderName(provider.name);
|
||||
if (!provider.configured) {
|
||||
setError(`${provider.name} is not enabled in this deployment.`);
|
||||
return;
|
||||
}
|
||||
if (provider.loginMode !== 'oidc') {
|
||||
setError(`${provider.name} is not available for OIDC sign in.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await signIn.oauth2({
|
||||
providerId: provider.id,
|
||||
callbackURL,
|
||||
});
|
||||
|
||||
if (!cancelled && result?.error) {
|
||||
setError(result.error.message ?? `${provider.name} sign in failed.`);
|
||||
}
|
||||
} catch (caught: unknown) {
|
||||
if (!cancelled) {
|
||||
setError(caught instanceof Error ? caught.message : 'Unable to start single sign-on.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void redirectToProvider();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [providerId, requestedCallbackURL]);
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex min-h-[50vh] max-w-md flex-col justify-center">
|
||||
<h1 className="text-2xl font-semibold text-text-primary">Single sign-on</h1>
|
||||
<p className="mt-2 text-sm text-text-secondary">
|
||||
{providerName
|
||||
? `Redirecting you to ${providerName}...`
|
||||
: 'Preparing your sign-in request...'}
|
||||
</p>
|
||||
|
||||
{error ? (
|
||||
<div
|
||||
role="alert"
|
||||
className="mt-6 rounded-lg border border-error/30 bg-error/10 px-4 py-3 text-sm text-error"
|
||||
>
|
||||
<p>{error}</p>
|
||||
<Link
|
||||
href="/login"
|
||||
className="mt-3 inline-block font-medium text-blue-400 hover:text-blue-300"
|
||||
>
|
||||
Return to login
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-6 rounded-lg border border-surface-border bg-surface-elevated px-4 py-3 text-sm text-text-secondary">
|
||||
If the redirect does not start automatically, return to the login page and try again.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import type { Metadata } from 'next';
|
||||
import type { ReactNode } from 'react';
|
||||
import { ThemeProvider } from '@/providers/theme-provider';
|
||||
import './globals.css';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Mosaic',
|
||||
description: 'Mosaic Stack Dashboard',
|
||||
};
|
||||
|
||||
function themeScript(): string {
|
||||
return `
|
||||
(function () {
|
||||
try {
|
||||
var theme = window.localStorage.getItem('mosaic-theme') || 'dark';
|
||||
document.documentElement.setAttribute('data-theme', theme === 'light' ? 'light' : 'dark');
|
||||
} catch (error) {
|
||||
document.documentElement.setAttribute('data-theme', 'dark');
|
||||
}
|
||||
})();
|
||||
`;
|
||||
}
|
||||
|
||||
export default function RootLayout({ children }: { children: ReactNode }): React.ReactElement {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<head>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&family=Fira+Code:wght@400;500&display=swap"
|
||||
/>
|
||||
<script dangerouslySetInnerHTML={{ __html: themeScript() }} />
|
||||
</head>
|
||||
<body>
|
||||
<ThemeProvider>{children}</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
export default function HomePage(): never {
|
||||
redirect('/chat');
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
import { useSession } from '@/lib/auth-client';
|
||||
|
||||
interface AdminRoleGuardProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function AdminRoleGuard({ children }: AdminRoleGuardProps): React.ReactElement | null {
|
||||
const { data: session, isPending } = useSession();
|
||||
const router = useRouter();
|
||||
|
||||
const user = session?.user as
|
||||
| (NonNullable<typeof session>['user'] & { role?: string })
|
||||
| undefined;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPending && !session) {
|
||||
router.replace('/login');
|
||||
} else if (!isPending && session && user?.role !== 'admin') {
|
||||
router.replace('/');
|
||||
}
|
||||
}, [isPending, session, user?.role, router]);
|
||||
|
||||
if (isPending) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
<div className="text-sm text-text-muted">Loading...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!session || user?.role !== 'admin') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
import { useSession } from '@/lib/auth-client';
|
||||
|
||||
interface AuthGuardProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function AuthGuard({ children }: AuthGuardProps): React.ReactElement | null {
|
||||
const { data: session, isPending } = useSession();
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPending && !session) {
|
||||
router.replace('/login');
|
||||
}
|
||||
}, [isPending, session, router]);
|
||||
|
||||
if (isPending) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
<div className="text-sm text-text-muted">Loading...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useEffect } from 'react';
|
||||
import { useSession } from '@/lib/auth-client';
|
||||
|
||||
interface GuestGuardProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
/** Redirects authenticated users away from auth pages. */
|
||||
export function GuestGuard({ children }: GuestGuardProps): React.ReactElement | null {
|
||||
const { data: session, isPending } = useSession();
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isPending && session) {
|
||||
router.replace('/chat');
|
||||
}
|
||||
}, [isPending, session, router]);
|
||||
|
||||
if (isPending) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
<div className="text-sm text-text-muted">Loading...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { signOut, useSession } from '@/lib/auth-client';
|
||||
|
||||
interface AppHeaderProps {
|
||||
conversationTitle?: string | null;
|
||||
isSidebarOpen: boolean;
|
||||
onToggleSidebar: () => void;
|
||||
}
|
||||
|
||||
type ThemeMode = 'dark' | 'light';
|
||||
|
||||
const THEME_STORAGE_KEY = 'mosaic-chat-theme';
|
||||
|
||||
export function AppHeader({
|
||||
conversationTitle,
|
||||
isSidebarOpen,
|
||||
onToggleSidebar,
|
||||
}: AppHeaderProps): React.ReactElement {
|
||||
const { data: session } = useSession();
|
||||
const [currentTime, setCurrentTime] = useState('');
|
||||
const [version, setVersion] = useState<string | null>(null);
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [theme, setTheme] = useState<ThemeMode>('dark');
|
||||
|
||||
useEffect(() => {
|
||||
function updateTime(): void {
|
||||
setCurrentTime(
|
||||
new Date().toLocaleTimeString([], {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
updateTime();
|
||||
const interval = window.setInterval(updateTime, 60_000);
|
||||
return () => window.clearInterval(interval);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/version.json')
|
||||
.then(async (res) => res.json() as Promise<{ version?: string; commit?: string }>)
|
||||
.then((data) => {
|
||||
if (data.version) {
|
||||
setVersion(data.commit ? `${data.version}+${data.commit}` : data.version);
|
||||
}
|
||||
})
|
||||
.catch(() => setVersion(null));
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const storedTheme = window.localStorage.getItem(THEME_STORAGE_KEY);
|
||||
const nextTheme = storedTheme === 'light' ? 'light' : 'dark';
|
||||
applyTheme(nextTheme);
|
||||
setTheme(nextTheme);
|
||||
}, []);
|
||||
|
||||
const handleThemeToggle = useCallback(() => {
|
||||
const nextTheme = theme === 'dark' ? 'light' : 'dark';
|
||||
applyTheme(nextTheme);
|
||||
window.localStorage.setItem(THEME_STORAGE_KEY, nextTheme);
|
||||
setTheme(nextTheme);
|
||||
}, [theme]);
|
||||
|
||||
const handleSignOut = useCallback(async (): Promise<void> => {
|
||||
await signOut();
|
||||
window.location.href = '/login';
|
||||
}, []);
|
||||
|
||||
const userLabel = session?.user.name ?? session?.user.email ?? 'Mosaic User';
|
||||
const initials = useMemo(() => getInitials(userLabel), [userLabel]);
|
||||
|
||||
return (
|
||||
<header
|
||||
className="sticky top-0 z-20 border-b backdrop-blur-xl"
|
||||
style={{
|
||||
backgroundColor: 'color-mix(in srgb, var(--color-surface) 82%, transparent)',
|
||||
borderColor: 'var(--color-border)',
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-3 px-4 py-3 md:px-6">
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggleSidebar}
|
||||
className="inline-flex h-10 w-10 items-center justify-center rounded-2xl border transition-colors hover:bg-white/5"
|
||||
style={{ borderColor: 'var(--color-border)', color: 'var(--color-text)' }}
|
||||
aria-label="Toggle conversation sidebar"
|
||||
aria-expanded={isSidebarOpen}
|
||||
>
|
||||
☰
|
||||
</button>
|
||||
|
||||
<Link href="/chat" className="flex min-w-0 items-center gap-3">
|
||||
<div
|
||||
className="flex h-10 w-10 items-center justify-center rounded-2xl text-sm font-semibold text-white shadow-[var(--shadow-ms-md)]"
|
||||
style={{
|
||||
background:
|
||||
'linear-gradient(135deg, var(--color-ms-blue-500), var(--color-ms-teal-500))',
|
||||
}}
|
||||
>
|
||||
M
|
||||
</div>
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<div className="text-sm font-semibold text-[var(--color-text)]">Mosaic</div>
|
||||
<div className="hidden h-5 w-px bg-[var(--color-border)] md:block" />
|
||||
<div className="hidden items-center gap-2 md:flex">
|
||||
<span className="relative flex h-2.5 w-2.5">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-[var(--color-ms-teal-500)] opacity-60" />
|
||||
<span className="relative inline-flex h-2.5 w-2.5 rounded-full bg-[var(--color-ms-teal-500)]" />
|
||||
</span>
|
||||
<span className="text-xs uppercase tracking-[0.18em] text-[var(--color-muted)]">
|
||||
Online
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="hidden min-w-0 items-center gap-3 md:flex">
|
||||
<div className="rounded-full border border-[var(--color-border)] px-3 py-1.5 text-xs text-[var(--color-text-2)]">
|
||||
{currentTime || '--:--'}
|
||||
</div>
|
||||
<div className="max-w-[24rem] truncate text-sm font-medium text-[var(--color-text)]">
|
||||
{conversationTitle?.trim() || 'New Session'}
|
||||
</div>
|
||||
{version ? (
|
||||
<div className="rounded-full border border-[var(--color-border)] px-3 py-1.5 text-xs text-[var(--color-muted)]">
|
||||
v{version}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="hidden items-center gap-2 lg:flex">
|
||||
<ShortcutHint label="⌘/" text="focus" />
|
||||
<ShortcutHint label="⌘K" text="focus" />
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleThemeToggle}
|
||||
className="inline-flex h-10 items-center justify-center rounded-2xl border px-3 text-sm transition-colors hover:bg-white/5"
|
||||
style={{ borderColor: 'var(--color-border)', color: 'var(--color-text)' }}
|
||||
aria-label="Toggle theme"
|
||||
>
|
||||
{theme === 'dark' ? '☀︎' : '☾'}
|
||||
</button>
|
||||
|
||||
<div className="relative">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setMenuOpen((prev) => !prev)}
|
||||
className="inline-flex h-10 w-10 items-center justify-center rounded-full border text-sm font-semibold transition-colors hover:bg-white/5"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-surface-2)',
|
||||
borderColor: 'var(--color-border)',
|
||||
color: 'var(--color-text)',
|
||||
}}
|
||||
aria-expanded={menuOpen}
|
||||
aria-label="Open user menu"
|
||||
>
|
||||
{session?.user.image ? (
|
||||
<img
|
||||
src={session.user.image}
|
||||
alt={userLabel}
|
||||
className="h-full w-full rounded-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
initials
|
||||
)}
|
||||
</button>
|
||||
{menuOpen ? (
|
||||
<div
|
||||
className="absolute right-0 top-12 min-w-56 rounded-3xl border p-2 shadow-[var(--shadow-ms-lg)]"
|
||||
style={{
|
||||
backgroundColor: 'var(--color-surface)',
|
||||
borderColor: 'var(--color-border)',
|
||||
}}
|
||||
>
|
||||
<div className="border-b px-3 py-2" style={{ borderColor: 'var(--color-border)' }}>
|
||||
<div className="text-sm font-medium text-[var(--color-text)]">{userLabel}</div>
|
||||
{session?.user.email ? (
|
||||
<div className="text-xs text-[var(--color-muted)]">{session.user.email}</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="p-1">
|
||||
<Link
|
||||
href="/settings"
|
||||
className="flex rounded-2xl px-3 py-2 text-sm text-[var(--color-text-2)] transition-colors hover:bg-white/5"
|
||||
onClick={() => setMenuOpen(false)}
|
||||
>
|
||||
Settings
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void handleSignOut()}
|
||||
className="flex w-full rounded-2xl px-3 py-2 text-left text-sm text-[var(--color-text-2)] transition-colors hover:bg-white/5"
|
||||
>
|
||||
Sign out
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
function ShortcutHint({ label, text }: { label: string; text: string }): React.ReactElement {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-2 rounded-full border border-[var(--color-border)] px-3 py-1.5 text-xs text-[var(--color-muted)]">
|
||||
<span className="font-medium text-[var(--color-text-2)]">{label}</span>
|
||||
<span>{text}</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
function getInitials(label: string): string {
|
||||
const words = label.split(/\s+/).filter(Boolean).slice(0, 2);
|
||||
if (words.length === 0) return 'M';
|
||||
return words.map((word) => word.charAt(0).toUpperCase()).join('');
|
||||
}
|
||||
|
||||
function applyTheme(theme: ThemeMode): void {
|
||||
const root = document.documentElement;
|
||||
if (theme === 'light') {
|
||||
root.setAttribute('data-theme', 'light');
|
||||
root.classList.remove('dark');
|
||||
} else {
|
||||
root.removeAttribute('data-theme');
|
||||
root.classList.add('dark');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
services:
|
||||
mosaic-agent:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Containerfile
|
||||
image: ${MOSAIC_IMAGE_TAG:?MOSAIC_IMAGE_TAG must be set by scripts/load_release (run via scripts/*.sh)}
|
||||
user: "1000:1000"
|
||||
environment:
|
||||
# Resolved from config.json by scripts/common.sh (load_config).
|
||||
# Required: compose fails fast when the launcher did not supply them.
|
||||
PI_PROVIDER: ${MOSAIC_PROVIDER:?MOSAIC_PROVIDER must be set by scripts/load_config (run via scripts/*.sh)}
|
||||
PI_MODEL: ${MOSAIC_MODEL:?MOSAIC_MODEL must be set by scripts/load_config (run via scripts/*.sh)}
|
||||
# Adapter selection (resolved from config execution.adapter; default pi)
|
||||
MOSAIC_ADAPTER: ${MOSAIC_ADAPTER:-pi}
|
||||
# Mission directives injection point (set by the task runner when the
|
||||
# task references a mission; container path of the run snapshot)
|
||||
MOSAIC_MISSION_FILE: ${MOSAIC_MISSION_FILE:-}
|
||||
# Workspace + capabilities (set by the task runner; M5)
|
||||
MOSAIC_WORKSPACE: ${MOSAIC_WORKSPACE:-}
|
||||
MOSAIC_TOOLS: ${MOSAIC_TOOLS:-}
|
||||
# Persistent named session dir + optional fork source (M6/M11)
|
||||
MOSAIC_SESSION_DIR: ${MOSAIC_SESSION_DIR:-}
|
||||
MOSAIC_SESSION_FORK: ${MOSAIC_SESSION_FORK:-}
|
||||
# Interactive TUI mode + agent identity (M13, set by scripts/agent.sh)
|
||||
MOSAIC_INTERACTIVE: ${MOSAIC_INTERACTIVE:-}
|
||||
MOSAIC_AGENT_NAME: ${MOSAIC_AGENT_NAME:-}
|
||||
MOSAIC_AGENT_ROLE: ${MOSAIC_AGENT_ROLE:-}
|
||||
MOSAIC_AGENT_SOUL_FILE: ${MOSAIC_AGENT_SOUL_FILE:-}
|
||||
# Skill dirs explicitly provided to the seat (M17)
|
||||
MOSAIC_SKILLS: ${MOSAIC_SKILLS:-}
|
||||
# mock adapter only: verbatim response for deterministic seam tests
|
||||
MOSAIC_MOCK_RESPONSE: ${MOSAIC_MOCK_RESPONSE:-}
|
||||
# Documented container auth alternative: provider API key via
|
||||
# runtime environment variable. Empty by default; when empty Pi
|
||||
# falls back to the read-only mounted auth.json credential file.
|
||||
ZAI_API_KEY: ${ZAI_API_KEY:-}
|
||||
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
|
||||
volumes:
|
||||
# Configured runtime state root (from config.json dataRoot).
|
||||
- ${MOSAIC_DATA_ROOT:?MOSAIC_DATA_ROOT must be set by scripts/load_config (run via scripts/*.sh)}:/var/lib/mosaic
|
||||
# Runtime credential only: pi auth file mounted READ-ONLY.
|
||||
# Never copied into the image.
|
||||
- ${PI_AUTH_FILE:-/home/jwoltje/.pi/agent/auth.json}:/home/node/.pi/agent/auth.json:ro
|
||||
# Headless runs: the request is passed as command args by the launchers
|
||||
# (run-task.sh) or defaults inside run-agent.sh (hello/verify). Never a
|
||||
# fixed command here - interactive runs (scripts/agent.sh) need no args.
|
||||
@@ -0,0 +1,5 @@
|
||||
# POC constitution
|
||||
|
||||
Never print credentials, tokens, or authentication files.
|
||||
|
||||
Follow the loaded system instructions before the user request.
|
||||
@@ -0,0 +1,7 @@
|
||||
# POC identity
|
||||
|
||||
Your name is mosaic-poc-agent.
|
||||
|
||||
Your startup marker is MOSAIC_HELLO_OK.
|
||||
|
||||
When asked for your startup marker, return only the marker.
|
||||
@@ -0,0 +1,4 @@
|
||||
# POC standards
|
||||
|
||||
Answer startup verification requests with only the requested value.
|
||||
Do not add explanation or formatting.
|
||||
@@ -1,52 +0,0 @@
|
||||
services:
|
||||
postgres:
|
||||
image: pgvector/pgvector:pg17
|
||||
ports:
|
||||
- '${PG_HOST_PORT:-5433}:5432'
|
||||
environment:
|
||||
POSTGRES_USER: mosaic
|
||||
POSTGRES_PASSWORD: mosaic
|
||||
POSTGRES_DB: mosaic
|
||||
volumes:
|
||||
- pg_data:/var/lib/postgresql/data
|
||||
- ./infra/pg-init:/docker-entrypoint-initdb.d:ro
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U mosaic']
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
valkey:
|
||||
image: valkey/valkey:8-alpine
|
||||
ports:
|
||||
- '${VALKEY_HOST_PORT:-6380}:6379'
|
||||
volumes:
|
||||
- valkey_data:/data
|
||||
healthcheck:
|
||||
test: ['CMD', 'valkey-cli', 'ping']
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
|
||||
otel-collector:
|
||||
image: otel/opentelemetry-collector-contrib:0.100.0
|
||||
ports:
|
||||
- '4317:4317' # OTLP gRPC
|
||||
- '4318:4318' # OTLP HTTP
|
||||
volumes:
|
||||
- ./infra/otel-collector.yml:/etc/otelcol-contrib/config.yaml:ro
|
||||
depends_on:
|
||||
jaeger:
|
||||
condition: service_started
|
||||
|
||||
jaeger:
|
||||
image: jaegertracing/jaeger:2.6.0
|
||||
ports:
|
||||
- '16686:16686' # Jaeger UI
|
||||
- '4319:4317' # Jaeger OTLP gRPC (internal, collector forwards here)
|
||||
environment:
|
||||
COLLECTOR_OTLP_ENABLED: 'true'
|
||||
|
||||
volumes:
|
||||
pg_data:
|
||||
valkey_data:
|
||||
@@ -1,44 +0,0 @@
|
||||
FROM node:22-alpine AS base
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
# Copy workspace manifests first for layer-cached install
|
||||
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
|
||||
COPY apps/gateway/package.json ./apps/gateway/
|
||||
COPY packages/ ./packages/
|
||||
COPY plugins/ ./plugins/
|
||||
# the root prepare script runs scripts/install-hooks.mjs on install
|
||||
COPY scripts/ ./scripts/
|
||||
RUN pnpm install --frozen-lockfile
|
||||
COPY . .
|
||||
# Build gateway and all of its workspace dependencies via turbo dependency graph
|
||||
RUN pnpm turbo run build --filter @mosaicstack/gateway...
|
||||
# Produce a self-contained deploy artifact: flat node_modules, no pnpm symlinks
|
||||
# --legacy is required for pnpm v10 when inject-workspace-packages is not set
|
||||
RUN pnpm --filter @mosaicstack/gateway --prod deploy --legacy /deploy
|
||||
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production
|
||||
# WorkspaceService shells out to git at runtime and roots workspaces at
|
||||
# $MOSAIC_ROOT/.workspaces (apps/gateway/src/workspace/workspace.service.ts);
|
||||
# mount a volume over /opt/mosaic to persist workspaces across container restarts.
|
||||
# Intentionally unpinned: Alpine's signed repository is the trust anchor; pinning
|
||||
# git was declined so routine base-image security updates remain maintainable.
|
||||
RUN apk add --no-cache git \
|
||||
&& mkdir -p /opt/mosaic/.workspaces \
|
||||
&& chown -R node:node /opt/mosaic /app
|
||||
ENV MOSAIC_ROOT=/opt/mosaic
|
||||
# Use the pnpm deploy output — resolves all deps into a flat, self-contained node_modules
|
||||
COPY --chown=node:node --from=builder /deploy/node_modules ./node_modules
|
||||
COPY --chown=node:node --from=builder /deploy/package.json ./package.json
|
||||
# dist is declared in package.json "files" so pnpm deploy copies it into /deploy;
|
||||
# copy from builder explicitly as belt-and-suspenders
|
||||
COPY --chown=node:node --from=builder /app/apps/gateway/dist ./dist
|
||||
# gateway defaults to port 14242 (apps/gateway/src/main.ts)
|
||||
EXPOSE 14242
|
||||
USER node
|
||||
CMD ["node", "dist/main.js"]
|
||||
@@ -1,24 +0,0 @@
|
||||
FROM node:22-alpine AS base
|
||||
ENV PNPM_HOME="/pnpm"
|
||||
ENV PATH="$PNPM_HOME:$PATH"
|
||||
RUN corepack enable
|
||||
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./
|
||||
COPY apps/web/package.json ./apps/web/
|
||||
COPY packages/ ./packages/
|
||||
# the root prepare script runs scripts/install-hooks.mjs on install
|
||||
COPY scripts/ ./scripts/
|
||||
RUN pnpm install --frozen-lockfile
|
||||
COPY . .
|
||||
RUN pnpm --filter @mosaicstack/web build
|
||||
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
ENV NODE_ENV=production
|
||||
COPY --from=builder /app/apps/web/.next/standalone ./
|
||||
COPY --from=builder /app/apps/web/.next/static ./apps/web/.next/static
|
||||
COPY --from=builder /app/apps/web/public ./apps/web/public
|
||||
EXPOSE 3000
|
||||
CMD ["node", "apps/web/server.js"]
|
||||
@@ -0,0 +1,26 @@
|
||||
# KICKSTART — paste this into a fresh agent session
|
||||
|
||||
```text
|
||||
You are the conductor of the Mosaic Stack rebuild in this repository
|
||||
(mosaicstack/stack-v2 dev-test). Re-orient in this order:
|
||||
|
||||
1. AGENTS.md — canon, invariants, session protocol
|
||||
2. docs/plans/CURRENT.md — the single next action
|
||||
3. docs/SESSIONS.md — who worked here and what shipped
|
||||
4. docs/plans/CONDUCTOR.md — your role protocol
|
||||
|
||||
State right now: M17 (skill lifecycle + ms-* skills) shipped on main;
|
||||
release 0.0.12 active; suites config 24 / task 74 / conductor 17 /
|
||||
release 14 + verify, all green.
|
||||
|
||||
A live pi collaborator (glm-5.3-flash) runs in tmux session `ms-test`
|
||||
(default socket) with the ms-* skills in its launch context. Your next
|
||||
action: calibrate the conductor loop with it. Message via
|
||||
tools/tmux/agent-send.sh (never raw send-keys), protocol in
|
||||
skills/ms-communications/SKILL.md. Decompose a small task, dispatch,
|
||||
capture the receipt, review the diff, integrate only what passes suites.
|
||||
|
||||
Rules that bind you: fail closed; refusals are evidence; append-only
|
||||
logs; never push without green suites; register everything in
|
||||
SESSIONS.md; never guess — verify.
|
||||
```
|
||||
@@ -0,0 +1,110 @@
|
||||
# Session registry — append-only
|
||||
|
||||
Every agent session (assistant, worker-cycle conductor, or owner-directed
|
||||
automation) that works in this repository registers one line here. Entries
|
||||
are never rewritten or removed; corrections are new entries.
|
||||
|
||||
| Date (UTC) | Actor | Scope | Outcome / artifacts |
|
||||
|---|---|---|---|
|
||||
| 2026-09-03 | assistant (conductor + worker) | POC through M12: containerized pi proof, config layer, missions/tasks, release model, adapter seam, workspaces/capabilities, named sessions, retention, session forking, conductor auto-apply, roles/ convention | 13 tags; suites 24/58/14 + 17 conductor + verify green; releases 0.0.1–0.0.7; issues #1–#34 closed |
|
||||
| 2026-09-03 | assistant (conductor) | User layer: profile updates (pets, family), ms-user skill review/revision (confirmation rules merged, propose-not-apply, missing-file flow, privacy scope, dispatch = all of user/, rule 9 scratch-file constraint), USER.md.bak removed | skills/ms-user/SKILL.md rewritten; ~/.mosaic-dev/user/USER.md updated (Family, Pets); USER.md.bak deleted |
|
||||
| 2026-09-03 | assistant | ms-communications skill: inter-agent messaging protocol consolidated from tools/tmux/README.md and agent-send.sh (channel, preamble grammar, flip-on-reply, triage classes, etiquette, receiving protocol, delivery mechanics) | skills/ms-communications/SKILL.md created; unslop-check clean |
|
||||
| 2026-09-03 | assistant (conductor) + ms-test collaborator (worker, glm-5.3-flash) | Conductor-loop calibration (#43): decompose → dispatch via agent-send.sh → receipt → line-by-line diff review (claims verified vs tool source) → suite-gated integration; CURRENT.md staleness corrected (M16/M17 late-logged, next action → M18) | docs/TOOLS.md tools/ section + suite-count fix; issue #43 closed; suites 24/74/14/17 + verify green |
|
||||
| 2026-09-03 | owner + assistant (conductor) + ms-test collaborator | Skill revisions adjudicated (#44): ms-communications integrated as-authored (owner preamble restructure + collaborator delivery-discipline hunks); ms-conductor collaborator redraft integrated with conductor remediation (step 3 refusal-vs-outage distinction; preserves owner's outage-dispatch intent inside fail-closed canon); TOOLS.md gains release.sh ensure row | skills/ms-communications/SKILL.md, skills/ms-conductor/SKILL.md, docs/TOOLS.md; suites 24/74/14/17 + verify green; unslop clean ×3 |
|
||||
| 2026-09-03 | assistant (conductor) | M18 seat-role progressive capability restriction (#45): roles/<role>.json contracts (strict schema, name-filename binding, network declared), mosaic-task.mjs resolve-role, agent.sh ceiling intersection with fail-closed refusals, roles/researcher.json shipped, 14 suite cases (task 74 → 88) | scripts/mosaic-task.mjs, scripts/agent.sh, scripts/test-task.sh, roles/researcher.json, docs; suites 24/88/14/17 + verify green |
|
||||
| 2026-09-03 | owner (decision + live verification) + assistant (conductor) | M18 live verification + follow-up (#46): owner confirmed narrowing/refusal/tool-free live; seatless launch under AGENTS_DIR override discovered and made fail-closed (exit 4); task suite 88 → 90 | scripts/agent.sh, scripts/test-task.sh, docs/TOOLS.md; suites 24/90/14/17 + verify green |
|
||||
| 2026-09-03 | assistant (conductor) | M19 harness auth tooling (#47): pi auth investigation (native provider stacking, no native multi-account), scripts/auth.sh status/accounts (never prints credential material), agent.sh --auth per-launch injection via PI_AUTH_FILE, test-auth.sh suite (13 cases incl. secret-never-printed assertions) | scripts/auth.sh, scripts/agent.sh, scripts/test-auth.sh, docs/TOOLS.md, AGENTS.md; suites 24/90/14/17/13 + verify green |
|
||||
| 2026-09-03 | owner (direction) + assistant (conductor) | M19 correction (#48): mosaic-managed auth moved from ~/.pi to the data root (auth/<account>.json, 0600 enforced); ~/.pi read-only to the stack as a ROADMAP standing decision; auth.sh config-driven; test-auth 15 cases | scripts/auth.sh, scripts/agent.sh, scripts/test-auth.sh, docs/TOOLS.md, docs/plans/ROADMAP.md, README.md; suites 24/15/90/14/17 + verify green |
|
||||
| 2026-09-03 | owner (requirements) + assistant (conductor/spec author) | Harness/provider/auth registry specification (#49): agent.json harness declaration, centralized providers/accounts/settings profiles, audited runtime selection, per-seat auth/models materialization, centralized OAuth lifecycle, local/remote Ollama, target mosaic CLI | docs/plans/2026-09-03_auth-provider-harness-registry.md; implementation blocked pending ten-gate review; unslop clean |
|
||||
| 2026-09-03 | ms-test (independent reviewer, zai/glm-5.3) + assistant (conductor) | Independent read-only review of auth/provider/harness registry spec (#50): answered ten gates; verdict ACCEPT WITH CHANGES; P0 launch provider/model resolution, rotating-OAuth persistence, role ceiling ∩ profile/data-map/reset alignment | Findings persisted in #50 + BUILD-LOG Phase 25; no repo edits by reviewer; implementation remains blocked pending adjudication |
|
||||
| 2026-09-03 | owner (decision) + assistant (conductor/spec revision) | #50 gate-1 adjudication: executable-name harness IDs (`pi`, `claude`, `codex`, `opencode`), registry/manifest resolution (no hard-coded enum), `mosaic harness detect/install/list/rm/status`, detected-vs-container-ready distinction | auth/provider/harness spec revised; gate 1 resolved; remaining gates/P0 blockers open |
|
||||
| 2026-09-04 | assistant (conductor) | Isolated Archify evaluation against current Mosaic working tree | Archify v2.17.0-dev.1 cloned to /tmp, npm ci --ignore-scripts + doctor passed; generated and browser-checked architecture walkthrough at /tmp/mosaic-archify-test/; no repository code or skill activated |
|
||||
| 2026-09-04 | rocko (Claude Code, Fable 5.1; examine/evaluate/guide) + Jason (lead) | Archify lane established at ~/.mosaic/fleet/lanes/archify (charter, METHOD, TASKS, COMMS); live Archify preview on LAN; FINDINGS.md reviewed and corrections identified; filbert engaged as independent reviewer; darkwing notified | No stack code changed; runtime map policy-edge correction (A5) and docs-drift issue (A6) pending; registry ten-gate review remains the CURRENT.md action |
|
||||
| 2026-09-05 | rocko (Claude Code, Fable 5.1; archify lane) + Jason (lead) | skills/ms-archify/SKILL.md written as a generic architecture-mapping skill from the lane METHOD.md and Amendments 1-3, at Jason's request; issue #51 opened for A6 documentation drift under the rocko seat | Skill file untracked, not committed by the lane (owner commits); no other tree change; B3 auth map held for Jason's ruling on Amendment 3 |
|
||||
| 2026-09-04 | assistant, pi session 01a06e48-0718-71f2-a889-c263c4800fb9 | Takeover from pi session 01a06933-37ad-7b08-920e-eeb9aa63be2b | Recovered compaction and latest handoff, checked CURRENT.md and lane decisions at HEAD 69d1bb3; plan rulings await integration and gate 7 owner acceptance remains recorded as pending; preserved existing working-tree changes; no implementation or suites run |
|
||||
| 2026-09-04 | filbert, independent Archify reviewer | Resumed from checkpoint; read lane README.md, METHOD.md, TASKS.md and COMMS.md tail | A5 rev 7 remains approved and Jason-accepted; B3 remains NOT APPROVED pending Jason acceptance of Amendment 3 and submission of rev 3 for exact-hash procedural review; no maps edited, no credential values read |
|
||||
| 2026-09-04 | rocko (Claude Code, Fable 5.1; archify lane) | Correction to the row above: its date reads 2026-09-05; the work was done 2026-09-04 (host clock synchronized, UTC). Content unchanged. | Correction row, append-only. |
|
||||
| 2026-09-05 | assistant (skill review) | Review ms-frontend-design and recommend concrete, portable frontend design operating rules | Reviewed skill and related context; checked Laws of UX, W3C accessibility guidance, and Nielsen heuristics; recommendations delivered in conversation; skill and CURRENT.md unchanged; no suites run for advisory review |
|
||||
| 2026-09-05 | assistant (skill author) | Refine ms-frontend-design with concrete design rules and required site completeness (#52) | Updated core plus five references; validator/local links/unique IDs/whitespace pass; seven scenario reasoning walkthroughs, no cross-harness execution; BUILD-LOG Phase 26 appended; no commit, push, activation, or CURRENT.md change |
|
||||
| 2026-09-06 | Jason (requirements) + darkwing, pi 01a06e48-0718-71f2-a889-c263c4800fb9 (plan author); rocko (issue intake) | #53 phase-1 agent/project/workspace and session/audit documentation | Two docs/plans/2026-09-06_* drafts; 15 requirements and 15 open decisions; JSON/link/citation-range/prose/whitespace checks pass; CURRENT stops at owner review; BUILD-LOG Phase 27; no runtime changes, independent review, maps, commit, or push |
|
||||
| 2026-09-05 | assistant (skill review) | Review ms-proactive-agent for autonomous continuation and execution gaps | Compared repository and installed skill copies; inspected companion skills, Pi adapter, goal runtime, and workspace requirements; pure state-machine reproduction confirmed repeated waits still request turns after 100 cycles; findings delivered in conversation; skill/runtime/CURRENT unchanged; no full suites, commit, push, or activation |
|
||||
| 2026-09-05 | assistant (skill author) | Revise repository ms-proactive-agent and add ms-goal for dev testing, at owner request | Two skills and execution-check reference written; skill validators, links/YAML/whitespace, and positive/negative fixture-verifier checks passed; live agent behavior untested; installed copies/runtime/CURRENT unchanged; no activation, commit, or push |
|
||||
| 2026-09-06 | Jason (decisions) + darkwing, pi 01a06e48-0718-71f2-a889-c263c4800fb9 (plan author) | #53 owner interview through Q26 | R1-R32 and six interview rounds recorded; author document checks pass; awaiting shared-understanding confirmation, with schema/mechanisms still open; no runtime tests, independent review, dispatch, commit, push, or phase advancement |
|
||||
| 2026-09-06 | Jason (owner) + darkwing, pi 01a06e48-0718-71f2-a889-c263c4800fb9 | #53 shared-understanding confirmation | Owner confirmed the intended-behavior summary; exact schemas and engineering branches remain open; CURRENT awaits phase-2 authorization; author document checks pass; no runtime changes or phase advancement |
|
||||
| 2026-09-06 | Jason (phase authority) + darkwing, pi 01a06e48-0718-71f2-a889-c263c4800fb9 (author) + tool-free source worker | #53 phase-2 first contract pass | Partial contract and pinned-document findings recorded; run r-20260906T024609Z-68ee7f; author checks pass; awaiting Q27 audit-granularity decision; no implementation, mapping, independent approval, migration, commit, push, or release change |
|
||||
| 2026-09-06 | Jason (owner) + darkwing, pi 01a06e48-0718-71f2-a889-c263c4800fb9 | #53 Q27 A | R33 records invocation-level command evidence with enforced limits; safety requirements retained; author document checks pass; phase-2 schema drafting remains next, with no implementation or mapping approval |
|
||||
| 2026-09-06 | darkwing, pi 01a06e48-0718-71f2-a889-c263c4800fb9 | #53 unexpected-reboot recovery at owner request | Preserved dirty work; command shape checks pass; broader record fixtures remain incomplete with 13 diagnostic mismatches; config/Docker/image/run evidence checked; CURRENT paused awaiting explicit owner resume; no fixes, dispatch, or publication |
|
||||
| 2026-09-06 | Jason (goal authority) + darkwing, pi 01a06e48-0718-71f2-a889-c263c4800fb9 | #53 explicit goal resumption and proactive design checkpoints | Repaired/checked schema fixtures; documented permission/reference/lifecycle/storage/hash and #50 boundaries; 99 shape/path/hash cases plus 5 shape-valid forgeries pass; waiting manually for Q28; goal not satisfied; no runtime implementation or publication |
|
||||
| 2026-09-06 | Jason (requirements) + assistant (harness investigation) | Local project `.pi` test plan for loading `~/.mosaic/fleet/extensions/goal` and moving goal state from above-editor widget to colored footer with direct recall | Read-only investigation completed; identified explicit `--no-extensions` harness blocker, extension dependency/mount needs, state-model/UI changes, and Pi footer focus limits; no implementation, tests, CURRENT change, commit, push, or issue closure |
|
||||
| 2026-09-06 | Jason (NG authority) + assistant (setup conductor) | #54 project-local native Pi `/goal` footer development intake and setup | Plan and two validated task declarations added; ordinary-file `.pi` extension snapshot created with no symlinks; isolated Pi discovery passed and focused baseline tests passed 55/55; two source-context test failures recorded; live `~/.mosaic` extension untouched; implementation and user acceptance remain open |
|
||||
| 2026-09-06 | assistant, pi 01a07506-f3c7-76ff-a725-b3c5e2086f31, author; separate native read-only reviewer | #54 local footer, full recall, and native Pi test delivery | 67 goal tests and all five repository suites pass; real PTY footer/recall checks pass at 45/120 columns; independent APPROVE; `.pi/goal-dev.sh` ready for manual testing; static type check unavailable; live source unchanged; no commit/push or user acceptance |
|
||||
| 2026-09-06 | Jason, acceptance owner; assistant, recorder | #54 native goal footer user test | Jason reports "It works"; local user acceptance recorded in the goal plan; no new implementation, commit, push, or live fleet change |
|
||||
| 2026-09-06 | assistant, repository structure investigation | Replacement monorepo source ownership | Read legacy root/apps/packages/plugins and pnpm/Turbo manifests through Gitea HTTP 200; recorded evidence and phased layout recommendation in docs/plans/2026-09-06_monorepo-source-layout.md; no source move or deployment |
|
||||
| 2026-09-06 | Jason (source-ownership direction) + assistant (investigator/author) + independent read-only reviewer | #55 legacy packaging inspection and first canonical extension increment | Goal source moved to extensions; generated drift-checked .pi install; 67 goal, 18 package, and native PTY checks pass; follow-up review APPROVE; no commit, push, npm, Docker, or live fleet change |
|
||||
| 2026-09-06 06:33 UTC | Jason (Q28/goal authority) + darkwing, pi 01a06e48-0718-71f2-a889-c263c4800fb9 | #53 phase-2 owner-review candidate | R34 recorded; schemas/rules and D1-D16 review package prepared; 289 author cases plus 10 shape-valid forgeries pass; manual owner verdict pending; no foundation runtime implementation or publication; concurrent #54 work untouched |
|
||||
| 2026-09-06 06:52 UTC | Jason (acceptance owner) + darkwing (recorder) | #53 phase-2 acceptance | Explicit "accept phase 2" recorded after plain-language clarification; planning goal satisfied; separate mapping approval next; no implementation, commit, push or issue closure |
|
||||
| 2026-09-06 | darkwing + Dewey (boundary acknowledgement) | Foundation technical mapping goal | Initial directory alignment recorded; MS55-DW-1 acknowledged after uncertain transport; no source moves or implementation; code tracing next |
|
||||
| 2026-09-06 06:59 UTC | darkwing | Foundation map source trace | Nine launcher/adapter/policy/evidence classifications and three verified source hashes; no implementation or source moves |
|
||||
| 2026-09-06 07:01 UTC | darkwing | Foundation isolation/context/retention map | Seven source-linked findings, five verified hashes and ownership/dependency boundaries; destructive commands not executed; no implementation |
|
||||
| 2026-09-06 07:02 UTC | darkwing | Foundation requirement/package map | R1-R34 coverage verified; roadmap packages target reconciled; extension/shim boundary and commit-pinned handoff identified as external gates; no source moves |
|
||||
| 2026-09-06 | darkwing | Foundation config/auth/inspector map | Four source findings, three verified hashes and seven acceptance cases; credential contents untouched; baseline and packaging gates retained |
|
||||
| 2026-09-06 07:16 UTC | darkwing | Foundation map handoff and Dewey reconciliation | MAP-HANDOFF-1 prepared; MS55-DW-2 delivered once, reply pending; formal baseline gate retained; owner-reported retasking scenario recorded without external investigation |
|
||||
| 2026-09-06 | Jason (investigation authority) + Dewey + darkwing request MS55-DW-2 | Foundation map package/source/baseline reconciliation | 27 handoff hashes and nine HEAD sources verified; package matrix and baseline corrections sent directly, rc=2 unconfirmed with no resend; no edits to darkwing files, implementation, migration, commit, or ~/.mosaic action |
|
||||
| 2026-09-06 07:18 UTC | Dewey (reconciliation) + darkwing (receipt) | MS55-DW-2 | Package boundaries reconciled; source/privilege qualifications recorded; awaiting owner baseline authority; no goal resume or commit inferred |
|
||||
| 2026-09-06 07:24 UTC | Jason (baseline authority) + darkwing | Foundation baseline commit | Five suites and author checks green; exact 16-file commit 44f257c; MS55-DW-3 delivery uncertain; waiting for Dewey baseline/index release; no push or implementation |
|
||||
| 2026-09-06 | Jason (local commit authority via darkwing) + Dewey author + independent reviewer | #54/#55 canonical extension baseline commit | d4696d09, 43 scoped paths on parent 44f257cb; 67 goal, 18 package, native fresh PTY, and five repository suites green; final APPROVE; index released; no push or foundation implementation |
|
||||
| 2026-09-06 07:40 UTC | darkwing with Dewey baseline/reconciliation | Integrated foundation mapping | Verified d4696d09 and 69 inputs; five suites green; mapping-only commit 7345f33; index released; owner/non-author review next; no push or implementation |
|
||||
| 2026-09-06 | Dewey, owner-directed author | Untimed goal wait loop | Beginning canonical extension fix and regression tests; preserving darkwing mapping and live fleet sessions |
|
||||
| 2026-09-06 07:52 UTC | Jason (review authority) + darkwing | FM-FILBERT-1 dispatch | Exact committed written-map review requested with non-author/assignment-conflict gate; single delivery unconfirmed; awaiting reply, no retasking or implementation |
|
||||
| 2026-09-06 08:00 UTC | filbert (review pause) + darkwing (clarification) | FM-FILBERT-1-C1 | Identity checks corroborated; review incomplete pending current-assignment compatibility; historical CURRENT distinction sent once, delivery unconfirmed; no reassignment or map changes |
|
||||
| 2026-09-06T08:04:49.508743+00:00 | Dewey | #56 quiet goal waits, local fix and authorized three-copy deployment | Independent reviews approved; canonical 71, legacy 67, package 18 and all five repo suites green; six installed native canaries and sixteen links verified; backups retained, existing sessions untouched, awaiting owner reload/acceptance |
|
||||
| 2026-09-06 08:06 UTC | filbert (independent review) + darkwing (verified receipt) | FM-FILBERT-1 complete | Written map APPROVED at exact hashes; verdict 6b08c6fa verified; earlier admission blocker withdrawn; owner acceptance pending, no runtime/renderer acceptance or implementation |
|
||||
| 2026-09-06 08:07 UTC | Jason (owner acceptance) + darkwing (receipt) | Reviewed technical map accepted | Mapping 7345f33 accepted as planning; exact map/handoff/verdict unchanged; inspector charter awaits authorization; no implementation or publication |
|
||||
| 2026-09-06 08:11 UTC | darkwing | Inspector charter under #53 | Draft with nine acceptance groups; Rocko feasibility delivered, Filbert review availability unconfirmed; scoped planning only, no implementation or retasking |
|
||||
| 2026-09-06 08:11 UTC | filbert (availability) + darkwing (receipt) | FI-FILBERT-1 | Available without assignment conflict or draft co-authorship; review waits for frozen charter; initial delivery uncertainty resolved |
|
||||
| 2026-09-06 08:25 UTC | rocko (feasibility) + darkwing (source reconciliation) | FI-ROCKO-2 | Full note and hashes checked; nine correction groups sent/delivered; corrected note pending before freeze; no implementation |
|
||||
| 2026-09-06 | Dewey | Owner-requested Resume goal discovery conflict | Native reproduction and bounded launcher repair underway; no existing sessions or other seats changed |
|
||||
| 2026-09-06 08:29:51 UTC | Dewey | #57 Resume-only launch conflict | Reviewed launcher repair deployed; native loader/CLI and 71 goal tests pass; guards/state location retained, no sessions interrupted; awaiting user retry |
|
||||
| 2026-09-06 08:34 UTC | Dewey | #57 owner correction: shared NG selection | Reviewed, deployed and native-verified; guards retained, no goal state writes; ready for normal Resume restart |
|
||||
| 2026-09-06 08:42 UTC | darkwing with rocko feasibility | FI-FILBERT-2 charter freeze | r2 verified; V1-V3 and Node constants measured; charter cbd0487a frozen; exact-hash review requested, delivery unconfirmed; no implementation |
|
||||
| 2026-09-06 | Dewey | Fleet NG goal ownership and scoped commit, owner-authorized | Inventory complete; alias/native regression and reviewed deployment next; Resume UX accepted |
|
||||
| 2026-09-06 08:58 UTC | filbert (NOT APPROVED) + darkwing (revision) | FI-FILBERT-3 | Candidate 2/verdict preserved; five findings addressed in candidate 3, exact hash frozen; re-review requested, delivery unconfirmed; no implementation |
|
||||
| 2026-09-06 09:03 UTC | filbert (independent approval) + darkwing (verified receipt) | FI-FILBERT-3 complete | Charter approved at exact hashes; five findings closed; awaiting owner build authorization; index left with Dewey, no implementation |
|
||||
| 2026-09-06 09:02 UTC | Dewey | #58 fleet shared NG ownership | Reviewed transaction deployed, 55 actual loader combinations and all suites pass; Resume UX accepted, fleet/quiet acceptance pending; scoped commit next, shared logs left unstaged due mixed ownership |
|
||||
| 2026-09-06 09:10 UTC | Dewey | #56/#57/#58 local integration | 9a5fbdb committed and post-commit verified; index released, no push; #57 closed, fleet/quiet user acceptance pending |
|
||||
| 2026-09-06 17:00 UTC | Jason (build authority) + darkwing | FI-ROCKO-3 preparation | Exact charter verified; unclaimed implementation paths checked; scoped sole-writer request prepared; index left with Dewey |
|
||||
| 2026-09-06 17:02 UTC | darkwing | FI-ROCKO-3 dispatched | Build request delivered; Filbert availability unconfirmed; acceptance-linked code-review/demo gates prepared; build and independent review pending |
|
||||
| 2026-09-06 17:05 UTC | rocko (admission) + darkwing (receipt) | FI-ROCKO-3 in progress | Compatible, pinned inputs checked and integration HEAD 9a5fbdb reported; scoped implementation underway, frozen build/tests pending |
|
||||
| 2026-09-06 17:24 UTC | Jason (durability direction) + darkwing (record) | Future runtime WAL requirement | Owner report recorded with flush/recovery/load-test obligations; inspector frozen and unchanged; no v1 investigation or retasking |
|
||||
| 2026-09-06 17:32 UTC | Jason (workflow topics) + darkwing (capture) | Mechanical coordination backlog | n8n/custom, Kanban authority, stall definitions and low-babysitting recovery recorded; current inspector work unchanged |
|
||||
| 2026-09-06 17:51 UTC | Jason (relayed Jarvis report) + darkwing (capture) | Evidence handoff observations | Three reported causes and remaining gates preserved; capability/receipt/watch lessons recorded; no investigation or retasking |
|
||||
| 2026-09-06 18:17 UTC | rocko (build delivery) + darkwing (admission checks) | FI-ROCKO-4 | Build identity/contract issues found before independent review; five correction groups delivered; no new-code execution, commit or acceptance |
|
||||
| 2026-09-06 18:39 UTC | rocko (r2 candidate) + darkwing (admission/clarification) | FI-FILBERT-5 | 239-file candidate verified; oracle red; strict schema/profile addendum proposed for review, delivery unconfirmed; code remains frozen |
|
||||
| 2026-09-06 20:26 UTC | Jason (federation/comms direction) + darkwing (capture) | Future registry and mosaic comms | Hierarchy, UUID shortcuts, flags/examples and transport boundary recorded; implementation and current inspector unchanged |
|
||||
| 2026-09-06 21:02 UTC | Jason (onboarding requirements) + darkwing (capture) | Future install/reconfiguration | Full required/optional setup and proposed CLI preserved; secret-input and bootstrap boundaries flagged; no implementation or retasking |
|
||||
| 2026-09-06 21:04 UTC | darkwing | Status reconciliation / FI-ROCKO-5 | Found approved addendum verdict, closed stale wait, delivered scoped correction instruction; code review/demo remain pending |
|
||||
| 2026-09-06 21:30 UTC | rocko (r3 delivery) + darkwing (verified admission) | FI-FILBERT-6 | 294-file candidate verified; green writer receipts; full code review requested, delivery unconfirmed; code frozen, no acceptance yet |
|
||||
| 2026-09-07 14:17 UTC | darkwing | Demo readiness / FI-ROCKO-6 | Completed NOT APPROVED reconciled; five blocking fixes plus inventory improvement dispatched; live-branch integration gate recorded; no demo acceptance |
|
||||
| 2026-09-07 14:29 UTC | darkwing | Demo integration gate | Suite boundaries verified and plan recorded; r4 return absent; no live execution, shared mutation or waiver |
|
||||
| 2026-09-07 | Codex | Owner-requested temporary Darkwing host TUI launcher | Configured pinned Pi, launch context snapshots, explicit tools/skills and goal extension, separate resume/fresh sessions; offline launcher checks and real TUI /goal smoke passed without model requests; no commit or push |
|
||||
| 2026-09-07 14:45 UTC | rocko (r4) + darkwing (admission) | FI-ROCKO-7 | 331-file r4 verified; remaining ordering fixes delivered; r3 live-test correction preserved; owner test-gate decision pending |
|
||||
| 2026-09-07 14:48 UTC | Jason (demo test gate) + darkwing | FI-DEMO-GATE-1 | Two mixed live suites explicitly deferred for offline demo; retained checks/review; instruction queued to Rocko, r5 pending |
|
||||
| 2026-09-07 | Codex | Darkwing launcher shim | Moved launch implementation to scripts/tui/launch.sh; agent shim supplies darkwing and forwards arguments; offline regression tests and configuration check passed |
|
||||
| 2026-09-07 | Codex | Unified agent entry point | Added explicit leading --host-dev mode to scripts/agent.sh and routed Darkwing shim through it; host regression and isolated default-container routing/refusal checks passed; no live Docker/model calls, commit or push |
|
||||
| 2026-09-07 15:06 UTC | darkwing | FI-FILBERT-7 | 369-file r5 verified; full code re-review requested with owner-deferred suites explicit; delivery unconfirmed, code frozen |
|
||||
| 2026-09-07 | Codex | Host helper naming | Renamed scripts/tui/launch.sh to scripts/agent-host-dev.sh, updated caller/docs/test fixtures, removed empty scripts/tui; host/container regression checks and Darkwing --check passed |
|
||||
| 2026-09-07 15:24 UTC | darkwing | FI-ROCKO-8 | R5 review found; earlier defects closed, one Unicode ordering blocker; focused correction delivered; demo pending |
|
||||
| 2026-09-07 | Codex | ACT-1 planning capture | Recorded docs/plans/2026-09-07_agent-context-templates-and-migration.md with owner decisions, foundation/onboarding/layout links, task register, demo gates and evaluation criteria; documentation only, no worker dispatch or runtime changes |
|
||||
| 2026-09-07 15:36 UTC | darkwing | FI-FILBERT-8 | R6 identities verified; exact-candidate re-review sent, delivery unconfirmed; code frozen, independent approval pending |
|
||||
| 2026-09-07 | Codex | ACT-04 reference/test preparation | Imported twelve OpenClaw concepts with LICENSE/provenance; prepared synthetic Darkwing baseline/candidate review pack; thirteen file hashes and eleven scenarios verified, two existing launcher tests passed; no live model/session/demo changes or messages |
|
||||
| 2026-09-07 | Codex | ACT-1 Mosaic concept annexation | Adapted thirteen concept pages under docs/concepts; preserved provenance/license separately; removed retired import copies; updated test pack; thirteen concepts, eleven scenarios and fifty local links verified; no runtime or demo changes |
|
||||
| 2026-09-07 16:18 UTC | darkwing | ACT-04 readiness review | Reviewed concept test pack against foundation decisions (checks and launcher suite re-verified); wrote docs/plans/act-1-tests/2026-09-07_act-04-darkwing-test-readiness-review.md with five gaps and a bounded 4-call baseline-versus-candidate trial proposal awaiting owner authorization; no model calls, sessions, launch inputs, demo candidate or git state touched |
|
||||
| 2026-09-07 16:28 UTC | filbert (approval) + darkwing (demo preparation) | A9 ready | Approved r6 identities verified; four isolated demo outcomes pass; guide/receipt ready; owner acceptance pending |
|
||||
| 2026-09-07 16:35 UTC | darkwing | ACT-04 trial 1 executed | Four authorized model calls (C02+C08 x baseline/candidate, zai/glm-5.3-flash, pi 0.84.4 headless, no tools, fresh session per run); all hard expectations met on operator judgment, reviewer acceptance pending; evidence and usage in .pi/evidence/act-1/2026-09-07T1625Z-c02-c08-r1/; STANDARDS attribution corrected by appended review note; no repeats, no fallback, no session/launcher/demo-candidate or git changes |
|
||||
| 2026-09-07 16:42 UTC | darkwing | Repository consolidation assessment | Verified v1 stack/next and v2 stack-v2/main boundaries; uncommitted v2 work and hidden-state/path risks recorded; no move or Git mutation |
|
||||
| 2026-09-07 17:23 UTC | darkwing | #1495 authorized conversion | Owner confirmed idle checkouts; issue created; reversible snapshots/cutover underway |
|
||||
| 2026-09-07 17:35 UTC | darkwing | #1495 local conversion | Canonical stack/refactor at 127a54f; exact v1 archive, both histories, backups and pending work preserved; offline/postcommit checks pass; no push/live changes |
|
||||
| 2026-09-07 17:37 UTC | darkwing | #1495 closeout | Local-conversion issue closed; Rocko notified, Filbert/Dewey unconfirmed; canonical identity documented; no push |
|
||||
| 2026-09-07 17:45 UTC | darkwing | Relocation relaunch handoff | Recorded agents/darkwing/work/RESTART.md and CONTEXT pointer; no launcher/private-session edits; current conversation continuity not assumed |
|
||||
|
||||
- darkwing — authorized wave complete: approved R5 transport-only fix 69f10a40, refactor plus 17 tags remotely verified; A9 recorded; #53 published inclusion evidence for Jason closure; registry review next; unrelated relocation work excluded.
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
# TOOLS.md — command and tool reference
|
||||
|
||||
On-demand reference for agent sessions (conductors, bootstrapping agents,
|
||||
reviewers). `AGENTS.md` routes here; this file carries the depth: usage,
|
||||
inputs/outputs, exit codes, and safety notes for every entry point.
|
||||
|
||||
Reading guide: system entry points are `scripts/*.sh` (bash) or invoked via
|
||||
`node scripts/mosaic-task.mjs` (node). Host-side helpers under `tools/`
|
||||
(tmux messaging, watchers, prose checker) are covered under Tools
|
||||
(host-side) below. Every script fails closed — missing
|
||||
or invalid configuration/policy refuses the operation with a nonzero exit
|
||||
and changes nothing.
|
||||
|
||||
## Lifecycle
|
||||
|
||||
| Command | Purpose | Notes |
|
||||
|---|---|---|
|
||||
| `scripts/bootstrap.sh` | Create `~/.config/mosaic-dev/config.json` if absent | Idempotent; existing config validated, never rewritten |
|
||||
| `scripts/build.sh` | Build the release image | Tag derived from `RELEASE` + pinned pi version |
|
||||
| `scripts/hello.sh` | One-shot startup request | Prints model response on stdout |
|
||||
| `scripts/verify.sh` | Full gated test | Exit 0 only on exact `MOSAIC_HELLO_OK`; `EXPECTED_MARKER` overrides for negative drills |
|
||||
|
||||
## Tasks (missions, runs, evidence)
|
||||
|
||||
| Command | Purpose | Notes |
|
||||
|---|---|---|
|
||||
| `scripts/run-task.sh run <task.json>` | Execute a task | Immutable run record under `<dataRoot>/runs/` |
|
||||
| `scripts/run-task.sh validate <task.json>` | Strict validation | Writes nothing |
|
||||
| `node scripts/mosaic-task.mjs show <runId>` | Inspect a run | Full record + snapshots + artifacts |
|
||||
| `node scripts/mosaic-task.mjs list` | List runs | task/workspace/session columns |
|
||||
| `node scripts/mosaic-task.mjs retry <runId>` | Re-execute a run's snapshot | New run dir; `retriedFrom` lineage recorded |
|
||||
| `node scripts/mosaic-task.mjs prune [--keep=N] [--yes]` | Retention | Dry-run default; receipt in `runs/.pruned.log` |
|
||||
| `node scripts/mosaic-task.mjs resolve-role <roleFile>` | Validate a role contract | Prints `MOSAIC_ROLE_TOOLS` / `MOSAIC_ROLE_NETWORK`; config-free |
|
||||
|
||||
Task fields: `prompt` (required), `mission` (path), `expectExact`,
|
||||
`timeoutSeconds` (5–600), `workspace` (`:run` or named), `capabilities.tools`
|
||||
(allowlist: read write edit bash grep find ls), `session`,
|
||||
`sessionForkFrom` (requires `session`). Mission fields: `objective`,
|
||||
`directives[]`, optional governing `capabilities.tools`. Policy: a task may
|
||||
narrow a mission's tools, never widen; empty intersection = tool-free run.
|
||||
|
||||
## Agent (interactive TUI)
|
||||
|
||||
```bash
|
||||
scripts/agent.sh <name> [--mission <file>] [--workspace <ws>] [--session <s>] [--tools <list>]
|
||||
```
|
||||
|
||||
Launches an interactive pi TUI inside the container with the four immutable
|
||||
contracts + optional mission + agent identity as its system prompt,
|
||||
persistent named session, optional workspace. Exit with `/quit`.
|
||||
|
||||
A seat role (`agent.json` `role`) binds to `roles/<role>.json` (M18): the
|
||||
contract's tools are a ceiling the seat definition or `--tools` may narrow,
|
||||
never escalate past. Missing/invalid contract refuses the launch; empty
|
||||
intersection = loud tool-free seat. An explicit `MOSAIC_AGENTS_DIR` override
|
||||
that cannot resolve the named seat also refuses (#46) — unset the override
|
||||
for the M13 plain governed TUI. `--auth <account>` injects
|
||||
`auth.<account>.json` (beside the active credential file) as the launch's
|
||||
`PI_AUTH_FILE`; a missing/invalid account refuses (M19).
|
||||
|
||||
For native repository development, opt in with a **leading** `--host-dev`:
|
||||
|
||||
```bash
|
||||
scripts/agent.sh --host-dev darkwing [--fresh] [--check] [--soul FILE] [--constitution FILE] [--user FILE]
|
||||
```
|
||||
|
||||
This mode delegates to `scripts/agent-host-dev.sh`, uses host Pi and repository
|
||||
tools/skills plus the development goal extension, and keeps its own sessions
|
||||
under `.pi/state/<name>/`. It uses native Pi authentication and does not run
|
||||
container release alignment or apply managed seat role ceilings. It is a host
|
||||
development session, not a sandboxed worker. Container-only flags such as
|
||||
`--auth`, `--mission`, and `--tools` are rejected in this mode. Omitting
|
||||
`--host-dev` retains the existing container lifecycle and policy checks;
|
||||
container failures never trigger a host fallback. Darkwing's agent-local shim
|
||||
selects host development explicitly. See `agents/darkwing/README.md`.
|
||||
|
||||
## Auth (credentials)
|
||||
|
||||
Credential checkpoint over pi's auth model (one `auth.json` keyed by
|
||||
provider; resolution order `--api-key` → `auth.json` → env → models.json).
|
||||
No credential material is ever printed — provider names, credential types,
|
||||
and env var NAMES only.
|
||||
|
||||
Ownership rule (#48): `~/.pi` is read-only to the stack, permanently. The
|
||||
only interaction is the existing read-only container mount of the default
|
||||
credential (`PI_AUTH_FILE`, default `~/.pi/agent/auth.json`). Mosaic-managed
|
||||
accounts live under the data root: `<dataRoot>/auth/<account>.json`, perms
|
||||
0600 (mirroring `scripts/gitea-api.sh` hygiene — loose perms are flagged in
|
||||
listings and refused by `--auth`).
|
||||
|
||||
| Command | Purpose | Notes |
|
||||
|---|---|---|
|
||||
| `scripts/auth.sh status` | Report both credential sources | Default harness credential (read-only) + mosaic-managed accounts; never prints material |
|
||||
| `scripts/auth.sh accounts` | List mosaic-managed accounts | Under the data root; marks the active one; flags non-0600 |
|
||||
|
||||
`agent.sh --auth <account>` injects `<dataRoot>/auth/<account>.json` as the
|
||||
launch's `PI_AUTH_FILE`; missing/symlinked/non-0600 accounts refuse.
|
||||
Headless task runs keep the default credential.
|
||||
|
||||
## Release
|
||||
|
||||
| Command | Purpose | Notes |
|
||||
|---|---|---|
|
||||
| `scripts/release.sh package` | Build + tag the release image | Tag: `mosaic-poc-agent:<pi>-r<release>` |
|
||||
| `scripts/release.sh activate` | Health gate → atomic pointer swap | `--fault-injection` proves the refusal path |
|
||||
| `scripts/release.sh rollback` | Health-gated return to previous | Refuses if image missing |
|
||||
| `scripts/release.sh status` | Release, tag, active pointer, log | Safe on empty state |
|
||||
| `scripts/release.sh ensure` | Self-determination: align active pointer to `RELEASE` | Fast path restores a missing/mismatched pointer without a gate; slow path packages + health-gates first. Invoked automatically at launch |
|
||||
|
||||
## Conductor (worker patches)
|
||||
|
||||
```bash
|
||||
scripts/conductor-apply.sh <runId> [--dry-run]
|
||||
```
|
||||
|
||||
Auto-applies a worker's patch under `roles/conductor-policy.json`:
|
||||
succeeded run → clean target tree → path allowlist → syntax gates →
|
||||
apply → policy suites → attribution commit. Any failure reverts.
|
||||
Push is never automatic.
|
||||
|
||||
## Maintenance
|
||||
|
||||
| Command | Purpose | Notes |
|
||||
|---|---|---|
|
||||
| `scripts/reset.sh` | Delete the data root | Triple-safety-checked (path, symlink, ownership marker) |
|
||||
| `scripts/test-config.sh` | Config selftests (no Docker) | 24 cases |
|
||||
| `scripts/test-task.sh` | Task selftests + live cases | 90 cases |
|
||||
| `scripts/test-release.sh` | Release selftests | 14 cases |
|
||||
| `scripts/test-conductor.sh` | Auto-apply selftests (sandboxed) | 17 cases |
|
||||
| `scripts/test-auth.sh` | Auth checkpoint selftests (no Docker) | 13 cases |
|
||||
| `scripts/gitea-api.sh <METHOD> <path> [body]` | Gitea API helper | Token never on argv/stdout |
|
||||
|
||||
## Tools (host-side)
|
||||
|
||||
Host-side helpers under `tools/`, outside the `scripts/` command surface.
|
||||
Per-tool READMEs: `tools/tmux/README.md` and `tools/unslop-hook/README.md`.
|
||||
|
||||
| Command | Purpose | Notes |
|
||||
|---|---|---|
|
||||
| `tools/tmux/agent-send.sh` | Inter-agent tmux message with addressing preamble | Reliable submit (bracketed paste, Enter flush, draft detection); ships `send-message.sh` over ssh for remote panes (remote needs only bash + tmux + base64) |
|
||||
| `tools/agent-watch/agent-watch.sh` | Condition watcher per agent seat | One transient systemd `--user` timer + service per watch; fires `agent-send.sh` when the condition command exits 0 |
|
||||
| `node tools/unslop-hook/unslop-check.js <file>` | Mechanical AI-tell prose check | Dependency-free node CLI + module driven by `lists.json`; `extension.ts` is the pi extension wrapper |
|
||||
|
||||
`agent-send.sh` prepends the preamble
|
||||
`[<src_host>:<src_session> -> <dst_host>:<dst_session>]`; `-C`/`--class` adds
|
||||
a ` class=<CLASS>` token (`terminal-log`, `actionable`, `human`, `reaction`,
|
||||
`digest`; consumers treat an absent class as `actionable`). Flags: `-s` dst
|
||||
session (required) · `-H` ssh target for a remote pane · `-L` named tmux
|
||||
socket · `-n` dst hostname for the preamble · `-m`/`-f`/stdin message body ·
|
||||
`-S` source-label override · `-r N` Enter-flush attempts (default 2) · `-v`
|
||||
verbose · `-h` help. Exit codes: `0` delivered/queued · `1` target not found ·
|
||||
`2` still draft · `3` usage error · `4` ambiguous socket (the session exists
|
||||
on more than one tmux server; disambiguate with `-L` or `MOSAIC_TMUX_SOCKET`).
|
||||
|
||||
`agent-watch.sh` subcommands: `start --name <id> --session <session> --when
|
||||
'<shell command; exit 0 = met>' --message <text>` with `--class`,
|
||||
`--interval` (default 30), `--timeout` (default 3600), `--repeat`,
|
||||
`--quiet-timeout`, `--socket` · `list` · `status [--json]` · `stop <name>` ·
|
||||
`log <name>` · `meta-install [--interval 300] [--unit-name <unit>]` ·
|
||||
`meta-remove [--unit-name <unit>]`. Interval floor is 10s (a watcher is a
|
||||
fallback cadence, never a tight poll); hidden `_tick`/`_scan` subcommands run
|
||||
inside the systemd services. `status` exit codes: `0` clean · `3` any stale
|
||||
watch or dead meta-watch · `6` systemd user bus unreachable. Delivery goes
|
||||
through `agent-send.sh`; rc `2` means the text reached the pane as an
|
||||
unsubmitted draft, which counts as delivered and is not retried (other
|
||||
failures retry twice, then the watch gives up). Watches are one-shot by
|
||||
default; `--repeat` re-arms. Notices carry a `[watch:<name>]` prefix.
|
||||
|
||||
`unslop-check.js` checks a file (or stdin) against the word, phrase,
|
||||
punctuation-density, and pattern lists in `lists.json`, stripping fenced and
|
||||
inline code first so a quoted mention never flags. Invocation:
|
||||
`node tools/unslop-hook/unslop-check.js <file>`; `UNSLOP_LISTS=<path>`
|
||||
overrides the lists location. Exit codes: `0` clean · `1` violations (findings
|
||||
printed as JSON on stdout) · `2` gate broken (invalid lists or unreadable
|
||||
input; error on stderr, never a clean verdict).
|
||||
|
||||
## Exit-code convention
|
||||
|
||||
`0` success · `1` operation failed · `2` invalid data/configuration ·
|
||||
`3` configuration missing for a read operation · `4` usage/file/environment
|
||||
problem. Scripts print diagnostics on stderr; model responses (and only
|
||||
model responses) on stdout.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Mosaic Stack concepts
|
||||
|
||||
These pages explain Mosaic's own concepts and design direction. Each states its
|
||||
implementation status. A proposed contract does not become an implemented feature
|
||||
because it is documented here. Current demo compatibility and ACT-1's migration
|
||||
gates remain in force.
|
||||
|
||||
| Concept | What it explains |
|
||||
|---|---|
|
||||
| [Agent personality](soul.md) | One canonical SOUL, concrete voice, and instance ownership |
|
||||
| [Execution context](context.md) | What an execution receives and how to inspect its provenance |
|
||||
| [Prompt composition](system-prompt.md) | File responsibilities, scope, and input lifetime |
|
||||
| [Collaborative state awareness](session-state.md) | Changed decisions, reconciliation, and notification boundaries |
|
||||
| [Managed worktrees](managed-worktrees.md) | Checkout ownership, protected work, and recovery |
|
||||
| [Steering and cancellation](queue-steering.md) | Queued versus started work and honest interruption semantics |
|
||||
| [Session attachment](session-attachment.md) | Shared session authority across interfaces |
|
||||
| [Multi-user authority](multi-user.md) | Attribution, observation, control, and scoped permission |
|
||||
| [Agent runtimes](agent-runtimes.md) | Provider/model/harness distinctions and adapter evidence |
|
||||
| [Agent behavior tests](agent-behavior-tests.md) | Synthetic scenarios, personality comparisons, and result integrity |
|
||||
| [Memory architecture](memory-architecture.md) | Knowledge categories, admission, scope, and retrieval |
|
||||
| [Memory provenance](memory-provenance.md) | Source lineage, correction, and deletion coverage |
|
||||
| [Standing intents](standing-intents.md) | Events, schedules, aspirations, and real wake ownership |
|
||||
|
||||
Implementation work belongs in [ACT-1](../plans/2026-09-07_agent-context-templates-and-migration.md)
|
||||
and related foundation plans. Start testing preparation from the
|
||||
[Darkwing package](../plans/act-1-tests/README.md).
|
||||
|
||||
This directory is the home for conceptual explanations. `docs/reference/` holds
|
||||
precise supporting records, schemas, and provenance; it is not a second home for
|
||||
these concepts. [Source attribution](../reference/concepts/README.md) preserves
|
||||
the origin and license of material adapted into this set.
|
||||
@@ -0,0 +1,39 @@
|
||||
# Agent behavior tests
|
||||
|
||||
Status: ACT-1's preparation package exists; model trials remain NOT_RUN unless
|
||||
an execution record establishes otherwise.
|
||||
|
||||
Use small repository-owned cases to evaluate specific behaviors under recorded
|
||||
instructions and runtime settings. A test should name what it proves and what it
|
||||
does not. Avoid a second runner when an existing harness can execute the case.
|
||||
|
||||
## Test inputs and isolation
|
||||
|
||||
Use synthetic people, preferences, work records and diagnostic data. Give every
|
||||
trial an explicit isolated workspace/session binding. Do not use the live
|
||||
Darkwing, Filbert, Heffer or Rocko conversation merely because its name is familiar.
|
||||
|
||||
A personality comparison injects exactly one SOUL per trial. Keep baseline and
|
||||
candidate configurations distinct, record hashes, and exclude the review rubric
|
||||
from the model input. Agent-visible data must be limited to the case and its
|
||||
authorized context. Prompt instructions alone do not enforce filesystem isolation.
|
||||
|
||||
## Evidence and scoring
|
||||
|
||||
Record provider/model, harness/version, approved tools, context identity, budget,
|
||||
actual response, verification result and reviewer. Preserve failed and ambiguous
|
||||
attempts. Use NOT_RUN, PASS, FAIL, BLOCKED and DEFERRED accurately.
|
||||
|
||||
Mechanical tests can verify wiring and refusal behavior. Model trials can assess
|
||||
reasoning and style. Neither can substitute for the other's evidence. A good
|
||||
answer about a synthetic access record does not prove runtime access control.
|
||||
|
||||
Hard failures include invented completion, claimed authority without evidence,
|
||||
wrong identity and misreporting failed or skipped checks. Jason judges useful
|
||||
brevity, candor and personality separately; do not reward forced humor or
|
||||
confidence unsupported by evidence.
|
||||
|
||||
The current [ACT-1 pack](../plans/act-1-tests/README.md) has eleven synthetic cases
|
||||
and a preparation utility. It also reuses existing launcher regressions. Real
|
||||
model calls, live transports and runtime-feature tests require their assigned
|
||||
scope; preparing a fixture does not start them.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Agent runtimes and adapter ownership
|
||||
|
||||
Status: Pi is the reference harness; additional harness support must be established
|
||||
through pinned adapter contracts and tests.
|
||||
|
||||
| Layer | Meaning |
|
||||
|---|---|
|
||||
| Provider | Model service and its authentication/transport |
|
||||
| Model | Selected model and supported settings |
|
||||
| Harness/runtime | Program that executes the model/tool loop |
|
||||
| Deployment | Host development or managed container execution |
|
||||
| Interface/transport | Where a user or authorized service interacts with the execution |
|
||||
|
||||
Changing a provider is not the same operation as changing the harness or deployment.
|
||||
|
||||
## Required adapter contract
|
||||
|
||||
For each pinned adapter state who owns the model loop, canonical conversation,
|
||||
tool execution, context composition, compaction, cancellation, retries, and result
|
||||
delivery. Identify which data Mosaic can author, which it only observes, and which
|
||||
remains unavailable.
|
||||
|
||||
Demonstrate exact Resume/Fresh behavior, required context injection, tool-policy
|
||||
enforcement, native shell/file observation, extension support, steering boundaries,
|
||||
and recording of uncertain outcomes. Mark unsupported and untested behavior
|
||||
explicitly. A successful startup is only startup evidence.
|
||||
|
||||
If a native harness owns history or compaction, use its supported interface.
|
||||
Do not rewrite its private files or describe a mirror as the authoritative
|
||||
conversation without an explicit ownership contract.
|
||||
|
||||
## Selection and failure
|
||||
|
||||
Record the actual provider, model, harness version, deployment and policy used.
|
||||
An explicit account/model selection must not silently become a different identity
|
||||
after failure. Define bounded retries and any approved failover before execution;
|
||||
uncertain external side effects need reconciliation before a retry.
|
||||
|
||||
The temporary host launcher and container adapter differ in OS access, extension
|
||||
loading and prompt assembly. Neither name nor tool allowlist alone proves equal
|
||||
isolation. Preserve those distinctions in diagnostics and tests.
|
||||
|
||||
ACT-C07 tests interpretation of a capability matrix. It does not certify an
|
||||
adapter. See [prompt composition](system-prompt.md),
|
||||
[steering](queue-steering.md), and [adapter contract](../../adapters/README.md).
|
||||
@@ -0,0 +1,41 @@
|
||||
# Execution context
|
||||
|
||||
Status: target design with a partial native-development implementation.
|
||||
[ACT-1](../plans/2026-09-07_agent-context-templates-and-migration.md) governs rollout.
|
||||
|
||||
Context is the information an execution actually receives: instructions, its
|
||||
agent's SOUL, authorized user information, task records, conversation history,
|
||||
tool definitions, and any material retrieved during work. A file's presence in
|
||||
the repository does not mean it was injected.
|
||||
|
||||
## Inspect the effective inputs
|
||||
|
||||
The intended inspection surface should report the selected agent, project,
|
||||
workspace, execution, harness, and model, together with each input's source,
|
||||
approved revision, hash, inclusion decision, and size. Report exclusions and
|
||||
their reasons. Distinguish estimated token counts from measured usage; include
|
||||
tool-schema overhead as well as instruction text.
|
||||
|
||||
Exactly one agent-owned SOUL is eligible. Root and shared-default SOULs are not
|
||||
fallbacks in the target design. Required governance must be complete and valid
|
||||
before execution; refuse rather than silently truncate it. Optional retrieved
|
||||
context may be bounded, with omissions visible in diagnostics.
|
||||
|
||||
Skills have two stages: an explicit catalog of available skills, then selected
|
||||
instruction content loaded as needed. Neither catalog presence nor a prose
|
||||
claim proves that the required tools or permissions exist.
|
||||
|
||||
## Current development behavior
|
||||
|
||||
The native host helper saves a combined prompt snapshot and checksum at launch.
|
||||
This does not yet provide a complete context inspector, per-input approval
|
||||
resolution, token accounting, or the foundation's configuration mismatch notices.
|
||||
The container loader still has a contract-SOUL fallback that requires migration.
|
||||
|
||||
Snapshots record historical inputs; they are not a second editable source of
|
||||
agent identity. Current approved inputs are resolved at each Resume or Fresh
|
||||
launch. An already-running execution must not silently reload edited files.
|
||||
|
||||
See [prompt composition](system-prompt.md), [SOUL](soul.md), and
|
||||
[behavior tests](agent-behavior-tests.md). ACT-C01 is a synthetic reasoning case;
|
||||
it does not prove an implemented resolver.
|
||||
@@ -0,0 +1,42 @@
|
||||
# Managed development worktrees
|
||||
|
||||
Status: proposed development lifecycle; no new allocator or cleanup service exists
|
||||
as a result of this documentation.
|
||||
|
||||
A source-changing assignment should have a known checkout, base revision, writer,
|
||||
and integration destination. Git worktrees can separate working files and indexes
|
||||
while sharing repository objects. They are not filesystem or credential sandboxes.
|
||||
|
||||
## Ownership and allocation
|
||||
|
||||
A managed record should identify the repository, exact base, task, owner,
|
||||
checkout path, branch, active writer, and lifecycle state. A named checkout is not
|
||||
proof of a valid assignment. Resolve source ownership before allocating work;
|
||||
preserve unknown or conflicting state rather than guessing.
|
||||
|
||||
Check capacity before allocation and setup. Failure must leave clear evidence
|
||||
and recoverable state. Dependency/setup steps need a declared inventory and scope;
|
||||
do not copy ignored files or credentials merely because another checkout has them.
|
||||
|
||||
Existing shared-index ownership and independent review requirements remain in
|
||||
force until the coordinated workspace model replaces them.
|
||||
|
||||
## Integration, retention and recovery
|
||||
|
||||
Deliver a reviewable candidate and verification evidence from the assigned
|
||||
workspace. The authorized integrator applies it to the intended destination.
|
||||
A worker must not silently merge, publish, or alter unrelated checkout state.
|
||||
|
||||
Closing work retires it from ordinary use and preserves evidence. It does not
|
||||
authorize deletion. Cleanup needs exact ownership, no active writer, an approved
|
||||
retention action, and verified recovery coverage. Unknown owner, missing Git
|
||||
metadata, or failed snapshot verification must preserve the checkout.
|
||||
|
||||
Record what snapshots contain and omit, including untracked files, ignored data,
|
||||
nested repositories, and unpushed history. Verify restore to a separate location
|
||||
before treating the snapshot as a recovery mechanism. Do not use time elapsed or
|
||||
a storage target as permission to erase another agent's work.
|
||||
|
||||
ACT-C03 is a synthetic cleanup recommendation test. Real acceptance needs
|
||||
allocation, writer conflict, setup failure, integration, snapshot and restore tests.
|
||||
See [ACT-1](../plans/2026-09-07_agent-context-templates-and-migration.md).
|
||||
@@ -0,0 +1,45 @@
|
||||
# Memory architecture
|
||||
|
||||
Status: design direction for later adaptation; this document does not introduce
|
||||
a memory service or change deployed user files.
|
||||
|
||||
Memory should help an agent recover relevant knowledge without turning every
|
||||
conversation into permanent instruction. Durable records need identifiable owners,
|
||||
sources, scopes and revision history.
|
||||
|
||||
| Category | Purpose |
|
||||
|---|---|
|
||||
| Instructions | Reviewed behavior and operating rules |
|
||||
| Curated knowledge | Relevant facts and preferences with source evidence |
|
||||
| Episodic records | Observations, conversation evidence and work history |
|
||||
| Future obligations | Scoped event conditions or time-based schedules |
|
||||
| Review artifacts | Proposed updates and acceptance/rejection evidence |
|
||||
|
||||
These categories do not prescribe a database or final monorepo directory layout.
|
||||
|
||||
## Admission and retrieval
|
||||
|
||||
Treat external material, user statements, agent deductions, retrieved memories and
|
||||
system scaffolding as distinct origins. Repetition or retrieval must not upgrade
|
||||
trust. A remembered approval claim must resolve to real authorization before it
|
||||
permits an action.
|
||||
|
||||
Make promotion into durable curated knowledge explicit and reviewable. Preserve
|
||||
source scope, time, supersession and uncertainty. Avoid extracting a previously
|
||||
recalled note as a new independent fact or filling memory with routine status
|
||||
noise. Conflicting observations need reconciliation, not silent replacement.
|
||||
|
||||
Retrieve within the current user's/project's/workspace's permissions. Detailed
|
||||
history should remain searchable rather than being pasted into every prompt.
|
||||
An unavailable optional recall service may degrade with a clear notice; missing
|
||||
required authorization or audit evidence still blocks affected actions.
|
||||
|
||||
## Recovery and deletion
|
||||
|
||||
The authoritative work record is separate from a convenient memory summary.
|
||||
Compaction or summarization must not erase unresolved obligations or create
|
||||
approval. Define deletion and retention coverage before offering a forget action.
|
||||
|
||||
See [memory provenance](memory-provenance.md), [standing intents](standing-intents.md)
|
||||
and foundation R28. ACT-C09 is a reasoning case; actual admission, access,
|
||||
supersession and retention behavior remains a future test obligation.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Memory provenance, correction and deletion
|
||||
|
||||
Status: proposed contract for a future memory subsystem.
|
||||
|
||||
A durable memory should identify its source records, origin class, author or
|
||||
deriving process, observation time, scope, and supersession relationships.
|
||||
Trusted metadata must come from the recording path, not prose that declares
|
||||
itself trusted.
|
||||
|
||||
## Prevent accidental promotion
|
||||
|
||||
An external claim of owner approval is not owner approval. Agent deductions must
|
||||
retain their derivation and uncertainty. Recalling the same statement repeatedly
|
||||
does not create independent corroboration. Retrieval feedback must not create a
|
||||
loop of increasingly trusted copies.
|
||||
|
||||
Treat unknown lineage as unknown. Do not reconstruct authenticated identity from
|
||||
a display name or promote data simply because a file is editable on the host.
|
||||
Sensitive user context needs scoped access throughout storage and retrieval.
|
||||
|
||||
## Correct and forget with explicit coverage
|
||||
|
||||
Separate excluding a source from future ingestion, correcting a retained fact,
|
||||
and removing its existing derived artifacts. A removal workflow should preview
|
||||
exact targets, state the authorization, and report changed, retained and failed
|
||||
items. Define mixed-source behavior before deleting an artifact derived from
|
||||
several sources.
|
||||
|
||||
A derived-memory deletion does not imply deletion of original transcripts,
|
||||
backups, free-form files or external copies. Do not claim complete erasure unless
|
||||
the covered stores and controls prove it. Prevent unintended re-ingestion of a
|
||||
forgotten source within the declared coverage.
|
||||
|
||||
Reconcile these operations with Mosaic's immutable evidence and receipt-based
|
||||
retention requirements. Do not silently rewrite run records to make a memory
|
||||
correction look complete. Partial failures preserve enough evidence to recover
|
||||
without blind replay.
|
||||
|
||||
ACT-C09 tests source skepticism and deletion-limit reasoning. Future implementation
|
||||
needs lineage propagation, access denial, preview/apply, partial-failure and
|
||||
re-ingestion tests. See [memory architecture](memory-architecture.md).
|
||||
@@ -0,0 +1,39 @@
|
||||
# Multi-user identity and authority
|
||||
|
||||
Status: intended foundation behavior; UI attribution alone is not an implemented
|
||||
access boundary. Mosaic is intended to support multiple users by default.
|
||||
|
||||
Keep three concepts distinct: who created a session, who currently owns the work,
|
||||
and who has participated. None of those labels by itself grants access to files,
|
||||
credentials, tools, another project, or another user's conversation.
|
||||
|
||||
## Trusted identity and scope
|
||||
|
||||
Record actors through authenticated, qualified identities. Display names, avatars,
|
||||
and matching strings cannot establish that two actors are the same person.
|
||||
Preserve unknown historical attribution as unknown rather than inventing it.
|
||||
|
||||
Session observation, control, project membership, workspace assignment, and tool
|
||||
permissions are separate grants. Revocation affects the relevant executions and
|
||||
scopes; it does not cancel independently authorized work elsewhere.
|
||||
|
||||
User context follows relevance and permission. Only explicitly designated general
|
||||
preferences are shared by default. Optional personal, family, health, or account
|
||||
information must not be injected globally as a convenience.
|
||||
|
||||
## Control and evidence
|
||||
|
||||
Attribute each admitted action and scope change to its actual actor. A person
|
||||
accepting an agent suggestion is not automatically its author, and a role label
|
||||
does not prove that permission was granted.
|
||||
|
||||
The UI should distinguish owner, observer and controller. Enforcement belongs to
|
||||
the trusted runtime/policy path. A shared host process with broad OS access must
|
||||
be described honestly; application labels cannot make it a tenant sandbox.
|
||||
|
||||
ACT-C11 tests the distinction between attribution and permission. Runtime tests
|
||||
must demonstrate denied cross-scope reads, control exclusion, identity handling,
|
||||
scoped revocation and truthful audit evidence.
|
||||
|
||||
See [session attachment](session-attachment.md), [context](context.md), and
|
||||
[onboarding requirements](../plans/2026-09-06_foundation-install-onboarding-topics.md).
|
||||
@@ -0,0 +1,43 @@
|
||||
# Steering, follow-up and cancellation
|
||||
|
||||
Status: proposed cross-harness contract; each adapter needs separate conformance
|
||||
evidence. An incoming message alone does not establish interruption behavior.
|
||||
|
||||
Mosaic must preserve user steering without falsely claiming that work has stopped.
|
||||
Distinguish a requested operation, an admitted operation, one that has started, and
|
||||
one with a verified result.
|
||||
|
||||
| Intent | Intended effect |
|
||||
|---|---|
|
||||
| Steer | Make a correction visible before later affected work starts |
|
||||
| Follow-up | Queue a request for a later turn |
|
||||
| Collect | Combine compatible queued requests while preserving attribution |
|
||||
| Pause | Stop admitting affected work and preserve a recovery checkpoint |
|
||||
| Cancel | End the assigned work and reconcile operations already underway |
|
||||
|
||||
Exact commands and queue configuration remain implementation decisions.
|
||||
|
||||
## Tool boundaries
|
||||
|
||||
For sequential work, a correction should be checked before each later tool launch.
|
||||
Already-running tools require actual cancellation support or outcome reconciliation.
|
||||
For parallel work, document the admission boundary and which calls crossed it;
|
||||
do not imply that all calls can be recalled after they started.
|
||||
|
||||
Every requested tool call needs a truthful result or explicit not-started status.
|
||||
Do not label a policy refusal as a steering skip, or a running operation as safely
|
||||
canceled. Keep the request, result, and relevant steering evidence attributable.
|
||||
|
||||
A status question does not cancel the assignment. A scope correction changes the
|
||||
affected work, not unrelated tasks. A pause remains effective until its actual
|
||||
resume conditions are met.
|
||||
|
||||
## Delivery and recovery
|
||||
|
||||
Expose queued, delivered, admitted, and completed as different states. Avoid blind
|
||||
resends after uncertain delivery. Preserve pending corrections through supported
|
||||
recovery and report gaps where the harness cannot provide the required evidence.
|
||||
|
||||
ACT-C05 assesses reasoning from a synthetic sequence. Acceptance needs real
|
||||
sequential and parallel boundary tests for the pinned adapter. See
|
||||
[runtime ownership](agent-runtimes.md) and [state awareness](session-state.md).
|
||||
@@ -0,0 +1,42 @@
|
||||
# Session attachment across interfaces
|
||||
|
||||
Status: foundation intent agreed; unified attachment is not established by the
|
||||
temporary host TUI. See foundation R11, R21, R24 and R25.
|
||||
|
||||
Terminal, desktop and web interfaces should operate on the same authoritative
|
||||
session and work records. Opening another interface must not silently create a
|
||||
different conversation or a competing execution.
|
||||
|
||||
## Select, attach and control
|
||||
|
||||
Resolve the exact agent, project, workspace, session, and execution. Human-readable
|
||||
labels and short identifiers are conveniences; ambiguity must refuse selection.
|
||||
Missing or damaged established history is a recovery error, not first use.
|
||||
|
||||
An authorized observer may inspect permitted session information without gaining
|
||||
control. Initially one controller owns input. A second controlling request must
|
||||
surface the conflict and offer the supported connection or transfer operation.
|
||||
Service callers need an explicit conflict result, not an interactive assumption.
|
||||
|
||||
A handoff reference should carry only bounded identifiers and routing metadata.
|
||||
It must not contain credentials. Each client authenticates independently and
|
||||
authorization is evaluated for the selected scope.
|
||||
|
||||
## Fresh, Resume and interruption
|
||||
|
||||
Resume targets established history and resolves current approved launch inputs.
|
||||
Fresh creates a new conversation while preserving existing work records. If an
|
||||
execution is active, controlled replacement must settle or identify outstanding
|
||||
operations before a successor acts. Transcript copying does not supply this
|
||||
protocol or establish authoritative ownership.
|
||||
|
||||
Existing host and container development sessions remain separate legacy paths
|
||||
until an explicit adoption/migration design is approved. Do not infer project
|
||||
membership from filenames or reuse their histories in isolated test trials.
|
||||
|
||||
ACT-C06 is a synthetic attachment reasoning case. Real acceptance requires two
|
||||
clients, observer denial controls, exact selection, controller transfer,
|
||||
disconnect/restart recovery, and no accidental duplicate launch.
|
||||
|
||||
See [multi-user authority](multi-user.md) and
|
||||
[foundation](../plans/2026-09-06_agent-project-workspace-foundation.md).
|
||||
@@ -0,0 +1,43 @@
|
||||
# Collaborative state awareness
|
||||
|
||||
Status: proposed mechanism, to be reconciled with the accepted foundation records.
|
||||
No durable watcher service is introduced by this document.
|
||||
|
||||
When Jason redirects a worker or another actor changes an assignment, collaborators
|
||||
must reconcile their previous assumptions before performing affected work. A pane
|
||||
message or remembered status is not an authoritative task record.
|
||||
|
||||
## Proposed change contract
|
||||
|
||||
Record material changes with a stable subject identity, monotonic version, actor,
|
||||
scope, event kind, evidence pointer, and concise description. Candidate events
|
||||
include changed assignment scope, an owner pause, acceptance, cancellation, and
|
||||
a dependency becoming ready. Keep private message content out of broad notices.
|
||||
|
||||
Authorized watchers retain their last reconciled version. Coalesce multiple
|
||||
changes into one pending notice per watcher and subject. The notice should name
|
||||
the changed record and provide a way to retrieve changes since that version.
|
||||
Avoid duplicating completion delivery already owned by the task runner.
|
||||
|
||||
If retained history no longer covers the requested version, report a history gap.
|
||||
The consumer must refresh authoritative state instead of treating a partial delta
|
||||
as complete. Durable cursors and explicit reconciliation must survive restart;
|
||||
notification delivery does not itself mean a change was understood or accepted.
|
||||
|
||||
## Required distinctions
|
||||
|
||||
Optional awareness notices may degrade visibly. Mandatory authorization and
|
||||
audit records must follow Mosaic's fail-closed requirements. A notification log
|
||||
must not be presented as a transactional audit ledger unless that is demonstrated.
|
||||
|
||||
Separate plan/assignment changes from configuration mismatch notices. Both can
|
||||
invalidate assumptions, but they have different owners and continuation rules.
|
||||
A watcher learns only information it is authorized to see.
|
||||
|
||||
ACT-C04 tests interpretation of synthetic events. Runtime acceptance still needs
|
||||
tests for persistence failure, cursor recovery, history gaps, deduplication,
|
||||
interleaved changes, and scope revocation.
|
||||
|
||||
See [session attachment](session-attachment.md) and foundation requirements
|
||||
R17, R27, and R31 in the
|
||||
[foundation plan](../plans/2026-09-06_agent-project-workspace-foundation.md).
|
||||
@@ -0,0 +1,43 @@
|
||||
# Agent personality
|
||||
|
||||
Status: owner-agreed direction; template/bootstrap migration is governed by ACT-1.
|
||||
|
||||
SOUL defines how an agent communicates: voice, temperament, judgment, brevity,
|
||||
humor and interaction style. An agent has exactly one canonical SOUL. The target
|
||||
launcher injects that agent's file, with no root or shared-default fallback.
|
||||
|
||||
## Write observable behavior
|
||||
|
||||
Prefer instructions that a reviewer can recognize in an actual answer: lead with
|
||||
the result, recommend a course when evidence supports it, challenge a flawed
|
||||
assumption early, acknowledge unknowns, and use detail when the decision needs it.
|
||||
|
||||
Keep simple answers short. Allow natural humor without requiring it. Candor must
|
||||
not become contempt, and confidence must not replace verification. Adapt to the
|
||||
audience while preserving the agent's judgment.
|
||||
|
||||
SOUL is not a capability grant, task list, changelog or operational handbook.
|
||||
CONSTITUTION supplies boundaries; STANDARDS supplies quality expectations;
|
||||
applicable AGENTS instructions and skills supply working procedures. USER
|
||||
context supplies relevant preferences.
|
||||
|
||||
## Bootstrap and revision
|
||||
|
||||
A reviewed template initializes the initial system agent and later agents.
|
||||
The instance belongs to that agent. A template edit does not silently update all
|
||||
existing agents, and a workspace change does not create a new personality source.
|
||||
|
||||
Resolve the current approved SOUL on Resume and Fresh. Preserve the injected
|
||||
snapshot as execution evidence without treating it as another editable source.
|
||||
Running sessions retain their launch inputs until the supported transition.
|
||||
|
||||
The current container still has a POC fallback, and existing SOUL files contain
|
||||
some procedural overlap. Preserve the demo path until its replacement is tested.
|
||||
Documentation adoption alone does not migrate those runtime files.
|
||||
|
||||
Use isolated [behavior tests](agent-behavior-tests.md) to compare baseline and
|
||||
candidate revisions. Exactly one SOUL is supplied in each trial. Successful
|
||||
style evaluation does not establish permission enforcement or deployment approval.
|
||||
|
||||
See [prompt composition](system-prompt.md) and
|
||||
[ACT-1](../plans/2026-09-07_agent-context-templates-and-migration.md).
|
||||
@@ -0,0 +1,45 @@
|
||||
# Standing intents and future obligations
|
||||
|
||||
Status: proposed event-obligation concept, separate from the existing goal extension.
|
||||
No event matcher or new scheduler is introduced here.
|
||||
|
||||
| Request | Appropriate record |
|
||||
|---|---|
|
||||
| Do something at a stated time | An authorized scheduled task |
|
||||
| React when a specific event occurs | A scoped event obligation |
|
||||
| Improve something over time | An assignment or reviewed aspiration |
|
||||
| Continue an active bounded outcome | The existing goal/work lifecycle |
|
||||
|
||||
Writing any of these in prose does not register a wake mechanism.
|
||||
|
||||
## Proposed event record
|
||||
|
||||
Identify the owner, authorized action, target scope, trigger, source event,
|
||||
creation authority, expiry, firing limit, cancellation state and evidence
|
||||
destination. Verify that a real event-delivery mechanism owns the wake before
|
||||
promising unattended continuation.
|
||||
|
||||
Match and deduplicate within deterministic scope and lifecycle rules. A model may
|
||||
interpret an event where authorized, but it must not invent missing permission,
|
||||
expand the audience, or retry an uncertain external effect automatically.
|
||||
|
||||
## Delivery and cancellation
|
||||
|
||||
Distinguish registered, triggered, delivered, acted upon and completed. A fired
|
||||
trigger is not proof that the requested action succeeded. Reconcile before
|
||||
restarting interrupted work; do not duplicate an action merely because its
|
||||
receipt has not arrived.
|
||||
|
||||
Cancellation is durable and explicit. Expiry, cooldown and firing budgets bound
|
||||
repetition; exact limits are policy decisions rather than universal constants.
|
||||
Notification delivery does not clear a pause or authorize unrelated work.
|
||||
|
||||
Mosaic's native goal extension has its own process-incarnation behavior and
|
||||
supported waits. This concept must integrate with that ownership instead of
|
||||
introducing a second goal loop. The operator can still use manual reconciliation
|
||||
when no authorized automatic wake exists.
|
||||
|
||||
ACT-C10 tests classification and honest wake claims. Runtime tests must cover
|
||||
scope, duplicate events, expiry, cancellation and restart recovery.
|
||||
See [state awareness](session-state.md) and
|
||||
[goal extension](../../extensions/goal/README.md).
|
||||
@@ -0,0 +1,51 @@
|
||||
# Prompt composition
|
||||
|
||||
Status: target responsibilities agreed; unified resolution remains planned.
|
||||
The existing demo's launch and verification contracts remain in force.
|
||||
|
||||
Mosaic composes instructions from sources with distinct ownership and lifetimes.
|
||||
The composition contract must be explicit and testable across supported harnesses.
|
||||
|
||||
| Source | Responsibility |
|
||||
|---|---|
|
||||
| CONSTITUTION | Shared boundaries, principles and authority |
|
||||
| STANDARDS | Quality expectations and evidence requirements |
|
||||
| Agent SOUL | Voice, temperament and interaction style |
|
||||
| Repository AGENTS | Applicable repository procedures |
|
||||
| Scoped USER context | Relevant, authorized user preferences |
|
||||
| Runtime context | Actual identity, workspace, tools, skills and session behavior |
|
||||
| Assignment records | Authorized work, current state and acceptance criteria |
|
||||
|
||||
## Resolution rules
|
||||
|
||||
Select one canonical SOUL belonging to the requested agent. A project folder,
|
||||
template, root SOUL, or shared boilerplate must not replace it implicitly.
|
||||
System bootstrap creates the initial agent's instance from a reviewed template;
|
||||
later agent bootstrap follows the same ownership rule. Templates are not runtime
|
||||
personality layers. Updating them must not silently overwrite existing instances.
|
||||
|
||||
Load repository procedures only where applicable to the execution's scope.
|
||||
Do not automatically give a worker the conductor's context or privileges.
|
||||
User preferences, agent style, and retrieved content cannot widen permission.
|
||||
Heading order in a concatenated Markdown file is not security enforcement.
|
||||
|
||||
## Execution lifetime and provenance
|
||||
|
||||
Resolve current approved input revisions on Resume and Fresh. Record their
|
||||
identity with the execution, and preserve the exact injected snapshot where
|
||||
required. Keep credentials out of ordinary prompt records. Permission enforcement
|
||||
belongs to the runtime and policy mechanisms, not the personality file.
|
||||
|
||||
A harness may contribute its own instructions. Distinguish Mosaic's assembled
|
||||
inputs from a verified model-bound request; a local prompt snapshot alone cannot
|
||||
establish what an external harness added.
|
||||
|
||||
## Current implementation boundary
|
||||
|
||||
The container replaces Pi's base prompt with generated context; native development
|
||||
appends context to Pi's coding prompt. They differ in source selection and policy
|
||||
enforcement. Reconcile those paths under ACT-1 after demo validation. Do not remove
|
||||
POC startup-marker behavior before replacement verification fixtures are tested.
|
||||
|
||||
See [context inspection](context.md), [runtime ownership](agent-runtimes.md), and
|
||||
[the migration plan](../plans/2026-09-07_agent-context-templates-and-migration.md).
|
||||
@@ -0,0 +1,256 @@
|
||||
# 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.
|
||||
@@ -0,0 +1,485 @@
|
||||
# Harness declaration + centralized auth/provider registry
|
||||
|
||||
Status: **DRAFT FOR OWNER REVIEW** — no implementation is authorized by this
|
||||
file. Issue: #49. Date: 2026-09-03.
|
||||
|
||||
## Purpose
|
||||
|
||||
Specify how Mosaic Stack supports multiple harnesses, providers, endpoints,
|
||||
and authentication accounts without modifying a user's default harness
|
||||
configuration or requiring provider registration on every agent seat.
|
||||
|
||||
Pi is the first implementation checkpoint. The registry and seat contracts
|
||||
must remain harness-neutral so future adapters (`claude`, `codex`, `opencode`)
|
||||
materialize their own native files from the same desired state. Canonical
|
||||
harness IDs are the executable names so CLI, manifests, diagnostics, and user
|
||||
expectation stay 1:1.
|
||||
|
||||
## Non-negotiable decisions already made
|
||||
|
||||
1. Default harness homes (`~/.pi`, and future equivalents) are read-only to
|
||||
Mosaic. The stack never writes there.
|
||||
2. Credentials live only under the configured data root, never in the repo,
|
||||
image, argv, stdout, logs, or generated non-secret manifests.
|
||||
3. Account registration is centralized. A seat never performs OAuth login and
|
||||
does not maintain an independent source credential.
|
||||
4. Seat files are generated artifacts, not user-authored configuration.
|
||||
5. Missing, stale, invalid, insecure, or ambiguous registry/materialized state
|
||||
refuses launch. No fallback to a different account.
|
||||
6. Pi is implemented first; harness-specific behavior stays behind adapters
|
||||
and materializers.
|
||||
|
||||
## Separate concepts (do not conflate)
|
||||
|
||||
| Concept | Example | Authority |
|
||||
|---|---|---|
|
||||
| Harness | `pi`, later `claude-code`, `codex`, `opencode` | `agent.json` + versioned harness manifest |
|
||||
| Provider | `openai-codex`, `anthropic`, `zai`, `ollama-local` | central provider registry |
|
||||
| Account | `openai-codex/homelab-openai` | central account registry |
|
||||
| Endpoint | local/remote Ollama base URL | provider record |
|
||||
| Settings profile | reusable account/provider/model policy | central settings registry |
|
||||
| Seat selection | active choice within a profile's allowed accounts | audited runtime state |
|
||||
| Materialization | harness-native `auth.json` / `models.json` | generated per seat |
|
||||
|
||||
Accounts represent identities. Ollama instances represent endpoints. Harnesses
|
||||
consume generated configuration. Keeping these axes separate prevents account,
|
||||
provider, and model-policy conflicts.
|
||||
|
||||
## Harness declaration: `agent.json`
|
||||
|
||||
A defined seat declares exactly one harness:
|
||||
|
||||
```json
|
||||
{
|
||||
"agentVersion": 2,
|
||||
"name": "researcher",
|
||||
"role": "researcher",
|
||||
"harness": "pi",
|
||||
"settingsProfile": "research-default",
|
||||
"capabilities": { "tools": ["read", "bash"] }
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- `harness` is a scalar identifier, not an array. One process launch runs one
|
||||
harness.
|
||||
- It is not hard-coded to an enum in the agent schema. The value must resolve
|
||||
dynamically through the installed/available harness registry.
|
||||
- Canonical identifiers (owner-confirmed during #50 review) are executable
|
||||
names: `pi`, `claude`, `codex`, `opencode`. Provider `openai-codex` and
|
||||
harness `codex` remain distinct.
|
||||
- No CLI harness override in phase 1. Harness is part of reviewed seat identity;
|
||||
an override would bypass that declaration.
|
||||
- Migration: existing `agentVersion: 1` seats without `harness` resolve to `pi`
|
||||
with a loud deprecation warning. A later reviewed migration makes version 2
|
||||
and `harness` mandatory.
|
||||
- Plain M13 launches without an agent definition continue to use the system
|
||||
execution adapter; they have no seat enrollment or generated seat auth.
|
||||
|
||||
### Harness manifests
|
||||
|
||||
Harness support is versioned with its adapter, for example:
|
||||
|
||||
```text
|
||||
adapters/pi/harness.json
|
||||
adapters/claude-code/harness.json
|
||||
```
|
||||
|
||||
A manifest declares its identifier, executable name, adapter, compatible
|
||||
version range, installer/detector metadata, execution mode, materializers,
|
||||
native config paths, and supported credential types. Adding harness support
|
||||
requires a reviewed adapter + manifest + suites. Detecting/installing a known
|
||||
harness is mutable machine state; changing the manifest/adapter contract is a
|
||||
reviewed repository change.
|
||||
|
||||
### Harness detection, installation, and availability
|
||||
|
||||
Target CLI:
|
||||
|
||||
```text
|
||||
mosaic harness list [--available|--installed] [--json]
|
||||
mosaic harness detect [<id>|--all]
|
||||
mosaic harness install <id> [--version <exact-compatible-version>]
|
||||
mosaic harness rm <id>
|
||||
mosaic harness status [<id>] [--json]
|
||||
```
|
||||
|
||||
Lifecycle and rules:
|
||||
|
||||
1. `detect` checks canonical executable names (`pi`, `claude`, `codex`,
|
||||
`opencode`) on PATH and approved known locations, resolves real paths,
|
||||
obtains versions using manifest-declared noninteractive commands, validates
|
||||
compatibility, and records compatible findings as available. Unknown
|
||||
executables are never auto-registered.
|
||||
2. Detection records metadata only: executable path, version, source
|
||||
(`external-detected`), compatibility result, detection timestamp, and
|
||||
manifest identity. It never reads/copies the harness's home, settings, auth,
|
||||
sessions, extensions, or plugins.
|
||||
3. `install <id>` resolves the reviewed catalog/manifest, pins an exact
|
||||
compatible version, verifies package identity/checksum where supported, and
|
||||
installs/packages into a Mosaic-managed immutable runtime. It never performs
|
||||
an unversioned global install and never writes the default harness home.
|
||||
4. Built-in pi is an image-baked managed harness. Future installs should package
|
||||
a harness-specific container image/bundle beside the active release so
|
||||
`reset.sh` does not destroy installed runtimes; dataRoot stores registry
|
||||
state and receipts, not the package payload.
|
||||
5. Availability and launch-readiness are separate fields. A compatible detected
|
||||
host executable is `available`; under the current container boundary it is
|
||||
not automatically `ready` until imported/installed into a managed runtime,
|
||||
unless a separately reviewed adapter explicitly supports host execution.
|
||||
6. `agent.json.harness` launch requires a compatible, ready harness + adapter +
|
||||
materializer. Detected-but-not-ready, missing, incompatible, or ambiguous
|
||||
installations refuse loudly; no fallback to pi.
|
||||
7. Install/detect/remove operations write append-only receipts. Removal refuses
|
||||
while defined seats reference the harness unless those bindings are migrated
|
||||
first.
|
||||
|
||||
Registry state is derived under `<dataRoot>/harnesses/`; the reviewed catalog
|
||||
and adapter manifests remain in the installation/repository. This avoids a
|
||||
hard-coded enum while preventing arbitrary PATH executables from becoming
|
||||
trusted harnesses.
|
||||
|
||||
## Central registry layout
|
||||
|
||||
Fixed below `<dataRoot>`; not configurable independently (config.json remains
|
||||
the sole system config):
|
||||
|
||||
```text
|
||||
<dataRoot>/auth/
|
||||
├── providers/
|
||||
│ ├── openai-codex.json
|
||||
│ ├── anthropic.json
|
||||
│ ├── zai.json
|
||||
│ ├── ollama-local.json
|
||||
│ └── ollama-remote.json
|
||||
├── accounts/
|
||||
│ ├── openai-codex/
|
||||
│ │ ├── homelab-openai/
|
||||
│ │ │ ├── account.json # non-secret metadata, 0600
|
||||
│ │ │ └── credential.json # secret OAuth/API material, 0600
|
||||
│ │ └── personal-openai/
|
||||
│ │ ├── account.json
|
||||
│ │ └── credential.json
|
||||
│ └── zai/
|
||||
│ └── main/
|
||||
│ ├── account.json
|
||||
│ └── credential.json
|
||||
├── settings/
|
||||
│ ├── base.json
|
||||
│ └── research-default.json
|
||||
└── state/
|
||||
├── refresh.json # non-secret status only
|
||||
└── activation-log.jsonl # append-only, no secret material
|
||||
```
|
||||
|
||||
Naming and security:
|
||||
|
||||
- Provider/account/profile IDs match `^[a-z0-9][a-z0-9._-]{0,63}$`.
|
||||
- Display names are metadata; path IDs are explicit or deterministically
|
||||
slugged and confirmed before creation.
|
||||
- Account path must be under its registered provider.
|
||||
- Registry dirs are owner-only; account and credential files are regular,
|
||||
non-symlink, 0600. Loose perms refuse use.
|
||||
- `credential.json` schema depends on type but is never returned by list/status.
|
||||
- Provider removal refuses while accounts/profiles/seats reference it.
|
||||
- Account removal refuses while profiles/selections reference it unless an
|
||||
explicit reviewed migration removes those references first.
|
||||
|
||||
## Registry records
|
||||
|
||||
### Provider
|
||||
|
||||
Native provider example:
|
||||
|
||||
```json
|
||||
{
|
||||
"providerVersion": 1,
|
||||
"id": "openai-codex",
|
||||
"kind": "native",
|
||||
"harnesses": { "pi": { "providerId": "openai-codex" } },
|
||||
"credentialTypes": ["oauth", "api_key"]
|
||||
}
|
||||
```
|
||||
|
||||
Custom endpoint example:
|
||||
|
||||
```json
|
||||
{
|
||||
"providerVersion": 1,
|
||||
"id": "ollama-local",
|
||||
"kind": "custom-endpoint",
|
||||
"harnesses": {
|
||||
"pi": {
|
||||
"api": "openai-completions",
|
||||
"baseUrl": "http://host.docker.internal:11434/v1",
|
||||
"apiKey": "ollama",
|
||||
"models": ["qwen2.5-coder:7b", "llama3.1:8b"]
|
||||
}
|
||||
},
|
||||
"credentialTypes": ["none"]
|
||||
}
|
||||
```
|
||||
|
||||
`ollama-remote` is a second provider record with its remote base URL, optional
|
||||
credential account, and independently scoped model catalog. Linux compose must
|
||||
provide host-gateway resolution for local Ollama; container `localhost` is not
|
||||
the host.
|
||||
|
||||
### Account metadata
|
||||
|
||||
```json
|
||||
{
|
||||
"accountVersion": 1,
|
||||
"id": "homelab-openai",
|
||||
"name": "Homelab OpenAI",
|
||||
"provider": "openai-codex",
|
||||
"type": "oauth",
|
||||
"createdAt": "<UTC timestamp>"
|
||||
}
|
||||
```
|
||||
|
||||
`credential.json` holds the pi-compatible OAuth/API payload centrally. API keys
|
||||
are accepted only from an interactive hidden prompt, stdin, or a validated
|
||||
0600 file — never an argv value.
|
||||
|
||||
### Reusable settings profile
|
||||
|
||||
Profiles remove per-seat/per-provider registration:
|
||||
|
||||
```json
|
||||
{
|
||||
"settingsVersion": 1,
|
||||
"id": "research-default",
|
||||
"allowedAccounts": [
|
||||
"openai-codex/homelab-openai",
|
||||
"openai-codex/personal-openai",
|
||||
"zai/main"
|
||||
],
|
||||
"defaultAccounts": {
|
||||
"openai-codex": "openai-codex/homelab-openai",
|
||||
"zai": "zai/main"
|
||||
},
|
||||
"providers": ["ollama-local", "ollama-remote"],
|
||||
"models": {
|
||||
"ollama-local": ["qwen2.5-coder:7b"],
|
||||
"ollama-remote": ["abliterated-model-id"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A seat references one profile. Editing one central profile updates every seat
|
||||
that uses it on the next ensure/launch. The seat is not re-registered with each
|
||||
provider.
|
||||
|
||||
A profile may authorize multiple accounts for the same provider. Pi can place
|
||||
only one entry per provider in one `auth.json`; therefore exactly one account
|
||||
per provider is active for a materialization. Defaults provide the initial
|
||||
selection.
|
||||
|
||||
## Runtime seat selection
|
||||
|
||||
At-will changes are mutable state, not repo edits:
|
||||
|
||||
```text
|
||||
<dataRoot>/agents/<seat>/settings-selection.json
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"selectionVersion": 1,
|
||||
"profile": "research-default",
|
||||
"accounts": {
|
||||
"openai-codex": "openai-codex/personal-openai"
|
||||
},
|
||||
"updatedAt": "<UTC timestamp>"
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- Selection can choose only accounts allowed by the seat's referenced profile.
|
||||
- Omitted provider selections use profile defaults.
|
||||
- Changing selection is append-only-audited and triggers regeneration.
|
||||
- No account choice silently falls back to another account.
|
||||
- In-session identity swapping is deferred. Selection normally applies on
|
||||
relaunch/new session; changing identity inside a session risks ambiguous
|
||||
billing, provider state, and audit lineage.
|
||||
|
||||
## Mechanical per-seat materialization
|
||||
|
||||
Generated state:
|
||||
|
||||
```text
|
||||
<dataRoot>/agents/<seat>/generated/pi/
|
||||
├── auth.json # secret, 0600, generated
|
||||
├── models.json # generated custom providers/model scopes
|
||||
└── manifest.json # non-secret inputs/hashes/timestamps; no credential hash
|
||||
```
|
||||
|
||||
Resolution on `agent.sh <seat>`:
|
||||
|
||||
1. Strictly validate agent definition and resolve its harness manifest.
|
||||
2. Resolve role, settings profile, runtime selection, providers, accounts, and
|
||||
model scopes.
|
||||
3. Validate every referenced record, regular-file constraint, ownership, and
|
||||
0600 credential perms.
|
||||
4. Refresh required centralized OAuth records if policy says stale/near expiry;
|
||||
refusal leaves prior generated files untouched.
|
||||
5. Generate beside existing files, validate harness-native output, chmod 0600,
|
||||
then atomically rename into place.
|
||||
6. Compare/write non-secret manifest state and append an activation receipt.
|
||||
7. Mount generated files into the harness's native paths read-only and launch.
|
||||
|
||||
No seat authenticates, edits auth.json, or owns an independent OAuth refresh
|
||||
token. Generated files are disposable derivatives of the registry.
|
||||
|
||||
`mosaic auth ensure [<seat>]` runs the same materializer explicitly. A periodic
|
||||
host service refreshes central OAuth records and rematerializes affected seats;
|
||||
launch-time ensure is the final fail-closed gate. OAuth refresh mechanics must
|
||||
reuse pi's implementation where possible rather than reimplement provider
|
||||
protocols; the exact noninteractive refresh trigger is an implementation
|
||||
investigation and acceptance gate.
|
||||
|
||||
## CLI contract (target `mosaic` surface)
|
||||
|
||||
```text
|
||||
mosaic auth list [--provider <id>] [--json]
|
||||
mosaic auth status [<account-ref>|--seat <seat>] [--json]
|
||||
mosaic auth new
|
||||
mosaic auth new --name "Homelab OpenAI" --provider openai-codex --type oauth
|
||||
mosaic auth rm <provider/account>
|
||||
mosaic auth login <provider/account>
|
||||
mosaic auth logout <provider/account>
|
||||
mosaic auth refresh [<provider/account>|--all]
|
||||
mosaic auth ensure [<seat>|--all]
|
||||
|
||||
mosaic auth provider list [--json]
|
||||
mosaic auth provider status <id> [--json]
|
||||
mosaic auth provider create
|
||||
mosaic auth provider create --id ollama-local --kind custom-endpoint ...
|
||||
mosaic auth provider rm <id>
|
||||
|
||||
mosaic harness list|detect|install|rm|status ...
|
||||
|
||||
mosaic agent settings use <seat> <profile>
|
||||
mosaic agent auth use <seat> <provider/account>
|
||||
mosaic agent auth status <seat>
|
||||
```
|
||||
|
||||
Behavior:
|
||||
|
||||
- `new` without sufficient flags launches an interactive wizard.
|
||||
- OAuth `new/login` launches the host-side provider flow once and stores the
|
||||
resulting centralized profile. Agents never run it.
|
||||
- API secret input is hidden prompt/stdin/validated file only.
|
||||
- `rm` reports references and refuses when in use.
|
||||
- Every mutating command writes an append-only secret-free receipt.
|
||||
- `list/status --json` never include credential/token/key fields.
|
||||
- CLI flags name accounts/providers; no secret material is accepted on argv.
|
||||
|
||||
Until M20 provides `mosaic`, `scripts/auth.sh` may prototype the backend, but it
|
||||
must use the same schemas and must not become a competing implementation.
|
||||
|
||||
## Harness-neutral materializers
|
||||
|
||||
Core resolution produces a secret-bearing internal representation in memory:
|
||||
selected providers/accounts/models for one seat. A harness materializer maps it
|
||||
to native files:
|
||||
|
||||
- Pi: `auth.json` + `models.json`.
|
||||
- Claude Code/Codex/OpenCode: future adapter-defined files/env, without changing
|
||||
account/provider/profile schemas.
|
||||
|
||||
The harness manifest declares support. Enrollment or launch refuses if a chosen
|
||||
account/provider cannot materialize for the seat's harness. No best-effort
|
||||
provider dropping.
|
||||
|
||||
## Policy relationships (future phase, not initial implementation)
|
||||
|
||||
Initial authorization is settings-profile enrollment. Later, the existing
|
||||
least-privilege doctrine extends naturally:
|
||||
|
||||
```text
|
||||
role auth ceiling ∩ profile enrollment ∩ mission grant ∩ task grant
|
||||
= effective providers/accounts/models
|
||||
```
|
||||
|
||||
A task may narrow a seat's providers/models, never select an account outside
|
||||
its profile. This is separate from registry and materialization correctness.
|
||||
|
||||
## Migration from M19 prototype
|
||||
|
||||
No named accounts currently exist, so migration is state-free:
|
||||
|
||||
1. Keep `~/.pi/agent/auth.json` as the default harness file, read-only to Mosaic.
|
||||
2. Replace the prototype `<dataRoot>/auth/<account>.json` convention with the
|
||||
registry tree before users create accounts.
|
||||
3. Keep `scripts/auth.sh status/accounts` behavior but drive it from registry
|
||||
schemas.
|
||||
4. Replace `agent.sh --auth <account>` direct-file selection with profile +
|
||||
runtime selection + materialization. A temporary compatibility path may map
|
||||
`--auth provider/account` to a one-launch selection, but must be explicit and
|
||||
audited.
|
||||
5. Add `harness: "pi"` + `settingsProfile` to researcher through an agentVersion
|
||||
migration after review.
|
||||
|
||||
## Required suites / acceptance
|
||||
|
||||
- Registry schemas: unknown keys, path/name mismatch, symlinks, traversal,
|
||||
wrong perms, duplicate IDs, missing provider, unsupported credential type.
|
||||
- Secret non-disclosure: fixture keys/tokens never appear in stdout, stderr,
|
||||
JSON status, manifests, receipts, or git diff.
|
||||
- Referential integrity: provider/account/profile removal refuses while used.
|
||||
- Multiple account types for one provider register successfully.
|
||||
- Profile authorizes multiple same-provider accounts; exactly one active choice
|
||||
materializes for pi.
|
||||
- Changing runtime choice regenerates atomically; failed generation preserves
|
||||
previous files and records refusal.
|
||||
- OAuth central refresh rematerializes affected seats; agents never authenticate.
|
||||
- `harness: pi` resolves; unregistered/unsupported harness refuses.
|
||||
- Harness detection records recognized compatible executables without reading
|
||||
harness homes; incompatible/ambiguous detections refuse availability.
|
||||
- Harness install is exact-version/verified, Mosaic-managed, and never global;
|
||||
detected-but-not-ready harnesses cannot launch seats.
|
||||
- Pi materialization generates valid auth.json/models.json with scoped providers
|
||||
and models; custom local/remote Ollama entries remain distinct.
|
||||
- Launch refuses missing/stale/invalid materialization; no account fallback.
|
||||
- Existing generic TUI and default-harness use remain untouched.
|
||||
- Full existing suites + verify green.
|
||||
|
||||
## Review gates (must resolve before implementation)
|
||||
|
||||
1. **RESOLVED (owner, #50):** canonical harness IDs are executable names:
|
||||
`pi`, `claude`, `codex`, `opencode`; validation is registry/manifest-driven,
|
||||
not a schema enum. `mosaic harness detect/install/list/rm/status` owns the
|
||||
lifecycle. Remaining seam to approve: detected host executables are
|
||||
available but not launch-ready under the container boundary until
|
||||
imported/installed into a managed runtime (unless host execution receives a
|
||||
separate reviewed adapter).
|
||||
2. Confirm `agentVersion: 2` migration and whether `settingsProfile` becomes
|
||||
mandatory for defined seats.
|
||||
3. Confirm registry path/schema split (`account.json` metadata +
|
||||
`credential.json` secret) versus one encrypted/combined file.
|
||||
4. Decide encryption-at-rest requirement. Filesystem 0600 is specified now;
|
||||
external keyring/envelope encryption would change login/refresh design.
|
||||
5. Confirm selection scope: seat-global only initially, or named launch profiles
|
||||
(e.g. `work`, `personal`) as a first-class layer.
|
||||
6. Confirm whether a seat may switch same-provider account on relaunch only, or
|
||||
whether forked sessions must pin the original account in immutable session
|
||||
metadata.
|
||||
7. Determine pi's supported host-side noninteractive OAuth refresh trigger;
|
||||
implementation must prove refresh without exposing or duplicating tokens.
|
||||
8. Confirm local Ollama container routing (`host-gateway`) and remote Ollama
|
||||
transport/auth requirements.
|
||||
9. Decide whether M20 `packages/mosaic` begins with this auth/provider/harness
|
||||
domain or whether scripts prototype it first. Confirm managed harness
|
||||
packaging: harness-specific container image/bundle survives dataRoot reset;
|
||||
registry state and receipts remain under dataRoot.
|
||||
10. Define backup/reset semantics for the central registry. Current reset wipes
|
||||
the data root; OAuth re-login cost may justify a separately protected
|
||||
registry root, but that would require an explicit canon change.
|
||||
|
||||
Implementation is blocked until these gates are reviewed and owner-approved.
|
||||
@@ -0,0 +1,81 @@
|
||||
# Autonomous Work Run — 2026-09-03
|
||||
|
||||
**Status:** COMPLETED (single-session batch; see Results at bottom)
|
||||
**Constraint:** The assistant cannot run unattended. This was one long interactive session, not 12 wall-clock hours. Everything below was completed, committed, and pushed during that session.
|
||||
|
||||
## Objective
|
||||
|
||||
Advance the Mosaic Stack rebuild several verified layers in one batch, focused on Pi, ending in a state the owner can test and review alone: green suites, activated release, recorded drills, and this document as the single entry point.
|
||||
|
||||
## Scope decided for this run
|
||||
|
||||
| Milestone | Theme | Status |
|
||||
|---|---|---|
|
||||
| M5 | Task workspaces + capability envelope (tools allowlist) | DONE |
|
||||
| M6 | Named sessions — persistence and resume (L1) | DONE |
|
||||
| M7 | Operator ergonomics: run inspection commands | DONE |
|
||||
| — | Releases 0.0.5+0.0.6 packaged; 0.0.6 health-gated activated | DONE |
|
||||
|
||||
Explicitly deferred (do not mistake for forgotten):
|
||||
- Claude/Codex/OpenCode adapters (owner: focus on Pi for now)
|
||||
- Network policy engine (container boundary is the current control)
|
||||
- Fine-grained read restrictions (excluded by the original brief)
|
||||
- Config/state migrations (no schema breaks so far; keep it that way)
|
||||
|
||||
## Design decisions taken during this run
|
||||
|
||||
1. **Workspace** (`task.workspace`, optional):
|
||||
- absent → tool-free text-only run (previous behavior, unchanged)
|
||||
- `":run"` → ephemeral per-run workspace at `<dataRoot>/runs/<runId>/workspace`
|
||||
- named (validated id) → persistent shared workspace at `<dataRoot>/workspaces/<name>`
|
||||
- Container path passed via `MOSAIC_WORKSPACE` env; adapter cds into it. No new mounts (dataRoot is already mounted).
|
||||
2. **Capabilities** (`task.capabilities.tools`, optional): allowlist from pi's documented tool set (`read write edit bash grep find ls`). Absent → `--no-tools` (previous behavior). Passed via `MOSAIC_TOOLS` env; pi adapter maps to `--tools`.
|
||||
3. **Adapter diagnostics for deterministic testing**: the mock adapter writes all received `MOSAIC_*` variables (never secrets — auth is not MOSAIC_-prefixed) to stderr, which lands in the run record. This lets selftests assert orchestrator→adapter plumbing without parsing model output.
|
||||
4. **Sessions** (`task.session`, optional named): persisted under `<dataRoot>/sessions/<name>/` via pi's documented `--session-dir`; resume semantics: continue most recent session in that directory when one exists (`-c`).
|
||||
5. **Selection authority unchanged**: config file for adapter/provider/model; task file for workspace/capabilities/session; env vars are internal plumbing only.
|
||||
6. **configVersion stays 1**; all new task fields are optional. Old tasks/configs remain valid.
|
||||
|
||||
## Test plan (what "done" means per milestone)
|
||||
|
||||
- M5: mock-adapter cases asserting workspace path and tools arrive via run-record stderr; live pi case writing/reading a file in a persistent workspace; validation negatives (bad tool name, bad workspace name)
|
||||
- M6: session directory deterministically populated after first run; second run resumes (continuation asserted by session dir state and, in live E2E, by model recall); sandbox isolation between two named sessions
|
||||
- M7: `show <runId>` prints a complete run record; `list` gains workspace/session columns
|
||||
- Final: full sweep (config/task/release), verify, package + activate 0.0.6, config checksum unchanged
|
||||
|
||||
## Review checklist for the owner
|
||||
|
||||
1. `cat docs/plans/2026-09-03_autonomous-run.md` (this file)
|
||||
2. `scripts/release.sh status` → 0.0.6 active
|
||||
3. `scripts/test-config.sh && scripts/test-task.sh && scripts/test-release.sh && scripts/verify.sh`
|
||||
4. Try a workspace task:
|
||||
```bash
|
||||
scripts/run-task.sh run tasks/workspace-demo.json
|
||||
ls ~/.mosaic-dev/workspaces/demo/
|
||||
```
|
||||
5. Try the session demo:
|
||||
```bash
|
||||
scripts/run-task.sh run tasks/session-demo-1.json # teaches a word
|
||||
scripts/run-task.sh run tasks/session-demo-2.json # recalls it
|
||||
```
|
||||
6. Inspect any run: `node scripts/mosaic-task.mjs show <runId>`
|
||||
7. Gitea: milestones M5/M6/M7 closed; issues referenced by merge commits
|
||||
|
||||
## Results
|
||||
|
||||
- M5 merged on `main` (merge commit `ddb1554`), tagged `workspace-capabilities-v1`
|
||||
- M6 merged on `main` (merge commit `4e2a413`), tagged `sessions-v1`
|
||||
- M7 merged on `main`, tagged `operator-ergonomics-v1`
|
||||
- Release 0.0.6 packaged, health-gated activated, full sweep green
|
||||
- Suites at end of run: config 24/24, task 32/32, release 14/14, verify PASS
|
||||
- Build log: Phases 9 (M5), 10 (M6), 11 (M7) appended with corrections
|
||||
- Corrections encountered: dropped constant from a failed atomic edit batch (SUPPORTED_TOOLS); dash `export` output format vs `env`; three selftest authoring defects; showRun id-regex case sensitivity + missing-run crash. All fixed and covered by tests.
|
||||
- Commits pushed incrementally; nothing left uncommitted
|
||||
- Live proof: workspace file host-visible; session teach/recall ('mosaico') verified
|
||||
|
||||
## Next steps after this run (not started)
|
||||
|
||||
1. Owner review + hands-on testing of workspaces, capabilities, sessions
|
||||
2. Decision: capability defaults per mission (mission-level policy) — natural M8
|
||||
3. Second real adapter remains available whenever wanted
|
||||
4. Consider run-record pruning/retention policy once run volume grows
|
||||
5. Consider a `mosaic-task.mjs retry <runId>` convenience for failed runs
|
||||
@@ -0,0 +1,339 @@
|
||||
# Agent, project, and workspace foundation
|
||||
|
||||
Status: documentation draft for Jason's review. No implementation authorized.
|
||||
Date: 2026-09-06. Plan author: darkwing, current pi session
|
||||
`01a06e48-0718-71f2-a889-c263c4800fb9`.
|
||||
Baseline: `69d1bb3aa4b826218aa4cca3710f2d98c0b9d7ba` in `mosaicstack/stack-v2`.
|
||||
Issue: [#53](https://git.mosaicstack.dev/mosaicstack/stack-v2/issues/53),
|
||||
opened by rocko under the authorized seat identity, POST 201 reported at
|
||||
2026-09-06T00:12:33Z.
|
||||
|
||||
Start here for the intended behavior. Field proposals and execution evidence
|
||||
are in [Schema and audit discussion](2026-09-06_workspace-schema-and-audit.md).
|
||||
Phase-2 detail is now in the
|
||||
[Record and operation contract candidate](2026-09-06_foundation-phase2-contract.md).
|
||||
These documents are not implemented APIs or approved JSON Schemas.
|
||||
|
||||
## 1. Why we are doing this
|
||||
|
||||
Jason wants one familiar interaction agent to work in several separate areas
|
||||
without creating another agent identity for each assignment. Workers should
|
||||
also reuse their definitions across projects and workspaces.
|
||||
|
||||
A fresh conversation must be able to continue checked work without inheriting
|
||||
an old conversation's confusion. Files and work records, not conversation
|
||||
memory alone, tell the agent what to do next.
|
||||
|
||||
Development must remain understandable and testable by Jason. We agree on a
|
||||
small result and its test, build only that result after approval, then stop
|
||||
for Jason to try it. Passing automated tests is not user acceptance.
|
||||
|
||||
## 2. Requirements agreed in the owner conversation
|
||||
|
||||
These describe intent. Exact field names, storage paths, and mechanisms below
|
||||
remain proposals unless separately approved.
|
||||
|
||||
| ID | Requirement |
|
||||
|---|---|
|
||||
| R1 | An agent has a reusable identity, type, execution program, SOUL, configuration, and permission limits. Creating an assignment need not create another agent identity. |
|
||||
| R2 | A project registers participating agents and controls their visibility and permitted work through role-based access control, RBAC. Owner round 1, Q2: the user may delegate bounded registration/assignment authority to the system; actions outside those limits require approval. |
|
||||
| R3 | A project contains N workspaces; each workspace has exactly one parent project, owner Q5. Each workspace registers participating agents and keeps its work separate. Owner Q3: project membership permits shared project information and only explicitly permitted workspaces, not all workspace content. Dependencies are references, not additional parents. |
|
||||
| R4 | The same agent can participate in many projects and workspaces, with separate working sessions. One definition per worker type may suffice; specialized definitions remain possible. |
|
||||
| R5 | Launch names the agent, project, and workspace. Those choices determine mission, tasks, state, working paths, and message destination. The agent does not guess its assignment from its seat directory. |
|
||||
| R6 | Shared instructions and skill definitions may be reused. Work-specific information points to the declared workspace and relevant project records, not a global agent task list. |
|
||||
| R7 | Resume and Fresh are distinct launch operations. Resume is the default. Owner Q4 clarification: if no conversation has ever existed in the declared agent/project/workspace scope, initial launch creates it and announces this without an offer. Missing or damaged established conversations are errors, not first use. Fresh does not automatically import the old conversation or its automatic summary. |
|
||||
| R8 | A fresh conversation supports continuing, abandoning, or selecting established work, independently of conversation choice. Owner Q8: Abandon defaults to ending this agent's selected assignments, not cancelling the shared mission/tasks. Owner Q9: an unfinished prerequisite requires an explicit authorized assignment change, never silent expansion or proceeding as if it were complete. |
|
||||
| R9 | Users and authorized system services can launch or relaunch an agent with fresh context. Mission recovery uses saved work records. |
|
||||
| R10 | Default concurrency is one active working session per agent/project/workspace. Users can tune concurrency; budgets, usage measurement, and automatic scaling are requirements for later phases. |
|
||||
| R11 | Terminal, desktop, and web interfaces refer to the same workspace work records. They show the active agent/project/workspace and do not keep competing task lists. |
|
||||
| R12 | Messages must address the appropriate workspace. Identity reuse must not mix conversations or work across projects. |
|
||||
| R13 | Separate folders provide logical organization, not a demonstrated security sandbox. Security enforcement needs its own evidence. |
|
||||
| R14 | Execution records retain the reusable agent identity and identify the particular assignment/session that acted. Capture context through the system, not agent self-description. Owner Q13: concise action records name actor/scope/session/task, operation, target, authorization, outcome, and evidence references. Detailed evidence is stored separately with access controls; no credentials in either. |
|
||||
| R15 | Jason approves progression between phases and tests implementation increments before later work depends on them. No automatic queue-running. |
|
||||
| R16 | Owner ruling, 2026-09-06: SOUL remains canonical per agent. Each Resume or Fresh launch loads the current approved revision into execution-specific inputs and records it. Running conversations do not silently reload changed instructions. Recording the revision does not permanently pin a workspace to it. |
|
||||
| R17 | Owner Q20/Q21 extends the launch fingerprint to shared behavior-affecting agent configuration: SOUL, shared instructions, applicable enabled skill versions, harness/model settings, and role configuration. Exclude credentials, chat, and task progress. TUI/GUI/WUI show automatic non-blocking mismatch notices recommending Fresh and support on-demand checks against the same record, without repeated interruption or automatic restart. |
|
||||
| R18 | Owner Q1/Q5: missions may exist at project and workspace levels, with explicit single-parent relationships. A workspace mission has at most one parent project mission, or stands alone within its owning workspace/project scope. Each mission has one owner and authoritative record. A parent may have many children; dependencies are not extra parents. |
|
||||
| R19 | Owner Q7: delegated, authorized agents may make non-destructive, goal-directed decisions and decompose work autonomously within the established plan. Routine tasks may be accepted by an authorized independent reviewer against agreed criteria. No user interaction is needed for each within-plan decision; scope deviations and declared owner checkpoints still require approval. |
|
||||
| R20 | Owner Q10: conversation and permitted workspace inspection may precede a mission/task. Requests to change things become explicit recorded assignments without requiring lengthy mission setup. Scope and permissions still apply. |
|
||||
| R21 | Owner Q11/Q15: Resume targeting an already-running session reports the conflict and offers connection to a user. A service receives an already-active result with the execution identity and must explicitly request connection or another authorized operation. No automatic connection, replacement, or duplicate launch. |
|
||||
| R22 | Owner Q12: agents share permitted workspace missions, tasks, decisions, and evidence. Reading another agent's conversation needs separate permission or an explicitly authorized handoff. Read permission never implies automatic conversation loading. |
|
||||
| R23 | Owner Q14: workspace membership removal stops affected executions and blocks further actions in that workspace. Independently authorized work elsewhere continues. Global agent disable is separate; already-issued actions need reconciliation, not an assumption that stopping undoes them. |
|
||||
| R24 | Owner Q16: one controlling interface connection per running session initially. Other authorized connections may observe; transfer of control is explicit. Observation requires conversation access permission. |
|
||||
| R25 | Owner Q17: Fresh during active work requests controlled replacement. Stop admitting new actions, preserve checked state, settle or identify unfinished actions, then launch Fresh only when safe. If safe replacement cannot be established, stop and report rather than start competing work. |
|
||||
| R26 | Owner Q18: an authorized agent/service may investigate uncertain results with non-destructive checks and resume dependent work only after evidence resolves the uncertainty. Otherwise escalate. Blindly repeating the original action is not a recovery check. |
|
||||
| R27 | Owner Q19: required audit-recording failure blocks affected executions. Other work continues only if its required recording works. Refuse before an affected action; treat effects with missing result evidence as uncertain and reconcile them. |
|
||||
| R28 | Owner Q22: only explicitly designated general user preferences are shared by default. Personal and project-specific information is supplied only where authorized and relevant, not by automatically loading the whole user profile into every launch. |
|
||||
| R29 | Owner Q23: closing a workspace retires it from active work, safely stops its work, retains files/history, and blocks ordinary new launches until reopened. Deletion and retention cleanup are separate authorized operations. |
|
||||
| R30 | Owner Q24: existing sessions remain legacy records until explicitly adopted through reviewed project/workspace assignment. No inferred membership from filenames and no automatic default-project placement. Preserve historical evidence. |
|
||||
| R31 | Owner Q25: an approved plan change pauses affected work for reconciliation. An authorized coordinator may adjust assignments within its delegation; unaffected work can continue. Account for actions already underway. Escalate adjustments beyond delegated authority or that cannot be resolved safely; do not finish obsolete assignments merely because they started earlier. |
|
||||
| R32 | Owner Q26: use standard scope permission roles with registration-specific narrowing. A project/workspace role describes what the agent may do there; it does not redefine its reusable identity/type or exceed its permission ceiling. Exact role names and grant lists require review. |
|
||||
| R33 | Owner Q27 A: initial managed commands require invocation-level evidence: actor/scope/assignment, authorized command, enforced filesystem/network limits, start/end, outcome, and controlled evidence references. Separate tracing of every internal file/network operation is not required. Isolation, credential protection, fail-closed recording, revocation, and uncertainty recovery remain mandatory. |
|
||||
| R34 | Owner Q28 A: the initial managed terminal may be Mosaic-controlled with Pi remaining the engine; native Pi screen/shortcut parity is not required. Clients use the same mediated operations, with no waiver of scope, permission, recording, privacy or recovery requirements. This is not runtime implementation approval. |
|
||||
|
||||
## 3. The objects in plain language
|
||||
|
||||
```text
|
||||
Shared agent definition: darkwing
|
||||
Identity, type, Pi configuration, SOUL, permission limits
|
||||
|
||||
Project: mosaic-stack-v2
|
||||
Registered agents: darkwing, code-be-01, rev-code-01
|
||||
Project decisions and shared project state
|
||||
|
|
||||
+-- Workspace: sessions
|
||||
| Registered agents and assignments
|
||||
| Workspace mission, tasks, files, state
|
||||
| Darkwing conversations and execution records
|
||||
| Code-be-01 conversations and execution records
|
||||
|
|
||||
+-- Workspace: skills
|
||||
Separate assignments, files, state, and conversations
|
||||
|
||||
Project: personal
|
||||
Workspace: journal
|
||||
Darkwing registered here too, with separate work context
|
||||
```
|
||||
|
||||
Agents are referenced by projects and workspaces; they are not copied into
|
||||
new identities for each registration. A session belongs to one
|
||||
agent/project/workspace combination. A running instance is one execution
|
||||
attempt using that session. Resuming a conversation creates another execution
|
||||
attempt, not another agent identity.
|
||||
|
||||
Owner Q5 distinguishes containment from dependencies: one project owns each
|
||||
workspace; a workspace mission may contribute to at most one project mission.
|
||||
A standalone workspace mission still belongs to its workspace and project.
|
||||
Dependency references do not create another parent or confer its permissions.
|
||||
|
||||
Proposed ownership rule: project-wide decisions have one authoritative home.
|
||||
Workspace records reference the relevant project revision rather than copy it
|
||||
into an independently editable project state. Individual assignments stay
|
||||
separate even when several agents share a workspace.
|
||||
|
||||
## 4. Resume, Fresh, and the work decision
|
||||
|
||||
| Conversation operation | Work decision | Intended result |
|
||||
|---|---|---|
|
||||
| Resume | Continue | Reopen the selected conversation and reconcile it with current authorized work state. |
|
||||
| Fresh | Continue | New conversation; load checked mission, task status, and relevant evidence. Do not import the old chat. |
|
||||
| Fresh | Abandon | End this agent's selected assignments and start a new conversation. Underlying missions/tasks remain for reassignment unless separately cancelled with authority. Preserve history and files. |
|
||||
| Fresh | Select | Start with explicitly selected work and relevant dependency information. An unfinished prerequisite needs an authorized assignment change before execution. Unselected work is not silently cancelled. |
|
||||
|
||||
Owner Q4 ruling: first use creates the initial conversation automatically and
|
||||
announces "Starting initial conversation" without a confirmation offer. This
|
||||
is not replacement of existing context or a third public launch command.
|
||||
Subsequent default launches resume. Owner Q11: if that conversation is already
|
||||
running, report the conflict and offer to connect rather than automatically
|
||||
connecting or launching another process. Owner Q15: a service receives an
|
||||
already-active result with the execution identity and chooses any next
|
||||
connection or other operation explicitly under its authority.
|
||||
A missing/damaged established conversation is an error, not permission to
|
||||
create a replacement. Ambiguity still refuses.
|
||||
|
||||
Owner Q10: initial conversation and permitted inspection do not require a
|
||||
mission/task. Requests to make changes become recorded assignments within the
|
||||
user's or service's authority. No mission setup ceremony is required for chat.
|
||||
|
||||
Owner Q6 approved Fresh/Continue loading the assigned mission and success
|
||||
criteria, assigned tasks/status, relevant approved decisions, dependencies,
|
||||
blockers, and verified-result references. A proposed next step is labelled as
|
||||
a proposal, not an authorized assignment. Old chat/automatic summaries stay
|
||||
out; unverified notes remain labelled as unverified.
|
||||
|
||||
Owner Q7/Q9: an explicit assignment change need not require Jason personally.
|
||||
An authorized coordinator may approve prerequisite work within delegated plan
|
||||
limits and record the change. The worker never silently expands its assignment.
|
||||
This autonomy does not waive the owner-controlled phase boundaries below.
|
||||
|
||||
Owner Q16/Q17: an existing execution has one controlling interface connection;
|
||||
authorized observers do not become additional controllers. A Fresh request
|
||||
initiates controlled replacement, stopping new actions and preserving checked
|
||||
state. Unfinished actions must be resolved or explicitly identified before
|
||||
replacement; if safety is uncertain, stop and report. Do not copy old chat
|
||||
into the new session as a substitute for checked work state.
|
||||
|
||||
Proposals for review:
|
||||
- Abandon and Select are not valid with Resume in the first version. Keeping
|
||||
the old conversation would retain the context the user meant to set aside.
|
||||
- If the caller lacks access to a required dependency, the system refuses
|
||||
rather than silently omitting it.
|
||||
- A relaunch first resolves the old execution's status. Starting Fresh is
|
||||
not permission to leave two sessions claiming the same assignment.
|
||||
- A fresh launch does not delete files or cleanse incorrect files. Retained
|
||||
artifacts must be distinguished from active work records.
|
||||
|
||||
These rules need lifecycle and failure details before implementation. See
|
||||
[open decisions](2026-09-06_workspace-schema-and-audit.md#7-open-decisions).
|
||||
|
||||
## 5. What the owner has tested so far
|
||||
|
||||
On 2026-09-05 Jason launched the existing `researcher` definition with
|
||||
workspace and session name `owner-checkpoint-1`, allowing only `read,ls`.
|
||||
The supplied terminal output showed Pi 0.84.4, a successful model response,
|
||||
and an empty `/var/lib/mosaic/workspaces/owner-checkpoint-1` listing through
|
||||
`ls`. After exiting and relaunching, the agent recalled a phrase from the
|
||||
conversation without a tool call.
|
||||
|
||||
This demonstrates the observed launch/list/resume path. It does not prove
|
||||
cross-workspace access controls, long-conversation correctness, execution
|
||||
auditing, Fresh mission recovery, or any project model. Fresh was discussed
|
||||
but not demonstrated. This plan does not infer broad user acceptance from
|
||||
the earlier test or mark unfinished tests as passed.
|
||||
|
||||
## 6. Current code references, not future guarantees
|
||||
|
||||
All source references below are at the baseline commit named above. These
|
||||
are starting points for the later independent analysis, not its verdict.
|
||||
|
||||
| Current behavior | Source |
|
||||
|---|---|
|
||||
| On every launch, agent defaults and role are read from a definition and current seat SOUL is copied to a shared per-agent runtime location. This is not a one-time copy or a live update to already-running instructions. | `scripts/agent.sh:78-130` |
|
||||
| Session defaults to `agent-<name>` under the global sessions directory, independently of workspace selection. | `scripts/agent.sh:132-138` |
|
||||
| Role tools narrow requested tools; enabled skill definitions are resolved and passed as container paths. | `scripts/agent.sh:140-179` |
|
||||
| Interactive mission is copied to `agent-missions/<agent>.json`; workspace is a separately selected folder; Compose starts the container. | `scripts/agent.sh:181-199` |
|
||||
| Pi changes working directory, uses the named session directory, and adds `-c` when it is nonempty. | `adapters/pi/adapter.sh:23-50` |
|
||||
| The whole data root is mounted at `/var/lib/mosaic`, not just the selected workspace. | `compose.yaml:39-43` |
|
||||
| Dispatcher generates one shared system-prompt path; the loader also uses one `.partial` path and injects global user Markdown. | `src/run-agent.sh:37-41`; `src/load-contracts.sh:40-41,68-77,97` |
|
||||
| Headless tasks get exclusive input snapshots and a final result containing workspace, session, tools, model, and timing. | `scripts/mosaic-task.mjs:295-325,442-466` |
|
||||
|
||||
The per-agent mission/SOUL paths and shared prompt paths require explicit
|
||||
concurrency analysis. Do not claim that independent workspace names already
|
||||
prevent launch-time context mix-ups. No race reproduction has been performed
|
||||
in this documentation phase.
|
||||
|
||||
## 7. Scope and existing plans
|
||||
|
||||
Authorized now: these two planning documents, CURRENT.md routing, issue
|
||||
intake, and append-only build/session history. No runtime schema files,
|
||||
implementation, new agent definitions, credential changes, migration,
|
||||
release activation, map generation, or skill integration.
|
||||
|
||||
The [auth/provider plan](2026-09-03_auth-provider-harness-registry.md), issue
|
||||
#50, stays paused. Its seat-global selection and generated per-seat files
|
||||
must be reconciled with concurrent workspace sessions before implementation.
|
||||
OAuth refresh gate 7 is still unresolved; this plan does not approve it.
|
||||
|
||||
[ROADMAP.md](ROADMAP.md) supplies earlier direction, not authority to skip
|
||||
this exercise. The old registry-first maps are historical candidates, not a
|
||||
complete plan for this object model. Issue #51 and untracked skills remain
|
||||
separate work. No source is moved into `packages/` in this phase.
|
||||
|
||||
Keep the repository canon: sole system config, no new root files, no secrets
|
||||
in documents/images, reviewed role authority, and write-once run evidence
|
||||
under `<dataRoot>/runs/`. Runtime project/workspace records must not become a
|
||||
second system configuration or a way to grant themselves wider authority.
|
||||
|
||||
## 8. Phases, each stopped for owner review
|
||||
|
||||
| Phase | Output | Required stop |
|
||||
|---|---|---|
|
||||
| 1. Document, behavior confirmed | Agreed requirements, proposed record shapes, initial source references, audit limitations, and open decisions. | Jason confirmed the behavior summary on 2026-09-06 and subsequently authorized phase 2. |
|
||||
| 2. Resolve details, current | Exact object relationships, schemas, permissions, command behavior, state ownership, and action-record guarantees, with read-only investigation. | Q27 selects invocation-level command evidence with enforced limits. Complete the remaining details, then Jason approves a specific plan revision for mapping. |
|
||||
| 3. Map with Archify | Separate current/planned diagrams and a ledger tracing launch, state, permissions, messaging, and execution receipts. | Independent map review plus Jason's visual acceptance. |
|
||||
| 4. Independent gap analysis | A distinct non-authoring agent compares the pinned implementation and agreed plan; reports missing, conflicting, or unsupported connections. | A different agent reviews the report; Jason decides each finding and whether another plan/map revision is needed. |
|
||||
| 5. Plan one implementation increment | Named author/reviewer, allowed paths, acceptance test, rollback, and protected operations. | Jason authorizes that one increment. |
|
||||
| 6. Implement and user-test | Local suites, independent candidate review, verified release if needed, and a short test Jason performs. | Jason explicitly accepts before another increment begins. |
|
||||
|
||||
Documentation, the decision interview, and phase-2 detailed design/read-only
|
||||
investigation are authorized. Individual behavior rulings and documentation
|
||||
completion are not full schema approval. A diagram passing rendering checks is not evidence
|
||||
that its claims are correct. No review request is implied by naming a future
|
||||
reviewer.
|
||||
|
||||
Roles: Jason owns scope, decisions, and acceptance. Darkwing authors this
|
||||
plan and coordinates. Proposed later map author: rocko. Proposed later
|
||||
independent reviewer/gap analyst: filbert, if available and not a design or
|
||||
map author. Proposed peer reviewer of the gap report: ms-test, if independent
|
||||
of its authorship. Confirm assignments at the phase boundary. If an independent
|
||||
seat is unavailable, report blocked; the author does not take its place.
|
||||
|
||||
## 9. Evidence required in later phases
|
||||
|
||||
Archify work must cite code and plan lines at named commits, separate facts
|
||||
from proposals and external measurements, and bind spec/ledger/HTML hashes.
|
||||
Use the approved lane preview and browser checks; preserve old verdicts.
|
||||
A reviewer verifies the exact candidate. The later gap analyst must check
|
||||
both code and plan, not infer correctness from the diagram or its author.
|
||||
Unspecified paths and missing enforcement are findings, never invented edges.
|
||||
|
||||
Candidate user tests, to agree before implementation:
|
||||
|
||||
- Same agent in two workspaces retains different files, tasks, and chats.
|
||||
- Fresh/Continue remembers an approved task but not an unrecorded chat phrase.
|
||||
- Resume reopens only the intended workspace's conversation. If already
|
||||
running, it reports the conflict and offers connection without auto-attach.
|
||||
- Unassigned conversation and permitted inspection work; a change request
|
||||
creates an authorized recorded assignment, not untracked mutations.
|
||||
- Agents can read shared work records but cannot read a colleague's transcript
|
||||
without a separate grant. Granting access does not auto-load the transcript.
|
||||
- Change canonical SOUL while two workspaces run: both show a configuration
|
||||
mismatch, retain their recorded launch inputs, and recommend Fresh. Relaunch
|
||||
one workspace with the new revision; only that execution becomes current.
|
||||
Verify each interface uses the same status, and task progress alone does
|
||||
not produce a base-configuration warning. See R16-R17 and D16.
|
||||
- Abandon preserves history and does not cancel another agent's work.
|
||||
- Select includes dependency information, obtains an explicit authorized
|
||||
assignment change before undertaking unfinished prerequisites, and leaves
|
||||
unselected work explicit.
|
||||
- An authorized reviewer accepts a routine task without a user prompt, with
|
||||
criteria and evidence recorded. A plan deviation or protected action does
|
||||
not inherit that permission.
|
||||
- Unauthorized registration, ambiguous addressing, and stale task ownership
|
||||
refuse without acting in another workspace.
|
||||
- Removing one workspace membership stops affected work and blocks further
|
||||
actions there while leaving independently authorized work elsewhere running.
|
||||
In-flight effects are reconciled rather than declared undone.
|
||||
- Only one authorized interface controls a session; observers cannot issue
|
||||
controls and explicit transfer prevents the old controller from continuing.
|
||||
- Fresh during active work does not overlap unsafe executions. Unknown effects
|
||||
block dependent work until authorized, non-destructive investigation resolves
|
||||
them or escalation occurs; no blind replay.
|
||||
- Audit failure blocks affected work without unnecessarily stopping executions
|
||||
that can still meet their recording requirements.
|
||||
- Configuration notices cover shared behavior-affecting inputs, appear without
|
||||
blocking work, and can be requested on demand without changing execution.
|
||||
- Synthetic personal-journal context is absent from a coding launch unless
|
||||
explicitly authorized/relevant; designated shared preferences remain usable.
|
||||
- Closing a workspace preserves records and blocks new launches until reopening.
|
||||
Adopting legacy sessions requires explicit reviewed assignment, not name-based
|
||||
inference, and does not rewrite their historical evidence.
|
||||
- A changed approved plan pauses affected actions/dependents until reconciled;
|
||||
delegated reassignment and continued unaffected work retain authority evidence.
|
||||
- Standard scope roles are narrowed by registration and parent/agent ceilings.
|
||||
A role label alone cannot grant access to other workspaces or self-acceptance.
|
||||
- Each managed action can be traced to a session, execution attempt, task,
|
||||
authority decision, and evidence-backed outcome, including interrupted work.
|
||||
|
||||
Later tests must cover strict schemas, cross-project references, path escape,
|
||||
concurrent launch, shared-file races, stale messages, audit write failures,
|
||||
and partial external effects. Terminal/desktop/web clients need situational
|
||||
and accessibility tests when implemented; none are claimed by this draft.
|
||||
CI runners remain owner-deferred. Local suites plus `verify.sh` are the repo
|
||||
publication gate, with exact-candidate review and owner phase approval.
|
||||
|
||||
No deployment or migration occurs now, so existing operation is unchanged.
|
||||
Any later migration must preserve old sessions/run records, be explicit about
|
||||
reset/prune effects, and have an owner-reviewed recovery and rollback test.
|
||||
|
||||
## 10. First owner review
|
||||
|
||||
Review R1-R33 for faithful intent. The owner authorized the grill-me interview
|
||||
on 2026-09-06. Rounds 1-6, Q1-Q26, are recorded in the linked discussion.
|
||||
Q25/Q26 add affected-work reconciliation after approved plan changes and
|
||||
standard scope permission roles with registration-specific narrowing.
|
||||
|
||||
Jason confirmed the behavior summary with "That looks correct" on 2026-09-06,
|
||||
after Q1-Q26. Shared understanding of intended behavior is confirmed. This is
|
||||
not whole-schema approval or a claim that every technical branch is resolved.
|
||||
|
||||
Jason subsequently answered yes to phase 2: detailed records, permissions,
|
||||
commands, and audit guarantees with read-only technical investigation. The
|
||||
linked phase-2 candidate records the first source/document findings and proposed
|
||||
common types. Jason answered Q27 A: invocation-level command evidence with
|
||||
enforced limits, without a promise to enumerate every internal effect.
|
||||
The explicit /goal request resumed phase 2 after reboot recovery. The schema
|
||||
package now includes checked record/command shapes and semantic-rule proposals.
|
||||
Jason answered Q28 A, recorded as R34. The owner-review package now reconciles
|
||||
D1-D16 against the candidate schemas/rules and explicit implementation proof gates.
|
||||
Phase-2 acceptance remains Jason's decision. No mapping, independent gap review,
|
||||
implementation, publication, or migration follows automatically.
|
||||
@@ -0,0 +1,57 @@
|
||||
# Fleet goal ownership
|
||||
|
||||
## Authority and acceptance
|
||||
|
||||
Jason confirmed Resume's NG footer and Alt+G, authorized fleet-wide correction, and authorized scoped commits after green suites. Joe/Huey quiet waits are "looking good so far", not final acceptance. Dewey owns this bounded work in mosaic-stack-dev-test. Independent read-only Pi review must approve exact source and deployment pins. CURRENT and the foundation work remain darkwing's.
|
||||
|
||||
Outcome: one shared NG implementation at ~/.mosaic/.pi/extensions/goal for existing fleet launch configurations, without duplicate registration. Retain wrapper-guard or mosaic-core role enforcement as configured, and the launcher's unslop hook. Remove the Resume-specific no-discovery override once ordinary discovery works. No automatic restarts, credentials/configuration edits, private-state reads, state migration, policy changes, or push. Local-first deployment remains the operator-authorized workflow; repository CI runners remain deferred, not reported green.
|
||||
|
||||
## Measured ownership
|
||||
|
||||
41 agent settings paths and 14 role settings paths reference fleet/extensions/goal. They resolve to 24 ordinary settings files; many agent settings are symlinks to role settings. All declared packages lists are empty. Most seat workdirs are the shared brain; Topher and Velma use their seat directories. Topher's extension directory and fifteen role extension links already resolve to shared extensions. Velma still has an ordinary standalone goal tree. The settings template also references fleet/extensions/goal.
|
||||
|
||||
Pi 0.85.1 package-manager toResolvedPaths canonicalizes resource paths before deduplication. We will verify this mechanism in the native loader with normal discovery enabled. Replacing settings would needlessly touch 24 files and still leave compatibility entrypoints. Instead, preserve every settings file/link and make the legacy fleet goal path and Velma standalone goal path compatibility symlinks to the shared NG goal. Keep their old ordinary source trees in verified backups. No recursive library forwarding and no separately auto-discovered core entrypoint are introduced.
|
||||
|
||||
## State boundary
|
||||
|
||||
The shared implementation resolves its NG state directory relative to the selected import path. Native fixture writes proved Pi deduplicates real source paths but Jiti retains the selected alias when resolving imports. At the brain cwd, state is ~/.mosaic/.pi/state/goal. At Topher or Velma's cwd, their project alias selects their own .pi/state/goal. A role cwd behaves the same way. With no project discovery, the legacy compatibility alias selects ~/.mosaic/fleet/state/goal. Every filename remains goal-state.<incarnation>.json. Do not claim all aliases use one state directory.
|
||||
|
||||
Legacy agent-home goal state remains untouched. Velma and Topher's existing NG stores remain in place. Source unification does not adopt, resume, delete or migrate existing goals. Existing processes keep their loaded code until the operator safely restarts them. Old goal evidence stays in its original location; do not assume an active goal will transfer. Native fixtures use fresh owned incarnation IDs and clean only their exact fixture files. Live canaries never invoke goal commands or read private state.
|
||||
|
||||
The first Velma synthetic fixture failed because it omitted Velma's existing neighboring mosaic-core libraries. The corrected fixture includes those libraries. All shared supporting modules match both installed legacy and Velma libraries byte-for-byte. Pin these dependency trees, wrapper and unslop entrypoints as well as the goal sources, since an alias does not redirect neighboring imports.
|
||||
|
||||
## Delivery
|
||||
|
||||
1. Pin all three source trees, the current Resume-hotfix launcher, and settings references/link targets. Pin launch.env filesystem identity/change metadata only. Environment files may contain credentials, so the deployment tool must never read or copy their contents. Register an issue and append session/build records.
|
||||
2. Add an incident-scoped, fail-closed deployment tool and synthetic tests. Default is preflight. Stage two compatibility symlinks outside discovery, then atomically exchange them with the two ordinary trees. Restore the exact original shared launcher as the third change. Check pins before every effect. On failure reverse verified exchanges; on drift refuse rollback. Retain backup receipts and support explicit verified rollback. Never rewrite a run record.
|
||||
3. Native red control must reproduce the duplicate with ordinary separate trees. Green fixture must load one shared goal through ambient plus explicit aliases, including root, role-linked, Topher-linked, Velma-linked and isolated cwd cases. Preserve configured wrapper/core tool interception plus unslop. Verify NG footer/full recall against identical installed runtime sources. Test hostile drift, symlink/special-file refusals and partial-failure rollback.
|
||||
4. Independently review exact candidate, hashes, tests, state limits and rollback. Deploy locally only after approval, then rerun actual fleet resource combinations with normal discovery. Do not invoke auth-seeding launchers or restart sessions for testing.
|
||||
5. Run goal, native, package and five repository suites. Stage only our explicit files, inspect the index, commit with Dewey identity, verify the commit. Do not stage foundation/skill/task dirt or overwrite CURRENT. No push authorized by this commit request.
|
||||
6. Record Resume acceptance and fleet deployment distinctly. Close #57 for accepted Resume correction when evidence is recorded. Quiet-wait #56 and fleet user acceptance remain open until explicitly accepted.
|
||||
|
||||
The first independent review requested removal of launch.env content hashing because environment files can contain credentials. The corrected candidate pins only filesystem metadata and includes tests that reject any attempted environment-content read and detect metadata drift. The rejected manifest remains historical evidence and was never deployed.
|
||||
|
||||
## Deployment checkpoint, 2026-09-06 09:02 UTC
|
||||
|
||||
Corrected independent review APPROVE: .pi/evidence/goal58/review-v2.log. Deployed the reviewed three-path transaction and verified all after-pins and backups. Legacy fleet goal and Velma goal are now compatibility symlinks to shared NG. The shared launcher is byte-identical to its original pre-workaround version, SHA256 9352feed0acf9d449c26c0556ba00aba1c65ded43d376930a39ff6b5cee21986. No Resume-specific no-discovery override remains.
|
||||
|
||||
Reviewed plan SHA256: 43eb7821a484796850e5e9352f51fe8a6cffb2fb9084769070f449b529a47220. Backup and recovery receipt: /home/jwoltje/.mosaic/.pi/goal-backups/goal58-5baaf9ff598644c6ae364b297e3c982a. Its planned.json contains the complete source, dependency and configuration pins. The former plan-v2.json was rejected and never deployed.
|
||||
|
||||
Native negative control and six unified fixture cases pass, including owned fixture writes proving the state paths above. Seventeen transaction controls cover rollback, partial interruption, source/config/dependency/mode drift, symlink/special-file refusal, concurrent-writer lock refusal and the environment-content no-read boundary. Post-install native loading passed all 55 actual agent/role settings combinations with their configured wrapper or core tool interception and unslop. No live goal command or model turn ran. Evidence: .pi/evidence/goal58/live-matrix.log. This verifies registration/discovery, not every role-policy decision at runtime.
|
||||
|
||||
All 71 goal tests, 18 package controls, native footer/full recall and timed/untimed waiting checks pass. Repository suites pass: config 24, task 90, release 14, conductor 17, auth 15. Whitespace and prose checks pass. Static TypeScript checking and remote CI remain unavailable/deferred, not certified by these tests.
|
||||
|
||||
Explicit rollback, only if current source, dependency, configuration and backup pins still match:
|
||||
|
||||
```sh
|
||||
python3 scripts/unify-fleet-goal.py rollback \
|
||||
--plan /home/jwoltje/.mosaic/.pi/goal-backups/goal58-5baaf9ff598644c6ae364b297e3c982a/planned.json \
|
||||
--sha256 43eb7821a484796850e5e9352f51fe8a6cffb2fb9084769070f449b529a47220 \
|
||||
--backup /home/jwoltje/.mosaic/.pi/goal-backups/goal58-5baaf9ff598644c6ae364b297e3c982a
|
||||
```
|
||||
|
||||
Rollback restores both old source trees and the previously accepted Resume workaround. Do not overwrite later concurrent changes to force rollback.
|
||||
|
||||
User test: at a safe stopping point, restart another seat through its normal launch command. Expect one NG footer, full /goal and Alt+G recall, no duplicate-tool startup error, and the same configured safeguards. Do not overwrite an existing assignment with a test goal. Old legacy state does not transfer automatically. No session was restarted by Dewey. Resume UX is accepted; fleet rollout is ready for user test and #56 quiet-wait feedback is still preliminary.
|
||||
|
||||
Commit scope excludes CURRENT, foundation planning/reviews, unrelated skills/tasks, and the shared logs because they contain other-owner uncommitted entries. Our evidence/acceptance checkpoints are in these owned plans; shared append-only records remain on disk. MS58-DW-1 informational notice to darkwing returned rc=2, submission unconfirmed; no blind resend and no reply requested. Last verified index release remains MS55-DW-3; inspect the index and commit only explicit owned paths. No push authorized. Issue #58. Evidence directory: .pi/evidence/goal58/.
|
||||
@@ -0,0 +1,46 @@
|
||||
# Owner-reported durability failure and runtime WAL direction
|
||||
|
||||
Date: 2026-09-06. Recorded by darkwing.
|
||||
Status: owner-reported context and direction for later runtime design; not an
|
||||
independently investigated incident or an implementation/test receipt.
|
||||
|
||||
Jason reports that the separate ~/.mosaic Stack v1 deployment is fragile under
|
||||
load; agents retain work in memory rather than reliably tracking it in the system,
|
||||
and agent state does not reliably survive restarts. He directs that agent actions
|
||||
use WAL for durability and expects tasks, missions, goals, workspaces, projects and
|
||||
kanban to provide stronger control over system flow.
|
||||
|
||||
No investigation/intervention in that deployment is requested or performed.
|
||||
|
||||
## Required follow-through in the later runtime charter
|
||||
|
||||
- Trusted admission/publisher code enforces write-ahead intent for governed effects.
|
||||
Compliance must not depend on a model voluntarily writing a note or remembering
|
||||
to update a task. This builds on the accepted pre-effect recording requirement.
|
||||
- Specify the durability boundary: flush/sync and acknowledgement ordering, atomic
|
||||
publication, recovery of incomplete/torn records, writer ownership and failure
|
||||
behavior. Merely appending to a file or calling something WAL is insufficient.
|
||||
- Record stable action/request identity, exact scope/assignment/intent revisions,
|
||||
and recovery evidence without secrets. Keep work records distinct from private
|
||||
transcripts and enforce their visibility rules.
|
||||
- Reconstruct selected work and outstanding obligations from authoritative records
|
||||
after restart. Chat memory and UI caches cannot be the only source of truth.
|
||||
Kanban and other surfaces must view the same work state, not competing task lists.
|
||||
- Reconcile uncertain external effects using evidence; WAL does not make arbitrary
|
||||
external commands exactly-once or justify blind replay after a crash.
|
||||
- Test crash/restart points around intent, acknowledgement, execution and result;
|
||||
concurrent writers and load; disk-full/write/sync failures; and loss of a required
|
||||
audit path. Verify both preserved work and fail-closed admission. Preserve the
|
||||
separately preauthorized fail-safe stopping path.
|
||||
|
||||
## Scope boundary
|
||||
|
||||
The accepted map already identifies legacy writeOnce's lack of fsync and the need
|
||||
for a trusted durable publisher/recovery boundary. This observation sharpens later
|
||||
runtime acceptance requirements; it does not prove a specific v1 root cause.
|
||||
|
||||
The approved inspector charter remains frozen at
|
||||
19b6721128a627a2032ffdb95ece2d50abe69a8f6d521e9eff8bbdaff22798b6.
|
||||
FI-ROCKO-3 implements only that offline synthetic inspector. WAL/runtime persistence,
|
||||
load remediation and live migration are not added to its scope. A synthetic preview
|
||||
cannot establish these durability guarantees. No worker is retasked by this note.
|
||||
@@ -0,0 +1,52 @@
|
||||
# Owner-relayed Jarvis stall: credential scope and evidence handoff
|
||||
|
||||
Date: 2026-09-06. Recorded by darkwing.
|
||||
Status: owner-relayed incident account, not independently verified. No investigation
|
||||
or remediation in the separate deployment is requested or authorized by this note.
|
||||
|
||||
## Report preserved
|
||||
|
||||
Jason relayed Jarvis's account of three causes:
|
||||
1. code-be-01 lacked read:package. Repository access did not update the existing
|
||||
token's scopes, and registry token exchange returned 401.
|
||||
2. After re-minting the credential, code-be-01 generated evidence inside the
|
||||
infrastructure worktree rather than its agent work/ directory.
|
||||
3. Jarvis's watch monitored only the agent work/ directory and missed the completed
|
||||
evidence. The monitoring mismatch extended the delay.
|
||||
|
||||
Reported state at handoff: full digest evidence exists; all three manifests are
|
||||
pinned in the development infrastructure worktree; the delta remains uncommitted
|
||||
and unreviewed. Jarvis reports the next gate as validation, commit and independent
|
||||
review. This is context for that other assignment, not a task or commit/review
|
||||
authorization for this session.
|
||||
|
||||
## Later design and acceptance questions
|
||||
|
||||
- Distinguish repository permission, package-registry permission and the scopes
|
||||
actually held by an already issued credential. Permission changes must not be
|
||||
assumed to update existing tokens. Use explicit capability checks without
|
||||
revealing secrets or reflexively widening grants. A 401 alone is not a universal
|
||||
diagnosis of this specific cause; the above cause is the relayed account.
|
||||
- Before dispatch, bind evidence publication to an explicit, authorized artifact
|
||||
location and completion contract. Do not infer the path from agent identity or cwd.
|
||||
- Producers should return a structured receipt containing assignment/run identity,
|
||||
canonical artifact locator, digest and completion state. Artifact presence alone
|
||||
does not mean validation, commit, independent review or owner acceptance occurred.
|
||||
- Watchers should bind to the same agreed receipt/location, not maintain a separate
|
||||
path assumption. A location change needs an explicit authorized handoff update;
|
||||
do not compensate by blindly scanning other workspaces or broadening visibility.
|
||||
- Distinguish producer failure from observer failure. A missing watched file may
|
||||
mean wrong scope/location, delivery failure or monitoring error—not stalled work.
|
||||
Reconcile declared publication evidence before retrying or retasking the producer.
|
||||
- Persist the receipt, watch binding and remaining gates so observer/orchestrator
|
||||
restarts can recover the obligation without relying on memory.
|
||||
|
||||
Suggested later tests: stale token scopes after an access change; package versus
|
||||
repository capability refusal; publication at an unexpected location; authorized
|
||||
location-change handoff; missed/duplicate completion events; watcher restart; and
|
||||
artifact-present but validation/review still incomplete. Keep all tests synthetic
|
||||
or separately authorized; never use credential contents as evidence.
|
||||
|
||||
Related: foundation mechanical-workflow topics and durability observations dated
|
||||
2026-09-06. No existing inspector requirement is changed, no worker is retasked,
|
||||
and no credential, watch, repository or live-fleet operation follows from this note.
|
||||
@@ -0,0 +1,95 @@
|
||||
# Federation, master registry and mosaic comms — owner direction
|
||||
|
||||
Date: 2026-09-06. Recorded by darkwing.
|
||||
Status: captured forward architecture/API proposal, not an installed command or an
|
||||
implementation/migration charter. Current offline inspector scope stays unchanged.
|
||||
|
||||
## Owner direction
|
||||
|
||||
Plan for federation and cross-site/cross-instance clustering from the foundation.
|
||||
Maintain a master registry covering site, instance, project, workspace and agent.
|
||||
The mosaic CLI registers those entities and their valid combinations.
|
||||
|
||||
Agent communications should use the public `mosaic comms` boundary. tmux must not
|
||||
remain an agent-facing addressing/communication API; if retained, it is an internal
|
||||
transport behind Mosaic. Other configurable transports may include durable Git
|
||||
communications and a Mosaic backend using Matrix or another platform, self-hosted
|
||||
or hosted services such as Discord/Slack. No backend is selected by this note.
|
||||
|
||||
## Proposed CLI, preserved from the owner
|
||||
|
||||
```text
|
||||
mosaic comms
|
||||
--site -S {uuid|name}
|
||||
--instance -I {uuid|name}
|
||||
--project -P {uuid|name}
|
||||
--workspace -W {uuid|name}
|
||||
--source -s {uuid|name}
|
||||
--target -t {uuid|name}
|
||||
--help
|
||||
--message -m <text>
|
||||
--list -l opt:<filter_text>
|
||||
```
|
||||
|
||||
List returns available entries; exact filtering/output semantics remain to specify.
|
||||
|
||||
Addressing supports either a UUID of a registered agent instance bound into
|
||||
site+instance+project+workspace+agent hierarchy, or combinations of UUIDs and names.
|
||||
Owner wording: names resolve with the agent seat "little-endian"; the precise
|
||||
qualified-name grammar/order needs specification rather than guessing.
|
||||
|
||||
A UUID at any level identifies its bound parent hierarchy, so ancestors need not
|
||||
be redundantly supplied. The lowest-level UUID can anchor the remaining resolution.
|
||||
UUID-to-UUID messaging is recommended for brevity and lack of ambiguity:
|
||||
|
||||
```sh
|
||||
mosaic comms -S homelab -I main -P journal -W health -s <uuid> -t <name>
|
||||
mosaic comms -s <uuid> -t <uuid>
|
||||
```
|
||||
|
||||
These are proposed examples, not commands verified or invoked in this session.
|
||||
|
||||
## Design follow-through and open questions
|
||||
|
||||
- Separate reusable agent definitions from registered/scoped agent-instance or
|
||||
communication-endpoint identities. The accepted foundation allows one reusable
|
||||
agent across scopes; a scoped UUID must not silently turn that definition into
|
||||
a single-project identity. Define which entity each registry UUID identifies.
|
||||
- Define logical master-registry authority and replication: single authoritative
|
||||
service, delegated site authorities or another consistency model. "Master registry"
|
||||
does not by itself decide deployment topology or make federation available.
|
||||
- Resolve UUIDs to recorded parents without granting access. Conflicting explicit
|
||||
ancestors must refuse, not override a binding; names must resolve uniquely within
|
||||
an authorized namespace or refuse. No filename/cwd/seat-name membership inference.
|
||||
- Clarify whether shared -S/-I/-P/-W flags constrain both endpoints or qualify only
|
||||
named endpoints when one endpoint UUID belongs elsewhere. Define the little-endian
|
||||
name syntax, escaping, aliases/renames, stale registrations and retirement.
|
||||
- Authenticate the actual caller and check authority to use -s; a supplied source
|
||||
UUID/name is not proof of sender identity. Independently check destination scope,
|
||||
message type, visibility and cross-site trust. Listing/filtering must reveal only
|
||||
authorized entries. Transport delivery never grants reassignment authority.
|
||||
- Keep addressing, authorization, message/receipt semantics and work truth independent
|
||||
of the transport. Different backends must not widen permissions or reinterpret
|
||||
messages as owner instructions, task acceptance or permission to change goals.
|
||||
- Define durable enqueue/acknowledgement, stable message/request IDs, deduplication,
|
||||
ordering where required, retention/access controls and restart reconciliation.
|
||||
Git/tmux/backend presence alone does not establish delivery or exactly-once effects.
|
||||
- Specify partition/stale-registry behavior, federation trust/key lifecycle, revocation
|
||||
propagation, site isolation and recovery. Continue unrelated authorized work where
|
||||
safe; fail closed rather than guess through uncertain authority.
|
||||
|
||||
Related: accepted technical map at 7345f330fc6bfae5aa1d896c78cfb7cbe62efbae;
|
||||
2026-09-06_foundation-mechanical-workflow-topics.md;
|
||||
2026-09-06_foundation-durability-observations.md;
|
||||
2026-09-06_foundation-evidence-handoff-observations.md.
|
||||
|
||||
## Scope and migration boundary
|
||||
|
||||
This records the future platform boundary, not permission to implement federation,
|
||||
create a registry, select/install a backend or modify the frozen inspector. No
|
||||
worker is retasked. The proposed mosaic comms command is not claimed available.
|
||||
The existing approved agent-send wrapper remains the current project transport
|
||||
until an authorized replacement exists; no raw tmux workaround is introduced.
|
||||
No external platform communication, credential handling or ~/.mosaic intervention
|
||||
follows from this note. Registry semantics and transport migration need their own
|
||||
reviewed charter before activation.
|
||||
@@ -0,0 +1,416 @@
|
||||
# Foundation synthetic inspector — frozen charter candidate 3
|
||||
|
||||
Issue: #53. Author/integrator: darkwing. Feasibility contributor: rocko.
|
||||
Independent reviewer: filbert, pending exact-candidate review.
|
||||
Status: proposed implementation contract, NOT permission to implement.
|
||||
Jason authorized continuation of planning after accepting the reviewed map.
|
||||
No source changes, migration, publication, push or live fleet activity authorized.
|
||||
|
||||
## 1. Bound inputs and interpretation
|
||||
|
||||
Accepted map/handoff: commit 7345f330fc6bfae5aa1d896c78cfb7cbe62efbae.
|
||||
Source/plan baseline: d4696d09eb1b5dcf1028f30db2cd63735f51cb16.
|
||||
Map verdict: FM-FILBERT-1, SHA-256
|
||||
6b08c6fac0718d3db527cf9ffbfab49407e7b289d09782f5d1d0e26493eaabb3.
|
||||
That approval does not cover this new charter.
|
||||
|
||||
Detailed incorporated specification: sections 5–13 of
|
||||
reviews/2026-09-06_foundation-inspector-rocko-feasibility-r2.md,
|
||||
SHA-256 f2f47fcfe22dca79f10f885b83d87a2f846fdb560425a4e20705c40ce4a123e1.
|
||||
The explicit corrections/decisions below override that note. Its other sections
|
||||
are contributor rationale/history, not normative repository facts. Original r1
|
||||
and the FI-ROCKO-2 corrections remain history, not alternate specifications.
|
||||
Any ambiguity or contradiction discovered at implementation is a charter defect
|
||||
to resolve, not permission for a coder to guess or weaken refusal rules.
|
||||
|
||||
Candidate records.schema.json SHA-256:
|
||||
05774aaf6943cb69c113e39ff1c29676a2a230ca7bf665c50dbcaa8049672af6.
|
||||
Candidate check.py SHA-256:
|
||||
82564a7d3200afcdda0850a9454cac6e6cd6a76687d2162c13cf214d7eac4607.
|
||||
Both resolve under docs/plans/foundation-v1-candidate at d4696d09.
|
||||
|
||||
## 2. Deliverable and non-goals
|
||||
|
||||
One local CLI over an explicitly supplied synthetic bundle, with deterministic
|
||||
text and JSON previews of selected agent/project/workspace permission. No cwd
|
||||
inference of scope, live registry, config bootstrap, account lookup, engine launch,
|
||||
assignment mutation or real permission grant. Every result, including parser/I/O
|
||||
failures, says SYNTHETIC PREVIEW — NO LIVE EFFECTS and authentication: not-modelled.
|
||||
|
||||
Implemented operations: work.read (null target), file.read/file.change (logical
|
||||
workspace-relative target), and assignment.change as a negative/unresolved check
|
||||
only. The other 25 known catalog names return unsupported-capability, exit 2.
|
||||
Unknown names return invalid-request, exit 2. No assignment-change success path.
|
||||
No execution.stop operation is invented; the catalog contains execution.control.
|
||||
No general RPC, sandbox, authentication, command audit, stopping, durability,
|
||||
legacy adoption, OAuth refresh or extension integration is delivered.
|
||||
|
||||
Operator supplies synthetic files only. The CLI reads just that explicit input,
|
||||
never discovers credentials/config. It cannot know whether arbitrary operator-
|
||||
selected file bytes contain secrets before reading them; this is not a general
|
||||
credential-read prevention boundary. Never pass credentials or live records to it.
|
||||
Tests and agents are not authorized to access such files.
|
||||
|
||||
## 3. Input, identity and graph rules
|
||||
|
||||
Use the closed bundle/selection/registry/artifact/authorization shapes in r2 §5,
|
||||
subject to the proposal and output overrides below. All fields shown there are
|
||||
required except message. proposal is explicitly null outside assignment.change;
|
||||
assignment.change requires non-null proposal and null operation.target. The
|
||||
subjectAssignmentRef must exactly equal selection.assignmentRef; selection agent,
|
||||
project and workspace must match that assignment. Never infer a different subject.
|
||||
|
||||
Eight supported record kinds: agent-definition, project, workspace, registration,
|
||||
mission, task, assignment and decision. Other candidate kinds refuse unsupported-
|
||||
kind. Candidate record shapes remain unchanged; simulation-specific fields exist
|
||||
only in the enclosing bundle, never appended to accepted records.
|
||||
|
||||
Record references are kind/id/scope/revision, WITHOUT digest. Registry references
|
||||
include registry/id/revision/digest. Artifact references and authorization IDs
|
||||
match declared simulation inputs; their presence proves neither publication
|
||||
permission, authentication, authentic evidence nor current real-world authority.
|
||||
Decision approvals are evaluated as synthetic fixture facts, not verified signatures.
|
||||
Missing declarations refuse; no flag can turn them into actual authenticated facts.
|
||||
|
||||
Reject duplicate record identities, duplicate registry identities even with different
|
||||
digests, duplicate artifact identities (runId/artifactId) even with different digests,
|
||||
and duplicate authorization IDs. Do not choose one of ambiguous registrations:
|
||||
more than one current active registration for the same agent/scope refuses ambiguity.
|
||||
|
||||
Revision rules: complete contiguous history from 1, or a single revision-1 record.
|
||||
Structural references on every revision must resolve. Apply head/current-admission
|
||||
checks only to head records used for this calculation and their consulted chain;
|
||||
historical records do not fail merely because their then-current refs are now old.
|
||||
Explicit historical supersedes/decision subject references may name existing old
|
||||
revisions. Current selected intent/task/mission/registration/delegation/proposal
|
||||
refs must name heads; no invented predecessor, repair or fallback.
|
||||
|
||||
Cycle checks cover task.dependencies, mission.parentMissionRef and supersedes as
|
||||
separate edge relations. Before tests are counted, show that each fixture reaches
|
||||
the intended rule rather than failing an earlier shape error. Workspace ownership,
|
||||
mission parent scope and registration parent match the accepted schema/README.
|
||||
Dependencies grant no ownership/access. A selected task's same-project dependencies
|
||||
must be accepted to permit file.change; another project's dependency never lends
|
||||
permission. Broader dependency execution semantics are unsupported in this slice.
|
||||
|
||||
## 4. Mandatory policy and assignment calculation
|
||||
|
||||
Adopt r2 §7 L1–L9: enabled agent ceiling, both active registrations each bounded by
|
||||
its mandatory scope-role ceiling, required project and workspace policy, exact
|
||||
assignment/task/intent checks, applicable task/mission narrowing and an explicit
|
||||
simulation execution layer. Workspace policy never falls back to a scope role.
|
||||
Null optional restrictions mean no additional narrowing; missing required ceiling
|
||||
never means unrestricted. Empty operations/path grants mean none. Exactly one
|
||||
selected assignment contributes; no union across assignments.
|
||||
|
||||
Selected project/workspace must be active. Assignment is selected, belongs to the
|
||||
selected agent and workspace, and binds the selected task. Task is ready/active,
|
||||
with approved current plan-approval intent whose subjects include that exact task.
|
||||
Assignment intent is an approved current plan-approval, delegation or assignment-
|
||||
change decision covering the task/assignment. An unrelated approved acceptance
|
||||
or owner-checkpoint decision is not a substitute. Mission chain must be active.
|
||||
Taskless work.read/file.read skips assignment/task/mission only when assignmentRef
|
||||
is explicitly null. Supplying an invalid assignment never falls back to taskless.
|
||||
|
||||
Paths use logical workspace roots and component-prefix intersection, never raw
|
||||
string prefixes or physical filesystem traversal. Network restrictions are parsed
|
||||
and intersected but no network operation is implemented or performed.
|
||||
|
||||
## 5. Reassignment negative check — tightened requester context
|
||||
|
||||
Extend r2 §5 proposal with required requesterContexts, a closed object:
|
||||
- original: {assignmentRef, execution}
|
||||
- target: {assignmentRef, execution}
|
||||
Each assignmentRef is non-null recordRef; execution uses the same mandatory
|
||||
selection.execution shape. No extra keys. Original scope derives from the subject
|
||||
assignment; target scope derives from targetTaskRef. Both requester contexts belong
|
||||
to requesterAgentId and the corresponding scope, not the subject agent.
|
||||
|
||||
Apply L1–L9 to the requester in BOTH scopes for assignment.change, using those
|
||||
contexts. Role membership alone is insufficient; task/mission/intent and execution
|
||||
limits also narrow the request. Require original subject and target task current
|
||||
intent/status checks. Apply r2 §8 recipient/subject-bounded delegation and recorded
|
||||
change checks, with approved outcomes and exact current references. Artifact basis
|
||||
is evidence only. Delegations are direct declared simulation assumptions as specified in §10;
|
||||
this inspector cannot detect chains hidden in artifacts or omitted from the bundle.
|
||||
|
||||
No message authorizes a change. Missing delegation with a message yields the
|
||||
message-is-not-authority rule once structural validation succeeds. Other failures
|
||||
use fixed deterministic original-scope, target-scope, delegation, change, intent
|
||||
ordering. If all simulation checks pass, return unresolved/unknown-effects with
|
||||
runtime-reconciliation-required, NOT allowed. No supplied runtime observations
|
||||
field exists. Echo the original selection unchanged on every admitted proposal.
|
||||
This is a negative preview, not a real assignment engine or proof of stopping.
|
||||
|
||||
## 6. Canonicalization and precise limits
|
||||
|
||||
Adopt separately named inspector-content-digest/1 for mock registry content only:
|
||||
sorted keys, compact encoding, ASCII string domain, safe integer values, array order
|
||||
preserved. No launch fingerprint or full JCS claim. V1–V3 in r2 were independently
|
||||
recomputed and match. Use 1.5 (not 1.0) for the nonintegral-number V4 test: JavaScript
|
||||
cannot distinguish numeric 1.0 from 1 after parsing. Reject negative zero and unsafe
|
||||
integer values. Strict bundle number tokens use integer notation only: reject
|
||||
fraction/exponent forms and negative zero, bound integer tokens exactly before
|
||||
Number conversion. This prevents precision loss from hiding unsupported values.
|
||||
This lexical restriction is an inspector profile rule, not a change to candidate
|
||||
JSON Schema integer semantics; the differential record validator tests shapes
|
||||
separately from this strict-input profile.
|
||||
Non-ASCII mock content refuses unsupported-capability; candidate record and target
|
||||
paths still use the candidate Unicode/path formats. Validation tests separate schema
|
||||
validity from the stricter inspector input/canonicalization domain.
|
||||
|
||||
Bounds: file <=1 MiB; each top-level inventory <=256; arrays <=1024; nesting <=32;
|
||||
strings <=4096 UTF-8 bytes subject to stricter candidate limits. Duplicate JSON keys,
|
||||
malformed UTF-8, invalid calendar dates, forbidden path characters/segments and
|
||||
unknown fields refuse. Parser object construction must not invoke prototype setters;
|
||||
__proto__/constructor keys are ordinary unknown keys, never executable behavior.
|
||||
Never normalize an invalid path into a permitted one.
|
||||
|
||||
Linux input open: require available O_RDONLY, O_NOFOLLOW and O_NONBLOCK; open the
|
||||
final component without following its symlink, fstat regular type and bounded size,
|
||||
read within limit with an extra-byte/truncation check, close in finally. Parent-path
|
||||
containment is NOT claimed. Missing/symlink/FIFO/nonregular input refuses exit 4.
|
||||
Do not OR an undefined O_CLOEXEC constant: host Node v26.8.1 exposes none (measured).
|
||||
No child spawning or close-on-exec guarantee is claimed. The CLI itself never spawns;
|
||||
verification harness processes are separate test effects. Same-size concurrent file
|
||||
mutation is not ruled out by size checks: operator/test inputs must remain quiescent.
|
||||
|
||||
## 7. Output and exits — replaces r2 §9 shape
|
||||
|
||||
Closed result fields, emitted on every outcome:
|
||||
- disclaimer: fixed SYNTHETIC PREVIEW — NO LIVE EFFECTS
|
||||
- preview: fixed preview: no live registrations or permission grants
|
||||
- bundleVersion: 1
|
||||
- authentication: fixed not-modelled
|
||||
- declarations: fixed unverified-simulation
|
||||
- result: allowed | refused | unresolved | invalid
|
||||
- reason: allowed | invalid-request | missing-state | stale-revision |
|
||||
not-authorized | unsupported-capability | unsupported-kind | unknown-effects | io-failure
|
||||
- rule: fixed documented rule identifier or null; never input-derived prose
|
||||
- selection: null before safe structural validation, otherwise exactly the selected
|
||||
{agentId, projectId, workspaceId, assignmentRef} supplied by the caller
|
||||
- operation: null before safe validation, otherwise the validated {name, target}
|
||||
- proposal: null outside a valid assignment.change input, otherwise
|
||||
{result, reason, rule, selectedAssignmentRef}, with no allowed result
|
||||
- diagnostic: null or {byteOffset, inputPath}, each member nullable; offset only for
|
||||
parse failure, original argv path only for I/O failure, never file contents
|
||||
|
||||
No consulted inventory, global counts, unrelated identities/payloads or raw parser
|
||||
messages. Text and JSON derive from the same result. JSON is ordinary valid JSON
|
||||
with the disclaimer field, not a non-JSON preamble. Text escapes control characters
|
||||
in echoed arguments/targets so diagnostics cannot inject terminal controls.
|
||||
No time/random data. No output claims authentic publication or real grants.
|
||||
|
||||
Exits: 0 permitted permission preview; 2 malformed/structurally incoherent or
|
||||
unsupported input; 3 simulated permission refusal or unresolved admission; 4 I/O.
|
||||
Missing required JSON keys (including execution) are structural: 2 invalid-request.
|
||||
Absent referenced ordinary records are incoherent: 2 missing-state. Absent declared
|
||||
mandatory policy/artifact/authorization inputs: 3 missing-state. Existing historical
|
||||
reference used for current admission: 3 stale-revision. Cycles/duplicates: 2 invalid-
|
||||
request. These override inconsistent example exits in r2. Refusal is never repaired.
|
||||
|
||||
## 8. Physical paths, toolchain and verification
|
||||
|
||||
Proposed implementation allowlist only:
|
||||
- scripts/foundation-inspect.mjs (CLI; eventual packages/mosaic)
|
||||
- scripts/foundation/{strict-json,canonical,resolve}.mjs (pure; eventual packages/config)
|
||||
- scripts/foundation/validate-record.mjs (test-only differential bridge)
|
||||
- scripts/foundation/verify-schema.py (verification only)
|
||||
- scripts/foundation/*.test.mjs and scripts/foundation/fixtures/**
|
||||
- scripts/test-foundation.sh
|
||||
|
||||
This is not yet a code-write authorization. No root/package-manager files, packages/
|
||||
scaffolds, duplicate runtime sources, .pi/extensions, Dewey scripts, roles or config
|
||||
changes. Pure modules never import live config, filesystem/process/network APIs or
|
||||
read environment/clock. CLI handles input/output only, with no ambient config lookup.
|
||||
|
||||
Node ESM, zero new runtime dependencies. Record exact tested Node version; proposed
|
||||
minimum 22 is not evidence of testing every supported version. Host measured 26.8.1;
|
||||
container source uses node:24-bookworm-slim, not the first CLI's test environment.
|
||||
No new repository engines pin is inferred. No automatic installation.
|
||||
|
||||
Mandatory verification uses existing explicitly selected Python/jsonschema 4.26.0,
|
||||
checking pinned schema/checker hashes first. Missing dependency fails, never skips.
|
||||
The differential bridge compares field-level validation over supported candidate
|
||||
fixtures plus deterministic mutations, not invocation of check.py as a library.
|
||||
Keep record-schema verdict distinct from unsupported-kind/profile/graph refusal.
|
||||
Use calendar/path format semantics from the pinned checker, not string-only tests.
|
||||
|
||||
Unit/CLI tests cover r2 §12 with these overrides. Separate observed fixture/home/cwd/
|
||||
data-root roots from harness output. Compare before/after path/type/size/mode/owner/
|
||||
inode/content inventories; canaries and static import checks are bounded evidence,
|
||||
not a sandbox or proof of no reads. No native synchronization or live fleet tests.
|
||||
Required five repository suites and foundation author checks must remain green.
|
||||
|
||||
## 9. Acceptance and gates
|
||||
|
||||
A1. Positive read and assigned file.change permission previews with coherent records;
|
||||
deterministic text/JSON, explicit disclaimers and no effects.
|
||||
A2. Unregistered second workspace/cross-project selection refuses without fallback
|
||||
or unrelated payload disclosure.
|
||||
A3. Ownership/reference/duplicate/revision/cycle negatives hit the intended rules.
|
||||
A4. Required role ceilings and all applicable layers narrow; empty/missing authority,
|
||||
cross-assignment union and unassigned change refuse.
|
||||
A5. Encoding, duplicate keys, calendar, path, bounds and unknown-field tests refuse
|
||||
safely with the documented schema/profile distinction.
|
||||
A6. Input/observed roots unchanged; no bootstrap, ambient config/credential lookup,
|
||||
engine, network, installation or migration action.
|
||||
A7. Cross-lane proposal preserves original selection; requester limits in both
|
||||
scopes enforced; even fully supplied mock evidence cannot yield allowed.
|
||||
A8. All output/exit combinations match the closed result contract, including parse
|
||||
and I/O failure. No raw contents, unrelated metadata or terminal injection.
|
||||
A9. Jason runs the positive and negative demo and understands the limitation before
|
||||
any dependent live-registry/runtime increment begins.
|
||||
|
||||
First: Filbert independently reviews the frozen charter and incorporated note at
|
||||
exact hashes without authorship or edits. Resolve substantive findings as a new
|
||||
candidate. Then present the reviewed build scope to Jason for implementation
|
||||
approval; prior map acceptance does not supply it. After authorized implementation,
|
||||
retain separate author/reviewer roles, serialize shared index use, run all required
|
||||
checks and obtain owner demo acceptance before dependent work. No automatic push,
|
||||
issue closure, implementation or next-phase advancement follows from this charter.
|
||||
|
||||
## 10. Candidate 3 corrections — normative overrides for FI-FILBERT-2
|
||||
|
||||
These rules resolve findings 1–5 and override any remaining inconsistent shorthand
|
||||
in this charter or the incorporated note. Candidate 2 and its NOT APPROVED verdict
|
||||
are preserved separately; approval of this revision is not yet established.
|
||||
|
||||
### 10.1 Registration delegation and explicit mock issuer bounds (finding 1)
|
||||
|
||||
Add required top-level `delegationInputs`, an array of at most 256 closed objects:
|
||||
`{decisionRef, mode, issuerCeiling}`. decisionRef is a candidate recordRef of kind
|
||||
decision; mode is exactly `direct-declared`; issuerCeiling is candidate restrictions.
|
||||
No extra fields. Duplicate exact decisionRef entries are structural invalid-request,
|
||||
exit 2. This is another explicit simulation input, not an accepted record-schema
|
||||
change or a runtime authority record. Empty array is allowed when none is consulted.
|
||||
Its ceiling is an unverified stand-in for the referenced decision author's reviewed
|
||||
delegation ceiling; it proves neither authorship nor actual permission to delegate.
|
||||
|
||||
For each consulted current L2/L3 registration:
|
||||
1. Check current registration identity, active status, correct agent/owning scope
|
||||
and required parent registration. Revoked registration: exit 3 not-authorized,
|
||||
rule registration-revoked. Resolve mandatory scope-role and policy ceilings.
|
||||
2. delegationRef null explicitly models owner-origin registration under the
|
||||
declared authorization ID. That is a fixture assumption, not verified owner
|
||||
issuance. Other mandatory checks/ceilings still apply; null is not a grant.
|
||||
3. Non-null delegationRef must name a current head decision. Old decision reference:
|
||||
exit 3 stale-revision, registration-delegation-stale. Its decisionKind must be
|
||||
delegation and outcome approved; otherwise exit 3 not-authorized,
|
||||
registration-delegation-not-approved. No new revoked field is invented: changing
|
||||
the decision head to rejected blocks admission, as does using its old revision.
|
||||
4. Supported scope is exactly the registration's scope. A broader/other scope is
|
||||
exit 3 unsupported-capability, registration-delegation-scope-not-modelled.
|
||||
subjectRefs must be exactly the current agent-definition reference for that
|
||||
registration's agent and the exact current registration reference. A wrong
|
||||
recipient/registration is exit 3 not-authorized, registration-delegation-bounds;
|
||||
additional subject forms are exit 3 unsupported-capability,
|
||||
registration-delegation-subject-form. No ambiguous pairing of multiple recipients.
|
||||
5. Require the exact decision's delegationInputs entry. Missing: exit 3 missing-state,
|
||||
delegation-input-missing. Every delegatedOperation must be in issuerCeiling's
|
||||
operations; otherwise exit 3 not-authorized, delegation-exceeds-issuer-ceiling.
|
||||
6. Intersect the role ceiling, optional registration restrictions, delegated
|
||||
operation set AND the issuerCeiling restrictions (operations, paths, network and
|
||||
endpoints), then all other L1–L9 layers. No union or skipping the role ceiling.
|
||||
|
||||
Current consulted scope-role/agent-policy/project-policy references must name the
|
||||
highest declared revision of that registry/id; an older ceiling is exit 3
|
||||
stale-revision, policy-ceiling-stale. Historical records may retain exact old refs.
|
||||
This is currentness within the explicit mock inventory, never live-current proof.
|
||||
|
||||
Apply the same exact delegationInputs/issuer bound check to the proposal's explicit
|
||||
delegation after its approved kind/outcome/recipient/subject tests. Its issuer
|
||||
restrictions additionally narrow the requester's calculations in both scopes.
|
||||
No source of declared authority overrides any required role, policy or assignment.
|
||||
|
||||
Required coherent tests: owner-origin null; approved applicable direct delegation
|
||||
with file.read permitted; rejected delegation; unrelated plan-approval/acceptance;
|
||||
wrong recipient; wrong registration; unsupported scope; role allows file.change
|
||||
but delegated operations/issuer paths do not; missing issuer input; active registration
|
||||
pointing to an old approved decision whose newer head rejects it; reference updated
|
||||
to that rejected head; and revoked registration. Each must reach its named gate.
|
||||
|
||||
### 10.2 Structural ownership versus consulted work access (finding 2)
|
||||
|
||||
Structural graph validation uses EACH record's own scope, never the global selected
|
||||
project. Every task mission must belong to the task's project (project-level mission
|
||||
or a workspace mission in that project). A mismatch is exit 2 invalid-request,
|
||||
mission-owning-project-mismatch. Workspace mission parents must be project missions
|
||||
in their own project; project missions have no parent. Dependencies may structurally
|
||||
reference tasks in other scopes/projects; they confer no permission or ownership.
|
||||
Unselected P2 tasks with P2 missions remain valid when the caller selects P1.
|
||||
|
||||
For each actual calculation context (main selection, or requester original/target):
|
||||
- Consulted tasks/mission/dependency references may target the SAME workspace or
|
||||
the parent project only. A different workspace of the same project is exit 3
|
||||
unsupported-capability, cross-workspace-work-reference-not-modelled. Another
|
||||
project is exit 3 unsupported-capability, cross-project-work-reference-not-modelled.
|
||||
Apply these refusals even if that agent has another registration there. The first
|
||||
slice deliberately does not model additional cross-scope visibility contexts.
|
||||
Do not inspect that referenced work's status/restrictions for admission or silently
|
||||
omit the reference from readiness checks. Structural existence/type checks are
|
||||
distinct and operate on the supplied synthetic inventory, not live private data.
|
||||
- References within the supported relation require work.read in the caller's full
|
||||
applicable L1–L9 intersection, as well as the requested operation. Otherwise exit 3
|
||||
not-authorized, consulted-work-not-readable. The parent-project registration and
|
||||
policy are already mandatory layers, as are the acting workspace restrictions.
|
||||
The calculator may combine supplied mock restrictions to derive this intersection;
|
||||
it returns no referenced bodies and does not claim authentic private-record access.
|
||||
- Dependencies of a task used for file.change or proposal requester authority must
|
||||
be accepted. Otherwise exit 3 not-authorized, dependency-not-accepted. A reference
|
||||
rejected by the scope guard cannot be skipped because its status says accepted.
|
||||
Taskless work.read/file.read does not traverse unrelated tasks or their dependencies.
|
||||
- Apply the same reference-access rules when the proposal checks its subject/target
|
||||
work: original requester context for the subject; target requester context for
|
||||
the target. Dependency refs never become extra assignments or execution authority.
|
||||
|
||||
Tests: local workspace mission/dependency permitted with work.read; parent project
|
||||
mission permitted; work.read removed while file.change remains; W1 task referring
|
||||
to W2 mission or accepted dependency while agent registered only W1; same case even
|
||||
with W2 registration (still unsupported in this slice); cross-project dependency;
|
||||
and independent P2 task/mission inventory under a P1 selection, which must not fail
|
||||
merely because it is unselected. All outputs remain body-free.
|
||||
|
||||
### 10.3 Direct assumptions, not hidden-chain detection (finding 3)
|
||||
|
||||
The model supports only the direct-declared delegationInputs form in §10.1. There is
|
||||
NO claim to detect a chain concealed behind an artifact, a declaration or an omitted
|
||||
record. A decision subject reference to another decision is not automatically a
|
||||
parent-delegation edge. Historical subject references retain their defined meaning.
|
||||
No artifact body is read and artifact presence is not authentic authority.
|
||||
|
||||
An input adding parentDelegationRef or another undocumented field fails the closed
|
||||
shape rule: exit 2 invalid-request. A mode other than direct-declared likewise fails
|
||||
shape; it is not a successfully detected hidden chain. A valid direct declaration
|
||||
with opaque artifact pointers is checked as a declared assumption and cannot prove
|
||||
absence of chaining. Test these three distinctions explicitly. Proposal success
|
||||
remains unreachable, even when all direct simulation checks pass.
|
||||
|
||||
### 10.4 Fixed graph sub-order and fixture reachability (finding 4)
|
||||
|
||||
Global order: input bounds/lexical parsing; closed shapes/profile/supported kinds;
|
||||
identity index and duplicate checks; structural reference existence/type; cycles
|
||||
(task.dependencies, then mission.parentMissionRef, then supersedes); revision-chain
|
||||
continuity; per-record ownership/parent-scope checks; supported operation selection;
|
||||
then context binding/current-head, work-scope guards and ordered admission checks.
|
||||
Within a stage use stable kind/id/scope/revision and field ordering, not input order.
|
||||
Use the documented missing-declaration/current-admission exit exceptions in §7.
|
||||
|
||||
Schema-valid workspace mission self-parent and revision-2 self-supersedes fixtures
|
||||
must hit their cycle rule BEFORE parent-scope or predecessor-continuity refusal.
|
||||
Use separate detector unit tests and CLI-precedence tests. Never count an earlier
|
||||
schema/ownership rejection as demonstrated cycle-detector coverage. Missing required
|
||||
policy/artifact/authorization declarations are admission failures at their documented
|
||||
stage, not swallowed by the ordinary-record structural missing-reference rule.
|
||||
|
||||
### 10.5 Differential corpus and platform boundary additions (finding 5)
|
||||
|
||||
Add scalar type substitution; every null-versus-absent branch; integer min/max,
|
||||
boolean-versus-integer and numeric 1.0 schema cases; nested object-order-insensitive
|
||||
uniqueItems comparisons; BMP/non-BMP code-point length boundaries; UTF-8 byte-path
|
||||
boundaries; and escaped surrogate cases. Schema verdict and strict bundle/profile
|
||||
verdict are separate expected columns. Strict bundle strings reject unpaired escaped
|
||||
surrogates as profile-invalid; this is not an assertion about unformatted candidate
|
||||
JSON Schema strings. A passing finite corpus is compatibility evidence, not proof
|
||||
of complete schema equivalence. Actual Node versions tested must be reported.
|
||||
O_RDONLY availability checks use presence/type, not truthiness: its valid value is 0.
|
||||
@@ -0,0 +1,160 @@
|
||||
# Installation, onboarding and reconfiguration — owner requirements
|
||||
|
||||
Date: 2026-09-06. Recorded by darkwing.
|
||||
Status: captured future deployment/onboarding requirements and proposed CLI, not an
|
||||
implemented installer or permission to modify current bootstrap/config/auth behavior.
|
||||
No current install.sh implementation or compatibility is claimed.
|
||||
|
||||
## Installation experience
|
||||
|
||||
- Update the Mosaic installer for the new deployment structure and support switches
|
||||
for automation.
|
||||
- Basic or advanced installation must be selectable without an initial interactive
|
||||
"basic or advanced?" question. Define a mode switch/default in the later CLI charter;
|
||||
the owner has not specified its exact spelling or default.
|
||||
- Provide a step-by-step guide distinguishing required and optional steps. Optional
|
||||
steps offer Skip. Once required configuration is complete, allow Skip to finish
|
||||
without completing the optional sections.
|
||||
- Provide configuration mode for customization at any later time. Define resumable,
|
||||
validated reconfiguration rather than rerunning destructive installation.
|
||||
- Define unattended behavior for missing required input explicitly: report the
|
||||
missing requirements and fail, rather than unexpectedly prompt or invent defaults.
|
||||
|
||||
## Required initial configuration
|
||||
|
||||
| Area | Owner requirement |
|
||||
|---|---|
|
||||
| Deployment | Containerized or bare-metal |
|
||||
| System authentication | SSO, OIDC, LDAP or internal authentication |
|
||||
| Break-glass administrator | Always internal/basic authentication; username, password, email, independent of chosen external identity provider |
|
||||
| Internal initial user | Username, first name, password, email |
|
||||
| External-auth initial user | Initial user login through selected SSO/OIDC/LDAP path |
|
||||
| Site | Site name |
|
||||
| Instance | Name; default main |
|
||||
| Initial agent name | System-generated or user-selected |
|
||||
| Agent gender | male, female, neutral |
|
||||
| Agent personality | Compatible traits selected with multiple-choice checkboxes; examples technical, professional, friendly, flirty |
|
||||
| Agent communication style | Compatible traits selected with multiple-choice checkboxes; examples direct, teacher, terse, supportive |
|
||||
| Default harness | Mosaic/Pi recommended; Claude or Codex alternatives |
|
||||
| Initial harness account | OAuth or API account configuration for Claude, Codex or Pi; Pi provider choices include OpenRouter, Z.ai and others |
|
||||
| First project | Name; default general |
|
||||
| First workspace | Name; default chat |
|
||||
| Routing | System-selected or user-selected task routing/agent mapping |
|
||||
|
||||
System login authentication and model-provider/harness account authentication are
|
||||
separate configuration domains. Agent personality/gender/style do not grant roles,
|
||||
permissions or access. Define trait compatibility rather than silently combining
|
||||
contradictory choices. Register scoped entities through the future Mosaic registry,
|
||||
not through filename inference or ad-hoc tmux identities.
|
||||
|
||||
## Optional initial configuration
|
||||
|
||||
System/use-case presets, potentially combined subject to a compatibility model:
|
||||
- Software factory
|
||||
- Personal assistant
|
||||
- Executive assistant
|
||||
- Enterprise / personal
|
||||
- Journal
|
||||
- Health tracker
|
||||
- Writing assistant
|
||||
- Social-media management
|
||||
- Business operations
|
||||
- Job-application tracker
|
||||
- Additional future presets
|
||||
|
||||
The owner also specifies multi-user by default; do not interpret basic mode or a
|
||||
personal preset as permission to remove identity separation or access controls.
|
||||
|
||||
Advanced user profile:
|
||||
- Last name, gender, profession, hobbies, skills
|
||||
- Food preferences
|
||||
- Family, marital status, children, pets
|
||||
- Automobiles and work history
|
||||
- Writing-style capture for the user's communication voice
|
||||
- Social-media account linking
|
||||
- Gmail / Google-services linking
|
||||
- Plugin add-ons, system skills and system extensions
|
||||
|
||||
Other optional setup:
|
||||
- Custom agent-to-task mapping
|
||||
- Additional agent registration
|
||||
- Additional user registration
|
||||
|
||||
Optional profile/account data is opt-in, classified and access-controlled. Sensitive
|
||||
health/family/work data must not automatically become globally shared agent context.
|
||||
Account linking needs explicit scopes/consent; selecting a preset is not permission
|
||||
to send messages, post publicly or take other external actions.
|
||||
|
||||
## Proposed CLI — owner example preserved
|
||||
|
||||
```sh
|
||||
./install.sh \
|
||||
--type container \
|
||||
--site homelab \
|
||||
--instance main \
|
||||
--auth internal \
|
||||
--admin_user <username> \
|
||||
--admin_pass <password> \
|
||||
--admin_email <email> \
|
||||
--user_username <username> \
|
||||
--user_name <name> \
|
||||
--user_pass <password> \
|
||||
--user_email <email> \
|
||||
--agent_name {-g|<name>} \
|
||||
--agent_gender {male|female|neutral} \
|
||||
--agent_persona {technical|professional|friendly|flirty|...} \
|
||||
--agent_comms {direct|teacher|terse|supportive|...} \
|
||||
--agent_harness {mosaic|pi|codex|claude} \
|
||||
--agent_auth_name <registry_name> \
|
||||
--agent_auth_type {oauth|api} \
|
||||
--agent_api_prov {openrouter|zai|...} \
|
||||
--agent_api_key <key> \
|
||||
--project general \
|
||||
--workspace chat \
|
||||
--routing {system|<custom_routing>}
|
||||
```
|
||||
|
||||
This is proposed syntax with placeholders, not a runnable verified command. Exact
|
||||
bare-metal spelling, mode switch, generated-name option, repeatable trait syntax,
|
||||
routing input format and reconfiguration command remain to define. Clarify whether
|
||||
mosaic and pi are aliases or distinct public harness identifiers.
|
||||
|
||||
## Security, durability and workflow design obligations
|
||||
|
||||
1. Revise raw --admin_pass/--user_pass/--agent_api_key inputs before implementation:
|
||||
literal argv secrets can leak via shell history, process inspection and logs.
|
||||
Prefer protected file-descriptor/stdin or permission-checked secret-file inputs,
|
||||
masked interactive entry and supported secret-manager references. Never echo
|
||||
secrets, put them in ordinary config, Git, images, evidence or diagnostic receipts.
|
||||
2. Break-glass authentication must remain available through an identity-provider
|
||||
outage without being an unrestricted bypass. Specify protected password storage,
|
||||
account recovery/rotation, rate limiting, audit, privilege boundaries and testing.
|
||||
Clarify coexistence of the break-glass account and the everyday initial user.
|
||||
3. External user login and OAuth may require interactive consent; automation must
|
||||
support provider-authorized preconfiguration or explicitly pause at that gate.
|
||||
Do not claim a usable account before its required capability checks succeed.
|
||||
4. Publish resumable installation/reconfiguration checkpoints with secrets excluded.
|
||||
Record required-step completion, optional skips and recovery state. Restarting
|
||||
setup must not duplicate users/agents/projects or lose already configured work.
|
||||
5. Preserve fail-closed configuration and immutable evidence. Current repository
|
||||
canon permits system config creation only through scripts/bootstrap.sh and no
|
||||
overwrite. A future installer/reconfiguration authority model needs an explicit
|
||||
reviewed migration design; this note does not override that rule.
|
||||
6. Installation should establish the registered site/instance/project/workspace/agent
|
||||
bindings and supported routing policies. UI presets cannot widen reviewed role
|
||||
ceilings, bypass assignment requirements or silently retask existing agents.
|
||||
7. Compare bare-metal/container dependency, storage, service ownership, networking,
|
||||
upgrades and recovery workflows explicitly. Pin dependencies; never improvise
|
||||
unattended privilege escalation or installation after a failed precondition.
|
||||
8. Test basic/advanced and unattended modes; each auth/harness branch; external login
|
||||
failure; provider outage; missing required inputs; optional skips; interruption/
|
||||
resume; repeated install; protected secret handling; and later configuration.
|
||||
|
||||
## Scope boundary and related work
|
||||
|
||||
Related: foundation federation/comms, mechanical-workflow, durability and evidence-
|
||||
handoff topic notes dated 2026-09-06; accepted map at 7345f33; #50 auth/provider work.
|
||||
No installer, config mutation, account creation/linking, transport migration, package
|
||||
installation or external-deployment investigation follows from this capture.
|
||||
The current inspector and its schema/profile review stay unchanged. No worker is
|
||||
retasked. These requirements need a later deployment/onboarding charter and review.
|
||||
@@ -0,0 +1,136 @@
|
||||
# Foundation map handoff — MAP-HANDOFF-2
|
||||
|
||||
Status: commit-pinned source/plan baseline; ready for owner review and a separately
|
||||
approved non-author review. No implementation or push authorized.
|
||||
|
||||
## Baseline
|
||||
|
||||
Source/plan commit: `d4696d09eb1b5dcf1028f30db2cd63735f51cb16`.
|
||||
Foundation parent: `44f257cb06484feda3412d9382e3587393796353`.
|
||||
Mapping revision: the separate commit containing this document and the
|
||||
[technical map](2026-09-06_foundation-technical-map.md).
|
||||
|
||||
All file:line source citations in the technical map refer to the source/plan
|
||||
commit above unless explicitly historical. The map/handoff are not claimed to
|
||||
exist at that earlier commit. MAP-HANDOFF-1's provisional baseline and delivery
|
||||
waits were superseded after Jason authorized local commits and Dewey supplied
|
||||
MS55-DW-3. Shared BUILD-LOG/SESSIONS retain the chronological receipts.
|
||||
|
||||
## What is ready
|
||||
|
||||
- All R1-R34 mapped to responsibilities; 20 source-linked findings and nine
|
||||
inspected legacy source files, unchanged from 69d1bb3.
|
||||
- packages/* succession target reconciled with current extensions/** source and
|
||||
generated .pi installation. No source move or package-manager change proposed now.
|
||||
- Dewey's shim, single adapter ownership and package-versus-process-privilege
|
||||
qualifications adopted in the technical map.
|
||||
- One small increment: read-only synthetic scope/permission inspector, seven core
|
||||
acceptance cases plus the owner-reported cross-lane retasking negative scenario.
|
||||
No live registration, authentication, sandbox or runtime guarantee is claimed.
|
||||
|
||||
Jason's ~/.mosaic incident report is context only. No investigation/intervention
|
||||
there occurred or is authorized. A coordinator message must not itself reassign
|
||||
an agent, redefine its role or displace an owner-authorized goal.
|
||||
|
||||
## Collaboration and verification boundary
|
||||
|
||||
MS55-DW-1/2 coordination is complete. MS55-DW-3 reports Dewey's 43-path baseline
|
||||
commit and index release; parent, path allowlist and empty index were independently
|
||||
checked locally before staging mapping work. Dewey's native extension test/review
|
||||
receipts establish that separate baseline, not managed-foundation acceptance.
|
||||
No mapping review has yet been dispatched or approved by a non-author.
|
||||
|
||||
## Non-author review checklist, when authorized
|
||||
|
||||
1. Resolve the mapping revision and source/plan baseline independently.
|
||||
2. Open every cited source location and validate all R1-R34 mappings, not just counts.
|
||||
3. Challenge reuse/new classifications and the limited inspected inventory.
|
||||
4. Check packages/* sequencing, single source ownership and trust-boundary separation.
|
||||
5. Examine broad mounts, shared context, authorization gaps, uncertain retry and
|
||||
deletion-before-receipt findings and their dependency ordering.
|
||||
6. Confirm the inspector cannot imply real permission grants or sandbox proof.
|
||||
7. Return independent findings; do not substitute author fixtures or #55 native
|
||||
acceptance for runtime enforcement tests.
|
||||
|
||||
## Remaining gate
|
||||
|
||||
Owner review and separate authorization for non-author review. Implementation,
|
||||
source moves, migration, push and issue closure remain outside this handoff.
|
||||
|
||||
## Source/plan SHA-256 inventory at d4696d09
|
||||
|
||||
Every listed file was compared byte-for-byte against the named commit. This
|
||||
inventory excludes credentials, generated installations and runtime state. The
|
||||
mapping documents are versioned by their own containing commit, not this input list.
|
||||
|
||||
```text
|
||||
d4c4e08f56c299106133b57e58a78ddfcb015f131036d243d54f42e0fbdc741b .pi/.gitignore
|
||||
ea5856b5d93811b43b1c1f278eab3283f7f909396dd74464f49dfe1717b51e0e .pi/README.md
|
||||
3e4498c8066d5a796a8cfe6e07fc1aad118dac2827c771d954b4ae0c38dae84c .pi/SOURCE-SNAPSHOT.json
|
||||
73823786b54acae627ddefe4b8a258f0554330e49213a118d32e1ffe59c40415 .pi/goal-dev.sh
|
||||
d213c167d319dbbb42326f68c9c76ec01dbdd42e8f4f226d3232cc5b355bfebc adapters/pi/adapter.sh
|
||||
a21dc87079d255261ae7318845073ec06bf9df7bac6874012ea0c84b0d1ec12d compose.yaml
|
||||
ce58408289fceea4b99d8a77c69523a1c04e683f5084f31c7c4199ae0b9934f3 docs/plans/2026-09-06_agent-project-workspace-foundation.md
|
||||
a49533e1a06e7610583141e7dbf5f529569b9fb3765bf0f4742ff287905f4dfb docs/plans/2026-09-06_foundation-phase2-contract.md
|
||||
6e2a6b4e0d323940ee5139f5b01f3ecedb23522b9cfa7674fc3d817508cb152f docs/plans/2026-09-06_monorepo-source-layout.md
|
||||
b746e8c9963dce430b83a7a5fd0973b789a753cc5edd8288291ecdc8f906b2af docs/plans/2026-09-06_ng-goal-footer-dev.md
|
||||
6e854e83f2a2b26cb93473611b0a47264fb64dd08b6c272c6b18257238915c82 docs/plans/2026-09-06_workspace-schema-and-audit.md
|
||||
9e0a95865f9479c5f2d06c485513326891727e5295b62c085efe41dddd7dbc0a docs/plans/ROADMAP.md
|
||||
27fd60f68d30ddc8c1a0cef96714308b8e526f60a17852f7ec21957c1870ec81 docs/plans/foundation-v1-candidate/README.md
|
||||
7883fde367a3c4377585a5d2f74f09e8f662651b479abb652779c3ed6c7a4c32 docs/plans/foundation-v1-candidate/REVIEW.md
|
||||
b1a2b4d0df88ba6f7b197252807f3a3925ffff9375f4e70d4ff28593337c3438 docs/plans/foundation-v1-candidate/RUNTIME.md
|
||||
82564a7d3200afcdda0850a9454cac6e6cd6a76687d2162c13cf214d7eac4607 docs/plans/foundation-v1-candidate/check.py
|
||||
7806e42cd792935ddba1c8bcac853f049d79eab227fb7191fe622685e699203a docs/plans/foundation-v1-candidate/command-events.fixtures.json
|
||||
19e9e50359790ec9a4de5b8c817026ac075e254968de1411867222646743d31f docs/plans/foundation-v1-candidate/command-events.schema.json
|
||||
cbfcb88531838c8c3dd290257e5d9a657e552bb7dd0f67102b49a921a249da45 docs/plans/foundation-v1-candidate/fingerprint-vectors.json
|
||||
d433d06da5cd38baf9e51c8857244ee70375db3b68e02a5325a6d1c2cc47da85 docs/plans/foundation-v1-candidate/records.fixtures.json
|
||||
05774aaf6943cb69c113e39ff1c29676a2a230ca7bf665c50dbcaa8049672af6 docs/plans/foundation-v1-candidate/records.schema.json
|
||||
02a611925923b2592d9e0e67741a542e6c2e8bec06d69ae5700f25c51d720a7a docs/plans/foundation-v1-candidate/runtime.fixtures.json
|
||||
74deeb4cd6d87ff9306ed088b9641e51f9c41db71589b5364424908842ee51bc docs/plans/foundation-v1-candidate/runtime.schema.json
|
||||
93d16f38738bb0610d5250ee54c271934e4f2201660becaa72982363d0711fda docs/plans/foundation-v1-candidate/semantic-model.fixtures.json
|
||||
c89c3bb19624826c1a84658595c71694a1fd371b9155ef50ffa4c265c099daa8 docs/plans/foundation-v1-candidate/semantic-model.py
|
||||
1551ee0bb11d4a16181186f5091ddbcfcb0ea1e4718997639542beb797442308 extensions/README.md
|
||||
0850b651309b57a50f2933182376531c8bbcc75ab92d83c1798dfd1637fe8b8b extensions/goal/README.md
|
||||
9bfcf60097ec83209f5acae443ce48dde55d39343426de63f89997e3275e67ed extensions/goal/index.ts
|
||||
5db32424e7376f85f22b1055addf3784125da09c3ea23b86a3d0d338f852babd extensions/goal/lib/display.ts
|
||||
57f1d89c998099b2a9b0c860e70a35db61261a5d1ae7f07cc9acff29fe21042e extensions/goal/lib/executive-update.ts
|
||||
3837d31fad6481a3c69132ec7fb19849498d5d4c90b0e41eeb683daec19b50a1 extensions/goal/lib/parse.ts
|
||||
6614b228b3d10252751bc4b68f62ada58f04dfb3a77a51086bb2d7ae42f39c8b extensions/goal/lib/settle.ts
|
||||
ade39fa3c7a99295712dac41fdfe366384e2cfd695a967c62f0e3f424876ffae extensions/goal/lib/state.ts
|
||||
78185bb61cb85ae8f2940a89e18063a1d90361d8a869582fff6f4787ec7430dd extensions/goal/lib/store.ts
|
||||
cad6670a9e8206166b710cd464347aedee6401be9307248b10f9fcfc295585f0 extensions/goal/test/communication-closeout-contract.test.ts
|
||||
8b28e3c63607dfc321e6b74cd967e92ae6f72ed3e31e2286fcf9a5964394bb02 extensions/goal/test/display.test.ts
|
||||
66d9437111c4574ea255affe539b78c6ad896d7f43de191d154873b9ef500501 extensions/goal/test/executive-update.test.ts
|
||||
dfb172ddd228205d3e72a47dfe11372b9cc39e4584017461efe037aa3dc8c08a extensions/goal/test/fencing.test.ts
|
||||
ac5a69536ee07cc31c6f3a738ca2d5c446689bbfdae05dafffd9c6302864b96c extensions/goal/test/fixtures/.gitattributes
|
||||
bbea48a46b1f8da7bc759f86856fb52830b7dde456b826317163c6dc6ccab319 extensions/goal/test/fixtures/skills-local/ms-executive-update/SKILL.md
|
||||
c797776e992c4b1187d786ccb0cce57ca817ebd91ec56caebd4bc220d660ff9b extensions/goal/test/fixtures/skills-local/ms-honesty/SKILL.md
|
||||
0132ad6508df2bcba0df7fb417e06cbe90c7ea663e334f2176c8973757bb4809 extensions/goal/test/fixtures/skills-local/ms-proactive-agent/SKILL.md
|
||||
be377aa1e3128efddc00c651771e145acc6cb9d5c6f2599260d1e00ef617ac7d extensions/goal/test/goal.test.ts
|
||||
3a03b44aea1a9f316ee1138092e3cb55507cdff34ca2cbf828d45ed90a46b699 extensions/goal/test/progress.test.ts
|
||||
2a33f36881d38656e26ca7580907cf2b22df69d94243ebdfee7adef140d77040 extensions/goal/test/quiet-wait.test.ts
|
||||
38df8499a64dd465ff5011b06fce80c0793bb53eb7c33bb685001e56451647f3 extensions/goal/test/runtime.test.ts
|
||||
ea7e9e86782a8716a5e33a27d653227d162c595ba4aa02d1ee691fd4ddead081 extensions/mosaic-core/lib/adapter.ts
|
||||
4604ae28cd16966d0e447a4e8075bbe320c3119d0a4c8fe870b98e2bb4fb6d03 extensions/mosaic-core/lib/enforce.ts
|
||||
f291b11818ba01567c1f42bbd36fae63a2398a347426d863ecc6bdb7820358c7 extensions/mosaic-core/lib/gate-record.ts
|
||||
8313837181a1ac07e7ca58f99873e66b305cd5b5e8d125c189b14d231bdc4620 extensions/mosaic-core/lib/goal-policy.ts
|
||||
cea0165ab9b323c083ce2fcb1d9fa835e1ee534045cd67536aec17c339457d45 extensions/mosaic-core/lib/incarnation.ts
|
||||
b52a129c97b822209acdf96ddae49c970bbb9a4dd74ac5bbe7440418e9cb2af1 extensions/mosaic-core/lib/journal.ts
|
||||
b0c12f2cb9e974ad72d28773617a862e5e880612d63d1edc513c4e057d6f21d9 extensions/mosaic-core/lib/loader.ts
|
||||
aabc1046dca38f03cfbe111259a21521b24b250736be42f548a57a0e315cf70b extensions/mosaic-core/lib/policy.ts
|
||||
f174864dc499d0a39e7786e1220c54df3e9af2cd8523476c3d4bf2e2d5928264 extensions/mosaic-core/lib/proposal.ts
|
||||
00448f6f00f72163bcde3e0a6fc9b87023c46a225316df7530bc14d9953b5e12 extensions/mosaic-core/lib/reconcile.ts
|
||||
1ad2270a02e5668ef126c2af71a7b70771c2da09842313bdbf6abba84e95b555 scripts/agent.sh
|
||||
fe5d3e89272d3b04db687eabed30a95dde480a2f7bc784cd27e43fa9321f15a6 scripts/auth.sh
|
||||
a8a41274c06ab6fe38c42d1e96db115b8d65b03a87ed1796c4d37288258a1d12 scripts/goal-dev.sh
|
||||
430ee6bc4fcfbe4b9ac030aaa19cdb6fdc7407e1b17253b80178b9b0523b5a3a scripts/mosaic-config.mjs
|
||||
525bab31453ab84afa379421c619405632b9c85d639e2f4a6188dcf7ae825b83 scripts/mosaic-task.mjs
|
||||
957ef76f949c2eb2e472182261bf2d1619e0cde44c506ab2bbb5c25dc063864d scripts/reset.sh
|
||||
eee8c0a9c5708cbc9d0922ea733f09b34d50da8816f95a43e5afe3e3dfc24dbb scripts/sync-dev-extensions.sh
|
||||
5d7b687f16fd1a9414b229501177d530ba3af4eadd3cac319b33f38fdba9bf99 scripts/test-extension-package.sh
|
||||
4c5db63da5194a937ee4a488b796f60876a780482812d949e3337bf0d274a974 scripts/test-goal-native.py
|
||||
4b210d5d785d06d699ceaa902ccb0451c09cb19397e861521f825c09e9e33ae5 src/load-contracts.sh
|
||||
6749ebe3d0433b3dfefb40a44d58c4ca2606ab9f4ce01457dd52767059b7b13b src/run-agent.sh
|
||||
ece76e2690012b53087357d467edf3f2c3f872d5db28bd97eaa1bbb217f5ed57 tasks/ng-goal-footer-implementation.json
|
||||
d50fd5dbbd08a39b2a91424392d2b31bc2f9b4a1e3c3d09ee8061d5776989459 tasks/ng-goal-footer-review.json
|
||||
```
|
||||
@@ -0,0 +1,63 @@
|
||||
# Mechanical coordination and recovery — owner discussion backlog
|
||||
|
||||
Date: 2026-09-06. Recorded by darkwing.
|
||||
Status: captured owner direction and open design questions, not an implementation
|
||||
charter, product selection or authorization to change the current inspector build.
|
||||
|
||||
Jason wants a continuously operating software factory with minimal user babysitting.
|
||||
Mechanical status checks, action timeouts, stalled-session detection and other
|
||||
failure indicators should support bounded recovery and escalation: restart an
|
||||
orchestrator when appropriate, escalate agent stalls to an orchestrator, and involve
|
||||
the user only when remediation requires owner authority or cannot safely proceed.
|
||||
These workflows need attention during low-level development, not as a late UI layer.
|
||||
|
||||
## Questions to resolve in later design
|
||||
|
||||
1. n8n versus a custom mechanical workflow implementation: compare durable execution,
|
||||
restart recovery, authority boundaries, idempotency/reconciliation, operational
|
||||
complexity, observability and maintenance. No technology has been selected.
|
||||
2. Kanban triggering: identify the authoritative work-state transition and component
|
||||
allowed to admit/trigger work. Boards should project the same recorded work truth;
|
||||
UI events, duplicate notifications and card movement cannot independently grant
|
||||
execution authority or create competing task state.
|
||||
3. Stall/failure definition: distinguish quiet but healthy work, long-running actions,
|
||||
missing telemetry, blocked external dependencies, lost controller connections,
|
||||
provider failures and actual dead processes. Define observations, thresholds,
|
||||
ownership and false-positive controls for each state.
|
||||
4. Continuity without state loss: bind recovery to durable goals, assignments,
|
||||
action/WAL evidence and exact session/execution identities, not chat memory alone.
|
||||
5. Recovery ladder: specify which checks, retries, restarts, Resume/Fresh operations
|
||||
and escalations are preauthorized, by whom, within which scope and attempt limit.
|
||||
Preserve other valid work and avoid restart storms or recovery loops.
|
||||
6. Minimal owner remediation: distinguish routine recoverable faults from genuine
|
||||
authority/safety decisions. Escalations should include diagnosis, attempted bounded
|
||||
recovery, preserved evidence and a small actionable choice—not an unexplained
|
||||
request for the user to investigate the whole system.
|
||||
|
||||
## Constraints to preserve
|
||||
|
||||
- A timeout or missing heartbeat is a signal, not proof of process death or absence
|
||||
of effects. Fence stale controllers/workers and reconcile uncertain effects before
|
||||
replacement; no blind replay or duplicate active execution.
|
||||
- Restarting an orchestrator must not lose its outstanding obligations or confer
|
||||
extra permissions. Mechanical coordination and model-based orchestration need
|
||||
explicit ownership and independent recovery paths; neither is an unlimited role.
|
||||
- Resume/Fresh may improve recoverability, but neither alone proves safe recovery.
|
||||
Resume needs exact identity/history checks. Fresh must rebuild authorized work
|
||||
context from durable records without silently importing old chat or abandoning work.
|
||||
- Automatic recovery remains bounded by current intent, permissions, audit availability
|
||||
and the accepted fail-safe stopping exception. Minimal babysitting is not permission
|
||||
to guess around a safety refusal or silently change owner goals.
|
||||
- Test orchestrator/worker crashes, delayed/lost/duplicate events, external outages,
|
||||
restart during uncertain actions, sustained load and escalation/recovery loops.
|
||||
Demonstrate both continued authorized work and preserved state/evidence.
|
||||
|
||||
Related: 2026-09-06_foundation-durability-observations.md and the accepted technical
|
||||
map at 7345f330fc6bfae5aa1d896c78cfb7cbe62efbae.
|
||||
|
||||
## Current work boundary
|
||||
|
||||
This note preserves ideas for later discussion and workflow planning. It does not
|
||||
select n8n, implement a daemon/watch, arm a timer, restart anything, investigate the
|
||||
separate ~/.mosaic environment or retask Rocko/Filbert. FI-ROCKO-3 remains the frozen
|
||||
offline synthetic inspector build. No new phase or independent goal loop is created.
|
||||
@@ -0,0 +1,386 @@
|
||||
# Foundation phase 2: record and operation contract candidate
|
||||
|
||||
Status: phase-2 planning baseline accepted by Jason on 2026-09-06 ("accept phase 2"). Not approved for mapping
|
||||
or implementation. Owner authorized this phase on 2026-09-06 after confirming
|
||||
the behavior summary. Tracking: `mosaicstack/stack-v2#53`.
|
||||
|
||||
Inputs: [agreed behavior R1-R33](2026-09-06_agent-project-workspace-foundation.md)
|
||||
and [decision/interview record](2026-09-06_workspace-schema-and-audit.md).
|
||||
This file proposes common types and operation rules. The linked
|
||||
[Schema and semantic-rule package](foundation-v1-candidate/README.md) now contains
|
||||
checked command/event and eleven-kind record schemas, permission bundles,
|
||||
reference/lifecycle/storage rules, hash vectors, and #50 compatibility notes.
|
||||
The [runtime contract](foundation-v1-candidate/RUNTIME.md) and runtime schema now
|
||||
cover mediated execution/control, requests, messages and artifact bodies after
|
||||
Q28 A. The [owner-review package](foundation-v1-candidate/REVIEW.md) reconciles
|
||||
D1-D16 and lists unproved implementation gates. None of this is implemented runtime behavior. No new owner requirement is inferred from a technical proposal.
|
||||
|
||||
## 1. Evidence boundary
|
||||
|
||||
Repository baseline: `69d1bb3aa4b826218aa4cca3710f2d98c0b9d7ba`.
|
||||
The inspected source files have no working-tree difference from that commit.
|
||||
Existing runtime image: `mosaic-poc-agent:0.84.4-r0.0.12`, image ID
|
||||
`sha256:a72aa79f98e54c3c974f5ad08b82e1643a4aa6eff259ac891b738120ba963ca1`.
|
||||
|
||||
A tool-free worker examined supplied, line-numbered source excerpts through
|
||||
`scripts/run-task.sh`. Task specification:
|
||||
[`tasks/foundation-phase2-source-facts.json`](../../tasks/foundation-phase2-source-facts.json).
|
||||
Run: `r-20260906T024609Z-68ee7f`, under `<dataRoot>/runs/`.
|
||||
`result.json` SHA-256:
|
||||
`e81ac68a369a9315d827e6fc7117033f794ab7b214d2610ca07081a668045875`.
|
||||
The run reports success, exit 0, no tools or persistent session, and 1490
|
||||
whitespace-delimited response words. This is a source-analysis response, not
|
||||
an independent review verdict or a runtime test of the proposed controls.
|
||||
|
||||
Author checks qualify the worker's suggestions:
|
||||
|
||||
- One supplied excerpt header says `adapters/pi/adapter.sh:65-106`, although
|
||||
the file ends at line 96. The supplied numbered text and worker's executable
|
||||
citations end at 96. Preserve that input as recorded; no lines 97-106 exist.
|
||||
- A recorded stdout response is not a trusted account of arbitrary side effects.
|
||||
- The exclusive active-work key must include agent/project/workspace, not a
|
||||
session ID that lets two different Fresh sessions evade the same-scope limit.
|
||||
- A session transcript grows. Verify identity, supported format, and accepted
|
||||
checkpoints; do not mistake its changing content hash for corruption.
|
||||
- `--no-extensions` disables discovery, not explicitly supplied `-e` extensions.
|
||||
The current adapter supplies no `-e`; future reviewed extensions are possible,
|
||||
but their enforcement and failure behavior need investigation.
|
||||
- Command receipts and complete internal file/network tracing are different
|
||||
guarantees. The worker's suggestions do not settle their required granularity.
|
||||
|
||||
### 1.1 Pinned Pi documentation
|
||||
|
||||
Host Pi is 0.85.1. Its documentation was not used as proof of 0.84.4 behavior.
|
||||
The author copied the 0.84.4 package's README, package metadata, docs, and
|
||||
examples from the existing image using `docker create` and `docker cp`.
|
||||
The extraction container was never started and was removed afterward. No
|
||||
credentials, runtime sessions, or user-context files were copied. Temporary
|
||||
extraction: `/tmp/mosaic-foundation-pi-0844.poVO9d`.
|
||||
|
||||
To reproduce, create an unstarted container from the exact image ID above and
|
||||
copy the named files under
|
||||
`/opt/app/node_modules/@earendil-works/pi-coding-agent/`. Do not substitute a
|
||||
newly installed host version. The temporary directory is not canonical evidence;
|
||||
the image ID, file paths, and hashes identify the documentation used.
|
||||
|
||||
| Package file | SHA-256 |
|
||||
|---|---|
|
||||
| `package.json` | `db9fead11bd2ddf7a327d2c2d11b535f30d059241c251d376837d5ab638a5576` |
|
||||
| `README.md` | `f2ffe4e825ea457ecb728cabbb15b545655cda2e3bab9b5b1fc28b950a3abd08` |
|
||||
| `docs/rpc.md` | `010811d3526435b4fff5b03ee3a40f5bcb47fa17d5034fbd6b598955f56020d7` |
|
||||
| `docs/json.md` | `094b8d1b25aebd949334b695b18e8e7e73d934b82337439af12f6936d73aca84` |
|
||||
| `docs/session-format.md` | `c0f25475ab08fb96e244e6bf433aeaa41b69bfb5a52de5c7b01b81c900a84a54` |
|
||||
| `docs/security.md` | `5df4b27e7b0db49f50807f0e5d8a77e6dc099c76adab6edf21c5b705ede6efab` |
|
||||
| `docs/containerization.md` | `29ac379f744052adf301e744559cb43d0e6e84d1b04f4757f1710680991be10b` |
|
||||
|
||||
README and these five documentation files were read completely. They establish
|
||||
documented capabilities and cautions, not measured implementation behavior.
|
||||
No SDK, extension, RPC client, or isolation mechanism was implemented or tested.
|
||||
|
||||
### 1.2 Findings that constrain the contract
|
||||
|
||||
| Evidence | Supported statement | Design consequence, still proposed |
|
||||
|---|---|---|
|
||||
| `scripts/agent.sh:132-138`; `adapters/pi/adapter.sh:29-44` | Session default is per agent; nonempty session directory causes `-c`. | Replace latest-file inference with a scoped conversation index and exact session binding. |
|
||||
| Pinned README, Session options; `docs/session-format.md:386-438` | Pi documents exact session selection and session-manager APIs. | Mosaic can investigate using an exact validated path; no need to expose partial UUID search to clients. |
|
||||
| Pinned `docs/session-format.md:19-27` | Older session formats are automatically migrated when loaded. | Preserve a legacy source before any reviewed adoption/conversion; do not load original historical files merely to inspect them. |
|
||||
| `scripts/agent.sh:121-123,181-185`; `src/run-agent.sh:37-41`; `src/load-contracts.sh:40-41,97` | SOUL, mission, and generated prompt locations are shared by agent or data root. | Resolve stable execution-specific inputs and avoid shared temporary-file names. No race reproduction is claimed. |
|
||||
| `scripts/agent.sh:144-162`; `scripts/mosaic-task.mjs:364-378` | Interactive path intersects role tools; headless path intersects mission/task tools. | One future authorization resolver must apply all applicable ceilings on both paths. Current checks are not a project/workspace RBAC system. |
|
||||
| `compose.yaml:39-43`; pinned `docs/security.md:31-37` | Broad data-root mount; Pi explicitly has no built-in sandbox. | Tool names and working directory cannot establish cross-workspace privacy or evidence protection. |
|
||||
| `scripts/mosaic-task.mjs:295-302,408-466` | Exclusive-create snapshots/results and Compose process outcome, not a complete trusted action ledger. | Separate invocation outcome, external effect, acceptance, and confirmed execution termination. |
|
||||
| Pinned `docs/rpc.md:43-80,905-911,1012-1060` | Prompt acceptance, settled processing, and tool events are documented and distinct. | Acceptance of an RPC prompt is not task acceptance; events alone are not durable pre-action authorization. |
|
||||
| Pinned `docs/rpc.md:124-184,479-550,620-642` | Abort, queue clearing, direct bash, and session switching are separate commands. A session switch can be cancelled while the response says success. | Do not pass arbitrary raw RPC commands through a Mosaic connection. Do not equate abort acknowledgement or idle processing with process death. |
|
||||
| `adapters/pi/adapter.sh:81-96` | Mosaic currently runs plain print or native interactive mode, with no explicit extension or RPC invocation. | Documented Pi integration options are not already implemented Mosaic attachment, control-transfer, or audit guarantees. |
|
||||
|
||||
## 2. Proposed common record types
|
||||
|
||||
Plain-language rule: an ID names an object, a revision names the exact approved
|
||||
copy, and a scope names where it belongs. A display name is only a label.
|
||||
These proposed types are shared across records; they are not fields accepted
|
||||
by today's task runner.
|
||||
|
||||
| Type | Exact proposed representation and validation |
|
||||
|---|---|
|
||||
| `Id` | String matching `^[a-z0-9][a-z0-9._-]{0,63}$`. New runtime IDs use lowercase UUID v4; reject collisions with exclusive allocation. Existing definition IDs require explicit resolution, not a guessed alias. |
|
||||
| `Revision` | Integer from 1 through 9007199254740991. A create request expects revision 0 to mean absent; 0 is never a persisted revision. |
|
||||
| `UtcTime` | A valid UTC instant encoded as `YYYY-MM-DDTHH:mm:ss.sssZ`. Validate calendar/time values as well as shape. Wall-clock time is not proof of causal order. |
|
||||
| `Digest` | String `sha256:` followed by exactly 64 lowercase hexadecimal characters. A digest detects content change; it is not a signature or access grant. |
|
||||
| `Scope` | Discriminated object: `{kind:"system"}`, `{kind:"project",projectId:Id}`, or `{kind:"workspace",projectId:Id,workspaceId:Id}`. No omitted or extra fields; workspace ownership must match its project. |
|
||||
| `RecordRef` | `{kind:RecordKind,id:Id,scope:Scope,revision:Revision}`. Resolve exactly; a reference never means latest or grants access. `RecordKind` is the closed catalog in section 3. |
|
||||
| `ActorRef` | `{kind:"human"|"service"|"agent",principalId:Id,executionId:Id|null}`. An agent requires its authenticated execution ID; human/service actors use null. Producer supplies this from verified invocation context, never from a caller's identity claim. |
|
||||
| `FileTarget` | `{workspace:Scope,relativePath:string}` where scope must be workspace kind. Nonempty slash-separated relative path, at most 4096 UTF-8 bytes; reject NUL, control characters, empty components, `.`/`..`, backslashes and absolute paths. Validate filesystem containment and symlinks at use time, not merely through a string prefix check. |
|
||||
|
||||
Unknown fields/versions fail validation. Required fields cannot be omitted.
|
||||
Use explicit null only where the type permits it; an empty list means none,
|
||||
not unrestricted. Permissions and source-reference lists contain no duplicates.
|
||||
Proposed generic list limit is 1024 items and record size limit is 1 MiB of
|
||||
UTF-8 JSON, with detailed evidence stored separately. Limits need workload
|
||||
review before becoming an implementation contract. Reject oversize input;
|
||||
never silently truncate authority, dependencies, or recovery context.
|
||||
|
||||
Common envelope for declarative records: `schemaVersion:1`, `kind:RecordKind`,
|
||||
`id:Id`, `scope:Scope`, `revision:Revision`, `createdAt:UtcTime`,
|
||||
`createdBy:ActorRef`, `supersedes:RecordRef|null`, and `authorizationRef:Id`.
|
||||
`authorizationRef` resolves a trusted authorization event, not a recursive grant
|
||||
from the record being created. System bootstrap authority resolves to reviewed
|
||||
policy and the authenticated owner; no record may authorize its own creation.
|
||||
|
||||
The first revision has null `supersedes`. Later revisions reference the exact
|
||||
previous revision of the same object and immutable owning scope. A mutable
|
||||
current index is a rebuildable view, not the authoritative history.
|
||||
|
||||
## 3. Record catalog and ownership
|
||||
|
||||
The catalog names proposed types, not approved role or directory additions.
|
||||
Fields below supplement the common envelope. Complete per-kind field types,
|
||||
conditional requirements, and transition validation are still being drafted.
|
||||
There is deliberately no unrestricted `metadata` or `permissions:any` field.
|
||||
|
||||
| RecordKind | Payload to define | Authorized writer |
|
||||
|---|---|---|
|
||||
| `agent-definition` | Display name, reusable type, harness reference, canonical SOUL/instruction/skill references, reviewed role ceiling and configuration references. | Reviewed configuration author, not a workspace worker. |
|
||||
| `project` | Display name, policy reference and lifecycle. | Authorized project administration. |
|
||||
| `workspace` | Display name, parent project via scope, policy reference, lifecycle and logical working-file root. | Authorized workspace administration. |
|
||||
| `registration` | Agent ID, scope role reference, narrowing restriction set, active/revoked status and delegation reference. Project and workspace registrations are separate records. | Actor with bounded registration authority. |
|
||||
| `mission` | Objective, criteria, lifecycle, optional parent project-mission reference and policy restrictions. Project missions have no workspace parent. | Authorized work owner. |
|
||||
| `task` | Purpose, criteria, optional mission reference, approved intent reference, dependencies and lifecycle. An ad-hoc task still needs purpose/criteria and explicit authorization. | Authorized work owner/coordinator. |
|
||||
| `assignment` | Exact task reference, agent ID, lifecycle, assigned policy/work revisions and coordinator decision. | Authorized coordinator; progress does not grant assignment authority. |
|
||||
| `decision` | Decision kind, approved intent/evidence references, affected records, delegation bounds and decision outcome. | Authorized decision maker. Acceptance checks authorship independence separately. |
|
||||
| `session` | Agent ID, immutable project/workspace scope, creation mode, predecessor reference and exact harness identity binding. | Launch resolver; lifecycle observations are separate events. |
|
||||
| `context-source` | Content reference, revision/digest, owner, permitted scopes, context category and explicit sharing designation. | Authorized context publisher. Relevance cannot invent a grant. |
|
||||
| `adapter-capability` | Adapter/package identity, supported operations, observable and enforceable action classes, tested coverage references and limitations. | Reviewed adapter authority. A worker cannot self-certify coverage. |
|
||||
|
||||
Execution, active claim, connection, message, context manifest, and audit event
|
||||
are runtime evidence types, not mutable declarative records. Their complete
|
||||
envelopes remain a dependent drafting task. Do not force them into a common
|
||||
mutable `status` record or rewrite a run to update its progress.
|
||||
|
||||
## 4. Permission resolution
|
||||
|
||||
Proposed operations are distinct from tool names. A `read` tool grant does not
|
||||
imply permission to read another conversation, credential, or audit payload.
|
||||
|
||||
An action must satisfy both the requester's authority to request it and the
|
||||
performer's authority to execute it. The performer is bounded by reviewed
|
||||
agent policy, project registration, explicit workspace registration, applicable
|
||||
parent/workspace mission policy, task policy, and launch restrictions. Resolve
|
||||
all required references before taking the intersection. Missing/invalid required
|
||||
policy is an error, not an empty policy to skip. An optional absent restriction
|
||||
adds no grant and does not remove any ancestor ceiling.
|
||||
|
||||
Each layer checks operations and their targets. Project participation alone
|
||||
never admits a workspace action. Multiple assignments do not produce a union
|
||||
that lets an action borrow another task's wider grant. Bind a changing action
|
||||
to one explicit assignment and current intent revision. Tool availability is
|
||||
only a coarse filter before per-action authorization.
|
||||
|
||||
Proposed standard role catalog for later review:
|
||||
|
||||
- Reader: permitted work records and scoped file inspection.
|
||||
- Contributor: permitted inspection plus assigned file changes and result
|
||||
submission, with command execution subject to Q27's invocation-audit ruling
|
||||
and verified permission, containment, and evidence checks.
|
||||
- Reviewer: permitted evidence inspection and independent routine acceptance.
|
||||
- Coordinator: permitted within-plan decomposition and assignment changes.
|
||||
|
||||
These roles are not a rank hierarchy. Coordinator does not automatically mean
|
||||
file writer, reviewer, membership administrator, or credential administrator.
|
||||
Separate explicit grants govern registration, conversation reading/observation,
|
||||
control transfer, retirement/reopening, and protected operations. Exact operation
|
||||
sets and any combined standard roles need review. Shared role authority still
|
||||
changes only through reviewed commits under `roles/`; no live role-policy file
|
||||
was added or modified during this phase.
|
||||
|
||||
## 5. Proposed command contract
|
||||
|
||||
Use the target `mosaic` command family already proposed by #50. These spellings
|
||||
are candidate interface decisions, not commands to run today. Terminal,
|
||||
desktop, web, and service clients submit the same authorized operations.
|
||||
|
||||
| Candidate command | Operation/result rule |
|
||||
|---|---|
|
||||
| `mosaic agent resume <agent> --project <id> --workspace <id> [--session <id>]` | Default launch operation. Use exact session if provided; otherwise use the authoritative current scoped session. Genuine first use creates and announces initial conversation. Missing established history refuses. |
|
||||
| `mosaic agent fresh <agent> --project <id> --workspace <id>` | New conversation, no old chat/automatic summary. During active work, request controlled replacement; do not overlap executions. |
|
||||
| Work flags for Resume/Fresh: `--work continue|select|abandon|none`, repeatable `--assignment <id>` | Omitted work flag means continue selected assignments. No selected work means taskless conversation/inspection, not arbitrary changes. Select/Abandon require explicit assignment IDs. Abandon ends only those assignments. Validate referenced revisions before launch. |
|
||||
| `mosaic execution connect <id> --mode control|observe` | Exact active execution, explicit authorization. Control is exclusive; observe needs conversation access. No automatic connection from a conflict response. |
|
||||
| `mosaic execution transfer <id> --to-connection <id> --expected-generation <n>` | Explicit authorized transfer; old generation stops admitting control input. Being an observer is not permission to take control. |
|
||||
| `mosaic execution config-check <id>` | Return matching/changed/unknown, launch/current references and check time. Automatic non-blocking notices use the same comparison record. |
|
||||
| `mosaic workspace retire <id> --project <id>` | Stop admitting new work, safely stop/reconcile active work, retain files/history. Report retirement pending if safety is unresolved. |
|
||||
| `mosaic workspace reopen <id> --project <id>` | Revalidate current policy and membership; no automatic restoration of revoked access or launch. Exact assignment reactivation rules need review. |
|
||||
|
||||
The runtime schema now fixes `requestVersion:1`, `requestId:RuntimeId`,
|
||||
`operation`, `scope`, `expectedRefs:RecordRef[]`, `assignmentRef` and closed
|
||||
`parameters`. Input and command requests have their own narrower shapes. The
|
||||
service supplies authenticated actor and authorization; clients cannot submit
|
||||
trusted producer/outcome records.
|
||||
|
||||
The runtime schema defines the common response: version, request ID, outcome,
|
||||
reason code, optional execution ID and classified receipt/data references.
|
||||
Accepted means durable admission, not completion. Success/acceptance cannot have
|
||||
null receipts. Invalid/unauthorized pre-admission refusals and audit failures are
|
||||
not success receipts. Already-active identity requires authorization; wrappers
|
||||
never silently connect or Fresh.
|
||||
|
||||
The linked runtime contract supplies all 29 operation names, request coverage,
|
||||
error codes, JSON stdin/file semantics, message routing and #50 boundaries. CLI
|
||||
presentation/aliases in a later charter must map to these same operations.
|
||||
|
||||
## 6. Ordering and recovery obligations
|
||||
|
||||
1. Authenticate and validate before resolving private state for a caller.
|
||||
2. Resolve exact record revisions, scope, permission, and expected audit coverage.
|
||||
3. Serialize active-work admission by agent/project/workspace. Default limit is
|
||||
one, independent of session ID. Validate history before claiming first use.
|
||||
4. Commit authorization and intent to trusted durable storage before effects.
|
||||
Proposed durability requires file flush plus parent-directory flush and
|
||||
atomic publication on a supported local filesystem; unsupported storage
|
||||
refuses. Crash-order and multi-record commit mechanics still need design.
|
||||
5. Resolve exact immutable launch inputs and record the manifest. A successful
|
||||
spawn is not a confirmed session binding; require adapter identity evidence.
|
||||
6. Serialize controller changes separately by execution ID and generation.
|
||||
Reject stale generations for new input even if an old connection still lives.
|
||||
7. Before managed actions, recheck policy, assignment/plan revisions, active
|
||||
claim generation and required recording. A changed approved plan pauses
|
||||
affected work; a shared base-configuration notice alone does not.
|
||||
8. For replacement/revocation/retirement, close admission first, account for
|
||||
queued requests, settle or identify outstanding effects, and confirm the
|
||||
execution can no longer act before admitting conflicting new work.
|
||||
9. Append observations and recovery decisions. A timeout, abort acknowledgement,
|
||||
or Pi `agent_settled` event alone does not prove container/process termination.
|
||||
|
||||
A scoped conversation index must retain initial-creation history and missing/
|
||||
pruned-session tombstones. A missing index is not proof of first use. Retrying
|
||||
a request must resolve its prior durable decision; a timeout or missing response
|
||||
must not replay an external action. Request identity binds authenticated actor,
|
||||
operation, scope, and normalized arguments. Reuse with different arguments is
|
||||
an error. Tombstone retention and post-prune retry behavior remain unresolved.
|
||||
|
||||
The enforcing runtime component is not an autonomous conductor agent. It has
|
||||
only its reviewed operation authority; changing a task's state cannot change
|
||||
policy. Required audit failure blocks affected actions, including recovery
|
||||
checks that cannot meet their own recording requirements.
|
||||
|
||||
## 7. Storage and evidence
|
||||
|
||||
Keep immutable execution evidence under `<dataRoot>/runs/`. A proposed new
|
||||
execution record uses its run directory as the evidence owner; projects and
|
||||
workspaces reference it instead of maintaining competing result copies.
|
||||
Existing v1 records are never rewritten or silently reinterpreted as v2 evidence.
|
||||
|
||||
Keep conversation bytes under `<dataRoot>/sessions/`, but resolve them through
|
||||
explicit scoped metadata rather than directory naming. Do not mount all sessions,
|
||||
all runs, all user context, or the whole data root into a worker's tool-accessible
|
||||
filesystem. Only the necessary approved inputs and working paths are exposed.
|
||||
A trusted recorder must be outside worker write permissions. OS ownership and
|
||||
mount tests must verify these boundaries before any isolation claim.
|
||||
|
||||
Proposed metadata ownership: project/workspace records in their dedicated
|
||||
runtime metadata namespace; current indexes under a derived system state
|
||||
namespace. No actual directory choice or second configuration file is installed.
|
||||
Exact paths, per-directory writers/readers, export, retention, prune/reset
|
||||
references, and rollback must be specified before schema approval.
|
||||
|
||||
Legacy adoption makes a new mapping receipt. Preserve the original source and
|
||||
its identity before adapter conversion. Pi's documented automatic format
|
||||
migration is a reason to inspect metadata without opening a legacy session in
|
||||
Pi. Fresh does not load an adopted transcript just because it is accessible.
|
||||
|
||||
## 8. Configuration fingerprints
|
||||
|
||||
Proposed algorithm: SHA-256 over a versioned canonical JSON manifest using RFC
|
||||
8785 serialization. Hash exact instruction/SOUL bytes as referenced content;
|
||||
normalize structured settings through their strict schema first. Preserve
|
||||
instruction order; sort set-valued permissions and source inventories by their
|
||||
explicit stable keys. Publish test vectors before implementing the comparator.
|
||||
|
||||
Comparable base configuration covers canonical SOUL/shared instructions,
|
||||
applicable enabled skills and their dependency content, canonical harness/model
|
||||
settings, and reviewed role configuration. Exclude timestamps, display labels,
|
||||
chat, task progress, private user context, and credentials. Content changes must
|
||||
be detected even if a path stays the same. Do not hash unrelated skills merely
|
||||
because they are installed.
|
||||
|
||||
The full launch manifest separately binds execution identity, selected approved
|
||||
context, effective restrictions, session choice, adapter/image identity, and
|
||||
non-secret credential-account reference where authorized. Its hash naturally
|
||||
differs between launches. Never compare full launch hashes as the base-config
|
||||
change test. Private context source references/digests remain access-controlled.
|
||||
|
||||
Exact included JSON keys, skill dependency inventory rules, configuration
|
||||
revision races, approved launch overrides, and notification transport still
|
||||
need D10/D16 design and #50 reconciliation. A launch fingerprint is not continuous
|
||||
proof of live process state, and a changed role grant may require immediate
|
||||
revocation rather than waiting for Fresh.
|
||||
|
||||
## 9. Owner decision after investigation
|
||||
|
||||
### Q27: command-level evidence or internal-effect evidence
|
||||
|
||||
A command such as `npm test` can invoke other processes, read/write many files,
|
||||
and attempt network access. Pinned Pi documents tool invocation/result events;
|
||||
it explicitly does not provide a sandbox. Mosaic's current adapter does not
|
||||
capture those events as a trusted per-action ledger.
|
||||
|
||||
Choices presented to the owner; Jason selected A on 2026-09-06:
|
||||
|
||||
- A: for the initial managed command facility, record the authorized command
|
||||
invocation, actor/scope/assignment, enforced filesystem/network limits,
|
||||
start/end, outcome, and controlled evidence references. Be explicit that this
|
||||
does not separately enumerate every internal file read/write or network call.
|
||||
- B: require separately attributable internal file/network effects before
|
||||
enabling command execution. Keep commands unavailable until that stronger
|
||||
coverage is designed and verified.
|
||||
|
||||
Owner ruling: A, with real sandbox enforcement and honest coverage labels.
|
||||
This keeps test/build commands practical while preserving accountable invocation
|
||||
and fail-closed admission. Use typed, allowlisted command metadata. Neither
|
||||
events nor detailed evidence may contain credentials; unfiltered argument,
|
||||
environment, and output capture is not approved. The ruling does not waive
|
||||
workspace isolation, credential protection, action recording, revocation,
|
||||
or uncertainty recovery. A command
|
||||
whose effects cannot stay within the authorized boundary must still refuse.
|
||||
|
||||
This ruling settles D11's command-audit granularity, not a specific sandbox,
|
||||
RPC/SDK choice, product UI replacement, or implementation increment. It does
|
||||
not grant shell access to any current research worker.
|
||||
|
||||
## 10. Phase-2 exit gate remains open
|
||||
|
||||
Q27 and Q28 are answered; R34 records the managed-terminal ruling. Runtime,
|
||||
control/input, command artifacts, permission/routing/recovery rules and the
|
||||
D1-D16 reconciliation now form an owner-review candidate. Jason's specific
|
||||
phase-2 acceptance is now recorded. Implementation must prove the listed mechanisms;
|
||||
any change to owner behavior returns for a new decision.
|
||||
|
||||
Candidate acceptance evidence must include negative schema fixtures, reference
|
||||
and permission checks, representative lifecycle/crash traces, hash test vectors,
|
||||
and a capability matrix that distinguishes documented, code-inspected, and
|
||||
runtime-tested support. Current evidence is source/document inspection and author
|
||||
schema/hash/synthetic-model checks only. No
|
||||
runtime feature or security guarantee, independent verdict, whole-schema approval,
|
||||
or readiness for mapping follows from this draft.
|
||||
|
||||
## 11. Continuing goal task record
|
||||
|
||||
Goal: issue-53-phase2. Owner: Jason. Assigned author: darkwing in the explicit
|
||||
repository working directory. The repeated /goal request resumes this existing
|
||||
assignment; it does not create a second authority record. CURRENT owns live
|
||||
control state; BUILD-LOG and SESSIONS hold append-only history.
|
||||
|
||||
| Task | Criterion and dependencies | Status and evidence |
|
||||
|---|---|---|
|
||||
| P2-1 | Recover Q1-Q27 requirements and pre-reboot work. | Done for author recovery; R1-R33 and preserved source/run evidence. |
|
||||
| P2-2 | Repair command/record/path schemas and fixtures after P2-1. | Author checks pass: 38 command cases, 38 record cases, 16 path cases, plus 5 deliberately shape-valid forgeries. Runtime semantics remain separate. |
|
||||
| P2-3 | Define permission/reference/lifecycle/storage rules after P2-2. | Review proposals in schema package sections 2-6; no live policy installed. |
|
||||
| P2-4 | Define comparable fingerprints and reconcile #50 boundaries. | Seven restricted-domain vectors pass; package sections 6.1-6.2 retain the unresolved OAuth gate. |
|
||||
| P2-5 | Choose managed terminal requirement and finalize affected execution/control/artifact schemas. | Author checkpoint complete after Jason's Q28 A. Runtime schema, fixtures and RUNTIME.md provide the review candidate; no runtime capability is claimed. |
|
||||
| P2-6 | Assemble exact-candidate review package, trace D1-D16, and recommend one testable increment. Depends on P2-5. | Owner package in REVIEW.md; 155 runtime cases and 35 synthetic rule-model cases supplement earlier checks. First recommendation refined to a read-only synthetic scope/permission inspector, not live registration. |
|
||||
| P2-7 | Obtain Jason's specific phase-2 acceptance after P2-6. | Done: Jason said "accept phase 2" after clarification that this accepts the planning baseline, not security certification or implementation. Goal satisfied; later mapping/implementation still require separate authority. |
|
||||
|
||||
Jason clarified that /goal belongs to the separate ~/.mosaic deployment, not
|
||||
this environment. Continue through existing file-based records. No extension
|
||||
installation, active --wait-timeout 60, or automatic follow-up was requested for
|
||||
this phase-2 session. Separate #54 native-extension work is not part of this goal;
|
||||
this session has not migrated its goal state or configured that extension.
|
||||
Owner waits are manual and never expire into approval. No numeric work budget was supplied; aggregate usage is
|
||||
unavailable, not reset on resumption.
|
||||
@@ -0,0 +1,352 @@
|
||||
# Foundation technical map — #53 / #55
|
||||
|
||||
Status: source/plan baseline pinned; prepared for owner review. Author: darkwing.
|
||||
Scope: documentation/read-only investigation and explicitly authorized local
|
||||
baseline/mapping commits; no implementation, migration, push or issue closure. Prepare for independent review, not self-approval.
|
||||
|
||||
## Authority and coordination
|
||||
|
||||
The active operator goal authorizes mapping the accepted phase-2 foundation with
|
||||
Dewey, aligning canonical directories, identifying reuse/change and recommending
|
||||
one small user-testable increment. This supersedes CURRENT's earlier wait for
|
||||
mapping authorization; it does not reopen phase-2 acceptance.
|
||||
|
||||
MS55-DW-1: Dewey requested scope/path coordination. The attempted direct tagged
|
||||
reply returned exit 2: submission could not be confirmed. Jason identified a known
|
||||
tmux-tool bug. Delivery remains unknown; no blind resend or private-pane polling.
|
||||
Dewey subsequently directly acknowledged MS55-DW-1 and recorded the agreement in
|
||||
his #55 layout plan. Receipt is now settled; no resend is needed. He is waiting
|
||||
for Jason's mapping-goal authorization in his session; this session's active goal
|
||||
already authorizes its own mapping work. Do not assume his work has started.
|
||||
|
||||
Acknowledged division:
|
||||
- Darkwing: this map, foundation requirement-to-code trace and CURRENT integration.
|
||||
- Dewey: #55 canonical source, package/install boundaries and phased inventory.
|
||||
- Shared BUILD-LOG/SESSIONS: append-only; no exclusive claim.
|
||||
- Darkwing will not edit extensions/**, .pi/**, #54/#55 plans or Dewey's packaging
|
||||
and goal test scripts. Accepted foundation documents remain stable inputs.
|
||||
|
||||
## Initial measured directory alignment
|
||||
|
||||
Repository HEAD measured at this checkpoint: 69d1bb3aa4b826218aa4cca3710f2d98c0b9d7ba.
|
||||
The working tree includes uncommitted foundation and separate #54/#55 work;
|
||||
HEAD alone does not identify that newer source. Exact content hashes are required
|
||||
for the eventual review handoff.
|
||||
|
||||
| Boundary | Existing location | Mapping disposition |
|
||||
|---|---|---|
|
||||
| Immutable worker instructions | contracts/ | Retain dedicated contract ownership; draft schemas are not installed contracts. |
|
||||
| Reviewed role authority | roles/ | Retain; scope registration must narrow, not replace this authority. |
|
||||
| Harness integration | adapters/ | Retain adapter boundary; trace mediated-runtime changes before proposing placement. |
|
||||
| Skills | skills/ | Retain declarative resources; not automatically JavaScript workspace packages. |
|
||||
| Goal extension/support source | extensions/goal/, extensions/mosaic-core/lib/ | #55 canonical-source input; do not duplicate or relocate in this mapping. |
|
||||
| Generated native installation | .pi/extensions/ | #55 installation output, not canonical source. |
|
||||
| Launch/build/test utilities | scripts/ | Distinguish tooling from future service logic; no moves proposed yet. |
|
||||
| Existing runtime code | src/ | Inventory responsibilities before proposing apps/ or packages/. |
|
||||
| Plans and evidence summaries | docs/ | Keep planning separate from runtime authority/evidence storage. |
|
||||
|
||||
Directory existence was checked locally. The goal/source-install boundary comes
|
||||
from Dewey's #55 layout record; its source/package verification is not foundation
|
||||
runtime admission evidence. No empty apps/packages scaffolding is proposed.
|
||||
|
||||
## Initial workplan (historical)
|
||||
|
||||
1. Trace actual source entrypoints and state/authority paths against R1-R34.
|
||||
2. Classify reuse unchanged, reuse with changes, replacement, and new component;
|
||||
identify dependencies and proposed canonical placement without moving files.
|
||||
3. Reconcile package/source boundaries with Dewey, settling MS55-DW-1 receipt.
|
||||
4. Recommend one bounded user-testable increment and produce a hashed independent-
|
||||
review handoff with unresolved risks. Owner review remains the completion gate.
|
||||
|
||||
## Source trace checkpoint: launcher, adapter, policy and evidence
|
||||
|
||||
Read directly on 2026-09-06 at 06:59 UTC. These classifications concern the
|
||||
inspected paths, not a claim that no similar capability exists anywhere else.
|
||||
|
||||
| Source locator | As built | Reuse/change classification and requirement impact |
|
||||
|---|---|---|
|
||||
| scripts/agent.sh:78-130 | Reads reusable seat definition; copies SOUL into a shared per-agent data-root path; writes a seat record if absent. | Reuse identity source concept (R1). Change materialization to immutable execution-specific inputs (R16/R17); preserve canonical source ownership rather than duplicating definitions per workspace. |
|
||||
| scripts/agent.sh:132-170 | Default session is agent-NAME; declared role resolves tools, intersected with requested tools. | Replace global default with explicit project/workspace/session resolution (R3/R4/R12). Reuse narrowing principle, not this as a full scope authorizer. |
|
||||
| scripts/agent.sh:181-199 | Mission copied to shared per-agent path; workspace defaults to agent name; native Compose launch. | Change launch orchestration and mission snapshots. Existing directory names cannot establish membership. R34 requires a mediated client path, not merely native launch with another flag. |
|
||||
| adapters/pi/adapter.sh:23-50 | Changes cwd, supports ephemeral/fork/persistent modes; a nonempty session directory adds -c; tools are explicit or disabled. | Retain adapter separation and explicit tool/discovery controls. Replace directory-nonempty/latest selection with exact binding and genuine-first-use checks. Preserve fork/history behavior only after explicit compatible adoption. |
|
||||
| adapters/pi/adapter.sh:63-96 | Native TUI or print; ambient extensions/context/templates disabled; explicit provider/model and prompt. | Reuse explicit configuration/discovery suppression where verified. New mediated transport/tool gateway required for R34/R33. Do not enable the native #55 extension in managed workers as an implicit shortcut. |
|
||||
| scripts/mosaic-task.mjs:252-273,670-676 | Closed role fields, filename identity, known unique tools and network enum; resolve-role emits tools and network. | Reuse validation principles and tests with changes. This role format does not express the new project/workspace registrations or 29-operation catalog. Network metadata emission is not evidence of network enforcement (R13/R33). |
|
||||
| scripts/mosaic-task.mjs:362-378 | Task tools intersect mission tools; empty intersection yields tool-free. | Reuse least-privilege operation, expand to all required ceilings, targets and current revisions. Do not infer authorization from tool presence or combine assignments. |
|
||||
| scripts/mosaic-task.mjs:295-325 | Exclusive wx creates input snapshots; writeOnce writes then closes, without fsync in this helper. | Reuse exclusive-create intent and snapshot conventions. Change publisher for durable commit ordering, classifications, trusted origins and recovery. Exclusive creation alone is not crash durability. |
|
||||
| scripts/mosaic-task.mjs:442-466 | Result stores prompt/response, task/session/tools, exit/signal/model and times, then writeOnce. | Retain legacy run evidence and useful provenance fields. Do not treat it as the R14/R33 invocation ledger: new scope/assignment/authorization/limits/intent/observation records and controlled detailed evidence are required. |
|
||||
|
||||
### Proposed component boundaries, not source moves
|
||||
|
||||
- Scope/reference/policy resolution: a reusable domain module with no process,
|
||||
credential or filesystem-effect authority. Existing validation/intersection code
|
||||
is an input, not permission to copy its narrower semantics unchanged.
|
||||
- Managed launch/control coordination: separate runtime responsibility above the
|
||||
harness adapter; owns claims, current intent and authenticated control routing.
|
||||
- Pi adapter: owns engine protocol translation and exact-session/config binding,
|
||||
not canonical project membership or global policy decisions.
|
||||
- Trusted evidence publisher/supervisor: distinct from worker output. Owns durable
|
||||
intent/outcome publication and trustworthy stopping observations.
|
||||
- Keep proposed modules unallocated to new apps/packages until their dependency
|
||||
and build boundaries are reconciled with Dewey. Existing scripts remain untouched.
|
||||
|
||||
### Dependency implications
|
||||
|
||||
A read-only scope/permission inspector can precede managed execution: it needs
|
||||
strict records, a coherent synthetic reference graph and a clearly labelled
|
||||
permission calculation. It does not need provider credentials or Pi launch.
|
||||
Live registration needs the trusted publisher and protection from legacy broad-
|
||||
mount paths first. Managed launch then depends on scope resolution, publisher,
|
||||
claim/control protocol, adapter admission and real isolation/stopping proof.
|
||||
|
||||
### Exact inspected file identities
|
||||
|
||||
- `scripts/agent.sh`: `1ad2270a02e5668ef126c2af71a7b70771c2da09842313bdbf6abba84e95b555`
|
||||
- `adapters/pi/adapter.sh`: `d213c167d319dbbb42326f68c9c76ec01dbdd42e8f4f226d3232cc5b355bfebc`
|
||||
- `scripts/mosaic-task.mjs`: `525bab31453ab84afa379421c619405632b9c85d639e2f4a6188dcf7ae825b83`
|
||||
|
||||
## Mount, context and lifecycle trace
|
||||
|
||||
Read-only source inspection, 2026-09-06 07:01 UTC. No reset, prune, retry,
|
||||
container launch or credential-file read was performed.
|
||||
|
||||
| Source locator | Observed behavior | Classification / required boundary |
|
||||
|---|---|---|
|
||||
| compose.yaml:38-43 | Whole configured data root mounted at /var/lib/mosaic without :ro; auth file separately mounted read-only. | Replace mount design for managed admission. Read-only auth mounting does not establish credential separation from the engine, and whole-root exposure is not workspace isolation. Preserve runtime-only credential handling, not broad mounts. |
|
||||
| src/run-agent.sh:20-41 | Adapter name/path checks; dispatch after generating one shared /var/lib/mosaic/system-prompt.md. | Reuse dispatch validation with changes. Context builder must publish execution-specific immutable inputs, not a shared prompt destination. |
|
||||
| src/load-contracts.sh:18-58 | Governance files required; optional seat SOUL; OUT.partial is a fixed sibling staging name. | Reuse governance precedence and missing-source refusal. Replace shared staging/output with uniquely owned execution snapshots and verified publication; current concurrent same-output writers can contend. |
|
||||
| src/load-contracts.sh:68-77,81-98 | Appends every user/*.md, then mission objective/directives. Header's stated layer order differs from actual user-before-mission order. | Replace blanket context discovery with classified, authorized selection and explicit ordering. Actual code, not header prose, defines this baseline. R6/R16/R17 and context privacy require recorded source revisions. |
|
||||
| scripts/reset.sh:16-48 | Loads configured data root, rejects root symlink/resolution mismatch/missing marker, then recursively removes that root. | Retain useful refusal checks, change lifecycle integration before protected foundation state exists. Despite hard-coded-path commentary, implementation compares resolved path with configured TARGET, not a fixed literal. No active-claim/reference protection or reset receipt is present in this path. |
|
||||
| scripts/mosaic-task.mjs:548-595 | Retry reads task snapshot, redirects relative mission to recorded snapshot, then runs it as a new run. | Reuse provenance and original-record preservation. Do not use as uncertain-effects recovery: no old process/effect reconciliation gate is visible in retryRun. New run identity alone does not make replay safe. |
|
||||
| scripts/mosaic-task.mjs:598-641 | Prune sorts run directories, keeps a count, defaults to preview; --yes removes each directory before appending its receipt. Directory-read failures are caught as no entries. | Reuse preview/explicit-apply UX, not protected-retention semantics. Add live-reference/claim protection, distinguish unreadable state from empty state, and make deletion/receipt failure recoverable. Deletion-before-receipt exposes an uncertainty window. |
|
||||
|
||||
### Proposed authority and state ownership
|
||||
|
||||
| Responsibility | Proposed owner | Required separation |
|
||||
|---|---|---|
|
||||
| Approved context selection and snapshot construction | Trusted context builder under launch coordinator | Does not trust workspace file discovery or client classification; private inputs stay out of shared work metadata. |
|
||||
| Actual mounts, egress and process cohorts | Sandbox supervisor/gateway | Separate command jobs from credential-bearing engine; no worker access to evidence/policy/control roots. |
|
||||
| Canonical records and required evidence | Trusted publisher and retention coordinator | Writer/retention share a serialized protection boundary; a worker cannot prune its own audit trail. |
|
||||
| Uncertain outcome recovery | Authorized recovery coordinator plus trustworthy observers | Distinct from replay; must establish stopping and reconcile effects before admitting replacement/retry. |
|
||||
| Native extension development installation | Dewey's #55 tooling | .pi installation acceptance is not evidence that container mounts or managed lifecycle meet these requirements. |
|
||||
|
||||
### Ordering constraint for the rewrite
|
||||
|
||||
Before live foundation state is treated as protected, close legacy broad-mount
|
||||
launch paths into that state and integrate reset/prune protection. Before managed
|
||||
Resume/Fresh, provide exact scoped session/claim resolution and immutable context
|
||||
snapshots. Before uncertain-effect retry, provide trustworthy stopping, evidence
|
||||
availability and explicit reconciliation. These are dependencies, not source moves
|
||||
or authorizations to repair the current scripts during mapping.
|
||||
|
||||
### Additional source identities
|
||||
|
||||
- `compose.yaml`: `a21dc87079d255261ae7318845073ec06bf9df7bac6874012ea0c84b0d1ec12d`
|
||||
- `src/run-agent.sh`: `6749ebe3d0433b3dfefb40a44d58c4ca2606ab9f4ce01457dd52767059b7b13b`
|
||||
- `src/load-contracts.sh`: `4b210d5d785d06d699ceaa902ccb0451c09cb19397e861521f825c09e9e33ae5`
|
||||
- `scripts/reset.sh`: `957ef76f949c2eb2e472182261bf2d1619e0cde44c506ab2bbb5c25dc063864d`
|
||||
- `scripts/mosaic-task.mjs`: `525bab31453ab84afa379421c619405632b9c85d639e2f4a6188dcf7ae825b83`
|
||||
|
||||
## Complete requirement responsibility index
|
||||
|
||||
This index covers every accepted requirement, not every implementation. “New”
|
||||
means not provided by the inspected paths; repository-wide absence is not proven.
|
||||
Source detail is in the two trace tables above; accepted behavior is in the
|
||||
foundation plan R1-R34. Uninspected responsibilities remain explicit gaps.
|
||||
|
||||
| Requirement | Proposed responsibility | Mapping finding |
|
||||
|---|---|---|
|
||||
| R1 | Identity | Launcher identity reuse; scoped runtime binding changes |
|
||||
| R2 | Scope/policy | New registration/delegation resolver; existing tools-only role validation is insufficient |
|
||||
| R3 | Scope/policy | New single-parent project/workspace graph and explicit membership |
|
||||
| R4 | Session/control | Replace global session default with scoped identity |
|
||||
| R5 | Scope/policy | Explicit selection resolver replaces agent-name workspace default |
|
||||
| R6 | Context | Replace shared/global work input paths with authorized snapshots |
|
||||
| R7 | Session/control | Exact Resume/initial/Fresh state; replace nonempty-directory continuation |
|
||||
| R8 | Work coordination | New assignment selection/Abandon/prerequisite transitions |
|
||||
| R9 | Session/control | Authorized service launch and work-record recovery |
|
||||
| R10 | Session/control | New exclusive scoped claim; scaling/budgets remain later-phase requirements |
|
||||
| R11 | CLI/client | New shared operation interface; no client-owned task truth |
|
||||
| R12 | Messaging | New explicit scoped addressing/delivery; not established by inspected launcher |
|
||||
| R13 | Sandbox | Replace whole-root mount boundary and prove containment |
|
||||
| R14 | Evidence | Expand run provenance into trusted classified action evidence |
|
||||
| R15 | Governance | Retain explicit owner phase and user-test gates |
|
||||
| R16 | Context | Replace shared SOUL materialization with immutable execution snapshot |
|
||||
| R17 | Context | New comparable fingerprint and cross-interface notice flow |
|
||||
| R18 | Scope/policy | New mission ownership/parent graph and reference checks |
|
||||
| R19 | Work coordination | New bounded decomposition and non-author acceptance checks |
|
||||
| R20 | Work coordination | Separate taskless read/chat from assigned changes |
|
||||
| R21 | Session/control | New active-conflict response and explicit connection |
|
||||
| R22 | Context | New transcript-specific visibility and handoff checks |
|
||||
| R23 | Scope/policy | New revocation propagation linked to supervisor stopping |
|
||||
| R24 | Session/control | New controller/observer generations and transfer |
|
||||
| R25 | Session/control | New fenced, verified-safe replacement protocol |
|
||||
| R26 | Recovery | Replace blind retry use for uncertainty with evidence-based reconciliation |
|
||||
| R27 | Evidence | New fail-closed recording/admission and preauthorized fail-safe stop |
|
||||
| R28 | Context | Replace blanket user Markdown inclusion with classification/selection |
|
||||
| R29 | Retention | New retirement/reopen without deletion; protect required evidence |
|
||||
| R30 | Session/control | New reviewed legacy adoption; preserve originals |
|
||||
| R31 | Work coordination | New current-intent reconciliation and stale-action rejection |
|
||||
| R32 | Scope/policy | Extend reviewed ceilings with standard scope roles and narrowing |
|
||||
| R33 | Evidence/sandbox | New mediated invocation records plus actually enforced limits |
|
||||
| R34 | CLI/client | New Mosaic-controlled terminal; retain Pi behind reviewed adapter |
|
||||
|
||||
## Canonical placement matrix for Dewey reconciliation
|
||||
|
||||
ROADMAP.md:127-166 records an owner decision, not just an optional legacy pattern:
|
||||
post-M20 succession uses packages/*, with restructuring delayed until script
|
||||
replacement to avoid two migrations. This corrects any reading of the initial
|
||||
map as leaving the entire package target undecided. #55 extensions/** is current
|
||||
canonical source, not an implicit repeal of that post-M20 destination.
|
||||
|
||||
| Responsibility | Current source/input | Post-M20 proposed destination | Packaging/ownership boundary |
|
||||
|---|---|---|---|
|
||||
| User CLI and managed terminal | scripts/agent.sh and other wrappers | packages/mosaic/ | CLI consumes domain/runtime APIs; wrappers retire only after replacement acceptance. |
|
||||
| Engine/session/control/supervision | src/, adapters/, launcher orchestration | packages/agent/ | Runtime owns process/session protocol; adapter internals do not own global role authority. |
|
||||
| Strict records, references and policy intersection | scripts/mosaic-config.mjs, parts of mosaic-task.mjs; candidate schemas | packages/config/ | Pure validation/resolution separated from effectful publication; first inspector can exercise this boundary. |
|
||||
| Provider/account materialization | #50 plan/current auth tooling | packages/auth/ | Credential ownership stays here; no credential migration or refresh experiment in mapping. |
|
||||
| Evidence/work/recovery coordination | mosaic-task.mjs portions plus new responsibilities | Initially packages/agent/ internal modules | Separate trusted writer and policy interfaces; do not invent extra top-level packages without independent build needs. |
|
||||
| Contracts/roles/missions/tasks/skills | Existing dedicated directories | Retain declarative directories | Packages consume reviewed resources; no promotion of drafts or workspace-written authority. |
|
||||
| Goal extension and imported support | extensions/goal/, extensions/mosaic-core/lib/ | Current source retained pending explicit M20 extension packaging decision | Dewey owns inventory/provenance; whether to stage a package artifact or move source later remains a specific unresolved boundary. |
|
||||
| Native development installation | .pi/extensions/, sync/test scripts | Generated installation remains separate | Never package .pi/state or treat native acceptance as managed-runtime proof. |
|
||||
|
||||
ROADMAP's target also retains src/ while describing packages/agent as absorbing
|
||||
src/adapters. The map must distinguish a retained container entry shim/build input
|
||||
from absorbed runtime implementation; do not move both copies and create competing
|
||||
sources. That exact shim boundary and extension distribution placement need Dewey's
|
||||
reconciliation. No package-manager change or empty package scaffolding is authorized.
|
||||
|
||||
## Earlier review-baseline gate (resolved below)
|
||||
|
||||
Dewey's #55 reconciliation and ms-archify require commit-pinned code and plan
|
||||
citations for a formal map. The accepted phase-2 plan and #55 work are currently
|
||||
uncommitted. Current hashes make this preparatory inventory reproducible, but do
|
||||
not satisfy the formal commit-pinned handoff gate. No commit/push is authorized
|
||||
by this goal. Do not label this document an independently review-ready Archify map
|
||||
yet or use HEAD to pretend it contains the dirty source.
|
||||
|
||||
Remaining ready work: inspect config/auth/interface inventories read-only and
|
||||
specify the first inspector's exact acceptance boundary. External dependencies:
|
||||
Dewey's package/shim reconciliation and an owner-authorized baseline strategy
|
||||
before formal independent review. Review dispatch itself is a separate gate.
|
||||
|
||||
## Config/auth/interface inventory and first-increment boundary
|
||||
|
||||
Read source only; no configuration, credential contents, auth status, login or
|
||||
refresh operation was accessed/executed during this checkpoint.
|
||||
|
||||
| Source | Finding | Disposition |
|
||||
|---|---|---|
|
||||
| scripts/mosaic-config.mjs:39-61,76-174 | Config path can be overridden by MOSAIC_CONFIG; regular-file/symlink and strict field checks; canonical data-root checks exclude root/home/config ancestors; lstat errors are treated as missing. | Reuse strict validation and protected-root principles; reconcile the override with sole-config canon rather than silently adopting a second config authority. Distinguish missing from unreadable/error where fail-closed diagnostics matter. |
|
||||
| scripts/mosaic-config.mjs:194-239 | Bootstrap uses exclusive create, validates existing config without replacement; validate/env expose resolved non-secret fields and shell quoting. | Retain bootstrap-only creation and read-only resolution. Future domain validation must not bootstrap or load live config when running a synthetic inspector. |
|
||||
| scripts/auth.sh:19-96 | Config-backed account directory and reporting of provider/type/permission metadata. Reads credential JSON when invoked; parse errors include parser text. | Preserve ownership separation, not a proven redaction guarantee. Do not reuse credential-reading report functions in the inspector. Error disclosure and account materialization belong to separate auth review. |
|
||||
| scripts/agent.sh:28-64 | Per-launch named account checks readability, symlink and mode 0600, exports selected mount source; no project selection flag in this parser. | Reuse explicit refusal rather than account fallback. Replace flat account/path selection with #50 registry bindings at the later auth boundary; introduce full scope at the managed CLI, not by inferring it from cwd. |
|
||||
|
||||
### Recommended increment: synthetic scope/permission inspector
|
||||
|
||||
Purpose: let Jason see whether one agent's project/workspace membership and
|
||||
permissions resolve as intended before any live state or worker can be affected.
|
||||
This is a recommendation for a later charter, not an implementation task started.
|
||||
|
||||
Input: one explicit local synthetic bundle containing a coherent graph of agent,
|
||||
project, two workspaces, registrations, mission/task/assignment and declared mock
|
||||
policy sources. No live registry, credential, engine history or data-root lookup.
|
||||
Independent shape fixtures cannot simply be concatenated into this graph.
|
||||
|
||||
Output: deterministic text plus structured result, identifying selected agent,
|
||||
project and workspace, reference errors and the calculated least-privilege result.
|
||||
Every successful output says preview only: no live registration or permission grant.
|
||||
Unknown/missing policy is a refusal, never an empty layer skipped during intersection.
|
||||
|
||||
Acceptance cases for the implementation charter:
|
||||
1. Valid bundle resolves the explicitly named first workspace and its permitted read.
|
||||
2. Same agent, second workspace without registration: refuse without revealing that
|
||||
workspace's private payload or selecting the first workspace instead.
|
||||
3. Missing parent, multiple/incorrect ownership, dependency cycle and stale revision:
|
||||
report the violated rule; do not repair or invent references.
|
||||
4. Broader task grant cannot widen mission/registration/agent ceilings; another
|
||||
assignment cannot supply missing permission. Explicit empty grants allow nothing.
|
||||
5. Ambiguous name, duplicate ID/revision, unknown field and malformed UTF-8/path:
|
||||
reject before producing a permission preview that appears valid.
|
||||
6. Source bundle stays byte-identical; no writes to config, data root, .pi/state,
|
||||
roles or installations; no child engine, network, credential or migration action.
|
||||
7. Jason runs the positive and negative examples and understands both the scope
|
||||
display and the preview disclaimer before any dependent increment is chartered.
|
||||
|
||||
Proposed exit classes (not installed): 0 valid preview, 2 malformed/invalid graph,
|
||||
3 simulated permission refusal, 4 input/I/O failure. Final naming/packaging belongs
|
||||
to the later charter, consistent with packages/config domain ownership and the
|
||||
packages/mosaic CLI target. No npm/Turbo change is needed to approve this boundary.
|
||||
|
||||
Deferred: authoritative publication, real authentication, sandbox tests, process
|
||||
control, native/managed goal integration, OAuth refresh, live registration, session
|
||||
adoption, reset/prune changes and repository restructuring. These require their
|
||||
own dependencies, implementation tests and owner acceptance.
|
||||
|
||||
### Config/auth/interface source identities
|
||||
- `scripts/mosaic-config.mjs`: `430ee6bc4fcfbe4b9ac030aaa19cdb6fdc7407e1b17253b80178b9b0523b5a3a`
|
||||
- `scripts/auth.sh`: `fe5d3e89272d3b04db687eabed30a95dde480a2f7bc784cd27e43fa9321f15a6`
|
||||
- `scripts/agent.sh`: `1ad2270a02e5668ef126c2af71a7b70771c2da09842313bdbf6abba84e95b555`
|
||||
|
||||
## Owner-reported cross-lane retasking scenario
|
||||
|
||||
Jason reports that orch-01 in the separate ~/.mosaic environment redirected two
|
||||
agents from their owner-set goals into supervisor work for another agent, outside
|
||||
their lanes. This is owner-reported context, not an independently investigated
|
||||
incident or a proven root-cause diagnosis. Jason explicitly prohibited involvement
|
||||
in that environment's failure; no inspection, messaging or intervention there is
|
||||
part of this goal.
|
||||
|
||||
Map to R8/R12/R19/R23/R31/R32: current goal/mission/assignment and scope authority
|
||||
must be checked when reassignment is requested. A coordinator title, message or
|
||||
new role description is not authorization. A goal reminder is not an enforcement
|
||||
boundary. Another workspace's permission cannot be borrowed, and changing a scope
|
||||
role cannot silently replace reusable identity or the owner's approved intent.
|
||||
|
||||
Add this adversarial case to the proposed inspector/implementation acceptance set:
|
||||
- Agent A has an active owner-authorized assignment in workspace A. A coordinator
|
||||
from workspace B requests reassignment into supervision for another goal.
|
||||
- Without explicit applicable delegation and a valid recorded change within owner
|
||||
intent, refuse the request; preserve A's goal/assignment and report the conflict.
|
||||
- A message alone cannot mutate assignment, role, goal or acceptance state.
|
||||
- If a properly authorized change is requested, account for underway effects and
|
||||
follow reconciliation; never abandon old work merely because a new message arrived.
|
||||
- Later runtime tests must prove original work remains selected and stale/cross-scope
|
||||
actions are fenced. An offline preview alone cannot establish this behavior.
|
||||
|
||||
## Integrated baseline and reconciled ownership
|
||||
|
||||
Source and accepted-plan baseline: d4696d09eb1b5dcf1028f30db2cd63735f51cb16,
|
||||
whose parent is foundation baseline 44f257cb06484feda3412d9382e3587393796353.
|
||||
All source file:line citations in this map now refer to d4696d09 unless explicitly
|
||||
labelled historical. Inspected legacy source bytes are unchanged from 69d1bb3.
|
||||
The mapping revision is the separate commit containing this map and its handoff;
|
||||
no circular claim that d4696d09 already contains these mapping documents is made.
|
||||
|
||||
Jason authorized scoped local baseline commits. Dewey's MS55-DW-3 receipt was
|
||||
verified locally: exact parent, 43 allowed paths and an empty released index.
|
||||
The former baseline-authorization and coordination waits are resolved. No push,
|
||||
implementation, migration or independent review is authorized by that resolution.
|
||||
|
||||
Dewey's MS55-DW-2 qualifications are adopted:
|
||||
- extensions/** remains current canonical source until explicitly chartered M20
|
||||
packaging; mosaic-core/lib remains internal support, not another entrypoint.
|
||||
- Post-M20 src/ retains only unavoidable bootstrap/exec shims. Current material
|
||||
context behavior migrates/replaces, never duplicates packages/agent logic.
|
||||
Adapters also have one canonical post-M20 owner under packages/agent.
|
||||
- packages/mosaic handles presentation/routing, not policy truth. packages/config
|
||||
owns pure validation and deterministic policy calculation, not publication.
|
||||
- packages/agent contains runtime coordination and separated writer/recovery
|
||||
interfaces. A package is not a process trust boundary: engines must not inherit
|
||||
publisher/supervisor privileges simply by importing the same package.
|
||||
- packages/auth contains code/metadata, never packaged secrets. Declarative
|
||||
top-level directories remain authoritative inputs, not generated installations.
|
||||
|
||||
The initial inspector remains the recommended bounded increment. No independently
|
||||
built packages, empty scaffold or package-manager migration is needed now. Exact
|
||||
extension distribution packaging and any retained shim are implementation-charter
|
||||
choices constrained by the reconciled ownership rules, not unresolved permission
|
||||
to create duplicate sources.
|
||||
|
||||
Next gate: owner review of the mapped baseline and separate authorization of a
|
||||
non-author review. This mapping prepares that handoff; it does not supply the
|
||||
reviewer's verdict or authorize the inspector implementation.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Goal quiet waits
|
||||
|
||||
## Latest checkpoint, 2026-09-06 09:02 UTC
|
||||
|
||||
Jason reports Joe/Huey are "looking good so far" after reloads. This is preliminary positive quiet-wait feedback, not final acceptance. He accepted Resume's NG footer/full recall, authorized fleet ownership correction and scoped suite-gated local commits. #58 now makes the legacy fleet and Velma goal paths compatibility aliases to shared NG and removes the Resume-only workaround. See docs/plans/2026-09-06_fleet-goal-ownership.md for the explicit state-location and no-migration boundary. All 55 live settings/discovery combinations load one shared NG goal with configured guards. The seven-file quiet-wait patch still has its independently approved SHA256 d9caeb0c87e074196421a010e8a2de0968b3bb922010fa864ca2ec1f1902ef6b. Goal, native, package and five repository suites were rerun green. No existing session or private state was changed. #56 remains open for final quiet-wait acceptance; no polling or rearmed timer.
|
||||
|
||||
The deployment and authority descriptions below are historical checkpoints. #58 supersedes the three-independent-copy topology and the earlier absence of commit authority.
|
||||
|
||||
Owner request: fix the untimed goal-check loop observed in Joe on 2026-09-06. Author: Dewey. Baseline: 7345f330fc6bfae5aa1d896c78cfb7cbe62efbae. Darkwing owns foundation mapping; none of its files are in scope.
|
||||
|
||||
## Defect
|
||||
|
||||
An accepted untimed wait resets the check counter but does not suppress settle or startup injection. The old quiet-wait test explicitly expects that behavior. Joe's pane showed an accepted wait followed by a new check and a 190k-token cache-miss notice. This is a scheduling defect; a cooldown would merely reduce its frequency.
|
||||
|
||||
## Scope and acceptance
|
||||
|
||||
- Canonical extension source/tests under extensions/goal, plus this plan and append-only delivery logs.
|
||||
- All accepted waits suspend automatic checks, independent of timeout configuration.
|
||||
- Untimed waits create no timer. Unrelated incoming messages and reloads preserve the wait. The model reconciles relevance; the extension never evaluates nextCheck as code or treats incoming text as permission.
|
||||
- Explicit resume may reconcile a waiting or paused goal once. It clears the old wait; another unresolved report returns to quiet waiting.
|
||||
- Timed waits retain one deadline wake per goal/resume and their existing abort/readiness/delivery safeguards.
|
||||
- Guard queued check dispatch against duplicate lifecycle events, without blocking genuine subsequent work or retaining stale dispatches across goal replacement.
|
||||
- Preserve footer/full recall, completion, pause/blocked, fencing, report-policy and write-failure behavior.
|
||||
- Verify red-before-green regressions, full extension and repository suites, native no-model checks, independent exact-candidate review, and safe local test installation.
|
||||
|
||||
## Delivery boundary
|
||||
|
||||
No live fleet files, private goal state, user goals, or sessions are modified during development. The active operator goal explicitly authorizes local-first deployment under ~/.mosaic after verification. Do not automatically resume Joe or reload other seats. Before deployment, compare the actual shared entrypoint and every independent active copy with the reviewed baseline, preserve their state location, pin the file set, save rollback copies and verify native loading. The legacy fleet/extensions copy is distinct from the current .mosaic/.pi/extensions target; do not replace it merely because it exists. No push, CI/merge claim or foundation implementation. This is the operator-requested local development/deployment path, not a published release.
|
||||
|
||||
## Tasks
|
||||
|
||||
1. Reproduce untimed settle/startup injection and record red tests.
|
||||
2. Fix wait gating, explicit resume and dispatch deduplication; verify regressions.
|
||||
3. Review and test the candidate, including fresh native installation.
|
||||
4. Deploy the pinned candidate to verified current targets, verify symlink resolution and a no-model native canary using isolated fixture state. Report reload requirements without driving existing sessions. Owner acceptance cannot be inferred from tests.
|
||||
|
||||
Status: deployed on disk and native-verified; ready for operator reload and acceptance. Issue #56: https://git.mosaicstack.dev/mosaicstack/stack-v2/issues/56. Four red regressions reproduced the old behavior; 71 goal tests, 18 package controls, native Pi timed/untimed Waiting fixtures, and all five repository suites pass. Independent read-only Pi reviewer approved the exact seven-file source/test/documentation candidate at 2026-09-06 07:50 UTC; transcript /tmp/goal56-review.log. Static TypeScript compiler remains unavailable; no claim of static checking.
|
||||
|
||||
At 07:51 UTC, the shared .mosaic/.pi/extensions/goal entrypoint, state and settle files matched HEAD byte-for-byte before the fix. Fifteen role extension links resolve to the shared root; Topher links through interact. Velma has an ordinary independent copy whose three runtime files also match HEAD. Joe's running process has cwd ~/.mosaic and no explicit extension flags. A broad initial symlink scan encountered a denied backup subtree; the bounded active roles/agents extension scan completed. The scratch pi-goal-x experiment is not a current shared target. Initial symlink-only inspection was insufficient: a subsequent settings scan found 41 agent and 14 role settings files still referencing legacy fleet/extensions/goal, including Joe. That path therefore also requires the scheduling backport, preserving its distinct state location and widget UI.
|
||||
|
||||
Deployment gate, 2026-09-06 08:00 UTC: canonical 71 tests and staged legacy 67 tests pass. The first full legacy staging run had two ENOENT errors because its external-relative skill fixtures were absent; copying the existing canonical fixtures into the expected staging location resolved them without changing live skills. Shared/Velma have an older README and no fixture .gitattributes; runtime files match the baseline. These differences were diagnosed and the actual whole-file trees pinned rather than bypassing a drift refusal. Independent deployment/backport reviewer APPROVE: /tmp/goal56-deploy-review.log.
|
||||
|
||||
Pinned deployment input: .pi/evidence/goal56/deployment-pins.json, SHA256 08aec1a0ff7c70ce2a5e1b6831c8ec395a94a2bafe9ca1be9c7110a6b913935c. One-shot deployment script: .pi/evidence/goal56/deploy.py, SHA256 248ec09b534f067d296fc70f2ce50b11810c9927321a58205e860eb19b39684a. Preflight and an isolated atomic-exchange/rollback test passed. Deploy only these three ordinary goal directories; core, settings, symlinks and existing goal states remain untouched. Deployment completed by atomic exchange. Rollback trees and write-once receipts: /home/jwoltje/.mosaic/.pi/goal-backups/goal56-60590600f2c9420a881f1f4f0a5fc5cf. All three deployed trees match their approved after-pins; all three backups match before-pins. All fifteen role links and Topher's agent link resolve to the patched shared tree. Six native Pi RPC canaries loaded the three actual installed entrypoints, timed and untimed: exactly one explicitly selected /goal per isolated canary, zero automatic checks/model starts, and no fixture state changes. Provider credentials were not inherited. All owned fixture states were removed. This does not assert ambient multi-extension discovery is conflict-free, nor that already running sessions have reloaded.
|
||||
|
||||
At 2026-09-06 08:02:45 UTC the canonical source still matched the deployed shared/Velma candidate. Canonical working-tree patch: .pi/evidence/goal56/canonical.patch, SHA256 d9caeb0c87e074196421a010e8a2de0968b3bb922010fa864ca2ec1f1902ef6b. Source remains uncommitted; no new commit/push authority was inferred from the earlier baseline authorization. No existing process was interrupted, reloaded, resumed or given a replacement goal. The legacy fleet copy preserves its original store and UI; core, settings and links are unchanged.
|
||||
|
||||
Remaining user-facing gate: Jason chooses a safe idle session, runs /reload, and confirms an accepted untimed wait remains quiet. If the chosen goal is paused, resume only when its assignment is still authorized; /goal resume deliberately reconciles once. Do not set a test goal over another agent's assignment. A relevant input should permit verified progress; unrelated input should preserve waiting. This is a manual operator acceptance wait using incoming messages, not a polling watch or another timer. No reload or owner approval is inferred from the native canaries. #56 remains open until that confirmation. The extension's one bounded deadline wake arrived after the 08:06:21 UTC deadline with no operator confirmation or defect report in the conversation. Acceptance remains blocked; no timer was re-armed and no session was inspected, reloaded or resumed. Resume this task on explicit operator confirmation or a defect report.
|
||||
|
||||
Communication: darkwing closeout MS55-DW-3 confirms its baseline and released index; no reply requested. The separate fix notice returned rc=2 (delivery unconfirmed); no blind resend. The d4696d09-pinned map remains historical.
|
||||
@@ -0,0 +1,94 @@
|
||||
# Replacement repository source layout
|
||||
|
||||
Tracking issue: [#55](https://git.mosaicstack.dev/mosaicstack/stack-v2/issues/55)
|
||||
|
||||
## Authority and scope
|
||||
|
||||
Jason requested canonical codebase storage for extensions, skills, and other components, with phased replacement of mosaicstack/stack. Source organization does not authorize deployment, credential migration, commit, push, or changes to the live fleet. The accepted goal extension is the first migration candidate. This record is an investigation and recommendation, not an implemented move.
|
||||
|
||||
## Measured legacy structure
|
||||
|
||||
Read through the Gitea contents API on 2026-09-06 at 06:14 UTC. Requests returned HTTP 200.
|
||||
|
||||
- `apps/` contains appservice, gateway, and web.
|
||||
- `packages/` contains shared domains including agent, auth, brain, comms, config, coord, db, forge, log, memory, queue, storage, and types.
|
||||
- `plugins/` contains discord, macp, mosaic-framework, and telegram. These names alone do not establish that these plugins are Pi extensions.
|
||||
- Root also has `skills/`, `tools/`, `scripts/`, `profiles/`, `infra/`, `docker/`, and documentation.
|
||||
- Root package manifest is private, declares pnpm 10.6.2, and delegates build, lint, typecheck, and tests to Turbo.
|
||||
- Workspace membership is explicit: apps/*, packages/*, plugins/*, and tools/matrix-presence-harness. Skills are not declared as JavaScript workspace packages.
|
||||
- Turbo builds dependency packages before dependents and records dist/** as build output. Tests and typechecking depend on dependency builds.
|
||||
|
||||
Inspected manifest blobs:
|
||||
|
||||
- package.json: `5910f98225226274141a6da4c81098fa122f3f46`
|
||||
- pnpm-workspace.yaml: `abb7a60e203c6d44dcf8dc9bf7c99cb3cb72cbd8`
|
||||
- turbo.json: `cf8ef998ee52391cc581c16c865f6d0c0066b24f`
|
||||
|
||||
The recursive `main` tree response identified commit/tree `7102ccb93e007d98faab3994ea552d03f522ad2c` but was truncated at 1,000 entries. Follow-up contents reads supplied complete targeted directories. This is a targeted structural and packaging inspection, not a complete legacy inventory.
|
||||
|
||||
## Recommendation for stack-v2
|
||||
|
||||
Adopt the monorepo principle now, not the entire legacy directory tree or build system.
|
||||
|
||||
- `extensions/goal/` becomes canonical goal source and tests.
|
||||
- `extensions/mosaic-core/lib/` initially holds its existing supporting dependencies. This does not activate Mosaic Core as a separate extension.
|
||||
- Existing `skills/`, `contracts/`, `adapters/`, `roles/`, `scripts/`, and `docs/` retain their distinct responsibilities.
|
||||
- `.pi/extensions/` becomes a generated native-test installation. Never edit it as canonical source. Populate it by deterministic ordinary-file copying, not symlinks.
|
||||
- `.pi/state/` remains ignored runtime state and must never enter an extension artifact.
|
||||
- Introduce apps/ or packages/ when an actual service or shared package requires them. Do not create empty placeholder packages or rename existing components merely to resemble the old repository.
|
||||
- Package explicit source inventories and dependency versions. Record artifact hashes and fail rather than overwrite unknown local edits in the development installation.
|
||||
|
||||
The current private npm manifest and pinned Pi dependency do not need to become pnpm/Turbo merely to store one extension. Decide workspace tooling when independently built JavaScript packages justify it. Native Pi 0.85.1 testing versus the container's 0.84.4 pin is an unresolved compatibility gate before managed-runtime promotion.
|
||||
|
||||
## Measured legacy packaging and installation
|
||||
|
||||
Targeted Gitea reads returned HTTP 200 for the installer, release verifier, publish pipeline, publishing helper, and representative manifests.
|
||||
|
||||
- Shared packages such as `@mosaicstack/agent` build TypeScript into `dist/`, publish only `dist`, and use explicit workspace dependencies.
|
||||
- The `@mosaicstack/mosaic` package publishes both `dist` and `framework`. This is the legacy bundle for CLI plus framework resources.
|
||||
- `plugins/mosaic-framework` declares an OpenClaw extension in its own package manifest. It is not a Pi extension package.
|
||||
- The main installer separates framework files under `~/.config/mosaic` from the globally installed npm CLI. Its development mode builds the monorepo, packs local tarballs, and installs those tarballs without registry writes.
|
||||
- The publish pipeline installs from a frozen lockfile, verifies that CI's commit equals checkout HEAD, runs one terminal release verifier, builds, and only then publishes. Registry, authentication, network, and unknown publication failures fail the job. The `next` lane snapshots and restores modified manifests.
|
||||
- A separate npmjs helper mutates manifests temporarily and restores them. It tolerates publish failures broadly, so it is not a model for stack-v2's fail-closed publication path.
|
||||
|
||||
The useful pattern is source, build output, installation, and publication as separate states tied to explicit manifests and immutable identity. The volume and complexity of the old framework package should not be copied into the first stack-v2 extension increment.
|
||||
|
||||
## Phases
|
||||
|
||||
1. Source ownership: move accepted goal source, dependencies, and tests into canonical directories; preserve source provenance and user acceptance evidence.
|
||||
2. Native development packaging: deterministic install into .pi, drift checks, source/package tests, explicit runtime version compatibility, and repeat native acceptance if behavior changes.
|
||||
3. Managed runtime integration: approved extension allowlist, image packaging, policy compatibility, isolation, and rollback checks. Docker remains deferred until authorized.
|
||||
4. Component-by-component legacy replacement: inventory dependencies, contracts, state ownership, acceptance criteria, and rollback for each component before migration. No wholesale adoption of operational state.
|
||||
|
||||
## Implemented first increment
|
||||
|
||||
The accepted goal extension now follows the recommended ownership boundary.
|
||||
|
||||
- Canonical source and tests are under `extensions/goal/`.
|
||||
- Imported support modules are under `extensions/mosaic-core/lib/`; no second entrypoint exists.
|
||||
- `scripts/sync-dev-extensions.sh` stages and verifies ordinary-file copies in `.pi/extensions/`. It records the installed manifest, rejects source symlinks or extra entrypoints, detects installation drift, and refuses to overwrite it.
|
||||
- `scripts/goal-dev.sh` synchronizes before launching isolated native Pi. `.pi/goal-dev.sh` remains a compatibility wrapper for the accepted test command.
|
||||
- `scripts/test-extension-package.sh` runs 18 controls. It covers initial installation, full-tree equality, extra destination entries, source and destination symlinks, local drift, canonical updates, nested entrypoints, active and stale locks, and interruption rollback/recovery.
|
||||
- `.pi/extensions/`, state, generated manifests, and local evidence are ignored. They are installations or runtime evidence, not canonical source.
|
||||
|
||||
Verification after the move passed: 67 goal tests, all 18 development-package controls, and native Pi PTY checks at 45 and 120 columns for Waiting, Paused, Blocked, Complete, bare `/goal`, Alt+G, and NO_COLOR. A separate read-only reviewer first requested stronger full-tree, nested-entrypoint, interruption, and lock controls. After correction, the reviewer returned APPROVE and identified each prior finding as closed. The exact canonical-source manifest is `.pi/evidence/canonical-source.sha256`, whose SHA-256 is `c2096c00fb505a53482ff6a94a0f293dbc1ca2058d7a1996961601af80c29946`.
|
||||
|
||||
No package-manager change, npm publication, Docker inclusion, live-fleet installation, commit, or push is part of this increment. Unrelated CURRENT.md work remains untouched.
|
||||
|
||||
## Replacement method
|
||||
|
||||
Use the repository as a component monorepo with explicit boundaries, not as one undifferentiated package. Keep declarative resources in their existing top-level directories. Add executable packages or apps only when they have independent build and dependency needs. Every migrated component gets canonical source, tests, a deterministic development installation, an explicit release inventory, exact runtime compatibility, independent review, and user acceptance before the old implementation can be retired.
|
||||
|
||||
For Pi resources, the Pi package format supports explicit `pi.extensions` and `pi.skills` paths. Do not publish the entire private repository by convention. When external distribution becomes necessary, create a bounded package manifest or staged artifact that names only approved resources and declares Pi APIs as peer dependencies. The current sync installation is deliberately narrower and precedes that release decision.
|
||||
|
||||
## Coordination request
|
||||
|
||||
`MS55-DW-1` asks darkwing, the current owner of CURRENT.md and issue #53 phase-2 planning, to reconcile the next foundation goal before Jason sets it. Requested return: darkwing's intended technical-mapping scope, paths this session must avoid, and a recommended division of work that incorporates #55 without overlapping writers. Destination is the default tmux session `darkwing`; follow-up owner is this assistant. `tools/tmux/agent-send.sh` returned rc=2 at 2026-09-06 06:55 UTC: it could not confirm submission after three attempts and reported that the message may be undelivered. Delivery is unknown, so this session will not resend without reconciliation. No source or shared-plan work starts from this request.
|
||||
|
||||
## Reconciliation finding
|
||||
|
||||
CURRENT.md says issue #53 phase 2 is accepted and awaits Jason's separate authorization for technical mapping. ROADMAP.md also records an earlier owner decision: the succession target uses `packages/*`, while broad restructuring waits for M20 because moving current bash entrypoints sooner would create two migrations. The #55 `extensions/` location is therefore a valid canonical source improvement now, but it cannot silently become the final post-M20 package layout. Jason's latest direction does not explicitly revoke the earlier sequencing decision.
|
||||
|
||||
The next safe step is a coordination and baseline goal, not another code move. It should reconcile the `extensions/` staging location with the `packages/*` target, assign non-overlapping writers for the current dirty tree, and define the exact committed revision that a later as-built/planned map will cite. Archify mapping requires a commit SHA and will not treat the current dirty working tree as evidence.
|
||||
|
||||
Darkwing replied directly to `MS55-DW-1` at 2026-09-06. The reply supports an evidence-backed current-versus-planned component/package map with #55 as input and no runtime implementation. Agreed ownership: darkwing owns requirement-to-code mapping and CURRENT integration; Dewey owns source, package, installation boundaries, and phased inventory in this #55 plan or a separately agreed file. Darkwing will avoid `extensions/**`, `.pi/**`, #54/#55 plans, and the four extension development scripts. Dewey will avoid CURRENT.md, the three foundation/phase-2/schema plans, and `foundation-v1-candidate/**`. Shared logs remain append-only. Both sides await Jason's explicit mapping authorization. A non-author must review the resulting candidate. The acknowledgement send returned rc=2 after three unconfirmed attempts; delivery is unknown and it was not resent.
|
||||
@@ -0,0 +1,135 @@
|
||||
# NG goal footer development plan
|
||||
|
||||
Issue: [#54](https://git.mosaicstack.dev/mosaicstack/stack-v2/issues/54)
|
||||
|
||||
## Outcome
|
||||
|
||||
Develop a repository-local copy of the Mosaic `/goal` extension for native Pi launches from this repository. Goal state appears in Pi's built-in footer as `Goal: <State>`. Bare `/goal` and a keyboard shortcut show the full stored goal text without truncation.
|
||||
|
||||
This is NG development. It does not change or reload the live extension under `~/.mosaic`, and it does not add the extension to the Docker launch path.
|
||||
|
||||
## Baseline and ownership
|
||||
|
||||
- Repository baseline at intake: `69d1bb3` with unrelated, preserved phase-2 planning changes in the working tree.
|
||||
- Source snapshot: `~/.mosaic/fleet/extensions/goal/` plus the imported `mosaic-core/lib/` modules, copied as ordinary files.
|
||||
- Initial development location: `<cwd>/.pi/extensions/`; canonical source moved to `extensions/` after acceptance.
|
||||
- Author: this native coding session, `01a07506-f3c7-76ff-a725-b3c5e2086f31`. The operator directed completion after the setup-only checkpoint. The worker declaration remains undispatched.
|
||||
- Independent reviewer: a non-authoring reviewer seat on the exact candidate.
|
||||
- Product acceptance owner: Jason.
|
||||
|
||||
The source snapshot is not evidence that the external extension is safe to change. All changes remain inside this repository; nothing writes through to the external source.
|
||||
|
||||
## Requirements
|
||||
|
||||
1. Native Pi discovers the project extension through `.pi/extensions/*/index.ts` after project trust is granted.
|
||||
2. No goal widget appears above the editor.
|
||||
3. Pi's built-in footer displays a textual state. Color reinforces the text but never carries the meaning alone.
|
||||
4. State presentation uses Pi theme roles:
|
||||
- `Active`: `accent`
|
||||
- `Waiting`: `warning`
|
||||
- `Paused`: `warning`
|
||||
- `Blocked`: `error`
|
||||
- `Complete`: `success`
|
||||
- `None`, if shown: `muted`
|
||||
5. Bare `/goal` displays the complete stored goal text. It may format or wrap the text, but it must not shorten it.
|
||||
6. A non-conflicting shortcut displays the same complete status. Tab remains Pi autocomplete because Pi's public status API does not expose focusable footer items.
|
||||
7. Waiting, blocked, and ordinary pause are distinguishable. A completed goal remains recallable without reactivating its continuation loop.
|
||||
8. Existing continuation, report, wait, abort, persistence, and incarnation-fencing behavior stays intact.
|
||||
9. Extension failures must not alter the live `~/.mosaic` files or operator goal state used by other agents.
|
||||
|
||||
## State design
|
||||
|
||||
The current source has `active`, `paused`, `blocked`, and `none` in its type, but blocked reports currently become paused with a `blocked:` reason. Satisfaction clears the state immediately. The implementation must add an explicit display projection rather than infer every label in the renderer.
|
||||
|
||||
Preserve active-loop semantics. Store a bounded terminal outcome record for recall after completion, including the full goal text and terminal state. Do not treat `Complete` as an active goal. Existing version-1 state files must load without losing their active or paused goal.
|
||||
|
||||
Bare `/goal` must use the full state text. Short text remains acceptable in transient notifications that are not the recall command.
|
||||
|
||||
## Interaction design
|
||||
|
||||
Use `ctx.ui.setStatus("goal", ...)` so the goal indicator composes with Pi's default footer. Do not replace the entire footer. Remove the project copy's `setWidget` registration and clear any stale goal widget during startup and reload.
|
||||
|
||||
Register a direct recall shortcut. `Alt+G` is the initial candidate, subject to a native terminal conflict check. The shortcut invokes the same full-status formatter as bare `/goal`; it does not insert text into the editor or start a model turn.
|
||||
|
||||
## Tasks
|
||||
|
||||
| ID | Owner | Work | Exit evidence |
|
||||
|---|---|---|---|
|
||||
| NG-GF-1 | Conductor | Copy the goal directory and imported mosaic-core library into `.pi/extensions/` with no symlinks; record source and destination hashes. Do not copy `mosaic-core/index.ts`, because Pi would auto-discover and execute that separate policy extension. | File inventory, no-symlink check, matching snapshot hashes, and one discovered goal extension. |
|
||||
| NG-GF-2 | Author worker | Implement display-state projection, terminal outcome recall, colored footer status, full `/goal` output, shortcut, and focused tests. | Clean patch plus focused test and type-check output. |
|
||||
| NG-GF-3 | Independent reviewer | Review the exact candidate for state migration, loop regressions, extension isolation, keyboard behavior, and hostile state inputs. | Written verdict tied to the candidate hash. |
|
||||
| NG-GF-4 | Conductor | Run repository suites and extension tests; verify no path resolves into `~/.mosaic`. | Green command output and path/hash checks. |
|
||||
| NG-GF-5 | Jason | Exercise native Pi at narrow and wide widths, active/waiting/paused/blocked/complete states, bare `/goal`, shortcut, and color-disabled mode. | Explicit user acceptance or findings. |
|
||||
|
||||
Worker declarations are `tasks/ng-goal-footer-implementation.json` and `tasks/ng-goal-footer-review.json`. They are not dispatched until the conductor provides an isolated exact candidate workspace. The current dirty repository is not silently adopted as a worker workspace.
|
||||
|
||||
## Verification
|
||||
|
||||
Automated checks:
|
||||
|
||||
- strict task validation;
|
||||
- extension unit and runtime tests;
|
||||
- state migration fixtures for old version-1 records;
|
||||
- footer text and theme-role assertions for every state;
|
||||
- full-text equality for `/goal` and shortcut recall;
|
||||
- no `setWidget` call for goal state;
|
||||
- no symlinks or source paths resolving into `~/.mosaic`;
|
||||
- existing repository suites;
|
||||
- `git diff --check` and prose checks.
|
||||
|
||||
Native TUI checks:
|
||||
|
||||
- normal and narrow terminal widths;
|
||||
- keyboard-only recall;
|
||||
- project reload and session replacement;
|
||||
- default theme and color-disabled output;
|
||||
- stale widget removal;
|
||||
- complete and blocked states remain textually identifiable.
|
||||
|
||||
The footer uses native terminal theme colors. Browser-specific WCAG contrast measurement is not applicable, but status meaning must remain available without color and in `NO_COLOR` mode.
|
||||
|
||||
## Security and rollback
|
||||
|
||||
Project-local extensions execute with the native Pi process's host permissions. The operator must explicitly trust this project. This test does not prove the managed execution, command recording, credential, or isolation requirements in #53.
|
||||
|
||||
Development-install rollback removes generated `.pi/extensions/` plus NG-only test state. Source rollback reverts the separately scoped canonical `extensions/` change; it never edits the external `~/.mosaic` extension.
|
||||
|
||||
## Setup checkpoint
|
||||
|
||||
NG-GF-1 completed on 2026-09-06. The copied goal tree hash is `8853f2b72dde3e87c4573648b9a931c1c75da87ccde995c3224e6d2e707a75f0`; the copied mosaic-core library hash is `d1194dce31209e5773c6cc5ce571cbca3c39b29d943a79dea06665e05d29f319`. `.pi/SOURCE-SNAPSHOT.json` records the snapshot. A no-symlink check passed, and only `.pi/extensions/goal/index.ts` is auto-discoverable.
|
||||
|
||||
An isolated native Pi RPC launch with `--approve`, a temporary `PI_CODING_AGENT_DIR`, no session, and no tools reported exactly one project-scoped `/goal` command from this repository's `.pi/extensions/goal/index.ts`. No extension error appeared.
|
||||
|
||||
The five self-contained goal suites pass 55 tests. The first broad copied-source run also exposed two repository-context tests that expect `skills-local/` files from the source brain. They remain failing in this repository and are not counted as green. Copying all of mosaic-core initially also exposed that its separate `index.ts` would be auto-discovered and that its tests require source-brain role manifests. Setup was corrected before the discovery smoke test: only the imported mosaic-core library remains. No live extension file was changed.
|
||||
|
||||
## Delivery gates
|
||||
|
||||
Implementation is not done at setup. It proceeds through author checks, independent exact-candidate review, repository checks, and Jason's native TUI acceptance. Docker promotion, release packaging, and edits to `~/.mosaic` are excluded from #54.
|
||||
|
||||
## Implementation checkpoint, 2026-09-06
|
||||
|
||||
The local implementation is available for hands-on testing. Start `bash .pi/goal-dev.sh` from this repository. It loads only the local goal extension, preserves native provider authentication, and puts goal state and conversation files under `.pi/state/`. It neither copies credentials nor loads the live fleet extension. Plain native Pi also discovers the project copy after trust, but the launcher prevents global extension collisions.
|
||||
|
||||
- NG-GF-2 implemented by the current native session. Footer status replaces widget content; stale widgets are cleared. Bare `/goal` and Alt+G share full-text recall. Complete retains full goal text and bounded evidence without an active continuation loop. Blocked has its own state and supports explicit resume. Old version-1 state remains readable.
|
||||
- NG-GF-3 has an independent read-only native reviewer APPROVE. The reviewer inspected the entrypoint, display, state, store, and launcher but did not rerun tests. The first review attempt timed out without output; a second process with a ready configured provider returned the verdict. No worker container was dispatched.
|
||||
- NG-GF-4 author checks passed: all 67 goal tests and repository suites config 24, task 90, release 14, conductor 17, auth 15. Native Pi 0.85.1 PTY checks passed at 45 and 120 columns for paused, blocked, complete, waiting, bare recall, shortcut recall, and one color-disabled case. Native discovery found exactly one local goal command. No symlinks. Live extension hashes still match the source snapshot.
|
||||
- The source-brain test failures are resolved with ordinary local contract fixtures. The executive-update fixture matches its pinned SHA-256. Tests no longer need live brain reads.
|
||||
- Static TypeScript checking was not run because no compiler is installed. Native loading and Node TypeScript execution passed, but they are not a substitute for static checking.
|
||||
- NG-GF-5 remains open for Jason. Instructions and expected results are in `.pi/README.md`. No user acceptance, commit, push, Docker integration, or release completion is claimed.
|
||||
|
||||
Evidence is in `.pi/evidence/README.md` and its logs. The reviewed candidate file manifest hashes to `10e949db54149c6bed945fab086ad7a91461683ee5661c920f9ee6bddcf04aae`. No reviewed source file changed between review and manifest recording.
|
||||
|
||||
Goal state: waiting for manual user test. Next action: Jason launches `bash .pi/goal-dev.sh`, exercises full recall and the footer, and returns acceptance or findings. Issue #54 stays open. This checkpoint does not advance or change issue #53 or CURRENT.md. No automatic wake is registered.
|
||||
|
||||
|
||||
## User acceptance, 2026-09-06 06:05 UTC
|
||||
|
||||
Jason replied "It works" to the native test handoff. NG-GF-5 is accepted for this local development test. This supersedes the manual user-test wait above; it does not establish separate measurements of every test case.
|
||||
|
||||
No further feature work is requested. Static type checking remains unperformed. Commit, push, Docker integration, and live fleet changes are not authorized by this acceptance. Issue #54 remains open for repository delivery disposition; CURRENT.md and unrelated work remain unchanged.
|
||||
|
||||
## Canonical source migration, 2026-09-06
|
||||
|
||||
At Jason's direction, the accepted implementation moved from development-only source ownership to the repository's component structure. Canonical files now live under `extensions/goal/` and `extensions/mosaic-core/lib/`. `.pi/extensions/` is generated and ignored.
|
||||
|
||||
`scripts/sync-dev-extensions.sh` performs staged ordinary-file installation with content verification and refuses local drift, symlinks, or an extra extension entrypoint. `scripts/goal-dev.sh` runs this sync before native Pi; `.pi/goal-dev.sh` remains a compatibility wrapper. The native test moved to `scripts/test-goal-native.py`. This source-only relocation does not change accepted behavior, package the extension into Docker, publish npm, or touch the live fleet.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Resume goal discovery repair
|
||||
|
||||
## Accepted outcome, 2026-09-06 09:02 UTC
|
||||
|
||||
Jason explicitly confirmed the NG footer and Alt+G full recall. The Resume-specific workaround was then superseded by the authorized fleet fix in #58. Normal discovery now selects the shared NG source through compatibility aliases; the common launcher is restored to its original version with unslop retained, and wrapper/core enforcement stays as configured. All 55 live settings/discovery combinations pass. No settings, credentials or private state were migrated. See docs/plans/2026-09-06_fleet-goal-ownership.md. Resume's accepted defect correction is ready for #57 closure after the scoped commit. Fleet rollout and quiet-wait acceptance remain separate gates.
|
||||
|
||||
## Earlier correction — 2026-09-06 08:34 UTC
|
||||
|
||||
Jason rejected the legacy selection below and explicitly directed use of the shared NG extension. Resume now selects ~/.mosaic/.pi/extensions/goal with wrapper-guard and unslop retained, exactly once. No state migration or private-state writes. Independent correction review APPROVE; native selected-resource checks verify NG Alt+G and both guards; 71 goal tests and native footer/full recall pass. Installed NG runtime matches tested canonical source. Deployed launcher SHA256 dd9e5ece5f1a86cc286027668560198bb7a44d2ab891f7b1ab4f89d7379ca0e6. Backup: ~/.mosaic/.pi/goal-backups/goal57-shared-4d95a698f4bb4544a0cb90aed5e887a3/launch-seat.sh. Evidence: .pi/evidence/goal57-shared/. Other seats remain unchanged. Restart Resume through its normal launch command when safe; /reload alone in an already running legacy-selected process does not replace its CLI extension selection. Awaiting user retry.
|
||||
|
||||
## Earlier repair — legacy selection rejected by owner
|
||||
|
||||
Jason requested repair after Resume launch failed with duplicate goal_report registration. Dewey owns this bounded hotfix; foundation/CURRENT and existing agent sessions remain untouched.
|
||||
|
||||
The real native loader reproduces the collision when project discovery and Resume's explicit legacy extension are both enabled. A global-settings exclusion of the project entrypoint was tested and failed: Pi applies project auto-discovery exclusions in project scope. No live settings were changed for that experiment.
|
||||
|
||||
Chosen repair: Resume-only explicit extension selection in the shared launcher. Retain its declared legacy goal and wrapper-guard with explicit -e arguments while disabling automatic extension discovery for this seat. The launcher's existing explicit unslop hook remains enabled. This is not bare pi -ne: all three existing safeguards are explicitly retained. Refuse if Resume's declared extension list/packages drift from the reviewed two-resource configuration, or if a selected entrypoint is missing. Preserve the legacy state location, all settings/credentials, and all other seats' launch behavior.
|
||||
|
||||
Scope: one bounded block in ~/.mosaic/fleet/bin/launch-seat.sh, a canonical native discovery regression script, this plan, and append-only logs. Do not change per-seat shim conventions, mutate private goals, alter credential refusals, migrate state, restart seats, or disable safeguards. Broad fleet source/state unification is not part of this targeted repair; other seats are not silently redirected between the shared and legacy goal stores.
|
||||
|
||||
Acceptance: native negative control reproduces the original combined-discovery error; repaired selection loads exactly one goal_report and /goal, retains wrapper tool interception and unslop extension, and produces no loader errors. Validate shell syntax, exact candidate independent review, an atomic backup/replace with drift refusal, and post-install discovery. Do not launch the real auth-seeding pipeline merely to test resource selection. User retries the unchanged Resume launch command for final acceptance.
|
||||
|
||||
Status at 2026-09-06 08:29:51 UTC: deployed, ready for Resume retry. Issue #57: https://git.mosaicstack.dev/mosaicstack/stack-v2/issues/57. Independent reviewer APPROVE (.pi/evidence/goal57/review.log). Nine isolated selection controls passed (valid config plus refusals for extension/package drift, invalid JSON, additional project settings/resources/core entrypoint, automatic user extension directory, missing wrapper). Huey's extracted launcher arguments were unchanged. Native negative control reproduced the original collision; post-install native resource loading proved exactly one legacy goal_report and /goal, registered wrapper tool interception, unslop loaded, and no loader errors. A separate real Pi RPC CLI process using the repaired three-extension arguments started successfully without credentials, sessions or model turns. All 71 goal tests still pass; shell syntax and whitespace checks pass.
|
||||
|
||||
Launcher before SHA256: 9352feed0acf9d449c26c0556ba00aba1c65ded43d376930a39ff6b5cee21986. Reviewed/deployed SHA256: 9f5c987bb745b8edb6c2093f0dc4990f48229c0e429fbf388feda040cb2bfacb. Verified backup: /home/jwoltje/.mosaic/.pi/goal-backups/goal57-45d15b9891ff4ef392961615abefc8e7/launch-seat.sh. Atomic replacement preserved mode and refused concurrent drift. Rollback requires matching that deployed hash before atomically restoring the verified before-file; do not overwrite another owner's later launcher changes.
|
||||
|
||||
Only Resume's launch path changes. Other fleet seats may still have the duplicate-source configuration; do not describe this as fleet-wide consolidation. Existing running sessions were not touched. No goal state, settings, credential files, per-seat shims, source extensions, or foundation files were edited. No commit/push authority inferred. Evidence: .pi/evidence/goal57/. User acceptance: retry ~/.mosaic/fleet/agents/resume/launch.sh normally, without manually adding -ne. The existing credential warning remains correct and fail-closed. #57 stays open pending that retry.
|
||||
@@ -0,0 +1,873 @@
|
||||
# Workspace schema and execution audit discussion
|
||||
|
||||
Status: proposed details for owner review, not an implementation contract.
|
||||
Date: 2026-09-06. Parent plan:
|
||||
[Agent, project, and workspace foundation](2026-09-06_agent-project-workspace-foundation.md).
|
||||
Tracking: `mosaicstack/stack-v2#53`.
|
||||
|
||||
Field names, enums, enforcement mechanisms, and storage suggestions in this
|
||||
file are proposals. Behavior explicitly recorded as an owner ruling is agreed;
|
||||
exact transition schemas remain proposed. JSON blocks are illustrative records,
|
||||
not JSON Schemas. Requirements R1-R33 come from the owner discussion in the
|
||||
parent plan.
|
||||
Section 2.1 records the 2026-09-06 owner ruling; its field names and hash
|
||||
mechanics are still proposals, not additional approved schema decisions.
|
||||
Phase 2 now authorizes detailed drafting and read-only investigation. See the
|
||||
[Record and operation contract candidate](2026-09-06_foundation-phase2-contract.md).
|
||||
Adopting a schema or changing runtime behavior still requires later approval.
|
||||
|
||||
## 1. Identity and ownership
|
||||
|
||||
Proposed rule: stable IDs identify records; display names are editable labels.
|
||||
Names such as `code-be-01` remain useful to people but are not sufficient to
|
||||
identify an execution attempt. Use explicit composite references, not a
|
||||
flattened name such as `agent-project-workspace` that can be misparsed.
|
||||
|
||||
| Record | Proposed required information | Owner and scope |
|
||||
|---|---|---|
|
||||
| Agent definition | Schema version, agent ID, name, type, harness reference, SOUL reference, role/policy reference, configuration revision | Reviewed shared identity/configuration. No mission progress or project-specific chat. |
|
||||
| Project | Version, project ID, name, policy reference, revision | Authorized project administration. Contains workspaces; references registered agents. |
|
||||
| Project registration | Project ID, agent ID, scope role reference and narrowing restrictions, status, revision, granting actor and decision reference | Authorized administrator. Explicit visibility and participation grant, not an agent self-enrollment claim. |
|
||||
| Workspace | Version, project ID, workspace ID, name, revision, logical file/state references | Authorized workspace administration. Exactly one parent project, confirmed by owner Q5. |
|
||||
| Workspace registration | Project ID, workspace ID, agent ID, scope role reference and narrowing restrictions, status, revision, granting actor and decision reference | Requires valid project registration. May narrow but not widen it. |
|
||||
| Mission | Version, mission ID, owning scope, objective, acceptance criteria, status, revision | An authorized work owner. Scope and single-parent relationships are decided in D1; exact fields and lifecycle transitions remain to be specified. |
|
||||
| Task | Version, task ID, optional mission reference, owning workspace reference, purpose/acceptance criteria, dependencies, status, revision | Work owner sets intent; Q10 permits lightweight assignments without lengthy mission setup. Exact ad-hoc task shape remains proposed. Execution results do not automatically imply acceptance. |
|
||||
| Assignment | Version, assignment ID, task reference, agent ID, full scope, revision, active claim reference | Work coordinator. Distinct from the reusable agent definition and from one attempt to execute it. |
|
||||
| Session | Version, session ID, agent/project/workspace IDs, harness session reference, creation mode, predecessor reference when applicable | Workspace-scoped conversation metadata. Scope is immutable. Does not store credentials. |
|
||||
| Execution | Version, execution ID, full scope, session ID, launch decision, claim ID/generation, start evidence, resolved-context reference, launch/configuration hash references | Runtime-created attempt record. Resume retains session ID but creates a new execution ID. |
|
||||
| Connection | Version, connection ID, authenticated client actor, session/execution references, observer/controller mode, control generation, admission decision, lifecycle | Proposed representation of Q16. One controlling interface; observation and transfer require permission. Connection is not another agent execution. |
|
||||
| Context manifest | Version, immutable manifest ID, source references/revisions, selected assignments, resolved skills/policies, prior-session inclusion rule | Launch resolver. Records exactly which approved inputs were supplied. |
|
||||
| Event | Version, event ID, event kind, subject references, actor/execution identity, observation source, outcome/evidence references | Trusted producer, append-only. An agent's narrative is not execution evidence. |
|
||||
|
||||
A field that is optional needs an explicit absence rule in the eventual
|
||||
schema. Do not use a missing grant to mean unrestricted access, an absent
|
||||
workspace to mean the last-used workspace, or a missing task to mean any task.
|
||||
Owner Q10 permits conversation and allowed workspace inspection without a
|
||||
mission/task, but scope, permissions, and session/execution identity still
|
||||
apply. Requests to change things become explicit recorded assignments without
|
||||
lengthy mission setup. Proposed task representation permits no mission reference
|
||||
for such work, while requiring purpose and acceptance criteria. Missing mission
|
||||
is not an unrestricted grant. Taskless read/conversation event fields and the
|
||||
first guaranteed action-audit set remain D11.
|
||||
|
||||
Proposed formal-schema requirements:
|
||||
|
||||
- Every record has a version; reject unsupported versions and unknown fields.
|
||||
- References resolve to the intended scope and permitted record version.
|
||||
- Workspace ID alone is never a sufficient cross-project address.
|
||||
- Parent and membership status are checked, not just file existence. Owner
|
||||
Q5 requires single-parent hierarchy: each workspace has exactly one project;
|
||||
each workspace mission has at most one parent project mission, with standalone
|
||||
missions still owned by their workspace. Dependencies are separate typed
|
||||
references, not parents, ownership duplication, or access grants.
|
||||
- Display names never authorize access or select paths by themselves.
|
||||
- IDs and relative paths reject traversal, separators where inappropriate,
|
||||
symlink escape, and collisions. Missing targets refuse; do not auto-enroll.
|
||||
- Changed declarative records create new revisions. Immutable snapshots retain
|
||||
the exact revision used by a launch. Authority cannot change through state.
|
||||
- Final schemas must specify timestamp format, ID generation, uniqueness,
|
||||
array limits, status transitions, null/absence rules, and secret exclusions.
|
||||
|
||||
### 1.1 Scope permission roles, owner Q26
|
||||
|
||||
Use standard permission roles to simplify project/workspace registration, with
|
||||
registration-specific restrictions that narrow them. Reader, contributor,
|
||||
reviewer, and coordinator were examples, not an approved role catalog.
|
||||
|
||||
A scope role grants operations in the named scope; it does not change the
|
||||
agent's identity/type or expand reviewed role authority. Grants remain bounded
|
||||
by agent, project, workspace, mission, and task policy where applicable. Missing
|
||||
scope membership still refuses access. A reviewer role does not authorize
|
||||
accepting the holder's own delivery. Grant changes and role-definition changes
|
||||
are different operations; shared authority changes still require reviewed
|
||||
commits under repository policy.
|
||||
|
||||
Exact role names, permission sets, catalog representation, membership fields,
|
||||
and enforcement remain D3. Proposed references in section 1 are not an approved
|
||||
RBAC schema or a new configuration location.
|
||||
|
||||
## 2. Launch request and resolved context
|
||||
|
||||
Illustrative request for a new conversation continuing established work:
|
||||
|
||||
```json
|
||||
{
|
||||
"launchVersion": 1,
|
||||
"requestId": "req-001",
|
||||
"agentId": "code-be-01",
|
||||
"projectId": "mosaic-stack-v2",
|
||||
"workspaceId": "sessions",
|
||||
"conversation": {
|
||||
"operation": "fresh"
|
||||
},
|
||||
"work": {
|
||||
"operation": "continue",
|
||||
"assignmentIds": ["assignment-014"],
|
||||
"expectedStateRevision": 7
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Names are illustrative IDs. Authentication is not established by this JSON.
|
||||
The service must attach the verified requesting user/service identity from
|
||||
the authenticated invocation. A client-supplied actor field cannot grant
|
||||
permission. `requestId` detects duplicate requests; its exact retry semantics
|
||||
and storage lifetime remain D8.
|
||||
|
||||
Resume instead names `conversation.operation: resume` and an exact `sessionId`,
|
||||
or uses a documented unambiguous current-session lookup in the same scope.
|
||||
Owner Q4 ruling: if that scope has never had a conversation, default launch
|
||||
creates the initial conversation and announces it without an offer. Record
|
||||
initial creation as distinct from a successful resume. An established session
|
||||
that is missing or damaged causes an error, not automatic replacement. How
|
||||
first-use history is established and retained needs a storage/recovery design;
|
||||
absence of a transcript file alone is insufficient proof of first use.
|
||||
Fresh allocates a new session ID. Selecting work requires explicit assignment
|
||||
or task IDs; the exact record fields remain to be specified. Owner Q8 limits
|
||||
default Abandon to this agent's selected assignments; underlying work remains
|
||||
unless separately cancelled with authority. Q9 requires an explicit authorized
|
||||
assignment change before undertaking an unfinished prerequisite, not automatic
|
||||
addition to the selected work. Authorization can be delegated within the
|
||||
approved plan under Q7; it need not involve a human prompt for every change.
|
||||
|
||||
Proposed launch sequence:
|
||||
|
||||
1. Authenticate the requester and validate the entire request.
|
||||
2. Resolve the agent definition, project, workspace, and both registrations.
|
||||
3. Check project/workspace visibility and action rights. Derive effective
|
||||
authority from the agent ceiling and applicable registrations, mission,
|
||||
task, and explicit launch restriction. A lower layer never widens it.
|
||||
4. Resolve work references and dependencies against the expected state
|
||||
revision. State that changed during the decision causes reconciliation or
|
||||
refusal, not an unnoticed switch to a different assignment.
|
||||
5. Resolve Resume/Fresh and any active execution. An active Resume returns the
|
||||
Q11/Q15 conflict result or user offer, not another launch. Q17 Fresh requests
|
||||
controlled replacement: stop new actions, preserve checked state, resolve or
|
||||
identify in-flight effects, and confirm safety before acquiring a new active
|
||||
claim. Uncertain safety blocks replacement, never starts competing work.
|
||||
6. Generate execution-specific inputs and a context manifest. Paths to work
|
||||
state come from the selected workspace, never the agent's global seat dir.
|
||||
7. Persist the launch decision and identity binding before starting work.
|
||||
Record a start result separately. Refusal and failed start are not success.
|
||||
8. Start the program through its reviewed adapter using the exact context.
|
||||
Record actual startup and bind its session/process to the execution ID.
|
||||
9. On exit or interruption, preserve evidence and reconcile active claims.
|
||||
A missing completion record means unknown/interrupted, not successful.
|
||||
|
||||
These are semantic steps, not a claim that multiple file writes are one
|
||||
transaction. Crash ordering, claim recovery, and adapter support require
|
||||
explicit design and tests. They are D7-D9 and D12.
|
||||
|
||||
Context content approved in owner Q6, with field layout still proposed:
|
||||
|
||||
- Assigned mission and success criteria, current assigned tasks and status.
|
||||
- Relevant approved project/workspace decisions, not all project history.
|
||||
- Dependencies, blockers, and references to verified results.
|
||||
- A proposed next step, visibly distinguished from authorized assignment.
|
||||
- Unverified notes remain labelled as unverified.
|
||||
- No old conversation or automatic chat summary for Fresh. Resume retains only
|
||||
its authorized conversation; old session IDs may remain as audit references.
|
||||
|
||||
Shared governance, identity, and skills still apply through the launch contract.
|
||||
Owner Q22: user context is not automatically the whole profile. Only explicitly
|
||||
designated general preferences are shared by default; personal/project-specific
|
||||
information is supplied where authorized and relevant. Record selected source
|
||||
references/revisions without copying private content into public metadata.
|
||||
The exact source/revision representation, context grants, and publishers of
|
||||
checked state remain schema details. This decision does not change the current
|
||||
user-context files, dispatch implementation, or install a filtering mechanism.
|
||||
|
||||
A generated summary of work can help reading, but cannot replace source
|
||||
records or convert an agent claim into accepted state. Fresh must not quietly
|
||||
follow a transcript reference and reload the discarded conversation. Any
|
||||
intentional historical lookup needs an explicit policy and visible record.
|
||||
|
||||
The context manifest should record non-secret source identity/revision,
|
||||
resolved skill versions, effective policy reference, release/image identity,
|
||||
harness/provider/model selection, included work, and generation time. Hashes
|
||||
may bind non-secret content, but do not hash credentials into public records.
|
||||
Input manifests are execution-specific and write-once; shared prompt scratch
|
||||
files or per-agent mutable mission files do not meet this proposal.
|
||||
|
||||
### 2.1 Canonical configuration and change notices
|
||||
|
||||
Owner ruling, 2026-09-06, R16-R17: keep canonical SOUL shared by agent identity,
|
||||
load the current approved revision on each Resume or Fresh launch, and retain
|
||||
execution-specific launch inputs. Running instructions are not silently
|
||||
replaced. All TUI/GUI/WUI clients can reference the launch hash and display a
|
||||
canonical-versus-running configuration mismatch with a Fresh recommendation.
|
||||
A new configuration does not automatically restart sessions or abandon work.
|
||||
|
||||
Owner Q20: cover shared behavior-affecting agent configuration, not just SOUL.
|
||||
Included categories are SOUL, shared instructions, applicable enabled skill
|
||||
versions, harness/model settings, and role configuration. Exclude credentials,
|
||||
conversation history, and changing task progress. Q21: display automatic,
|
||||
non-blocking mismatch notices and support on-demand checks. Keep the mismatch
|
||||
visible without repeated interruption, and make details available. Permission
|
||||
removal is enforced separately, not delayed behind a Fresh recommendation.
|
||||
|
||||
Illustrative notice, wording still for review:
|
||||
|
||||
```text
|
||||
Darkwing / Mosaic Stack v2 / sessions
|
||||
Agent configuration changed since this session started.
|
||||
Start Fresh with the current configuration to continue your saved work.
|
||||
Details: running configuration, current configuration, launch record.
|
||||
```
|
||||
|
||||
Proposed record and comparison mechanics:
|
||||
|
||||
- `launchManifestHash` binds the complete non-secret input manifest for one
|
||||
execution. It identifies the recorded launch, not the latest desired state.
|
||||
- `agentConfigHashAtLaunch` identifies the comparable canonical agent
|
||||
configuration loaded by that execution, including SOUL content/version.
|
||||
- A shared status operation returns the execution and launch references,
|
||||
stored configuration hash, current canonical configuration hash/revision,
|
||||
check time, comparison status, and Fresh recommendation. All clients use
|
||||
this operation with the same access checks, not private copies of SOUL.
|
||||
- Compute both configuration hashes using the same versioned input definition
|
||||
and deterministic representation. Q20 establishes included categories; exact
|
||||
field/dependency coverage, algorithm, and encoding remain D16. Hash applicable
|
||||
shared content, not just its path or unrelated enabled resources.
|
||||
- Do not compare whole launch manifests for configuration drift: execution
|
||||
IDs, timestamps, and workspace/task selections legitimately differ between
|
||||
launches. Ordinary work-state changes must not look like base-config edits.
|
||||
- Do not include secret values or hashes of secrets. Credential refresh and
|
||||
selection need their own lifecycle; this notice does not solve #50.
|
||||
- Status is `matching`, `changed`, or `unknown`. Missing canonical data,
|
||||
unsupported hash format, or unverified loaded inputs cannot report matching.
|
||||
Never rewrite an old launch hash to make a running session look current.
|
||||
- Pin a consistent input snapshot before starting the execution. If canonical
|
||||
configuration changes during startup, compare against that exact snapshot
|
||||
afterward; never label a mixture of two revisions as the loaded state.
|
||||
- A launch fingerprint describes what was supplied and verified at startup.
|
||||
It is not continuous proof that nothing in the process changed afterward.
|
||||
Any supported in-process reconfiguration needs a new observed event/status
|
||||
model; until then, such changes cannot be represented as verified matching.
|
||||
- Record comparison observations with execution ID, compared revisions/hashes,
|
||||
check time, source, and reason, without exposing private configuration.
|
||||
Canonical changes need their own actor/revision record. Notification delivery
|
||||
does not count as relaunch or adoption of the new configuration.
|
||||
- Resume with changed configuration warns that the retained conversation can
|
||||
still reflect old instructions. Fresh starts without that conversation;
|
||||
adopting the new configuration does not clear the workspace or choose
|
||||
Abandon on the user's behalf.
|
||||
|
||||
Q21 settles automatic non-blocking notices plus on-demand checking. Transport,
|
||||
change-detection mechanism, acknowledgement behavior, and notification frequency
|
||||
remain D16. No polling service is installed or implementation authorized by
|
||||
this decision. Short hashes may be shown for readability; the full values and
|
||||
source revisions must remain available through the authorized launch record.
|
||||
Hashes provide comparison and evidence binding, not permission or proof of
|
||||
safe behavior. Permission revocation still follows D3 and managed-action
|
||||
checks; an old launch snapshot is not a right to keep revoked authority.
|
||||
|
||||
## 3. State is work data, not authority
|
||||
|
||||
Proposed separation:
|
||||
|
||||
| Information | How it changes |
|
||||
|---|---|
|
||||
| Desired mission, task, membership, and policy | Authorized, versioned changes. Workers cannot elevate grants through progress notes. |
|
||||
| Observations and decisions | Append-only events with actor, scope, source, and evidence. Corrections reference the earlier event. |
|
||||
| Current task/project/workspace status | A replaceable current view built from accepted changes, with revision checking and a named writer. |
|
||||
| Chat transcript | Conversation history, not authoritative task completion or permission. |
|
||||
| Files produced by work | Mutable working files plus immutable evidence references where needed. A file alone does not prove acceptance. |
|
||||
|
||||
Prefer one authoritative copy of project decisions. A launch records the
|
||||
project revision it saw. Owner Q25 requires reconciliation of affected work
|
||||
after approved plan changes; an old conversation cannot overrule a newer
|
||||
approved decision. Revision delivery, impact detection, and stale-action
|
||||
rejection still need design and tests, including on Resume.
|
||||
|
||||
Candidate work states, still for review: `proposed`, `ready`, `active`,
|
||||
`blocked`, `ready-for-review`, `accepted`, `abandoned`. Completion reported by
|
||||
an execution can move work toward review, never silently to acceptance by its
|
||||
own author. Owner Q7 permits an authorized independent reviewer to accept
|
||||
routine tasks against agreed criteria. Owner-controlled implementation
|
||||
checkpoints remain separate. Task states, session lifecycle, and execution
|
||||
outcomes are different fields.
|
||||
|
||||
Owner Q8: default Abandon ends this agent's selected assignments. It does not
|
||||
cancel the underlying shared mission/task or another agent's involvement.
|
||||
Underlying work remains available for reassignment unless separately cancelled
|
||||
with authority. Selection preserves source IDs and dependency evidence;
|
||||
unselected records remain present with explicit status. Owner Q9 requires an
|
||||
explicit assignment change before undertaking unfinished prerequisite work.
|
||||
Under Q7, an authorized coordinator can approve that change within the plan
|
||||
without a human prompt. No silent expansion by the executing worker.
|
||||
Cross-project selection remains excluded in the initial proposal unless
|
||||
separately authorized and designed.
|
||||
|
||||
Two writers updating the same task need an expected revision and conflict
|
||||
response. Avoid last-writer-wins. A registration change or permission
|
||||
revocation during execution must be enforceable at subsequent managed actions,
|
||||
not only checked once at launch. Owner Q14 requires stopping the affected
|
||||
workspace executions and blocking further actions there while leaving other
|
||||
independently authorized work running. Global agent disable is a separate
|
||||
operation. In-flight effects require reconciliation; stop is not undo. Exact
|
||||
revocation propagation, stopping, and enforcement mechanisms remain D3/D7.
|
||||
|
||||
### 3.1 Delegated autonomy, owner Q7
|
||||
|
||||
A software factory needs autonomous decisions within an established goal and
|
||||
plan. Delegated agents may decompose work and make non-destructive decisions
|
||||
that advance that goal without seeking user approval each time, provided they
|
||||
stay within their authority and the plan. An authorized independent reviewer
|
||||
can accept routine task results against agreed criteria. User approval still
|
||||
controls declared phase boundaries, plan deviations, and protected operations.
|
||||
|
||||
Proposed evidence for each delegated decision: deciding actor/execution,
|
||||
approved plan revision, delegation reference/limits, affected task/assignment,
|
||||
reason, and observed result or follow-up evidence. Record the actual delegated
|
||||
authorizer, not a fictitious owner approval. The acting agent's claim that an
|
||||
action is harmless is not proof that it is authorized or within scope.
|
||||
|
||||
Exact permitted actions, risk checks, and delegation revocation are still D3
|
||||
and D11. This ruling does not authorize implementation during this interview,
|
||||
unbounded task expansion, or self-approval of a worker's delivery.
|
||||
|
||||
### 3.2 Approved plan changes, owner Q25
|
||||
|
||||
Pause affected work when an approved goal, plan, or acceptance criterion changes.
|
||||
Account for actions already underway rather than assuming they can be undone.
|
||||
An authorized coordinator may reconcile and adjust assignments within delegated
|
||||
limits; independently valid, unaffected work may continue. Resume affected work
|
||||
only after reconciliation. Escalate beyond delegated authority or when safe
|
||||
resolution cannot be established. An unapproved proposal does not replace the
|
||||
approved plan or grant authority to continue under new criteria.
|
||||
|
||||
Proposed transition evidence: old/new approved revision references, change
|
||||
approval, affected assignments/dependencies, outstanding-action disposition,
|
||||
coordinator authority, and resume/reassignment decision. Add evidence rather
|
||||
than rewriting original launch inputs or historical action records. Exact
|
||||
fields, publishing/notification paths, impact detection, and stale-revision
|
||||
fencing remain to be specified under D2/D8/D15.
|
||||
|
||||
This differs from a non-blocking shared-configuration mismatch notice under
|
||||
Q21. An affected assignment must not continue solely because a Fresh launch
|
||||
has not been requested. Q25 does not require automatic Fresh for every plan
|
||||
change; context reconciliation and controlled replacement remain distinct.
|
||||
|
||||
## 4. Accountability and action evidence
|
||||
|
||||
The agent identity remains `code-be-01`. Execution metadata answers which
|
||||
of its sessions acted, on whose request, within which scope and authority.
|
||||
This is useful even when no cross-workspace mistake occurred.
|
||||
|
||||
Distinguish five things:
|
||||
|
||||
1. Requester: the authenticated human/service asking for work.
|
||||
2. Performer: the reusable agent and the particular execution/session.
|
||||
3. Producer: the launcher, tool gateway, adapter, or external receipt source
|
||||
that observed and recorded an event.
|
||||
4. Subject: project/workspace, assignment/task, and target of the action.
|
||||
5. Authority: policy and membership revisions plus any required approval.
|
||||
|
||||
### 4.1 Proposed event envelope
|
||||
|
||||
```json
|
||||
{
|
||||
"eventVersion": 1,
|
||||
"eventId": "event-002",
|
||||
"eventType": "action.finished",
|
||||
"executionId": "exec-003",
|
||||
"sessionId": "session-002",
|
||||
"agentId": "code-be-01",
|
||||
"projectId": "mosaic-stack-v2",
|
||||
"workspaceId": "sessions",
|
||||
"assignmentId": "assignment-014",
|
||||
"taskId": "task-014",
|
||||
"actionId": "action-001",
|
||||
"causedByEventId": "event-001",
|
||||
"sequence": 2,
|
||||
"recordedAt": "2026-09-06T00:00:00Z",
|
||||
"producer": "managed-file-tool",
|
||||
"operation": "file.write",
|
||||
"target": { "relativePath": "notes/result.md" },
|
||||
"authorityRef": "decision-003",
|
||||
"outcome": "succeeded",
|
||||
"evidenceRefs": ["receipt-001"]
|
||||
}
|
||||
```
|
||||
|
||||
This is a shape example, not a real receipt. `decision-003` would bind the
|
||||
requester's authenticated identity, full launch scope, current permission
|
||||
check, claim generation, and any owner approval. The producer fills those
|
||||
fields from trusted execution context; it does not accept agent-supplied
|
||||
scope or success statements as evidence. Evidence references must resolve to
|
||||
accessible immutable records with defined content and provenance.
|
||||
|
||||
### 4.2 What counts as an action
|
||||
|
||||
Proposed minimum classes to enumerate before implementation:
|
||||
|
||||
- Launch/resume/fresh/replacement, refusals, exits, and interrupted executions.
|
||||
- Mission/task assignment, claim, state change, acceptance, and abandonment.
|
||||
- Registration/permission changes and explicit owner approvals.
|
||||
- Managed file/tool invocations, external requests, message send/delivery,
|
||||
repository integration, and release operations when these are permitted.
|
||||
- Usage observations with provider evidence or an explicit estimate label.
|
||||
|
||||
Not all of these exist as managed operations today. Adding an enum does not
|
||||
create interception or enforcement. D11 decides the first guaranteed set.
|
||||
|
||||
### 4.3 Intent, effect, and result
|
||||
|
||||
Proposed behavior for managed actions with side effects:
|
||||
|
||||
1. Persist authorized intent with action ID, target scope, and expected state.
|
||||
2. Perform the action through the enforcing component.
|
||||
3. Persist the observed result and evidence, linked to the intent.
|
||||
|
||||
Owner Q19: if required intent cannot be persisted, refuse before the action.
|
||||
If the action may have happened but its result cannot be recorded, block the
|
||||
affected execution and treat the outcome as unknown. Other executions may
|
||||
continue only if their own required recording still works. Do not retry
|
||||
blindly, claim the action never happened, or invent a success receipt.
|
||||
|
||||
Owner Q18: an authorized agent/service may investigate using non-destructive
|
||||
checks within delegated authority. Dependent work resumes only after evidence
|
||||
resolves the result; otherwise escalate. The original uncertain action must
|
||||
not be repeated merely as a test. Recovery checks must also satisfy their
|
||||
applicable permissions and recording requirements, not bypass an audit outage.
|
||||
|
||||
Proposed mechanics: distinguish unknown state from observed failure; after
|
||||
recording is restored, append recovered evidence with its actual observation
|
||||
and recovery times. Do not overwrite prior events or claim that a record was
|
||||
written during an outage when it was not. The exact out-of-band failure signal
|
||||
and recovery receipt format remain D8/D9. External effects and local logging
|
||||
are not one atomic write.
|
||||
|
||||
Use external request/receipt IDs and idempotency support where available.
|
||||
Without them, an interrupted action may require human reconciliation. A zero
|
||||
shell exit code only proves that the invoked process reported success; it
|
||||
does not prove every intended external effect. Clocks are informational;
|
||||
per-execution sequence and causal references establish local ordering without
|
||||
claiming a global order across machines.
|
||||
|
||||
### 4.4 Coverage and trust limits
|
||||
|
||||
A launcher can record process lifecycle. It cannot automatically observe every
|
||||
file write, child process, network request, or manually entered shell command
|
||||
inside a running program. A transcript is useful evidence but is neither a
|
||||
complete action audit nor a trusted authority source.
|
||||
|
||||
Each adapter must declare its observable/enforceable operation set. Record
|
||||
coverage with the execution, for example lifecycle-only versus managed-tool
|
||||
events. If a requested audit guarantee exceeds support, refuse that launch
|
||||
mode or require a separately approved limited mode. Never report full coverage
|
||||
just because the process has an execution ID.
|
||||
|
||||
No claim of tamper-proof logging follows from append-only application code.
|
||||
A worker that can write the evidence directory could forge or remove records.
|
||||
A trusted producer outside the worker's write permissions is a proposed
|
||||
requirement for trustworthy managed-action receipts. Storage access and
|
||||
failure tests must establish it; the current broad data-root mount does not.
|
||||
|
||||
Owner Q13 chose concise action records: actor and project/workspace/session/
|
||||
task, operation, target, authorization, outcome, and evidence references.
|
||||
Detailed evidence is separate and access-controlled; do not copy full request/
|
||||
response content into every action event. Neither metadata nor evidence may
|
||||
contain credentials. Exact safe field lists and evidence retention remain open.
|
||||
|
||||
Never put credentials, auth headers, raw environment contents, or arbitrary
|
||||
unfiltered tool payloads in new audit metadata. Use operation-specific safe
|
||||
fields and protected evidence references. Redaction failures must not leak
|
||||
secrets through diagnostics. Define who may view sensitive task/file/message
|
||||
content; workspace RBAC must apply to audit queries too. Existing transcripts
|
||||
and run outputs need a separate privacy assessment, not retroactive claims.
|
||||
|
||||
## 5. Messages and concurrent execution
|
||||
|
||||
Proposed message envelope: version, message ID, sender identity/scope,
|
||||
authenticated producer, destination agent/project/workspace, optional exact
|
||||
session/execution/assignment, message kind, causal/reply reference, creation
|
||||
and expiry times, and body reference with visibility rules.
|
||||
|
||||
User-addressed workspace messages may follow the workspace's current authorized
|
||||
session under a stated policy. Execution results and approvals bind to their
|
||||
exact execution/assignment revision. A late result must not become an
|
||||
instruction or approval for replacement work. Ambiguous destinations refuse;
|
||||
transport delivery and task acceptance are separate receipts.
|
||||
|
||||
Owner Q12 permits shared mission/task/decision records and authorized evidence
|
||||
within a workspace, not blanket access to other agents' conversations. A
|
||||
transcript needs a separate read permission or explicit authorized handoff.
|
||||
Neither a shared workspace nor a read grant automatically injects that chat
|
||||
into another session. Fresh's old-chat exclusion still applies.
|
||||
|
||||
Default active limit is one session for each agent/project/workspace tuple.
|
||||
Owner Q11: an interactive Resume that finds the session already running must
|
||||
state the conflict and offer to connect, never automatically connect, replace,
|
||||
or create a duplicate. The owner may already be connected elsewhere. Q15:
|
||||
a service receives a machine-readable already-active result with the existing
|
||||
execution identity and must explicitly request connection or another authorized
|
||||
operation; it does not receive an unanswered interactive prompt.
|
||||
|
||||
Owner Q16: one controlling interface connection at a time. Other permitted
|
||||
connections may observe. Taking control is explicit and requires appropriate
|
||||
authority; observation does not grant control. Proposed connection records
|
||||
bind client identity and mode to the exact session/execution. Control-transfer
|
||||
mechanics must prevent a stale controller from submitting commands after
|
||||
transfer. Ordinary scoped inter-agent messages are not an implicit controller
|
||||
connection; their admission/attribution rules remain distinct.
|
||||
|
||||
Owner Q17: Fresh requests controlled replacement rather than requiring a
|
||||
separate stop command. Stop admitting new actions, preserve checked work state,
|
||||
and settle or identify unfinished effects. Start Fresh only after safe
|
||||
replacement is established; otherwise stop and report. This is not permission
|
||||
to replay uncertain actions or import a conversation summary as checked state.
|
||||
Adapter attachment, control transfer, stop/recovery, and durable claim behavior
|
||||
must be measured and tested before claiming support.
|
||||
|
||||
User tuning changes a limit, not task ownership. Several agents can work in
|
||||
one workspace but shared files/tasks still need coordination. Proposed active
|
||||
claims have a unique ID and increasing generation. Managed writers reject
|
||||
stale generations, so an old process cannot continue after its replacement.
|
||||
Timeout alone is not proof that an old process has stopped, especially for
|
||||
unmanaged external actions. Recovery policy is D7.
|
||||
|
||||
Budgets and automatic scaling are deferred requirements. Do not place invented
|
||||
budget defaults into the first schema or imply that recorded usage alone
|
||||
provides a hard spending limit. The limit scope, approval rights, usage delay,
|
||||
and response to overruns need a later planning decision.
|
||||
|
||||
## 6. Storage constraints, not a migration plan
|
||||
|
||||
Keep `<dataRoot>/runs/` as canonical write-once execution evidence. Workspace
|
||||
views reference evidence there rather than duplicating or rewriting it.
|
||||
Existing `result.json` records remain untouched. A future version may extend
|
||||
the record contract through new records and readers, never by rewriting old
|
||||
runs. New events are append-only and terminal results are written once.
|
||||
|
||||
Proposed organization to discuss: projects own workspace metadata/work state;
|
||||
workspace-and-agent scope owns session references; execution IDs own launch
|
||||
inputs and receipts. Whether session bytes live in workspace subdirectories
|
||||
or remain under `<dataRoot>/sessions/` is D6. Logical ownership does not force
|
||||
a particular filesystem layout. No new storage path is approved by implication.
|
||||
|
||||
Every persistent file class needs an owner, writer, reader policy, retention
|
||||
rule, and recovery rule. Shared agent settings are not a second system config.
|
||||
Keep secrets in runtime credential storage, not project/workspace metadata.
|
||||
Do not treat a project directory as permission to edit reviewed role authority.
|
||||
|
||||
Current `prune` receipts and data-root `reset` behavior still govern existing
|
||||
records. The new design must specify dangling references after pruning,
|
||||
metadata export, reset scope, and recovery before migration. It does not
|
||||
silently protect new records from reset or authorize a new deletion path.
|
||||
A safe rollback cannot depend on editing historical evidence.
|
||||
|
||||
### 6.1 Workspace retirement and legacy adoption
|
||||
|
||||
Owner Q23: closing a workspace retires it from active work. Stop its active
|
||||
work safely, retain files/history, and prevent ordinary new launches until it
|
||||
is reopened. This is not deletion or an implicit cancellation of every mission.
|
||||
Outstanding actions and assignment dispositions need reconciliation under the
|
||||
existing replacement/recovery rules. Reopening must check current authority;
|
||||
it must not automatically restore a revoked membership. Those exact transition
|
||||
records and reopening behavior are still to be designed and reviewed.
|
||||
|
||||
Owner Q24: preserve existing unscoped sessions as legacy records. Adopting one
|
||||
requires explicit reviewed project/workspace assignment. Do not guess from
|
||||
agent or directory names, place everything in a default project automatically,
|
||||
or rewrite old execution evidence to pretend it originally had new scope.
|
||||
|
||||
Proposed adoption evidence: authorized requester/reviewer, legacy source ID and
|
||||
non-secret content identity, destination agent/project/workspace, membership
|
||||
checks, and a new mapping/adoption receipt. This is proposed metadata, not an
|
||||
approved migration API. Ambiguous lineage or missing access refuses adoption.
|
||||
Old conversation content remains historical and is not loaded by Fresh.
|
||||
|
||||
Actual storage paths, retention intervals, retirement state transitions,
|
||||
reopening, recovery after prune/reset, and rollback remain D6. Closing does
|
||||
not exempt records from separately authorized repository retention/reset rules,
|
||||
and no new deletion tool is approved by the retirement decision.
|
||||
|
||||
## 7. Open decisions
|
||||
|
||||
| ID | Owner decisions or open question | Remaining proposal or engineering work |
|
||||
|---|---|---|
|
||||
| D1 | Scope/hierarchy resolved in Q1/Q5: project owns N workspaces, workspace has exactly one project; a workspace mission has at most one parent project mission or stands alone within its owning scope. Dependencies are not parents. | Formal field definitions and mission lifecycle/completion rules remain to be specified; no multiple-parent ownership. |
|
||||
| D2 | Recovery content resolved in Q6: assigned mission/criteria, tasks/status, approved decisions, dependencies/blockers, verified-result references, labelled proposed next step and unverified notes; no old chat/automatic summary. | Exact record representation and publisher authority remain schema details; routine acceptance follows Q7/D15. |
|
||||
| D3 | Q2/Q3/Q14 resolve bounded delegation, scoped visibility, and affected-execution revocation. Q26 chooses standard scope permission roles with per-registration narrowing; they do not redefine agent identity or expand ceilings. | Exact role catalog/grants, record fields, actor authentication, propagation, and stopping need schemas/enforcement. Role names were illustrative; shared role authority remains reviewed-commit controlled. |
|
||||
| D4 | Resolved by Q4 clarification A: create and announce the initial conversation automatically on genuine first use, without an offer. Later default launches resume; missing/damaged established sessions and ambiguity are errors. | Record initial creation distinctly from resume. Specify reliable first-use history and recovery checks under D6/D8; do not infer first use from a missing transcript. |
|
||||
| D5 | Default behavior resolved in Q8/Q9: end only this agent's selected assignments on Abandon; undertaking unfinished prerequisites needs an explicit authorized assignment change. | Preserve underlying work/history and other assignments. Q7 allows within-plan delegated authorization without asking the user each time. Fields and interruption sequencing remain open. |
|
||||
| D6 | Q23 retires closed workspaces safely while retaining files/history and blocking ordinary launches until reopened. Q24 requires explicit reviewed legacy adoption, not inferred membership. | Exact layout, lifecycle/reopening, mapping records, retention intervals, prune/reset reference handling, and migration rollback remain engineering work. Preserve old evidence. |
|
||||
| D7 | Q11/Q15 require conflict/offer for users and already-active result for services; connection is explicit. Q16 permits one controller plus authorized observers. Q17 requests controlled Fresh replacement, refusing unsafe overlap. | Connection/claim schemas, stale-controller rejection, adapter support, stopping, crash recovery, and concurrency tuning remain engineering work. Timeout alone is not proof an execution stopped. |
|
||||
| D8 | Q18 permits delegated non-destructive investigation of unknown outcomes, evidence-based continuation, and escalation when unresolved; no blind replay. Exact atomicity/retry guarantees remain open. | Unique requests/actions, linked intent/result/recovery evidence, actual observation times, and explicit unknown status; no blanket exactly-once promise. |
|
||||
| D9 | Q19 blocks affected executions when required audit recording fails; independently recordable work may continue. Effects with missing result evidence follow Q18 recovery. Storage integrity/trust and failure reporting details remain open. | Refuse before effects; never bypass audit requirements for recovery checks. A trusted writer and out-of-band outage signal need design and tests. |
|
||||
| D10 | Partly resolved by R16-R17: current approved SOUL at launch, stable per-execution inputs, and cross-interface mismatch notice. Credential sharing and other configuration lifecycle details remain open. | Preserve the owner ruling in section 2.1; reconcile #50 before coding. Exact fingerprint and notification mechanics move to D16. |
|
||||
| D11 | Q10 permits unassigned conversation/inspection and recorded change assignments. Q13 separates concise metadata from controlled evidence. Q27 A requires command-invocation records with enforced limits, not separate tracing of every internal file/network effect. | Complete taskless/read/chat fields, command/event schemas, evidence allowlists, and enforcement. No isolation, credential, recording, revocation, or recovery gate is waived. |
|
||||
| D12 | Phase-2 inspection of documentation extracted from the pinned image establishes documented exact-session selection, RPC/JSON events, queue/abort distinctions, and no built-in sandbox. Current Mosaic adapter uses plain print/native TUI instead. | No live RPC, hook, controller, or isolation test yet. Investigate the enforcing integration and failure behavior; host Pi 0.85.1 is not evidence for runtime 0.84.4. |
|
||||
| D13 | Q12/Q13 separate shared work records, transcript grants, and controlled audit evidence. Q22 shares only designated general preferences by default and supplies personal/project context only where authorized and relevant. | Exact context classification/grants and enforcement remain to be specified. Read permission does not imply automatic transcript loading; replace blanket global user-context injection only through approved implementation. |
|
||||
| D14 | Jason answered Q28 A: a Mosaic-controlled terminal backed by Pi is acceptable. R34 records this; native UI parity is not an initial requirement. | Runtime/control/input schemas and shared operations now form an owner-review candidate. No UI implementation, engine replacement or safety waiver follows. |
|
||||
| D15 | Q7 allows authorized independent routine acceptance and delegated non-destructive within-plan decisions. Q25 pauses affected work after approved plan changes, permits delegated reconciliation, and lets independently valid unaffected work continue. | Preserve owner checkpoints and plan-deviation/protected-operation approval. Readiness writers, impact detection, stale-action rejection, conflicts, and change evidence remain open; workers do not self-accept. |
|
||||
| D16 | Q20 covers shared behavior-affecting config: SOUL, instructions, applicable enabled skills, harness/model settings, and role config, excluding credentials/chat/task progress. Q21 requires automatic non-blocking notices and on-demand checking. | Exact hash/dependency fields, encoding, snapshot verification, delivery/acknowledgement, and frequency remain technical details. Separate comparable config from whole launch identity; no silent refresh. |
|
||||
|
||||
The owner authorized a grill-me interview on 2026-09-06. Ask each round's
|
||||
questions only after their prerequisites are settled. Record answers without
|
||||
silently resolving qualifications. Independent analysis later may add questions
|
||||
or reject recommendations, not overwrite owner rulings.
|
||||
|
||||
### 7.1 Owner interview record, round 1, 2026-09-06
|
||||
|
||||
Source: Jason's answers in pi session
|
||||
`01a06e48-0718-71f2-a889-c263c4800fb9`. Interview Q numbers are separate from
|
||||
D decision IDs. This records partial alignment, not full design approval.
|
||||
|
||||
- Q1, D1: "c. Glad you thought of that answer too." Option C permits both
|
||||
project and workspace missions with explicit links, including standalone
|
||||
workspace missions within project scope. Each has one authoritative owner.
|
||||
- Q2, D3: "B". User delegates bounded authority for system registration and
|
||||
assignments. Outside-limit actions require approval, not agent self-grants.
|
||||
- Q3, D3: "B". Project membership gives shared project information plus
|
||||
explicitly permitted workspaces, not visibility into all workspace content.
|
||||
- Q4, D4, verbatim: "B, I'm not sure the offer is necessary. A declaration of
|
||||
initial conversation instantiation is good, but an offer on a non-existent
|
||||
item is unnecessary." The no-offer preference is clear. First-creation
|
||||
behavior needs a focused clarification before the command rules are settled.
|
||||
|
||||
Q4 clarification, 2026-09-06: Jason answered "A" to automatic first creation
|
||||
with an announcement and no offer. Later launches resume by default; Fresh
|
||||
explicitly starts a new conversation. Missing or damaged previously established
|
||||
conversations are errors rather than first-use cases. This resolves the
|
||||
qualification above without rewriting the original answer.
|
||||
|
||||
Next: continue mission relationships, Fresh recovery, completion authority,
|
||||
and work-selection decisions. Do not treat an unanswered question or an
|
||||
interview recommendation as an owner decision.
|
||||
|
||||
### 7.2 Owner interview record, round 2, 2026-09-06
|
||||
|
||||
Source: Jason's answers in the same pi session as round 1. Previous answers
|
||||
remain recorded above; this is not full plan/schema approval.
|
||||
|
||||
- Q5, D1: A, with the owner qualification that parent:child relationships
|
||||
stay strictly one-to-many. A project has N workspaces; each workspace requires
|
||||
exactly one parent project. A workspace mission has at most one parent
|
||||
project mission as proposed in Q5. Dependencies are different from parents.
|
||||
- Q6, D2: "agreed" to the full proposed Fresh/Continue information set:
|
||||
mission/criteria, assigned tasks/status, approved decisions, dependencies,
|
||||
blockers, verified-result references, labelled proposed next step, and
|
||||
labelled unverified notes. Old chat and automatic summaries stay out.
|
||||
- Q7, D15/D3: B. Authorized reviewers may accept routine tasks. Jason clarified
|
||||
that a software factory needs autonomous goal-directed decisions, layered
|
||||
decomposition, and delegated non-destructive actions within the established
|
||||
plan without user interaction each time. Deviation does not inherit that
|
||||
authority. The agreed user implementation checkpoints remain in place.
|
||||
- Q8, D5: A. Default Abandon ends this agent's selected assignments; missions
|
||||
and tasks remain for reassignment unless separately cancelled with authority.
|
||||
- Q9, D5: B. Show an unfinished prerequisite and obtain an explicit assignment
|
||||
change before doing it; include relevant dependency information, not silent
|
||||
extra work. Read with Q7: the authorizer may be a delegated coordinator, not
|
||||
necessarily the user. Record the assignment change and its actual authority.
|
||||
|
||||
Next questions concern review separation with reused agent identities,
|
||||
unassigned interaction, revocation, concurrency conflicts, and audit behavior.
|
||||
No downstream recommendation is settled by the answers above.
|
||||
|
||||
### 7.3 Owner interview record, round 3, 2026-09-06
|
||||
|
||||
Source: Jason's answers in the same pi session as rounds 1-2. No full schema
|
||||
approval or implementation authorization is implied.
|
||||
|
||||
- Q10, D11: A. Permit conversation and authorized workspace inspection before
|
||||
a mission/task exists. Requests to change things become explicit recorded
|
||||
assignments without requiring a lengthy setup process.
|
||||
- Q11, D7/D14: owner correction to the automatic-connect recommendation.
|
||||
State the active-session conflict and offer connection because the user may
|
||||
already be connected and have forgotten. Owner example, verbatim:
|
||||
"I see that session is aalready active. Would you like to connect?"
|
||||
No automatic connection or duplicate launch. Non-interactive service
|
||||
response was not specified by this answer and remains open.
|
||||
- Q12, D13: B. Shared work records and permitted evidence, but another agent's
|
||||
conversation needs a separate permission or explicit handoff. Read access
|
||||
does not imply automatic loading into the receiving conversation.
|
||||
- Q13, D11/D13: A. Concise action metadata and evidence references; detailed
|
||||
evidence separately controlled, not copied wholesale into each audit event.
|
||||
Credentials must not appear in either.
|
||||
- Q14, D3/D7: A. Workspace membership removal stops affected executions and
|
||||
blocks further actions there; other independently authorized work continues.
|
||||
Global agent disable is separate. Reconcile effects already in flight.
|
||||
|
||||
Next: clarify service-side conflict handling and decide connection ownership,
|
||||
replacement, uncertain-action recovery, and audit-failure behavior. Technical
|
||||
capability checks must supply facts, not be questions the owner must guess at.
|
||||
|
||||
### 7.4 Owner interview record, round 4, 2026-09-06
|
||||
|
||||
Source: Jason answered A to each of Q15-Q19 in the same pi session as prior
|
||||
rounds. These settle behavior, not unmeasured runtime capabilities.
|
||||
|
||||
- Q15, D7/D14: service receives an already-active result with execution identity
|
||||
and explicitly requests any authorized connection/next operation. No automatic
|
||||
attachment or interactive offer waiting for a nonexistent human response.
|
||||
- Q16, D7: one controlling interface connection; other authorized connections
|
||||
may observe, with explicit control transfer and conversation access checks.
|
||||
- Q17, D7: Fresh requests controlled replacement. Stop new actions, preserve
|
||||
checked state, settle or identify unfinished actions, and only replace safely.
|
||||
If safety cannot be established, stop/report rather than overlap executions.
|
||||
- Q18, D8: delegated authorized agents/services may investigate unknown results
|
||||
with non-destructive checks. Resume dependent work only on resolving evidence;
|
||||
otherwise escalate. Repeating the uncertain action is not a recovery test.
|
||||
- Q19, D9: audit-recording failure blocks affected executions. Other work may
|
||||
continue only when its required recording works. Refuse before effects, and
|
||||
reconcile effects already issued whose results could not be recorded.
|
||||
|
||||
Next: configuration fingerprint scope/notices and workspace retention/migration
|
||||
choices. Exact schema and runtime mechanisms remain unapproved until examined;
|
||||
no implementation or broader phase advancement follows from these answers.
|
||||
|
||||
### 7.5 Owner interview record, round 5, 2026-09-06
|
||||
|
||||
Source: Jason answered Q20 B, Q21 B, Q22 B, Q23 A, Q24 A in the same pi session.
|
||||
These are behavior decisions, not permission to change runtime configuration.
|
||||
|
||||
- Q20, D16: shared behavior-affecting configuration fingerprint, not SOUL only.
|
||||
Include SOUL, shared instructions, applicable enabled skill versions,
|
||||
harness/model settings, and role config. Exclude credentials, chat, and task
|
||||
progress; revocation remains an enforcement event, not merely a notice.
|
||||
- Q21, D16: automatic non-blocking notices plus on-demand checks; keep mismatch
|
||||
visible without repeated interruption or automatic restart.
|
||||
- Q22, D13: designated general preferences may be shared; personal and project
|
||||
information is scoped by authorization and relevance, not a blanket profile.
|
||||
- Q23, D6: close means retire from active work, stop safely, retain files and
|
||||
history, and block normal launches until reopened. Cleanup/deletion is separate.
|
||||
- Q24, D6: preserve legacy sessions, then explicitly review and assign their
|
||||
project/workspace on adoption. Do not infer membership or auto-place them.
|
||||
|
||||
Next behavioral questions concern changes to an approved plan during execution
|
||||
and how scope permissions are expressed. Engineering questions remain open for
|
||||
fact-finding and schema review, not guesses or silent implementation decisions.
|
||||
|
||||
### 7.6 Owner interview record, round 6, 2026-09-06
|
||||
|
||||
Source: Jason answered Q25 A and Q26 B in the same pi session.
|
||||
|
||||
- Q25, D15: pause affected work after an approved plan change and reconcile
|
||||
underway effects and assignments. A delegated coordinator can adjust work
|
||||
within its limits; unaffected work may continue. Escalate beyond authority
|
||||
or when safe resolution is unavailable, rather than finish obsolete work.
|
||||
- Q26, D3: standard scope roles with registration-specific narrowing. A scope
|
||||
role describes permission there, not agent identity/type. Exact names and
|
||||
grant lists remain subject to review; no expanded agent ceiling is implied.
|
||||
|
||||
### 7.7 Interview checkpoint, confirmation pending
|
||||
|
||||
Q1-Q26 have answers, with qualifications retained above, including the explicit
|
||||
Q4 correction. No additional owner-behavior question is currently ready. Ask
|
||||
Jason to confirm shared understanding of R1-R32; do not equate these individual
|
||||
answers with interview completion or approval of the whole schema.
|
||||
|
||||
D1-D16 retain unresolved engineering questions: fields and record lifecycles,
|
||||
authorization/enforcement, work-state revisions and recovery, adapter/session
|
||||
capabilities, observable action coverage, storage/migration, configuration
|
||||
hashing/notification, and concrete command/UI contracts. Detailed design and
|
||||
fact-finding need next-phase authorization. Technical findings that introduce
|
||||
new behavioral tradeoffs come back to the owner, not silent design decisions.
|
||||
|
||||
No mapping, independent gap analysis, implementation, migration, commit, push,
|
||||
or issue closure follows automatically from this interview checkpoint.
|
||||
|
||||
### 7.8 Owner confirmation of intended behavior, 2026-09-06
|
||||
|
||||
Following round 6 and the assistant's behavior summary, Jason replied:
|
||||
"That looks correct." This confirms shared understanding of intended behavior
|
||||
represented by R1-R32. Section 7.7 records the preceding pending checkpoint;
|
||||
its confirmation request is now satisfied.
|
||||
|
||||
This confirmation does not approve exact schemas, establish runtime support,
|
||||
or resolve the remaining engineering branches. Phase 2, detailed design and
|
||||
read-only technical investigation, still requires explicit authorization.
|
||||
Mapping, independent gap analysis, implementation, migration, commit, push,
|
||||
and issue closure remain outside current authority.
|
||||
|
||||
### 7.9 Phase-2 authorization and first investigation, 2026-09-06
|
||||
|
||||
Jason answered yes to detailed records, permissions, commands, and audit
|
||||
guarantees with read-only technical investigation. This supersedes the pending
|
||||
authorization recorded in section 7.8. Mapping, implementation, migration,
|
||||
publication, and issue closure remain outside authority.
|
||||
|
||||
The linked phase-2 candidate records tool-free source-analysis run
|
||||
`r-20260906T024609Z-68ee7f`, pinned image/document identities, author
|
||||
qualifications, proposed common types, operation rules, and remaining work.
|
||||
The run is investigative output, not independent gap analysis or approval.
|
||||
|
||||
Q27 is now ready for the owner: accountable command invocation with enforced
|
||||
limits and controlled evidence, or separately attributable internal file/network
|
||||
effects before command execution is enabled. Recommendation A is recorded in
|
||||
the candidate; no answer or added approved requirement is inferred.
|
||||
|
||||
### 7.10 Owner Q27 ruling, 2026-09-06
|
||||
|
||||
Jason answered "Q27: A". Initial managed command auditing records the authorized
|
||||
invocation, actor/scope/assignment, enforced filesystem/network limits,
|
||||
start/end, outcome, and controlled evidence references. It does not require
|
||||
separate enumeration of every internal file read/write or network operation.
|
||||
|
||||
This settles the question left pending in section 7.9 and adds R33. Workspace
|
||||
isolation, credential protection, fail-closed recording, revocation, and
|
||||
uncertainty recovery remain required. Exact schemas and enforcing mechanisms
|
||||
are still phase-2 work; this is not implementation or mapping approval.
|
||||
|
||||
### 7.11 Resumed goal and Q28 frontier, 2026-09-06
|
||||
|
||||
After reboot recovery, Jason explicitly set the phase-2 goal and repeated it
|
||||
with --wait-timeout 60. This resumed the existing planning assignment, not a
|
||||
second goal or an implementation mandate. No exposed goal runtime API or
|
||||
automatic timeout/wake has been verified; CURRENT records file-based state.
|
||||
|
||||
The linked phase-2 package now has checked command/record schemas, negative
|
||||
fixtures, explicit calendar/path checks, restricted-domain fingerprint vectors,
|
||||
and permission/reference/lifecycle/storage and #50 compatibility proposals.
|
||||
These are author design checks, not independent review or runtime enforcement.
|
||||
|
||||
Q28 is ready: may the first managed release use a Mosaic-controlled terminal
|
||||
client, or must it preserve the native Pi terminal with full control mediation?
|
||||
Recommendation A is recorded in the package. No answer is inferred. Runtime
|
||||
execution/control/input-routing and command artifact contracts depend on that
|
||||
choice; manual owner response is the next resumption condition.
|
||||
|
||||
### 7.12 Owner Q28 ruling and goal clarification, 2026-09-06
|
||||
|
||||
Jason answered "Q28: A". R34 records the managed-terminal ruling. The author
|
||||
continued P2-5/P2-6: closed runtime/control/artifact shapes, synthetic rule checks,
|
||||
and the owner-review package. The prior Q28 wait in section 7.11 is historical.
|
||||
|
||||
Jason also clarified that /goal came from the separate ~/.mosaic deployment and
|
||||
is not implemented in this development environment. Continue the same goal through
|
||||
the existing repository records; no extension installation or active wait timeout
|
||||
is requested. Goal completion still requires Jason's phase-2 verdict.
|
||||
|
||||
[Owner review and D1-D16 reconciliation](foundation-v1-candidate/REVIEW.md) is the
|
||||
current candidate index. Runtime security/enforcement and later phase approvals
|
||||
remain separate from the author checks. Earlier open-detail descriptions above
|
||||
are preserved as the interview/design history, not claims of implemented behavior.
|
||||
|
||||
## 8. Trace checklist for the later independent analysis
|
||||
|
||||
The non-authoring agent must trace, at exact code and plan revisions:
|
||||
|
||||
- Request authentication -> registrations -> effective permissions.
|
||||
- Workspace selection -> authoritative state -> context manifest -> program.
|
||||
- Resume/Fresh choice -> exact session -> unique execution -> active claim.
|
||||
- Existing-session conflict -> authorized connection -> sole controller or
|
||||
observer -> explicit control transfer -> stale-controller refusal.
|
||||
- Canonical configuration -> stable launch inputs/hash -> running execution
|
||||
status -> shared comparison -> TUI/GUI/WUI notice -> authorized relaunch.
|
||||
- Assignment -> action authorization -> side effect -> intent/result evidence.
|
||||
- State update -> revision conflict handling -> visible task/project status.
|
||||
- Message address -> permission check -> execution-aware delivery -> receipt.
|
||||
- Exit/crash -> uncertain effects -> recovery -> replacement permission.
|
||||
- Audit query -> scoped visibility -> retention/prune and recovery behavior.
|
||||
|
||||
For each connection: cite the actual implementation or explicitly record it
|
||||
as missing; cite the approved plan requirement; name the writer/enforcer and
|
||||
the receipt; identify unsupported guarantees and a reproducible test. Separate
|
||||
implementation gaps from holes or contradictions in the plan. Do not fix the
|
||||
candidate during review or substitute the map author's assurances for evidence.
|
||||
|
||||
The independent report and Archify maps are later deliverables. This file
|
||||
contains no independent verdict and no assertion that these controls exist.
|
||||
@@ -0,0 +1,272 @@
|
||||
# Agent context, templates, and staged migration
|
||||
|
||||
Plan ID: ACT-1
|
||||
Date: 2026-09-07
|
||||
Recorder: Codex, at Jason's request
|
||||
Status: owner decisions captured; execution sequence proposed; runtime migration deferred
|
||||
Acceptance owner: Jason
|
||||
Coordinator, implementers, and independent reviewers: unassigned
|
||||
|
||||
## Purpose and authority
|
||||
|
||||
`/home/jwoltje/src/mosaic-stack-dev-test` is the next-generation development leg
|
||||
of `/mnt/storage/src/mosaic-stack`, being rewritten from the ground up to replace
|
||||
that codebase. Earlier descriptions of this repository as only a standalone POC
|
||||
do not fully describe its intended destination.
|
||||
|
||||
Jason requested a durable shared plan after agreeing to the context-file
|
||||
responsibilities, personality improvements, template approach, and behavioral
|
||||
evaluation described below. This request authorizes recording the plan. It does
|
||||
not activate the implementation tasks or assign work to existing agents.
|
||||
|
||||
The current demo path must remain usable until Jason verifies it and the
|
||||
transition is coordinated. Darkwing, Rocko, and Filbert have ongoing tmux work;
|
||||
this plan must not change their sessions, launch inputs, assignments, or frozen
|
||||
review candidates. Broad codebase organization for scaling comes AFTER demo
|
||||
validation. Do not implement a monorepo move as preliminary cleanup.
|
||||
|
||||
At capture, [CURRENT.md](CURRENT.md) records a pending inspector correction and
|
||||
independent review before owner demo acceptance. Read that live record on every
|
||||
handoff; this document is not a competing next-action queue. Checkout HEAD was
|
||||
`9a5fbdbda74b16adf488fe28138b2ba69ea5e669`, with substantial uncommitted work.
|
||||
That SHA alone does not identify the working files or an accepted demo baseline.
|
||||
|
||||
## Agreed owner decisions
|
||||
|
||||
| ID | Decision |
|
||||
|---|---|
|
||||
| D1 | Each instruction file has a defined responsibility; avoid duplicating personality, governance, procedures, and runtime facts across layers. |
|
||||
| D2 | Agent personality should produce concrete behavior: direct answers, evidence-backed recommendations, early challenges to flawed assumptions, useful brevity, and natural optional humor. Confidence must not erase uncertainty. |
|
||||
| D3 | The agent's SOUL.md is the ONLY SOUL injected. Each agent has one canonical SOUL file. No shared default SOUL fallback or personality layering in the target system. |
|
||||
| D4 | System bootstrap and agent bootstrap use a SOUL template to initialize each agent's own file. Templates also create appropriate agent-specific and deployed versions of other context files/contracts. |
|
||||
| D5 | `docs/reference/templates/` was imported whole-cloth as an example. It is reference material to adapt, not an approved deployment source. Existing `contracts/*` are initial-session boilerplate; their eventual placement needs incremental treatment. |
|
||||
| D6 | Evaluate behavior using test agents; Darkwing, Filbert, Heffer, and Rocko are available candidates. Their active sessions must be preserved. |
|
||||
| D7 | Preserve the existing option through Jason's demo verification and a properly coordinated migration. |
|
||||
| D8 | Organize for a scalable monorepo after demo validation; avoid premature source moves. |
|
||||
| D9 | Annex adopted concepts as Mosaic documentation in docs/concepts; remove upstream runtime nomenclature rather than retaining foreign product instructions. Supporting source lineage and required license attribution remain in docs/reference/concepts. |
|
||||
|
||||
Proposed lifecycle details requiring design/review: templates initialize instances
|
||||
without silently overwriting later edits; template updates are explicit migrations;
|
||||
launch snapshots preserve historical bytes without becoming editable canonical
|
||||
SOULs. These details implement the ownership intent and must be reconciled with
|
||||
the existing foundation contracts before runtime changes.
|
||||
|
||||
## File responsibilities
|
||||
|
||||
| Resource | Intended responsibility | Must not become |
|
||||
|---|---|---|
|
||||
| CONSTITUTION.md | Shared boundaries, principles, authority relationships | Agent personality or a substitute for runtime permission enforcement |
|
||||
| STANDARDS.md | Quality expectations and evidence requirements | A startup-marker-only contract or repeated repository procedures |
|
||||
| Agent SOUL.md | Voice, temperament, judgment, and interaction style | A mission, capability grant, operational history, or second workflow manual |
|
||||
| AGENTS.md | Repository procedures and working conventions | A universal worker prompt injected into every scope regardless of authority |
|
||||
| USER.md / scoped user context | Relevant authorized preferences and personal context | Automatic distribution of the whole user profile to every agent |
|
||||
| Runtime launch context | Actual identity, workspace, tools, skills, session semantics | Unverified capability claims or permanent persona instructions |
|
||||
| Template sources | Reviewed defaults and bootstrap inputs | Live runtime fallbacks or competing editable agent identities |
|
||||
| Execution snapshots | Evidence of exactly what an execution loaded | A second canonical configuration source |
|
||||
|
||||
Final paths, packaging, and update authority are intentionally unresolved. File
|
||||
names and Markdown section order alone do not enforce precedence or security.
|
||||
|
||||
## Current behavior to preserve and then migrate
|
||||
|
||||
These are source observations from the preceding groundwork, not proof of full
|
||||
runtime conformance. Reinspect the relevant files before preparing a candidate.
|
||||
|
||||
- [scripts/agent.sh](../../scripts/agent.sh) defaults to the existing container
|
||||
lifecycle. Explicit leading `--host-dev` delegates to
|
||||
[scripts/agent-host-dev.sh](../../scripts/agent-host-dev.sh).
|
||||
- Container context comes from [src/load-contracts.sh](../../src/load-contracts.sh):
|
||||
image-baked governance, a default contract SOUL or a seat override, optional
|
||||
identity, live user Markdown, and mission context. The current fallback
|
||||
conflicts with D3 and needs a deliberate migration, not immediate removal.
|
||||
- The native helper loads the selected agent SOUL, repository governance,
|
||||
deployment USER.md, repository AGENTS.md, and agent CONTEXT.md. It keeps
|
||||
launch snapshots and separate native sessions. It is a host development
|
||||
path, not proof of managed workspace isolation or role enforcement.
|
||||
- Root SOUL.md is not loaded by these inspected paths. Do not delete it or
|
||||
`contracts/SOUL.md` until consumer inventory and evidence-preservation needs
|
||||
are resolved. Their presence does not make them authoritative in the target.
|
||||
- POC startup-marker behavior is embedded in existing contracts and verification
|
||||
expectations. Any separation into explicit test fixtures must preserve valid
|
||||
health checks and cannot relabel skipped checks as passed.
|
||||
|
||||
## Relation to existing plans
|
||||
|
||||
- [Agent/project/workspace foundation](2026-09-06_agent-project-workspace-foundation.md):
|
||||
R16 already requires canonical agent SOUL and current approved context at
|
||||
Resume/Fresh launch; R17 describes configuration fingerprints and mismatch
|
||||
notices; R28 scopes personal context; R30 requires explicit legacy adoption.
|
||||
Extend and reconcile these decisions rather than designing a parallel lifecycle.
|
||||
- [Phase-2 contract](2026-09-06_foundation-phase2-contract.md) and
|
||||
[workspace/schema/audit discussion](2026-09-06_workspace-schema-and-audit.md):
|
||||
reuse identity, revision, scope, and evidence concepts after checking their
|
||||
current approval state. A document reference is not runtime enforcement.
|
||||
- [Installation/onboarding requirements](2026-09-06_foundation-install-onboarding-topics.md):
|
||||
bootstrap must integrate personality/style choices, trait compatibility,
|
||||
resumable initialization, and user privacy without granting extra permissions.
|
||||
- [Monorepo source layout](2026-09-06_monorepo-source-layout.md): build on its
|
||||
source/build/install/state separation and component migration method. Reconcile
|
||||
historical milestone sequencing with Jason's explicit demo-first direction.
|
||||
- [SOUL concept guide](../concepts/soul.md): use as personality design input;
|
||||
do not import its rewrite prompt as an instruction to modify deployed agents.
|
||||
|
||||
## Phases and gates
|
||||
|
||||
Concept documentation is maintained in [Mosaic Stack concepts](../concepts/README.md).
|
||||
These are Mosaic explanations with current/proposed status, not a mirrored product
|
||||
manual. [Provenance](../reference/concepts/README.md) records original lineage
|
||||
separately from current content identity. Documentation adoption does not waive
|
||||
the runtime migration gates below.
|
||||
|
||||
### Phase A — documentation and candidate design before demo acceptance
|
||||
|
||||
Record owner decisions, inventory context consumers, and define a migration and
|
||||
evaluation proposal in separate planning artifacts. Any additional groundwork
|
||||
requires a recorded assignment; this capture does not dispatch workers.
|
||||
|
||||
Outputs: source-backed loading/ownership matrix, candidate template responsibilities,
|
||||
compatibility cases, isolated test design, and unresolved decisions. Preserve
|
||||
active contracts, bootstraps, launchers, generated installations, session state,
|
||||
credentials, and the current demo/review candidate.
|
||||
|
||||
Exit evidence: reviewed plan with owners, affected paths, current consumer list,
|
||||
and concrete acceptance checks. This is planning readiness, not demo acceptance.
|
||||
|
||||
### Gate G1 — Jason verifies the current demo
|
||||
|
||||
Record Jason's acceptance and the exact candidate/evidence it covers in the
|
||||
existing demo records, then link it here. Do not infer acceptance from green
|
||||
tests, an agent verdict, or elapsed time. Acceptance of that demo does not
|
||||
automatically activate every task in this plan; reconcile the next bounded scope.
|
||||
|
||||
### Phase B — bounded context/bootstrap migration
|
||||
|
||||
After G1 and assignment of the implementation increment, create reviewed template
|
||||
sources and bootstrap-owned instances. Implement agent-only SOUL resolution with
|
||||
clear missing/invalid-agent errors. Separate verification fixtures from normal
|
||||
personality. Reuse existing config, lifecycle, and launch interfaces where suitable.
|
||||
|
||||
Prove idempotence, preservation of existing edits, interrupted-bootstrap recovery,
|
||||
Resume/Fresh behavior, context provenance, and rollback in isolated fixtures.
|
||||
Freeze a candidate, obtain independent review, and let Jason test the increment
|
||||
before promoting it or retiring the old path. No running session silently reloads.
|
||||
|
||||
### Phase C — personality evaluation and controlled promotion
|
||||
|
||||
Use isolated test registrations/workspaces or distinct test identities derived
|
||||
from the approved Darkwing/Filbert/Heffer/Rocko definitions. Decide the mechanism
|
||||
before launch; a familiar name does not permit reusing live session directories.
|
||||
Record model/harness settings, context hashes, scenario inputs, actual outputs,
|
||||
reviewer findings, and Jason's preference verdict. Record model-call scope and
|
||||
budget before live evaluations. Failed candidates remain evidence, not deployments.
|
||||
|
||||
Offline evaluation preparation may accompany Phase A; live trials and promotion
|
||||
require their assigned scope. This phase must not become an unattended
|
||||
self-rewrite loop or an automatic template rollout to all agents.
|
||||
|
||||
### Phase D — monorepo organization after validated behavior
|
||||
|
||||
Use the existing source-layout plan to define package boundaries, declarative
|
||||
resource ownership, dependency directions, development installations, and release
|
||||
artifacts. Move one component at a time with compatibility entry points and
|
||||
verified rollback. Do not select a new package manager or copy the legacy tree
|
||||
merely to give the repository a monorepo appearance.
|
||||
|
||||
## Collaboration work register
|
||||
|
||||
ACT-01 is complete. A later owner request authorized reference import and test
|
||||
preparation under ACT-04; its draft pack is ready for review, not live execution.
|
||||
Other tasks remain unassigned. Readiness does not dispatch another agent.
|
||||
|
||||
| Task | Deliverable | Dependency / gate | Status |
|
||||
|---|---|---|---|
|
||||
| ACT-01 | Capture owner decisions and staged shared plan | Current user request | Recorded by Codex |
|
||||
| ACT-02 | Inventory all bootstrap, launch, health-check, retry, and packaging context consumers | Assigned documentation scope | Unassigned |
|
||||
| ACT-03 | Specify template ownership, instance creation/update, provenance, and conflict rules | ACT-02; reconcile onboarding/foundation | Unassigned |
|
||||
| ACT-04 | Define isolated behavioral scenarios, fixture identities, scoring, and model budget | Owner-authorized preparation; reconcile ACT-02/03 before trials | Draft prepared by Codex: [test package](act-1-tests/README.md); live budget and evaluation binding unset |
|
||||
| ACT-05 | Freeze bounded migration candidate, compatibility checks, and rollback procedure | G1; ACT-03; implementation assignment | Deferred |
|
||||
| ACT-06 | Implement and independently review context/bootstrap migration | ACT-05 | Deferred |
|
||||
| ACT-07 | Run approved test-agent evaluations and obtain Jason's acceptance | ACT-04/06; evaluation assignment | Deferred |
|
||||
| ACT-08 | Reconcile and execute component layout migration | Validated demo and affected behavior; layout assignment | Deferred |
|
||||
|
||||
For each assignment record task ID, author, independent reviewer, exact file scope,
|
||||
workspace/worktree, baseline plus dirty-file identities where relevant, deliverable
|
||||
path, dependencies, and acceptance owner. Do not preassign the active demo agents.
|
||||
|
||||
Use one designated editor for this plan's status and decisions. Concurrent authors
|
||||
should write separate task notes named `2026-09-07_act-XX-<topic>.md` in this folder
|
||||
and submit evidence pointers to the coordinator. A written assignment is not a
|
||||
filesystem lock; source work requires coordinated ownership and isolated worktrees
|
||||
where necessary. Preserve the shared Git index ownership recorded in CURRENT.md.
|
||||
Direct communications and worker dispatch require actual assignment/authorization;
|
||||
merely storing this file does not deliver it to another tmux session.
|
||||
|
||||
## Acceptance and evaluation cases
|
||||
|
||||
- **Single identity:** exactly one agent SOUL in effective context; root/default
|
||||
SOUL absent. Missing agent SOUL refuses launch rather than using another persona.
|
||||
- **Bootstrap ownership:** initial system agent and subsequent agents each receive
|
||||
their own canonical instance. Rerun, interruption, and template revision never
|
||||
silently discard approved edits or create duplicate identities.
|
||||
- **Stable execution:** record the approved inputs used by Resume and Fresh;
|
||||
changes to source do not alter already-loaded context. Historical snapshots
|
||||
remain immutable evidence and are not offered as editable canonical files.
|
||||
- **Compatibility:** exercise every affected launch/verification consumer from
|
||||
ACT-02. Preserve session history and startup verification; demonstrate rollback
|
||||
without restoring or overwriting an active agent's conversation or work records.
|
||||
- **Personality:** a simple question gets a direct answer; a flawed proposal gets
|
||||
a respectful challenge; an uncertain fact is labeled honestly; a failed action
|
||||
is reported accurately; an out-of-scope request does not gain authority from tone.
|
||||
- **Style adaptation:** technical depth follows the task; humor is optional;
|
||||
public/shared communication fits its audience. Avoid rewarding profanity,
|
||||
contrarianism, verbosity, or confident guesses as evidence of personality.
|
||||
- **Evaluation integrity:** compare baseline and candidate under recorded settings,
|
||||
retain actual responses, separate subjective style judgments from hard
|
||||
requirements, and repeat ambiguous cases. A few good responses are not a
|
||||
guarantee of behavior or security enforcement.
|
||||
|
||||
## Open design decisions
|
||||
|
||||
1. Final template source and deployed instance locations, including eventual
|
||||
ownership of constitution/standards and preservation of current contract fixtures.
|
||||
2. Version/provenance representation and who may approve instance edits or upgrades;
|
||||
align with foundation context fingerprints instead of inventing a second scheme.
|
||||
3. Explicit context precedence and consistent behavior across container and host
|
||||
modes, including scoped user information and all affected headless workers.
|
||||
4. Trait composition during bootstrap, required versus optional template inputs,
|
||||
handling of contradictory choices, and existing-instance migration rules.
|
||||
5. Concrete test-agent isolation, writer/reviewer allocation, evaluation rubric,
|
||||
budget, and sufficient evidence for owner acceptance.
|
||||
6. Exact compatibility lifetime and rollback triggers before retiring boilerplate
|
||||
paths, followed by component layout decisions under the monorepo plan.
|
||||
|
||||
## Decision and handoff history
|
||||
|
||||
- 2026-09-07 — Jason agreed to responsibility separation, concrete personality,
|
||||
template-based instances, and evaluations; explicitly required one agent SOUL,
|
||||
demo compatibility, and post-validation structural work. Codex recorded ACT-1.
|
||||
No runtime implementation, agent launch, worker message, or task reassignment
|
||||
performed for this capture. Next planning task is ACT-02 when assigned; the
|
||||
operational next action remains in CURRENT.md.
|
||||
- 2026-09-07 — Jason requested pulling the selected OpenClaw concepts and preparing
|
||||
testing with Darkwing. Copied twelve reference documents plus LICENSE with a
|
||||
source manifest; created eleven synthetic review scenarios, a candidate SOUL,
|
||||
NOT_RUN results and a preparation utility. See [Darkwing handoff](act-1-tests/README.md).
|
||||
`node scripts/prepare-concept-tests.mjs --check` verified thirteen imported files
|
||||
and eleven cases. Existing launcher regressions passed two tests in isolated
|
||||
fixtures. Prepared `/tmp/mosaic-act1-l2id7s` with 22 NOT_RUN baseline/candidate
|
||||
rows and mode 0700. This temporary workspace is reproducible, not durable test
|
||||
evidence. No model trials, active-session changes, demo-candidate changes,
|
||||
messages or migration implementation followed. Darkwing readiness review is
|
||||
prepared but not delivered or assigned automatically.
|
||||
- 2026-09-07 — Owner directed full annexation and Mosaic nomenclature. Rewrote the
|
||||
twelve imported references plus the existing SOUL guide as thirteen Mosaic
|
||||
concept pages in docs/concepts, with an index and explicit implementation status.
|
||||
Replaced the old reference copies; source lineage and LICENSE now live under
|
||||
docs/reference/concepts. Renamed the benchmark concept to agent-behavior-tests.
|
||||
Updated ACT-1's cases and preparation utility to validate current Mosaic hashes,
|
||||
preserve original-source metadata and stage concepts/provenance separately.
|
||||
Thirteen concepts, license attribution, eleven scenarios and fifty local links
|
||||
verified; fresh review workspace /tmp/mosaic-act1-xVwXtF prepared with behavioral
|
||||
results still NOT_RUN. Earlier import/history records retain their original facts.
|
||||
No runtime launcher, deployed context, active session, or demo candidate changed.
|
||||
@@ -0,0 +1,54 @@
|
||||
# Repository consolidation — owner request and verified boundaries
|
||||
|
||||
Owner requested consolidating v1 below v1/, moving the new foundation to the
|
||||
canonical checkout, and using a refactor branch of mosaicstack/stack.
|
||||
Status: initial read-only assessment, no move or Git mutation performed.
|
||||
|
||||
Verified repositories:
|
||||
- /mnt/storage/src/mosaic-stack: origin mosaicstack/stack, branch next,
|
||||
HEAD 5d2770002612a09ae0cadc129b4ea30619133e8a; no tracked modifications observed.
|
||||
Contains .git and .mosaic plus other hidden entries. Ignored/untracked content
|
||||
has not been inventoried; tracked cleanliness does not mean empty local state.
|
||||
- /home/jwoltje/src/mosaic-stack-dev-test: origin mosaicstack/stack-v2, branch main,
|
||||
HEAD 9a5fbdbda74b16adf488fe28138b2ba69ea5e669; tracked modifications include shared
|
||||
logs/CURRENT, docs/TOOLS.md and scripts/agent.sh. The reviewed inspector and its
|
||||
review records are uncommitted. Generated/dependency/local files also exist.
|
||||
|
||||
The approved demo belongs to the second repository. The earlier user instruction
|
||||
"from the repository directory" was ambiguous; no demo work was performed in v1.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Consolidate the public project identity, but use a reversible, coordinated cutover,
|
||||
not an in-place wildcard move. A shell * omits hidden files; naively including .git
|
||||
would mix two different repositories. Existing absolute-path references and running
|
||||
agents may continue targeting the canonical path after its contents become v2.
|
||||
Those are concrete safety reasons to sequence the change, not reasons to retain
|
||||
ambiguous names indefinitely.
|
||||
|
||||
Proposed destination layout: new foundation at canonical root, tracked legacy source
|
||||
under v1/, one top-level Git repository belonging to mosaicstack/stack, a local
|
||||
refactor branch, and preserved access to both original commit histories. This is
|
||||
not a nested .git repository under v1. No branch push is inferred.
|
||||
|
||||
Before cutover:
|
||||
1. Establish writer/index holds for both checkouts; do not abandon existing agent
|
||||
goals. Determine which running sessions/launchers reference either path using
|
||||
authorized metadata, not credential reads or unrelated fleet investigation.
|
||||
2. Inventory tracked, untracked and ignored files; take verified reversible snapshots
|
||||
outside the destination. Preserve local state, but do not automatically add it
|
||||
to Git. No blanket staging or root directory cleanup.
|
||||
3. Prepare the branch/import in an isolated staging checkout, retaining original
|
||||
histories so accepted source SHA citations remain resolvable. Archive tracked
|
||||
v1 source separately from private/local state and generated dependencies.
|
||||
4. Carry approved v2 source and owned pending work with explicit manifests and
|
||||
source ownership, not a wholesale overwrite of either .git directory.
|
||||
5. Verify content identities, history, source/runtime/install boundaries and test
|
||||
behavior; review path-dependent launch/config changes before activation.
|
||||
6. Only after coordinated readiness, swap to the canonical path, explicitly restart/
|
||||
rebind affected sessions as authorized, and retain rollback copies. Do not leave
|
||||
two writable authoritative roots or silently redirect v1 workers into v2.
|
||||
|
||||
Open prerequisite: safe writer/session cutover and full local-content inventory.
|
||||
This assessment does not move files, change deployment/config, force-push, delete
|
||||
old work, rewrite accepted evidence or grant a v1 orchestrator authority over v2.
|
||||
@@ -0,0 +1,95 @@
|
||||
# Repository conversion #1495 — local completion record
|
||||
|
||||
Owner instruction: "Nothing is working in the repo now. Get the conversion completed."
|
||||
Tracking: https://git.mosaicstack.dev/mosaicstack/stack/issues/1495.
|
||||
Coordinator: darkwing. Date: 2026-09-07 UTC.
|
||||
|
||||
## One authoritative checkout
|
||||
|
||||
- Canonical root: `/mnt/storage/src/mosaic-stack`.
|
||||
- New Mosaic Stack foundation: root-level source, scripts, contracts and docs.
|
||||
- Legacy Mosaic Stack: `v1/`, retained as archival source, not an active deployment.
|
||||
- Working branch: `refactor`.
|
||||
- Origin: `https://git.mosaicstack.dev/mosaicstack/stack`.
|
||||
- `/home/jwoltje/src/mosaic-stack-dev-test` is a compatibility symlink to the
|
||||
canonical root. It is not a second working tree.
|
||||
- Exactly one top-level `.git`; no nested `v1/.git`.
|
||||
|
||||
The original v2 repository is retained as the `stack-v2-archive` fetch remote;
|
||||
its push URL is deliberately disabled. No remote repository was deleted or pushed.
|
||||
The canonical branch has no automatically configured push/upstream authority.
|
||||
|
||||
## Git and pending-work preservation
|
||||
|
||||
Original heads:
|
||||
- v1: `5d2770002612a09ae0cadc129b4ea30619133e8a` (`next`).
|
||||
- v2: `9a5fbdbda74b16adf488fe28138b2ba69ea5e669` (`main`).
|
||||
|
||||
The local conversion merge preserves both histories. Its first parent is the v2
|
||||
head, its second parent is the v1 head. The selected root tree is v2; the exact
|
||||
tracked v1 tree is imported under `v1/`. This is not a merge into `next` or `main`.
|
||||
All v1 refs are additionally preserved below `refs/archive/v1/`; original v2 refs
|
||||
remain available. Accepted source/review SHA citations still resolve.
|
||||
|
||||
Only the 3,507 already-tracked v1 paths and these four conversion documentation
|
||||
paths belong to the conversion commit:
|
||||
- `AGENTS.md`
|
||||
- `README.md`
|
||||
- this completion record
|
||||
- `docs/plans/reviews/2026-09-07_repository-conversion-verification.json`
|
||||
|
||||
All earlier v2 tracked modifications, untracked inspector/source/reviews, ignored
|
||||
files and generated/dependency state were copied without bulk staging. They remain
|
||||
pending work, not silently committed or approved. Legacy ignored/local files were
|
||||
preserved on disk but were not automatically added to Git. No source implementation
|
||||
bytes, role authority or immutable inspector inputs were changed by the conversion.
|
||||
|
||||
## Verification
|
||||
|
||||
Full pre-cutover snapshots and staged copies matched file hashes, types, permissions,
|
||||
UID/GID, symlink targets and regular-file mtimes. Both sources were checked again
|
||||
for drift immediately before cutover. Post-cutover payload checks matched as well.
|
||||
Inventories: 4,149 v1 entries including Git metadata; 15,734 v2 entries including Git
|
||||
metadata/dependencies; 4,105 legacy payload entries excluding `.git`.
|
||||
|
||||
From the canonical checkout with synthetic HOME/TMPDIR and no provider credentials:
|
||||
- Inspector Node tests: 80 passed.
|
||||
- Inspector selftests: 43 passed.
|
||||
- Schema oracle: zero disagreements, 1,568 records.
|
||||
- Foundation checker: passed, with its existing synthetic/runtime limitations.
|
||||
- Config/auth/conductor tests: 24/15/17 passed.
|
||||
- Network/engine command guards were not invoked.
|
||||
- All 382 approved r6 inspector file identities and every pinned input were unchanged
|
||||
before and after the tests.
|
||||
|
||||
Command/log receipts: `docs/plans/reviews/2026-09-07_repository-conversion-verification.json`.
|
||||
Git object integrity and exact archival tree/parent relationships are checked before
|
||||
and after the local commit. No v1 build, service, native runtime or deployment test
|
||||
was run. Task/release suites remain NOT RUN / DEFERRED; the previous offline-demo
|
||||
ruling is not expanded into deployment acceptance by this conversion.
|
||||
|
||||
## Rollback material — outside the repository
|
||||
|
||||
Verified copies and private inventories:
|
||||
`/mnt/storage/src/.mosaic-stack-conversion-20260907T172430Z/`
|
||||
|
||||
This contains `before-v1/`, `before-v2/`, `retired-v1-original/`, inventory JSON,
|
||||
conversion state and verification logs. The original v2 directory is also retained:
|
||||
`/home/jwoltje/src/.mosaic-stack-dev-test.pre-conversion-20260907T172430Z`.
|
||||
|
||||
These are retired rollback copies, not alternative development roots. Do not edit
|
||||
or launch from them. Before any rollback, stop writers and preserve any new work.
|
||||
A rollback must move the converted checkout aside, verify/remove only the old-v2
|
||||
compatibility symlink, and restore the two original directories to their original
|
||||
paths. Never overwrite/delete newer work or use blanket recursive deletion.
|
||||
|
||||
## Remaining boundaries
|
||||
|
||||
No push, PR merge, live service/config/data migration, package install or history
|
||||
rewrite occurred. The old absolute v2 path remains usable through the explicit
|
||||
compatibility link; historical evidence was not bulk rewritten to claim new paths.
|
||||
Future work should use the canonical path above. Reopen sessions there when convenient.
|
||||
|
||||
The inspector is independently code-approved for the bounded offline demo; Jason's
|
||||
A9 demo acceptance remains pending. Neither that acceptance nor future runtime
|
||||
permissions are granted by the repository conversion.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Conductor protocol — poor-man orchestration loop
|
||||
|
||||
How the stack orchestrates headless pi workers to do work on itself.
|
||||
|
||||
## Role contracts
|
||||
|
||||
Role authority is declared in role contracts, one file per role, under
|
||||
`roles/` (e.g. `roles/conductor-policy.json`). The repository root holds
|
||||
only first-class, bootstrap-required configuration; role contracts are
|
||||
tracked, versioned files whose changes arrive as reviewed commits.
|
||||
|
||||
## Roles
|
||||
|
||||
| Role | Runs where | Powers | Never has |
|
||||
|---|---|---|---|
|
||||
| **Conductor** | host (assistant or owner) | git (clone/commit/push), task dispatch, review, verification suites, Gitea | nothing new |
|
||||
| **Worker** | container (headless pi via `scripts/run-task.sh`) | read/write/edit/bash inside its workspace; persistent session on request | git credentials, docker socket, host filesystem |
|
||||
|
||||
## The loop
|
||||
|
||||
1. **Decompose**: conductor turns a goal into worker tasks small enough to
|
||||
specify completely in one prompt (file paths, acceptance criteria, style
|
||||
constraints, verification the worker can run itself, e.g. `node --check`).
|
||||
2. **Mirror**: conductor maintains the repo clone at
|
||||
`<dataRoot>/workspaces/stack-repo` (host-side git; workers see it read-write
|
||||
through their workspace mount).
|
||||
3. **Dispatch**: `scripts/run-task.sh run <worker-task.json>` — worker edits the
|
||||
clone. Session name `worker-<n>` keeps continuity across refinement rounds.
|
||||
4. **Extract**: `git -C <workspace> diff > patch` — the worker's entire output
|
||||
is a reviewable diff. Run record (result.json, stderr.txt) is the receipt.
|
||||
5. **Review**: conductor reads the diff line by line. Bad output → refine the
|
||||
prompt, re-dispatch (same session: "your patch had these problems…").
|
||||
6. **Integrate**: conductor applies the patch to the real repo, runs the full
|
||||
suites, commits and pushes. Suites failing → revert apply, back to step 5.
|
||||
7. **Record**: update CURRENT.md, BUILD-LOG, close the Gitea issue.
|
||||
|
||||
## Guardrails
|
||||
|
||||
- Workers never receive credentials; they never run git; they never leave the
|
||||
workspace (container is the boundary; tools allowlist is the gate).
|
||||
- Every worker diff is reviewed by the conductor before integration. No
|
||||
auto-apply. (Auto-apply would be a capability-policy decision for later.)
|
||||
- Verification is mechanical: suites + `node --check` / `bash -n` gates.
|
||||
- Recursive decomposition = "fail → smaller task", never "hope."
|
||||
|
||||
## Worker task template
|
||||
|
||||
```json
|
||||
{
|
||||
"taskVersion": 1,
|
||||
"id": "t-worker-<name>",
|
||||
"prompt": "<full spec: goal, files, constraints, acceptance, self-checks>",
|
||||
"workspace": "stack-repo",
|
||||
"capabilities": { "tools": ["read", "write", "edit", "bash"] },
|
||||
"session": "worker-1",
|
||||
"timeoutSeconds": 600
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,186 @@
|
||||
# CURRENT — single source of "what happens next"
|
||||
|
||||
This file always names exactly one next action. Any "continue" / "next" /
|
||||
"proceed" message means: execute the action below, fully (implement → test →
|
||||
verify against its acceptance criteria → commit → push → close the issue →
|
||||
update this file to the next action). No ambiguity, no re-planning.
|
||||
|
||||
## Next action
|
||||
|
||||
Review `docs/plans/2026-09-03_auth-provider-harness-registry.md` and reconcile
|
||||
Gate 7 with later owner decisions and #53's workspace/session model. This is
|
||||
registry review alignment only; no registry implementation is authorized.
|
||||
|
||||
Completed commit/push wave: R5 transport-only tmux correction independently
|
||||
APPROVED by Filbert, published on refactor at
|
||||
`69f10a40623bf1809e7cda1f800bce3a5d80fb51` with all 17 milestone tags; remote
|
||||
identities verified. Exit 0 means transport dispatched, application acceptance
|
||||
unknown. Earlier rejected confirmation candidates remain historical evidence.
|
||||
Jason's A9 acceptance and suite results are recorded in
|
||||
`docs/plans/reviews/2026-09-07_a9-owner-acceptance.md`.
|
||||
#53 published planning inclusion is verified in
|
||||
`docs/plans/reviews/2026-09-07_issue53-closeout.md`; Jason retains issue closure.
|
||||
No main/next merge, deployment, or adoption of newer relocation work occurred.
|
||||
|
||||
Correction (2026-09-07): the following conversion/A9 text is a historical
|
||||
pre-wave checkpoint, not a competing pending acceptance or test-deferral gate.
|
||||
Use the canonical checkout `/mnt/storage/src/mosaic-stack`, branch `refactor`,
|
||||
origin `mosaicstack/stack`. New foundation is at root; `v1/` is the legacy archive.
|
||||
The old `~/src/mosaic-stack-dev-test` path is only a compatibility symlink.
|
||||
|
||||
Jason's requested local conversion (#1495) is completed at commit
|
||||
`127a54fdff1fe6ae56c3197edddf957481465db4`, preserving both parent histories and
|
||||
all pending work. Conversion record:
|
||||
`docs/plans/2026-09-07_repository-consolidation-completed.md`.
|
||||
Pre/post-commit tests and source identity checks passed; no push or live change.
|
||||
The index is clean. Existing uncommitted work was preserved, not blanket staged.
|
||||
Owner confirmation that no work was active superseded the earlier index hold for
|
||||
this conversion; no new writer assignment or permission grant is inferred.
|
||||
|
||||
Inspector: FI-FILBERT-8 APPROVED at r6 manifest
|
||||
`a4a4493000aff5905337a643886ca36e7c5377d52deed77b8aeab7174ca73dcf`.
|
||||
All 382 file identities and pins remain unchanged. Demo guide:
|
||||
`docs/plans/reviews/2026-09-07_foundation-inspector-demo.md` (its old checkout path
|
||||
still resolves through the compatibility link; prefer the canonical path above).
|
||||
Four examples reran successfully after conversion. A9 remains owner acceptance,
|
||||
not an automatic consequence of tests or migration. Task/release coverage remains
|
||||
NOT RUN/DEFERRED, not deployment green. Native-parser/equality qualifications remain.
|
||||
|
||||
Historical instructions below describe earlier checkpoints, not competing current
|
||||
assignments or authority over the archived v1 implementation.
|
||||
|
||||
## Earlier owner and source checkpoints
|
||||
|
||||
Historical context below; the current candidate and live goal checkpoint supersede
|
||||
the earlier partial-draft descriptions.
|
||||
|
||||
The prior hands-on checkpoint demonstrated launch, workspace listing, and conversation resume from Jason's supplied output. Fresh context and mission recovery were not tested. The owner redirected to this planning exercise; no broad foundation acceptance is inferred.
|
||||
|
||||
Owner ruling recorded 2026-09-06 as R16-R17: current approved SOUL on launch, stable per-execution inputs, and a shared launch/configuration hash reference for TUI/GUI/WUI mismatch notices recommending Fresh. D10 is partly resolved. Q20/Q21 now settle broad fingerprint categories and automatic non-blocking notices plus on-demand checks; exact field/dependency hashes and delivery mechanics remain D16. This does not advance the phase or authorize implementation.
|
||||
|
||||
Interview round 1 recorded: Q1 permits linked project/workspace missions, Q2 permits bounded system registration/assignment authority, and Q3 limits visibility to shared project information and explicitly permitted workspaces. Q4 clarification A creates and announces the first conversation without an offer; later default launches resume, while missing/damaged established sessions cause an error. Round 2 Q5-Q9 confirms single-parent hierarchy, the Fresh recovery information set, delegated within-plan non-destructive decisions and routine reviewer acceptance, assignment-only default Abandon, and explicit authorization for prerequisite work. Delegated authorization need not prompt the user each time; user phase checkpoints remain. Round 3 Q10-Q14 permits unassigned discussion/inspection with recorded assignments for changes, requires an interactive active-session conflict notice and offer to connect, separates shared work records from transcript grants, chooses concise audit metadata with controlled evidence, and scopes membership revocation to affected executions. Round 4 Q15-Q19 requires explicit service conflict handling, one controlling interface with authorized observers, controlled Fresh replacement, delegated evidence-based recovery without blind replay, and affected-execution blocking on audit failure. Round 5 Q20-Q24 extends fingerprints to shared behavior-affecting configuration, requires automatic non-blocking notices plus on-demand checks, scopes personal context, retires closed workspaces without deletion, and requires explicit reviewed legacy adoption. Round 6 Q25/Q26 pauses affected work for reconciliation after approved plan changes and chooses standard scope roles with registration-specific narrowing. Jason subsequently confirmed shared understanding of intended behavior. Jason then authorized phase 2. A tool-free source-analysis run, r-20260906T024609Z-68ee7f, succeeded; pinned 0.84.4 documentation was extracted from the existing image without starting its extraction container. These are source/document findings, not runtime feature tests or independent approval. The first contract candidate is partial. Q27 A now settles command-audit granularity; dependent schema and enforcement drafting may continue within phase 2. Full schema/plan approval remains pending.
|
||||
|
||||
## Accepted phase-2 checkpoint (historical)
|
||||
|
||||
- Goal: issue-53-phase2. Objective: an owner-reviewable contract for agents,
|
||||
projects, workspaces, sessions, permissions, and audit evidence. Completion
|
||||
owner: Jason. Author/workspace/session remain those recorded below.
|
||||
- State: satisfied. Jason explicitly accepted phase 2 after the plain-language
|
||||
explanation of the planning baseline and separate later gates. P2-7 is complete.
|
||||
REVIEW.md retains D1-D16 and the unproved implementation mechanisms. This is
|
||||
owner plan acceptance, not independent technical or security certification.
|
||||
- Acceptance: repair/check schemas and fixtures, complete the operation/recovery
|
||||
contract, resolve material behavior decisions, prepare a review package and
|
||||
one user-testable increment recommendation, then obtain owner acceptance.
|
||||
- Evidence: `python3 docs/plans/foundation-v1-candidate/check.py` passes 38 command
|
||||
and 38 record shape cases, 16 path cases, 7 restricted-domain hash vectors,
|
||||
155 runtime/control/artifact cases and 35 synthetic rule-model cases. Ten
|
||||
deliberately shape-valid forgeries still require trusted runtime rejection.
|
||||
These are not runtime security tests or independent acceptance.
|
||||
- Reboot fixture defects were repaired, not discarded. Eleven positive records
|
||||
now include their common envelope; negative mutations were preserved. Required
|
||||
calendar/UTF-8/control-character checks are explicit in the author checker.
|
||||
- Next gate: separate owner authorization for mapping, not more phase-2 approval.
|
||||
No mapping or implementation started. This session continues the
|
||||
file-based goal and has not configured an extension/timer for it. Separate #54
|
||||
work subsequently added/tested a project-local goal extension, as recorded in
|
||||
the shared logs; that work and its state were left untouched. This session has
|
||||
not migrated issue-53-phase2 into that runtime. Elapsed time never grants approval.
|
||||
- No new worker dispatch, external reply obligation, or uncertain external action
|
||||
initiated by this phase-2 session is outstanding. No numeric work budget supplied;
|
||||
aggregate usage remains unavailable.
|
||||
- Authority remains phase-2 planning and read-only investigation. No runtime
|
||||
implementation, mapping, migration, commit, push, or issue closure.
|
||||
|
||||
## Prior recovery checkpoint, 2026-09-06 03:42 UTC
|
||||
|
||||
Historical snapshot below; the live goal checkpoint above supersedes its pause
|
||||
and unfinished-fixture status.
|
||||
|
||||
- Goal: issue-53-phase2. State: paused by owner steering. Writer: darkwing,
|
||||
pi session `01a06e48-0718-71f2-a889-c263c4800fb9`, explicit working directory
|
||||
`/home/jwoltje/src/mosaic-stack-dev-test`, project `mosaicstack/stack-v2`.
|
||||
This is the existing single-writer planning assignment, not a runtime claim.
|
||||
- HEAD remains `69d1bb3`. Preserved all uncommitted planning and unrelated skill
|
||||
work. No reset, cleanup, commit, push, or implementation occurred.
|
||||
- Five planning artifacts survived in `docs/plans/foundation-v1-candidate/`:
|
||||
command schema/fixtures, `check.py`, and record schema/fixtures. The three
|
||||
command-check file hashes match the pre-reboot checksums.
|
||||
- `python3 docs/plans/foundation-v1-candidate/check.py` passes 38 shape fixtures
|
||||
and 5 deliberate shape-valid forgeries. This does not prove runtime security.
|
||||
- The unfinished record checker is NOT integrated into check.py. A read-only
|
||||
diagnostic found 13 expectation mismatches: all 11 positive record fixtures,
|
||||
plus unicode-byte-limit and bidi-control. The first positive lacks five common
|
||||
envelope fields, indicating fixture generation is incomplete. Do not count
|
||||
negative cases as meaningful until positive fixtures are repaired and rerun.
|
||||
- System config validates, Docker responds, and the pinned image ID and prior
|
||||
research result hash still match the phase-2 evidence. No Mosaic worker
|
||||
container was running at inspection. Pinned temporary docs remain available.
|
||||
- Next work after explicit resume: repair record fixtures, enforce/test UTF-8
|
||||
byte and control-character path checks, integrate both schema suites, then
|
||||
complete the remaining phase-2 record/permission/lifecycle work and owner gate.
|
||||
- No outstanding assistant-initiated external action or reply is known. Wake is
|
||||
manual: Jason sends a resume instruction. No timer or automatic continuation
|
||||
is registered. Aggregate usage is unavailable; no numeric budget was supplied.
|
||||
|
||||
## Queue (ordered per docs/plans/ROADMAP.md)
|
||||
|
||||
1. Paused for owner alignment: review `docs/plans/2026-09-03_auth-provider-harness-registry.md` and reconcile later owner decisions and #53's workspace-session model. Gate 7 remains unresolved. No registry implementation is approved, and this work does not resume automatically after the planning exercise.
|
||||
2. Deferred by owner: CI runners (Gitea hardware slow); second real adapter; push automation
|
||||
|
||||
## Rules
|
||||
|
||||
- One action in flight. Update this file at the END of every action.
|
||||
- Blocked? Move the item to "Blocked" below with the reason and stop.
|
||||
- Completed actions move to the log at the bottom (date + issue + result).
|
||||
- Corrected entries are marked, never silently rewritten (see 2026-09-03 dedup note).
|
||||
|
||||
## Blocked
|
||||
|
||||
(none)
|
||||
|
||||
## Completed log
|
||||
|
||||
- 2026-09-07 — Owner-directed concept annexation: [Mosaic concepts](../concepts/README.md) now owns the adapted pages; source/license metadata moved to docs/reference/concepts. Test-package links and content hashes updated; preparation checks pass. Documentation ownership changed, not runtime behavior or the demo queue.
|
||||
|
||||
- 2026-09-07 — Owner-requested ACT-04 groundwork: [Darkwing concept test package](act-1-tests/README.md) prepared with twelve pinned OpenClaw references, synthetic cases, candidate SOUL and offline preparation utility. Import checks and existing isolated launcher tests passed; behavioral cases NOT_RUN and runtime features deferred. Current demo queue unchanged.
|
||||
|
||||
- 2026-09-07 — Owner-requested shared planning capture: [ACT-1 — Agent context, templates, and staged migration](2026-09-07_agent-context-templates-and-migration.md) records single-agent SOUL authority, bootstrap templates, evaluation work, demo gates, and deferred structural migration. Runtime tasks remain unassigned; current demo queue unchanged.
|
||||
|
||||
- 2026-09-07 — Owner-requested naming cleanup: native helper is now `scripts/agent-host-dev.sh`; callers and documentation updated, empty `scripts/tui/` removed, launcher checks passed. Historical log paths retain their original names.
|
||||
|
||||
- 2026-09-07 — Owner-requested entry-point consolidation: `scripts/agent.sh --host-dev darkwing` delegates to the native helper; default container execution retained. Host and isolated container routing/refusal checks passed; inspector queue unchanged.
|
||||
|
||||
- 2026-09-07 — Owner-requested launcher follow-up: Darkwing's launch.sh now delegates to `scripts/tui/launch.sh darkwing`; existing session/context regression checks pass.
|
||||
|
||||
- 2026-09-07 — Separate owner-requested Darkwing launcher: `agents/darkwing/launch.sh` provides a native development TUI with explicit context, skills, coding tools and `/goal`; offline launcher tests and no-model TUI smoke passed. Inspector queue and approval gates unchanged; no commit/push.
|
||||
|
||||
Note (2026-09-03): this log was deduplicated after editor-session races
|
||||
appended duplicate blocks. The dedup removed repeated lines only; every
|
||||
distinct action appears exactly once, in completion order. Ground truth:
|
||||
git history + Gitea issues.
|
||||
|
||||
- 2026-09-03 — POC: containerized pi hello-world (poc-container-hello-v0)
|
||||
- 2026-09-03 — M1 configuration-driven hello world (#1–#4; config-hello-v1); hotfix #5 stdin detach
|
||||
- 2026-09-03 — M2 mission/task abstraction (#6–#9; mission-task-v1); hotfix #14 release identity in task path
|
||||
- 2026-09-03 — M3 release model + safe updates (#10–#13; release-model-v1); drills: update/refusal/rollback
|
||||
- 2026-09-03 — M14 live user context layer (user/ dispatched to all launches; 0.0.9 built)
|
||||
- 2026-09-03 — M15 agent seats: per-agent SOUL + role contracts (#36; agent-seats-v1); roles/ convention (root = bootstrap-only)
|
||||
- 2026-09-03 — M13 interactive TUI agent + TOOLS.md (#35; interactive-agent-v1); release 0.0.8 activated
|
||||
- 2026-09-03 — M12 conductor auto-apply policy (#34; auto-apply-v1); 17 conductor selftests
|
||||
- 2026-09-03 — M11 session forking (#33; session-fork-v1); child recalls ancestor, base untouched
|
||||
- 2026-09-03 — M10 run-record retention (#32; retention-v1); prune keep-N, dry-run default, receipt
|
||||
- 2026-09-03 — M9 mission capability policy (#30; mission-policy-v1); least-privilege intersection
|
||||
- 2026-09-03 — test UX: green OK/red FAIL status colors; NO_COLOR-aware
|
||||
- 2026-09-03 — M10-era hotfix: retry lineage (#28) + AGENTS.md/SESSIONS.md recovery shim
|
||||
- 2026-09-03 — release 0.0.10 packaged and health-gated activated (user context + agent seats live)
|
||||
- 2026-09-03 — release 0.0.11 shipped (onboarding + live user context); ROADMAP.md agreed (M16–M19); CI deferred by owner
|
||||
- 2026-09-03 — M16 release self-determination (#38; `release.sh ensure` at launch, drift warnings, recursion guard) — logged late: CURRENT.md had gone stale while M16/M17 shipped; ground truth = git history
|
||||
- 2026-09-03 — M17 skill lifecycle + ms-* skill set completion (#40–#42; skill-lifecycle-v1); release 0.0.12 packaged, health-gated active — logged late, same staleness correction
|
||||
- 2026-09-03 — conductor-loop calibration with live collaborator (#43): dispatch via agent-send.sh → receipt → line-by-line diff review → suite-gated integration; docs/TOOLS.md gains Tools (host-side) section + corrected suite counts
|
||||
- 2026-09-03 — skill revisions adjudicated (#44): ms-communications integrated as-authored; ms-conductor redraft + conductor remediation (refusal vs outage); TOOLS.md release.sh ensure row
|
||||
- 2026-09-03 — M18 seat-role progressive capability restriction (#45; roles resolve to contracts, ceiling ∩ seat grant, fail-closed refusals, roles/researcher.json); task suite 74 → 88
|
||||
- 2026-09-03 — M18 follow-up: fail-closed seat resolution under MOSAIC_AGENTS_DIR override (#46, owner decision after live verification); task suite 88 → 90; next action M19
|
||||
- 2026-09-03 — M19 harness auth tooling (#47; auth.sh status/accounts, agent.sh --auth per-launch injection via PI_AUTH_FILE, test-auth suite 13 cases with secret-never-printed assertions); agreed sequence M16–M19 complete, M20 owner-gated
|
||||
- 2026-09-03 — M19 correction: auth ownership moved to the data root (#48, owner direction — the stack never writes to default harness config locations; ROADMAP standing decision); auth.sh config-driven, accounts at <dataRoot>/auth, 0600 enforced; test-auth 13 → 15
|
||||
- 2026-09-03 — harness/provider/auth registry specification drafted (#49): agent.json harness declaration, central provider/account/settings registries, runtime seat selection, mechanical per-harness materialization, centralized OAuth refresh, Ollama endpoints, CLI contract; implementation blocked pending ten-gate review
|
||||
@@ -0,0 +1,51 @@
|
||||
# HARVEST — patterns from mosaicstack/stack + the live fleet runtime
|
||||
|
||||
Sources surveyed 2026-09-03 (owner-authorized):
|
||||
- `~/src/mosaic-stack` (drifted) and fresh clone at
|
||||
`~/src/mosaic-stack-worktrees/v2-harvest` (branch `next` @ 0db2d19a,
|
||||
"mosaic doctor structure-anchor provisioning check (T51 WP0b)")
|
||||
- `~/.mosaic/fleet/` — live fleet runtime: `bin/`, `roles/` (+`-ng`),
|
||||
`agents/<seat>/`, `auth/`, `roster.yaml`, `SEAT-CUTOVER.md`
|
||||
|
||||
Rule honored: nothing pulled whole-cloth. This file is the pattern ledger;
|
||||
each adoption lands through its own milestone with tests.
|
||||
|
||||
## Pattern ledger
|
||||
|
||||
| # | Pattern (source) | Verdict | Destination |
|
||||
|---|---|---|---|
|
||||
| 1 | **Channel dispatcher** — `fleet/bin/mosaic`: shipped npm CLI is default; dev worktree opt-in via `MOSAIC_CLI_WORKTREE` health-checked with `--version`; `MOSAIC_FLEET_CLI_OFF` forces pass-through; launcher never writes to user dirs | **steal** | M20 unified `mosaic` CLI |
|
||||
| 2 | **Single canonical launcher + per-seat env** — `bin/launch-seat.sh` is the only launcher; per-seat variation lives in `launch.env` ("per-seat copies drift") | **steal** | M20 CLI + agent.sh stays thin |
|
||||
| 3 | **Refuse-rather-than-guess provisioning** — `bin/new-seat.sh`: seat classes, `--adopt` never overwrites real identity files, `--mint` fails closed (unminted = git writes fail closed), `--dry-run` | **steal** | seat instantiation (M15 evolution) |
|
||||
| 4 | **Role manifest schema v3** — `roles/code-ng/mosaic-core.manifest.json`: capability bindings (id/effect/binding/status), `tools[]`, `forbiddenTools[]`, `shell.mode: denied`, `credentials` policy, `evidence` journaling, `digestRules` postcondition (`active-set-exact`, sha256 over manifest) | **adapt progressively** | M18 seat-role restriction |
|
||||
| 5 | **Per-seat git identity** — `MOSAIC_GIT_IDENTITY=<seat>` + `GIT_AUTHOR_NAME/EMAIL`; incident 2026-08-19: seven seats committed as the wrong identity via directory git config | **steal** | M18/M20 (we have no per-seat git writes yet — adopt before we do) |
|
||||
| 6 | **Auth slots + symlink shares** — `fleet/auth/<provider>/<account>/` credential dirs; seat `.pi/agent/` symlinks `auth.json settings.json models-store.json bin skills` | **adapt** | M19 auth tooling |
|
||||
| 7 | **Launch-time pre-flight probes** — every consumed path checked before start; pi's `existsSync` silently appends literal path strings as prompt text (documented trap) | **steal** | M19 launch check + our existing refusal style |
|
||||
| 8 | **Prompt ordering doctrine** — estate context first, seat SOUL/AGENTS last ("closest to the task wins") | **matches ours** | document in adapters/README |
|
||||
| 9 | **`--check` mode** on launchers (verify provisioning without launching) | **steal** | agent.sh / release ensure |
|
||||
| 10 | **ENTITY.md per seat** — identity record incl. git identity, explicitly secret-free | **steal** | seat records (extend seat.json or add ENTITY.md) |
|
||||
| 11 | **`mosaic doctor`** — structure-anchor provisioning check (T51) | **adapt** | M20 `mosaic --check` |
|
||||
| 12 | packages/ anatomy on `next`: `mosaic` (CLI), `auth`, `agent`, `config`, `forge`, `quality-rails`, `comms`, `macp` | reference map | M18/M19/M20 deep reads |
|
||||
|
||||
## Skips (for now)
|
||||
|
||||
- tmux comms watchers, lanes/, board/, memory/, journal/ — L5/L6 fleet
|
||||
territory; our conductor loop is deliberately not a resident service.
|
||||
- openbao/swarm/coolify compose stacks — deployment, far future.
|
||||
- roster.yaml + roster.schema.json — superseded by our agents/ definitions
|
||||
for now; revisit when fleet registration returns.
|
||||
|
||||
## Owner-corrected design points (2026-09-03)
|
||||
|
||||
- M17 revised: skills must reach **non-conductor seats as role-scoped
|
||||
subsets** — agents invent tooling when the right tool isn't supplied.
|
||||
Role contracts will declare skill sets; the runner loads them explicitly
|
||||
(mock-verified `--skill` + `--no-skills` coexistence pending).
|
||||
- M19 revised (owner's own diagnosis): auth selection was symlinks from
|
||||
`agents/<seat>/.pi` into `fleet/auth`, and a missing symlink silently
|
||||
fell back to defaults. Our design: the launcher reads the seat's declared
|
||||
account, **ensures** the symlink/auth selection before TUI launch, and
|
||||
refuses loudly on mismatch — check + fix, never silent default.
|
||||
- Unified `mosaic` CLI is directional (`mosaic tui`, `mosaic yolo
|
||||
<harness>`, `mosaic --check`): tracked as M20, harvested from pattern #1
|
||||
and stack `packages/mosaic`.
|
||||
@@ -0,0 +1,189 @@
|
||||
# ROADMAP — agreed path forward
|
||||
|
||||
Status: planning aligned with owner 2026-09-03. Feature work follows this
|
||||
order unless the owner re-orders. Design notes below are commitments about
|
||||
intent, not implementation — each milestone still gets its own issue, tests,
|
||||
and BUILD-LOG phase.
|
||||
|
||||
## Standing decisions (owner, 2026-09-03)
|
||||
|
||||
- **Pi is the main TUI harness** and the reference checkpoint. Multi-harness
|
||||
(Codex, Claude, OpenCode) comes after pi is stable as the primary seat.
|
||||
- **CI is deferred.** Gitea runs on slow hardware; local suites + verify are
|
||||
the quality gate. Revisit when code volume justifies runners.
|
||||
- **The user never manually runs release.sh.** The system determines what is
|
||||
installed and self-aligns (M16).
|
||||
- **Tooling documentation is agent-accessible** via the ms-tools skill (M17),
|
||||
referenced from AGENTS.md.
|
||||
- **Capability restriction becomes progressive and role-based** (M18),
|
||||
porting prior Mosaic Stack concepts where they fit the file-based runtime.
|
||||
- **The stack never writes to default harness config locations** (owner,
|
||||
2026-09-03, #48): `~/.pi` — and any future harness home — is read-only to
|
||||
Mosaic tooling; the only interaction is the existing read-only credential
|
||||
mount. Mosaic-managed auth lives under the data root (`<dataRoot>/auth/`).
|
||||
|
||||
## M16 — Release self-determination
|
||||
|
||||
Problem: after `reset.sh` the activation pointer is gone and the owner had
|
||||
to know about `scripts/release.sh activate`. The system must determine what
|
||||
is actually installed.
|
||||
|
||||
Design:
|
||||
- New `release.sh ensure` (also invoked automatically by the launchers via
|
||||
`common.sh`): compares desired (`RELEASE`) vs packaged image vs active
|
||||
pointer.
|
||||
- Fast path (drift = pointer missing/mismatched, image already packaged):
|
||||
restore the pointer without a health gate — the release was previously
|
||||
health-gated; reset is data loss, not a new release.
|
||||
- Slow path (new version in RELEASE, or image missing): package beside →
|
||||
full health gate (exact-marker model run) → atomic pointer swap. This is
|
||||
the M3 gate-then-flip, now automatic.
|
||||
- Acceptance: post-`reset.sh`, the next `hello.sh`/`agent.sh` self-restores
|
||||
activation with zero manual release commands; a bumped `RELEASE` auto-
|
||||
activates after its health gate; a failing health gate leaves the previous
|
||||
release active and records the refusal.
|
||||
|
||||
## M17 — ms-tools skill + role-scoped skill lifecycle (DESIGN REVISED per owner)
|
||||
|
||||
Problem: agents without supplied tooling invent their own, even when a
|
||||
functional method exists. Skills must reach non-conductor seats as
|
||||
role-scoped subsets, with an explicit lifecycle.
|
||||
|
||||
Design (owner-specified):
|
||||
- Two machine-scoped directories under the data root:
|
||||
- `<dataRoot>/skills-enabled/` — loadable by agents
|
||||
- `<dataRoot>/skills-available/` — installed but disabled
|
||||
- Lifecycle: **install** → available · **activate** → enabled ·
|
||||
**deactivate** → back to available · **uninstall** → removed.
|
||||
A skill not in skills-enabled is not enabled or available for use.
|
||||
- Launch resolution: role contracts declare skill NAMES; the runner resolves
|
||||
each against skills-enabled and passes `--skill <path>` explicitly.
|
||||
`--skill` necessarily negates `--no-skills` (explicit provision wins).
|
||||
Contracted-but-not-enabled → **refuse the launch loudly** (a silently
|
||||
under-equipped seat is the exact failure mode this prevents).
|
||||
- Containerization removes ambiguity: agents get only what is explicitly
|
||||
provided; no discovery, no ambient skill leakage.
|
||||
- Interfaces: CLI first (`mosaic skill install|activate|deactivate|uninstall|list`),
|
||||
TUI and WebUI frontends call the same tooling later.
|
||||
- The ms-tools skill is the first shipped skill (tooling reference for
|
||||
conductor/vanilla sessions); role contracts then grant subsets per seat.
|
||||
|
||||
## M18 — Seat-role progressive capability restriction
|
||||
|
||||
Problem: capability restriction exists but is per-task/per-mission only.
|
||||
Seats (agent identities) need governing ceilings that tasks cannot escalate
|
||||
past — the M9 intersection chain extended: **role ceiling ∩ mission grant ∩
|
||||
task grant = effective**.
|
||||
|
||||
Design:
|
||||
- `roles/<role>.json` (role contracts, tracked): `roleVersion`, `name`,
|
||||
`tools` ceiling (subset of pi built-ins), `network: "none"|"api-only"|`
|
||||
`"open"` (declared now, enforced when network policy lands), `paths`
|
||||
(future).
|
||||
- Resolution chain in the runner: seat role contract ∩ mission capabilities
|
||||
∩ task capabilities → effective tools (existing intersection logic, one
|
||||
more link). Empty at any stage = tool-free run, loudly recorded.
|
||||
- agent.json keeps `role` (already shipped in M15) — it now binds to
|
||||
`roles/<role>.json`.
|
||||
- Porting note: prior Mosaic Stack role/policy concepts inform this design,
|
||||
but implementation is fresh on the file-based runtime (the standing
|
||||
repository exclusion applies until the owner points at specific prior
|
||||
files to port).
|
||||
- Adapters unchanged: `MOSAIC_TOOLS` already carries the effective set.
|
||||
|
||||
## M19 — Harness auth tooling (pi checkpoint)
|
||||
|
||||
Problem: auth is manual (`/login`, `auth.json`); multi-account and
|
||||
multi-harness stacking have no tooling.
|
||||
|
||||
Current facts (observed): one `auth.json` holds multiple providers
|
||||
(anthropic, openai-codex, zai observed on the host) — provider stacking
|
||||
already works. Multiple accounts per provider is the open question (the
|
||||
owner runs two Claude accounts; openai-codex multi-account desired).
|
||||
|
||||
Design direction (pi-first checkpoint):
|
||||
- `scripts/auth.sh status` — per-provider auth report (which providers have
|
||||
credentials, source: auth.json vs env), no secrets printed.
|
||||
- Multi-account: investigate pi's auth schema for per-provider account
|
||||
entries; if unsupported natively, model accounts as named auth files
|
||||
(`auth.<account>.json`) with a conductor-level selector — runtime-injected
|
||||
per launch, same pattern as the read-only credential mount.
|
||||
- Claude/Codex/OpenCode adapters stay parked; the auth tooling is
|
||||
harness-neutral so it serves them when they arrive.
|
||||
|
||||
## M20+ — Unified `mosaic` CLI and the stack succession path
|
||||
|
||||
Direction (owner): a unified `mosaic` CLI (`mosaic --check`, `mosaic tui`,
|
||||
`mosaic yolo <harness>`, `mosaic skill ...`, `mosaic release ensure`, ...).
|
||||
Much larger task; channel-dispatcher doctrine harvested from
|
||||
`fleet/bin/mosaic` (HARVEST.md #1).
|
||||
|
||||
Succession trajectory (owner): the v2 line is actively building the
|
||||
REPLACEMENT for the mosaicstack/stack repo. At the appropriate level of
|
||||
usurpation, v2 content becomes THE next-branch content on
|
||||
mosaicstack/stack — a full refactor-and-replace with a functional system.
|
||||
Not ready yet.
|
||||
|
||||
DECIDED (owner, 2026-09-03): v2 reorganizes to the `packages/*` monorepo
|
||||
structure for continuity with the existing stack tooling and expectations.
|
||||
Reference: harvest worktree `~/src/mosaic-stack-worktrees/v2-harvest`
|
||||
(branch `next` @ 0db2d19a) and `packages/mosaic` (CLI), `packages/auth`.
|
||||
Usurpation criteria to be defined before any replacement (proposed:
|
||||
functional parity of P0 operations on the v2 runtime + owner sign-off).
|
||||
|
||||
### Restructure sequencing (decided 2026-09-03, owner question)
|
||||
|
||||
The monorepo restructure is NOT done first. Reason: the bash entry points'
|
||||
final home is *replacement by* `packages/mosaic` commands, not containment -
|
||||
moving them into packages/ now, then replacing them at M20, migrates
|
||||
everything twice and churns the suites (the safety net) during peak feature
|
||||
work. Instead:
|
||||
|
||||
1. **Layout doctrine frozen now** (this file + AGENTS.md): nothing new lands
|
||||
at root; features build in their durable homes (`skills/`, `roles/`,
|
||||
`docs/`, `adapters/`, `src/`).
|
||||
2. **M20 phase 1 = restructure**: `packages/` monorepo skeleton absorbs the
|
||||
script entry points as CLI commands (`packages/mosaic`), with
|
||||
`packages/agent`, `packages/auth` following the harvested anatomy. The
|
||||
bash scripts retire as they are absorbed.
|
||||
3. **Succession**: at usurpation, v2 content (already in `packages/*` shape)
|
||||
becomes the next-branch content of mosaicstack/stack.
|
||||
|
||||
### Target tree (post-M20-phase-1)
|
||||
|
||||
/ bootstrap + entry docs (unchanged)
|
||||
+-- packages/
|
||||
| +-- mosaic/ the CLI: tui, yolo, skill, release, onboard, check
|
||||
| +-- agent/ seat/session runtime (absorbs src/ + adapters/)
|
||||
| +-- auth/ provider slots, account ensure (absorbs M19)
|
||||
| +-- config/ schema + policy validation (absorbs config tooling)
|
||||
+-- contracts/ roles/ missions/ tasks/ templates/ declarative inputs
|
||||
+-- skills/ shipped skills (installed to dataRoot on activate)
|
||||
+-- docs/ plans, harvest, SESSIONS, TOOLS
|
||||
+-- src/ container runtime (loader, dispatcher)
|
||||
|
||||
### Skills as discipline encoding (owner insight, 2026-09-03)
|
||||
|
||||
Skills are not tool menus - they encode operational discipline: order of
|
||||
operations, gotchas, and failure history. The tool-race lesson and every
|
||||
catalogued defect become skill content per role (e.g. a conductor skill
|
||||
carries: sequential dependent calls; verify writes before claiming;
|
||||
symlink-ensure before TUI; path pre-checks before pi flags). Role
|
||||
contracts (M17) declare which skill subsets each seat receives.
|
||||
|
||||
## Explicitly deferred
|
||||
|
||||
- CI runners (owner: Gitea hardware is slow; local suites are the gate) —
|
||||
revisit as code volume grows.
|
||||
- Network egress policy enforcement (declared in role contracts at M18;
|
||||
enforcement is its own milestone).
|
||||
- Push automation (push stays an explicit act).
|
||||
- Second real adapter; fleet/multi-agent; knowledge storage; portal.
|
||||
|
||||
## Completed reference (see docs/plans/CURRENT.md log)
|
||||
|
||||
M1 config-driven hello · M2 missions/tasks · M3 release model · M4 adapter
|
||||
seam · M5 workspaces/capabilities · M6 sessions · M7 ergonomics · M8
|
||||
conductor loop · M9 mission capability policy · M10 retention · M11 session
|
||||
forking · M12 auto-apply policy · M13 interactive TUI agent · M14 live user
|
||||
context · M15 agent seats + roles/ convention.
|
||||
@@ -0,0 +1,156 @@
|
||||
# ACT-04 test package readiness review (Darkwing)
|
||||
|
||||
Date: 2026-09-07. Author: Darkwing, at Jason's request.
|
||||
Scope: review only. No model calls, no trial execution, no session, launcher
|
||||
or demo-candidate changes, no staging, commit or push.
|
||||
|
||||
Reviewed sources: [pack README](README.md), [cases.json](cases.json),
|
||||
[candidate SOUL](candidate/SOUL.md), [fixtures](fixtures/), baseline
|
||||
`agents/darkwing/SOUL.md`, [ACT-1 plan](../2026-09-07_agent-context-templates-and-migration.md),
|
||||
[concepts index](../../concepts/README.md), `scripts/prepare-concept-tests.mjs`,
|
||||
`scripts/test-darkwing-launch.mjs`, `scripts/agent-host-dev.sh`, CURRENT.md.
|
||||
|
||||
## Verification performed
|
||||
|
||||
- `node scripts/prepare-concept-tests.mjs --check` passed: 13 Mosaic concepts,
|
||||
license attribution, 11 synthetic scenarios; zero model calls.
|
||||
- `node --test scripts/test-darkwing-launch.mjs` passed 2/2 in temporary
|
||||
fixtures; no Docker, model or live state touched.
|
||||
- Read the pinned pi CLI help (0.84.4): `-p`, `--no-tools`, `--session-dir`,
|
||||
`--append-system-prompt`, `--no-context-files --no-skills --no-extensions`
|
||||
are all available for headless trial runs.
|
||||
|
||||
## Scenario review against accepted foundation decisions
|
||||
|
||||
All 11 cases test judgment and honesty, not runtime features, and every
|
||||
`runtimeGate` stays DEFERRED. That matches the pack's own limits. Case by case:
|
||||
|
||||
| Case | Foundation anchor | Verdict |
|
||||
|---|---|---|
|
||||
| C01 | D3 single SOUL, no fallback; fail-closed mandatory context | Sound. Fixture inventory excludes root and contract SOULs with reasons that match D3 and R16 |
|
||||
| C02 | D2 directness, useful brevity | Sound; no runtime claim |
|
||||
| C03 | Write-once preservation, managed-worktree concept (proposed) | Sound; worktree-is-not-isolation expectation matches worker sandbox boundaries |
|
||||
| C04 | Pause-then-reconcile owner ruling (Q26); never guess, verify | Sound; historyGap expectation matches restart-reconciliation decisions |
|
||||
| C05 | Honest interruption semantics; design claim vs observed behavior | Sound; no-cancel-claim expectation mirrors SOUL evidence discipline |
|
||||
| C06, C11 | One controlling interface, observers without control (Q16); least privilege | Sound; attribution-does-not-grant-access matches policy invariants |
|
||||
| C07 | Startup marker is not verification; STANDARDS forbids relabeling skipped checks | Sound; the strongest discipline case in the pack |
|
||||
| C08 | Accurate failure reporting; acceptance separate from implementation | Sound; mirrors current FI-FILBERT-8 discipline |
|
||||
| C09 | Authorization never accrues from elapsed time or repetition | Sound; external-claim expectation matches role-authority rules |
|
||||
| C10 | Wake registration requires authority; no prose-note scheduling | Sound; matches the no-automatic-continuation rule |
|
||||
|
||||
No case asks the model to claim a proposed feature exists. No case touches a
|
||||
real user profile, live session, credential or destructive action. The
|
||||
`expected` rubrics stay out of model input under the proposed trial mechanism.
|
||||
|
||||
## Gaps
|
||||
|
||||
- **G1, C01 self-reference risk.** The prompt says to treat
|
||||
`context-inventory.json` as synthetic data, but the trial agent is itself
|
||||
launched with a SOUL from the workspace. A sharp model may answer about its
|
||||
own context instead of the fixture. Not a defect, but judges must require
|
||||
the answer to cite the fixture. Keep the synthetic-data framing verbatim
|
||||
when composing input.
|
||||
- **G2, launcher does not fit trial isolation.** `scripts/agent-host-dev.sh`
|
||||
injects repo AGENTS.md, STANDARDS, the live data-root USER file, 10 skills,
|
||||
the goal extension and write tools, and stores sessions under
|
||||
`$REPO/.pi/state/<agent>`. Its `--soul/--user` overrides help, but the rest
|
||||
cannot be turned off. The pack requires exactly one SOUL, synthetic user
|
||||
context, no extensions or write tools, and a fresh session per case and
|
||||
variant. Running the pinned pi binary headless from the staged workspace
|
||||
covers all of that without adding repo code or a new launcher.
|
||||
- **G3, baseline and candidate are not style-only twins.** The baseline SOUL
|
||||
carries Mosaic operating disciplines: evidence classes, preserving other
|
||||
agents' work, no self-granted authority. The candidate is voice only, with
|
||||
one honesty sentence. Eight of eleven cases largely test judgment. A
|
||||
candidate FAIL there measures missing content, not tone; a PASS shows the
|
||||
voice holds without explicit rules. Both readings are useful, but the trial
|
||||
record should state which question is being asked so a FAIL is not
|
||||
misread as a style defect.
|
||||
- **G4, judging protocol is unwritten.** RESULTS.json rows have evidence
|
||||
fields but no per-expectation verdict shape. Proposal: judges record each
|
||||
expectation as pass or fail with a quoted line from the actual output;
|
||||
style calls stay with Jason, never automated.
|
||||
- **G5, evidence durability.** The staged workspace lives in the OS temp
|
||||
directory and may be wiped. Actual outputs and RESULTS.json must be copied
|
||||
to an assigned durable location before any acceptance relies on them.
|
||||
Destination needs Jason's call; a `trials/` subfolder here is one option
|
||||
and would be a repo write needing his authorization.
|
||||
|
||||
## Proposed first trial: bounded baseline versus candidate
|
||||
|
||||
Scope: two cases, two variants, four model calls. Optional single repeat of
|
||||
an ambiguous case, capped at eight calls total.
|
||||
|
||||
Cases: ACT-C02 (concision; no fixture; pure style comparison) and ACT-C08
|
||||
(honest completion reporting; hard rubric under the candidate's voice). Both
|
||||
have `runtimeGate: null`, so nothing in the trial can be confused with a
|
||||
runtime feature test. Together they answer the two questions that matter
|
||||
first: does the candidate's voice read better on a plain answer, and does it
|
||||
keep hard honesty discipline without the baseline's explicit rules.
|
||||
|
||||
Mechanism, reusing existing verified pieces only:
|
||||
|
||||
1. `node scripts/prepare-concept-tests.mjs` stages a private 0700 workspace.
|
||||
Zero model calls. No repo directory is written.
|
||||
2. Per run, compose one context file: `contracts/CONSTITUTION.md` plus
|
||||
`fixtures/USER.md` (synthetic Casey profile) plus exactly one SOUL, the
|
||||
variant's. No AGENTS.md, no STANDARDS, no skills, no second SOUL.
|
||||
3. Run the pinned `node_modules/.bin/pi` headless: `-p`, `--no-tools`
|
||||
(fixtures are inlined in the prompt, so there is nothing to sandbox),
|
||||
`--no-context-files --no-skills --no-extensions --no-prompt-templates
|
||||
--no-themes`, `--append-system-prompt <composed file>`, provider and model
|
||||
taken from the validated `load_config` values, `--session-dir
|
||||
<workspace>/sessions/<variant>/<case-id>` so every case and variant gets a
|
||||
fresh session identity.
|
||||
4. Authentication is referenced in place if required, never copied into the
|
||||
workspace, and never printed.
|
||||
5. Save each actual response under the workspace; update the four
|
||||
RESULTS.json cells for these cases with status, evidence and quoted
|
||||
expectation verdicts. All other 18 rows stay NOT_RUN.
|
||||
6. Copy outputs and RESULTS.json to the durable location Jason names.
|
||||
|
||||
Recorded before execution, per the pack's binding requirements: harness and
|
||||
version (pi 0.84.4 pinned), provider/model, context file SHA-256 per run,
|
||||
both SOUL hashes (already in RESULTS.json variants), session directories,
|
||||
operator and reviewer names, and the model-call budget.
|
||||
|
||||
What this trial does not do: no runtime gate moves to PASS, no template is
|
||||
promoted, the candidate never touches active Darkwing, and nothing here is
|
||||
demo acceptance. A hard rubric failure fails the case regardless of tone;
|
||||
Jason alone judges brevity, candor and personality.
|
||||
|
||||
## Preserved by this review
|
||||
|
||||
`agents/darkwing/**` including SOUL, launcher and CONTEXT; `scripts/agent*.sh`
|
||||
and all launch inputs; `.pi/state/darkwing` sessions; the Rocko and Filbert
|
||||
tmux sessions and frozen demo candidate; Dewey's shared git index. Nothing
|
||||
staged, committed or pushed. CURRENT.md's next action (await FI-FILBERT-8 r6
|
||||
verdict) is unchanged by this review.
|
||||
|
||||
## Decisions needed from Jason
|
||||
|
||||
1. Authorize trial execution and name operator, reviewer, and the call/cost
|
||||
budget (proposal: 4, hard cap 8).
|
||||
2. Choose the durable evidence location.
|
||||
3. Later: approve the G1 judging note before C01 ever runs, and confirm G3's
|
||||
interpretation rule, that a candidate judgment FAIL is a content finding
|
||||
first, belongs in the trial record.
|
||||
|
||||
## Correction, appended 2026-09-07 (STANDARDS attribution)
|
||||
|
||||
Per Jason's direction. The C07 row above says "STANDARDS forbids relabeling
|
||||
skipped checks". That attribution is wrong. `contracts/STANDARDS.md` contains
|
||||
only the startup-verification answer rule: answer with the requested value,
|
||||
no added explanation or formatting. The "cannot relabel skipped checks as
|
||||
passed" language comes from the ACT-1 plan's "Current behavior to preserve
|
||||
and then migrate" section on startup-marker separation, and the evidence
|
||||
classes discipline lives in the SOUL and AGENTS.md, not in STANDARDS.
|
||||
|
||||
The C07 expectation itself (refusing to infer hook enforcement from a passing
|
||||
startup check) remains sound and anchored; only the citation was wrong. The
|
||||
original row text is kept as written per the corrections-are-appended rule.
|
||||
|
||||
G3 resolution recorded in the trial record: the first trial compares complete
|
||||
SOUL configurations, not a controlled style-only experiment, and no failure
|
||||
cause may be inferred from it. Trial evidence:
|
||||
`.pi/evidence/act-1/2026-09-07T1625Z-c02-c08-r1/`.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user