Splinter → Human.work — Features Porting Plan
Author: Alex Dankov (@Dankovk) Source: Notion — Splinter → Human.work Features Porting (2026-05-20) Status: Architectural target and reasoning for what gets ported from Splinter (now Rig —
humanity-org/rig) into Human.work, and what does not. Companion docs:
ISOLATION_SANDBOXING_BINARIES.md— full isolation/sandbox architecture deckRIG_HARD_CUT_RUNTIME_PORT_PLAN.md— implementation plan landed by PR #696Port Splinter's control-plane discipline, not Splinter as a whole product. End-state architecture is five layers: control plane / runtime manifest / capability shell / OS sandbox / inspector — see
ISOLATION_SANDBOXING_BINARIES.mdfor the deep-dive on layers 2–4.Cross-ref: the layered isolation model here is the runtime/sandbox half of the broader resource-class isolation matrix defined in ADR-020 — Isolation Classification. ADR-020 is the source of truth for which resources isolate by Org-only vs (Org × Specialist) vs Specialist-only; this porting plan is how the (Org × Specialist) runtime tier gets built.
Purpose
This document describes which Project Splinter architecture pieces are worth porting into Human.work, why they are worth porting, and what the resulting architecture should look like. This is not a roadmap. It is the architectural target and reasoning. The core idea is simple: port Splinter’s control-plane discipline, not Splinter as a whole product. Splinter contains strong patterns for controlled execution, policy enforcement, event timelines, approvals, artifacts, runtime inspection, and sandboxing. Those patterns fit Human.work. Splinter’s desktop/TUI surfaces and code-agent PR machinery mostly do not.
High-level position
Human.work should become safer and more observable before it becomes more autonomous. The useful Splinter pieces are:
- explicit run state;
- durable runtime events;
- approval and user-input requests;
- a unified tool gateway;
- policy/guard evaluation;
- structured artifacts;
- runtime/operation inspection;
- controlled execution through a capability shell;
- OS/container sandboxing around that shell;
- validation pipelines and plugin validators. The wrong port is:
- raw shell access;
- broad file tools for normal Specialist workflows;
- desktop/TUI cockpit;
- worktree/PR machinery as the default execution model;
- autonomous scheduling before approval/policy/event primitives exist.
Why Splinter is relevant
Splinter is built around the idea that autonomous or semi-autonomous agents need a control plane. It treats agent execution as something that must be constrained, observed, replayed, approved, and validated. That matters for Human.work because Specialist behavior touches client data, client communications, integrations, credentials, and external channels. The platform needs to answer hard operational questions:
- What did the model try to do?
- Which tool was requested?
- Was the tool allowed, blocked, or approval-gated?
- Which policy rule matched?
- What data was accessed?
- Was anything sent externally?
- Who approved it?
- What artifact/log proves the result?
- Which runtime/policy/tool manifest was active?
- Why did a task get stuck or fail? Splinter has patterns for those questions. Human.work should adapt them to its product model.
End-state architecture
The target architecture has five layers.
Human.work control plane
owns scope, credentials, policy, approvals, audit, events, artifacts, tool dispatch
Runtime manifest / compiled runner
carries allowed operation surface, policy hash, tool manifest hash, runtime identity
Capability shell
executes structured non-secret operations: file/artifact/process/browser primitives
OS/container sandbox
enforces filesystem, syscall, process, network, memory, and time boundaries
Ops/Expert inspection surface
shows runs, events, approvals, denied operations, artifacts, runtime health
Implementation status (last updated 2026-05-28): Layer 1 control plane and Layer 5 inspection are landed; Layers 2–4 are now locally scaffolded in this branch (compiled-runner workflow/registry, Zig capability shell + Python adapter, ADR-008 substrate scaffold/nightly deny-battery) but production closure still needs CI secret configuration, ADR cosign, merge, and nightly soak evidence.
The important separation is authority vs execution.
- The control plane decides what is allowed.
- The runner proves what it is allowed to execute.
- The capability shell performs narrow structured operations.
- The sandbox enforces hard boundaries.
- The inspector makes all of it visible.
Architectural flow
Client message or internal task
↓
Run is created
↓
Control plane resolves policy, tool manifest, runtime manifest
↓
Model proposes message/tool/action
↓
Control plane evaluates action
↓
If action needs approval:
create approval request and pause
↓
If action is a credentialed business tool:
control-plane tool gateway performs/proxies call
↓
If action is file/process/browser-like:
capability shell executes structured operation
↓
OS/container sandbox enforces hard limits
↓
Events, artifacts, audit records are written
↓
Expert/Ops surfaces show timeline and state
Secrets stay in the control plane or dedicated credential systems. The execution runtime receives minimal capabilities, not ambient credentials.
Splinter part: run state machine
What Splinter has
Splinter defines explicit run/runtime statuses in packages/contracts/src/runtime.ts. It separates runs, runtimes, approvals, user input requests, and worktrees.
Representative statuses:
createdqueuedpreparingrunningwaiting-approvalwaiting-user-inputvalidatingreviewingcompletedfailedcancelledpaused
Why port it
Without a canonical run state, every feature invents a partial state machine: conversation status, Expert queue status, tool execution status, agentic task status, channel delivery status. That makes the platform harder to debug and easier to break. A run state machine gives one authoritative representation of execution.
How to port it
Create a Human.work run model that captures each meaningful model/tool/task execution.
Suggested concept: agent_runs.
Important fields:
- run id;
- conversation/task references;
- Specialist/runtime references;
- run kind;
- status;
- model/runtime adapter;
- runner hash;
- policy hash;
- manifest hash;
- pending approval count;
- pending input count;
- timestamps;
- error text. Adapt status names to Human.work terminology where useful:
waiting_expert_approvalinstead of genericwaiting-approval;waiting_client_inputinstead of genericwaiting-user-input;tool_executingwhere a tool call is in progress.
End architecture
Every Specialist response, tool action, and internal agentic execution has a run. Runs are visible in Ops and can be replayed from events. Queue items and approvals refer to runs instead of duplicating runtime state.
Splinter part: durable event bus
What Splinter has
Splinter has an append-only event bus in packages/runtime/src/events.ts. Events are written as JSONL and retained as execution evidence.
Why port it
Human.work needs a durable execution timeline. Audit logs are necessary but not enough. Operators need a chronological view of what happened inside a run. Events are the connective tissue between model calls, tool calls, approvals, sandbox decisions, artifacts, and final outcomes.
How to port it
Use a database-backed append-only event table rather than local JSONL.
Suggested concept: run_events.
Event envelope:
{
"id": "evt_...",
"runId": "run_...",
"type": "tool.requested",
"source": "api|agent|runner|sandbox|expert|system",
"actor": { "type": "specialist|expert|runtime|system", "id": "..." },
"correlationId": "...",
"payload": {},
"createdAt": "..."
}
Initial event types:
run.createdrun.startedrunner.selectedrunner.policy.loadedmodel.startedmodel.completedtool.requestedtool.allowedtool.blockedtool.startedtool.succeededtool.failedapproval.requestedapproval.resolvedsandbox.exec.startedsandbox.exec.deniedsandbox.exec.finishedartifact.createddraft.createdmessage.sentrun.completedrun.failed
End architecture
Every meaningful runtime action emits a durable event. Ops and Expert surfaces render timelines from these events. Event payloads are masked and never contain raw secrets.
Splinter part: approvals and input requests
What Splinter has
Splinter models approvals and user-input requests as first-class runtime objects. A run can pause until approval or input is resolved.
Why port it
Human.work is fundamentally HITL. Approval should be a platform primitive, not an incidental field inside a task JSON blob or queue item. Approval is needed for:
- high-risk replies;
- sending messages externally;
- integration writes;
- data exports;
- risky browser actions;
- tool calls with irreversible side effects;
- missing context questions.
How to port it
Create first-class approval and input request models. Approval request fields:
- request id;
- run id;
- request kind;
- risk level;
- status;
- payload;
- requested by;
- resolved by;
- resolved at;
- expiration;
- created at. Decision states:
pendingapprovedrejectedcancelledexpired
End architecture
A run can pause for explicit approval or input. Experts and Ops see pending approvals in a single inbox. Resolving an approval resumes or blocks the run and emits events/audit records.
Splinter part: policy guard
What Splinter has
Splinter has a PolicyGuard that evaluates several action types through a unified decision engine: command execution, tool calls, file access, content writes.
Why port it
Human.work currently needs consistent decisions across business actions. Tool permissions, risk classification, auto-reply policy, integration credentials, and approvals should not be scattered across unrelated if-statements. A single policy evaluator gives predictable, auditable decisions.
How to port it
Implement a Human.work-specific ActionPolicyService.
Evaluation contexts:
tool-callmessage-sendintegration-readintegration-writedata-exportcredential-accesschannel-sendartifact-readartifact-writeexec-argvbrowser-actionDecision values:allowobserverequire_expert_approvalrequire_client_approvalblockPolicy rule examples:- block cross-scope data access;
- require Expert approval for high-risk replies;
- require approval for integration writes;
- block raw credential exposure;
- block production infrastructure mutation without explicit approval;
- observe new tool integrations before enforce mode;
- require artifacts for browser smokes;
- block unredacted PII in event/audit payloads.
End architecture
Every risky action has a policy decision. The decision is emitted as an event, stored in audit, and shown in run timelines. Policy can run in observe mode before enforce mode.
Splinter part: tool gateway
What Splinter has
Splinter uses a runtime tool gateway/materialization pattern. Tools are not random functions scattered through the system; they have descriptors and a controlled execution path.
Why port it
Human.work already has multiple tool planes: API registry, legacy tool registry, Python agent tool specs. This will drift as integrations grow. The platform needs a single source of truth for:
- tool names;
- actions;
- schemas;
- risk levels;
- credential requirements;
- integration type;
- policy subject;
- dispatch handler;
- audit behavior.
How to port it
Make the Nest/API tool registry canonical. Generate or contract-test Python agent tool specs against it. Tool gateway pipeline:
request
→ schema validation
→ scope check
→ permission check
→ policy decision
→ approval if needed
→ credential resolution or proxy
→ execution
→ result normalization
→ audit
→ event
→ metrics
Vendor/integration calls should go through this gateway:
- Nango;
- Jumio;
- Shopify;
- Amazon;
- future CRM/accounting/KYC tools.
End architecture
The model can only request tools that the platform advertises. The API validates and dispatches every tool call. Python specs cannot drift from API dispatch. Tool results and failures are normalized.
Splinter part: artifacts
What Splinter has
Splinter defines artifact summaries tied to runs/tasks. Artifacts represent generated outputs, logs, screenshots, validation evidence, and other durable evidence.
Why port it
Human.work needs durable evidence for Expert review, Ops debugging, compliance, and smoke tests. Raw logs are not enough. Artifacts are especially important for:
- prompt snapshots;
- model outputs;
- redacted tool input/output;
- generated documents;
- browser screenshots/traces;
- validation logs;
- sandbox execution logs.
How to port it
Create a run_artifacts model backed by object storage for large blobs.
Artifact fields:
- artifact id;
- run id;
- task/conversation reference;
- kind;
- label;
- storage URL/path;
- metadata;
- redaction level;
- created at.
End architecture
Runs link to artifacts. Artifacts can be inspected from Ops/Expert surfaces. Sensitive artifacts are redacted or permission-gated.
Splinter part: plugin validators
What Splinter has
Splinter has a plugin validator system in packages/runtime/src/plugins.ts. Validators run against tasks and emit started/finished events.
Why port it
Human.work needs repeatable guardrails that catch known classes of mistakes:
- tool registry drift;
- missing org scoping;
- audit PII leakage;
- bad env variable names;
- Nango key-tier mistakes;
- test weakening;
- terminology errors;
- unsafe integration credential handling. These are better handled by validators than by relying on memory and code review alone.
How to port it
Implement a small validator interface:
interface Validator {
id: string;
appliesTo(context): boolean;
run(context): Promise<ValidationResult>;
}
Initial validators:
- registry drift validator;
- IDOR/org-scope validator;
- audit masking validator;
- Nango environment/key-tier validator;
- test integrity validator;
- terminology validator;
- legacy route validator.
End architecture
Validators run in CI and scheduled health checks. Their results are recorded as validation events/artifacts and visible in Ops.
Splinter part: inspector
What Splinter has
Splinter has inspector tools and journals for runtime inspection, follow-up notes, active runs, and operational debugging.
Why port it
Human.work needs internal Ops visibility. Operators should not need Railway logs to understand a stuck Specialist run or failed tool call.
How to port it
Do not port the TUI/desktop surface. Build inside /ops.
Suggested Ops surfaces:
- active runs;
- run detail timeline;
- pending approvals;
- failed tools;
- denied sandbox operations;
- artifacts;
- integration health;
- runner/policy/manifest hashes;
- runtime status.
End architecture
AMs/SuperAdmins can inspect runtime behavior directly in the product. Run timelines and artifacts become the primary debugging interface.
Splinter part: validation queue
What Splinter has
Splinter has queue/scheduler machinery for validating and verifying tasks.
Why port it
Human.work needs scheduled and on-demand validation, but not necessarily autonomous code-agent scheduling. Useful validation jobs:
- Nango health;
- Jumio sandbox;
- channel webhook smoke;
- Expert workspace browser smoke;
- client onboarding smoke;
- agent eval calibration;
- integration credential drift;
- provider config checks.
How to port it
Create validation runs rather than generic code-agent queues. Validation runs produce:
- status;
- events;
- artifacts;
- failures;
- follow-up tasks.
End architecture
Integration and UI smokes run periodically. Failures show up in Ops with artifacts and remediation notes.
Splinter part: sandboxing and isolation
What Splinter has
Splinter has sandbox backend abstraction and runtime isolation ideas. It can select sandbox backends, wrap runtime commands, and reason about filesystem/network policy.
Why port it
Human.work will eventually need safe execution for file processing, scripts, browser automation, and internal Ops/code tasks. But normal Specialist conversation should not get arbitrary shell access. Sandboxing is useful only when paired with:
- structured operations;
- no ambient secrets;
- policy decisions;
- event logging;
- approval gates;
- OS/container enforcement.
How to port it
Use a layered sandbox model:
- control-plane policy;
- runtime manifest;
- capability shell;
- OS/container sandbox;
- events/artifacts/inspection. Do not expose a general shell. Expose typed operations.
End architecture
File/process/browser work runs inside a restricted runtime. The model cannot access arbitrary filesystem/network/secrets. Denied operations are visible and auditable.
Per-runtime compiled runner
Why compile/bundle a runner
A compiled or bundled runner reduces the execution surface and improves provenance. Benefits:
- only allowed operations are present;
- policy/tool manifest can be baked or signed;
- no source tree or dev tooling by default;
- runner hash is auditable;
- stale/unknown runners can be rejected;
- runtime identity is explicit.
What compilation does not solve
Compilation alone does not:
- enforce syscalls;
- prevent network exfiltration;
- protect env secrets;
- enforce database scoping;
- make arbitrary shell safe;
- replace the control plane.
What to bake in
Bake in:
- environment;
- runner version;
- allowed tool manifest;
- allowed operation manifest;
- policy hash;
- prompt/spec bundle hash;
- event schema version;
- control-plane verifier/public key;
- build provenance. Do not bake in:
- vendor secrets;
- OAuth tokens;
- DB credentials;
- signing keys;
- master encryption keys;
- infrastructure tokens.
End architecture
A run records exactly which runner build was used. The control plane rejects stale or unknown runner builds. Secrets are still fetched/proxied by the control plane.
Zig capability shell
Why Zig
A small native runtime can be easier to reason about than a full Python/Node shell environment. Zig is suitable for a minimal capability shell because it can produce a compact binary with explicit process/file/network handling.
The point is not that Zig is magic. The point is that a small runtime with a narrow protocol is easier to constrain than /bin/sh plus a full application source tree.
Shell is the wrong abstraction
The model should not get shell strings. It should get operation schemas. Supported operations should look like this:
{ "op": "artifact_read", "path": "inputs/customer.csv", "maxBytes": 1048576 }
{ "op": "artifact_write", "path": "outputs/result.json", "contentRef": "blob:..." }
{
"op": "exec_argv",
"argv": ["python", "scripts/normalize.py", "inputs/customer.csv"],
"cwd": "/workspace",
"network": "off",
"timeoutMs": 30000,
"maxOutputBytes": 262144
}
Capability shell responsibilities
The shell should enforce:
- JSON operation schema;
- deny by default;
- path allowlists;
- argv-only execution;
- no shell interpolation;
- env scrubbing;
- timeout caps;
- output caps;
- process tree cleanup;
- working directory restrictions;
- network mode declaration;
- event emission.
End architecture
The runtime can perform useful file/process operations without giving the model a general shell. Every operation is structured, constrained, and auditable.
No-env secrets policy
Why env secrets are dangerous
Environment variables are ambient authority. Child processes can often read them through:
env;/proc/self/environ;- process listings;
- crash dumps;
- logs;
- debugging tools. If secrets are in env, arbitrary subprocess execution becomes much more dangerous.
Preferred pattern
Credentialed business tools should execute through the control-plane gateway.
runtime requests tool action
→ control plane checks policy and approvals
→ control plane uses credential
→ control plane returns redacted result
The runtime never sees the raw secret.
Capability token fallback
If direct runtime access is unavoidable, use short-lived capability tokens:
- scoped to one run;
- scoped to one action;
- scoped to one resource;
- short TTL;
- signed;
- passed over stdin/fd/memory;
- never stored in env;
- never logged.
End architecture
Runners have no broad ambient credentials. Secrets stay in credential systems or the control plane. Runtime operations receive the minimum authority needed for a single action.
OS/container sandbox
Why Zig is not enough
A Zig runtime can constrain itself and choose how to launch children. It cannot fully prevent child syscalls, arbitrary filesystem access, or network exfiltration unless an OS/container sandbox enforces those constraints.
Enforcement layer
For Linux-style workers, use:
- namespaces;
- read-only root filesystem;
- bind mounts for workspace/artifacts only;
- tmpfs for temp;
- seccomp-bpf;
- cgroups;
- dropped capabilities;
- no-new-privileges;
- network namespace or egress proxy;
- optional Landlock.
Sandbox modes
Useful modes:
read-onlyworkspace-writeartifact-writenetwork-offnetwork-proxy-onlydanger-full-accessfor explicit dev-only use
End architecture
The capability shell runs inside an enforceable sandbox. Even if a child process misbehaves, the OS boundary prevents broad filesystem/network/process abuse.
Browser automation
Why port carefully
Browser automation is useful for onboarding smokes, integration setup validation, and internal QA. It is also risky because browsers handle credentials and can perform irreversible actions.
How to port
Treat browser sessions as sandboxed, artifact-producing operations. Rules:
- domain allowlists;
- no raw credential exposure to model;
- screenshots/traces stored as artifacts;
- submit/write actions approval-gated;
- browser sessions linked to runs;
- all actions emitted as events.
End architecture
Browser automation is available for controlled Ops/validation workflows first. It is not a default Specialist capability.
Worktree isolation
Why not default
Splinter’s worktree isolation is excellent for code agents. Human.work’s normal Specialist workflows are not code-editing tasks.
How to port
Use worktrees only for internal code/Ops tasks. Runtime modes:
conversationops_workspaceops_worktree
End architecture
Internal code tasks can run in isolated worktrees. Client-facing Specialist conversations remain conversation/tool workflows.
What not to port
Do not port:
- Splinter desktop/TUI as a Human.work surface;
- PR/branch/Greptile loop as default workflow;
- raw filesystem tools for normal Specialists;
- broad autonomous scheduler before policy/events/approvals;
- native snapshots before file/code-agent workloads justify them;
- codebase-specific rules as product policy. Port the architecture patterns, not the entire product.
Final architecture summary
The desired Human.work runtime architecture is:
1. Control plane creates and owns runs.
2. Every execution emits durable events.
3. Risky actions become approval/input requests.
4. Tools execute through one policy-aware gateway.
5. Business credentials stay in the control plane or credential systems.
6. File/process/browser operations go through a structured capability shell.
7. The capability shell receives no ambient secrets.
8. OS/container sandboxing enforces hard boundaries.
9. Artifacts preserve redacted evidence.
10. Ops and Expert surfaces inspect timelines, approvals, artifacts, and failures.
This gives Human.work a controlled agent runtime: observable, approvable, policy-governed, secret-minimized, and eventually sandboxed enough for higher-autonomy internal work.