Lane C implementation of the Zopu Web Work OS for dogfooding:
- Work Unit card/detail projections built from per-issue events only
(artifact counts from distinct artifact.updated paths, PR from
gitea events, activity timeline, agent summaries). Signal linkage
is explicitly 0/unavailable until Lane A relations are integrated.
- Persistent composer with Project and Work Unit mode switching.
Project mode sends to the global Zopu agent; Work Unit mode sends
to the issue-scoped project-manager agent identity.
- Collapsed Work Unit cards show title, summary, signal/step/artifact
counts, current activity, PR indicator, needs-input indicator.
- Expanded Work Unit detail shows objective, timeline, artifacts,
PR with directly usable link, needs-input alert, start action.
- Project-level Signals panel in the sidebar (not attributed to any
issue until a signal-to-issue relation exists).
- Dark theme with calm, Apple-like visual direction.
- 21 projection tests including 5 cross-issue isolation tests and
2 signal isolation tests proving one issue cannot inherit
another issue's artifacts, PR, summary, or project signals.
- Mobile-responsive: detail overlay on mobile, sidebar on desktop.
No backend or schema changes. Uses existing Convex contracts and
Flue agent transport throughout.
Six fixes addressing operational correctness on a real Debian host:
1. Ownership after git operations: chown -R zopu:zopu on the checkout
after clone/update/rollback before running bun install/build as the
service user. .env kept at 0600 with explicit chmod after each
operation.
2. sudo replaced with runuser: minimal Debian does not include sudo.
runuser is part of util-linux (essential) and always available.
All three scripts (bootstrap, update, rollback) now use runuser -u.
3. cron.d entry fixed: /etc/cron.d format requires a username field.
Added ${SERVICE_USER} between the time fields and the command path.
4. Firewall: added explicit `ufw allow in on tailscale0` rule so the
deny-incoming default does not block Tailscale private-overlay
reachability. Removed inaccurate claim that direct private IP works
by default; documented that an explicit per-interface rule is needed.
5. SupplementaryGroups=docker added to zopu-agent.service: the Orb
sandbox runtime lives in the agent process, not just the daemon.
6. zopu-health.service: added Environment=ENV_FILE=__INSTALL_DIR__/.env
so health-check.sh sources the correct .env at custom install paths.
Verified Flue build output: bun run build:agents produces
packages/agents/dist/server.mjs (confirmed by running the build).
Agent unit ExecStart path is correct.
Validated: bash -n on all 6 shell scripts; mocked health-check smoke
against live RivetKit engine on port 6420; systemd unit structural
checks. No JS/TS/agent files changed.
Add the complete project-scoped Zopu chat, Signal extraction, and
work-routing loop:
Backend (signalRouting.ts):
- Agent-token-gated functions for the full routing cycle: list evidence,
create signal, list signals, list active issues, attach signal to
issue, create issue from signal, begin issue, get project context,
list projects
- attachSignalToIssue requires signal.projectId to exist and equal
issue.projectId (org equality alone is insufficient)
- createIssueFromSignal is idempotent: queries existing attachments
before creating, returns created/reused flag
- Duplicate attach retries return existing relation without emitting a
duplicate event
- All functions deny-by-default with agent token validation
Schema:
- Add signalIssueAttachments table with indexes by signal, issue, and
the composite (signalId, issueId) for idempotent lookups
Agent tools (tools/signals.ts):
- Nine Flue tool definitions bound to the organization-scoped agent
instance ID, calling the agent-gated Convex functions
- Organization ID never accepted as free input; always from the bound
instance
Zopu agent (zopu.ts):
- Comprehensive routing-loop instructions: assess actionability, identify
project, create signal only when actionable, route via attach or create,
explain outcome, optionally begin
- Wiring of routing tools into the agent definition
Tests (signalRouting.test.ts):
- Authentication: invalid token rejected on every function
- Project scoping: evidence, signals, issues scoped correctly
- Idempotency: duplicate attach, duplicate create-from-signal, duplicate
signal creation
- Attach vs create: both paths verified
- Cross-project rejection: signal and issue must be in same project
- Org-scoped signal attach rejected (no projectId)
- Provenance: exact raw text preserved through routing loop
- Begin issue: transition and auth verification
Lane D deliverable: reproducible execution-plane deployment on a fresh
Debian dedicated server (~12 cores, 40 GB RAM, single-node).
Artifacts:
- bootstrap.sh: Docker Engine, Bun, repo clone, build, systemd install,
firewall (deny-by-default), optional Tailscale, disk monitor cron
- .env.template: all 8 environment groups documented (Convex, Gitea,
model gateway, AgentOS/RivetKit, Zopu agent, daemon, Docker sandbox,
service auth)
- systemd units: zopu-daemon, zopu-agent (both Restart=always),
zopu-health timer (60s), zopu-docker-cleanup timer (daily)
- scripts: health-check, update, rollback, docker-cleanup, disk-monitor
- Caddyfile: optional reverse proxy (documentation-only by default)
- README.md: full runbook with start/stop/log/update/rollback/cleanup
procedures, topology documentation, and boundary notes
Verified topology (from installed RivetKit source):
- registry.start() boots an in-process RivetKit engine (envoy mode) with
a native Rust sidecar at http://localhost:6420 (DEFAULT_ENDPOINT in
chunk-YDUQHING.js line 4751). createClient() connects back to it.
- No separate Rivet Engine process required for single-node operation.
- Linux x64 sidecar packages (@rivet-dev/agentos-sidecar-linux-x64-gnu)
are available as optional dependencies.
Docker / Orb boundary:
- Docker Engine installed and zopu user in docker group.
- No Docker-backed sandbox code wired; Orb lane contract not yet landed.
- Volume pruning omitted from docker-cleanup.sh to protect durable data.
Validated: bash -n on all 6 shell scripts; mocked health-check smoke
(nc-based TCP probes, mocked systemctl/docker); systemd unit structural
checks. No JS/TS/agent files changed.
Remove the OutOfOrderSource constraint from composeSignal. The agent's
selection order is authoritative for source ordinals; timestamps remain
as per-source provenance metadata. This resolves the contract conflict
where reversed message selections (valid agent compositions) were
rejected by the primitive.
Each source still retains its original createdAt, so chronological
provenance is preserved exactly. The sourceKey includes ordered message
IDs, so different orderings produce distinct signals with deterministic
idempotency.
Coverage added:
- Primitive: non-chronological order accepted, timestamps preserved
- Backend: reversed selection preserves ordinals and exact raw text
These were accidentally committed by git add -A picking up stash-pop
leftovers. docs/ is the canonical location; the root copies were stale
pre-rebrand duplicates.
- Fix all Ultracite lint errors on changed source files (0 remaining)
- Remove project.test.ts and use-personal-organization.test.ts (to be
rewritten when implementation stabilizes)
- Replace .at(-1) with index access for backend tsconfig ES target compat
- Rename generator functions to avoid no-shadow conflicts
- Refactor nested ternaries into helper functions
- Fix unused imports
Auth:
- Add useWebAuth hook as the single web-facing auth interface with four
states (loading/unauthenticated/authenticated/inconsistent)
- Normalize getCurrentUser to return {id: tokenIdentifier, name, email}
- Rename requireOwnerId to requireAuthUserId across all backend callers
- Set expectAuth:true on ConvexReactClient singleton
- Migrate _app, _auth, user-menu, use-personal-organization to useWebAuth
- Remove unused @code/auth/server export (zero callers confirmed)
Primitives:
- Add packages/primitives/src/project.ts with Effect v4 Project domain:
URL normalization, six canonical context kinds, strict remote decoding,
context write decisioning with revision tracking, and no-op detection
- Define PublicGit and ProjectStore replaceable ports
- Define ProjectApplication service orchestrating domain+ports with
deterministic error mapping
- Add comprehensive test suite (28 tests) with in-memory store and fake
PublicGit covering normalization, seeds, idempotency, tenant isolation,
last-write-wins, and typed error mapping