humanish 0.0.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +77 -0
- package/CONTRIBUTING.md +39 -0
- package/LICENSE +21 -0
- package/README.md +435 -8
- package/SECURITY.md +34 -0
- package/dist/actor-contract.d.ts +107 -0
- package/dist/actor-contract.js +286 -0
- package/dist/actor-contract.js.map +1 -0
- package/dist/actor-registry.d.ts +73 -0
- package/dist/actor-registry.js +93 -0
- package/dist/actor-registry.js.map +1 -0
- package/dist/adapter-extension.d.ts +54 -0
- package/dist/adapter-extension.js +214 -0
- package/dist/adapter-extension.js.map +1 -0
- package/dist/argv.d.ts +1 -0
- package/dist/argv.js +8 -0
- package/dist/argv.js.map +1 -0
- package/dist/artifact-reference.d.ts +22 -0
- package/dist/artifact-reference.js +47 -0
- package/dist/artifact-reference.js.map +1 -0
- package/dist/browser-evidence-hygiene.d.ts +7 -0
- package/dist/browser-evidence-hygiene.js +40 -0
- package/dist/browser-evidence-hygiene.js.map +1 -0
- package/dist/claude-agent-sdk.d.ts +94 -0
- package/dist/claude-agent-sdk.js +356 -0
- package/dist/claude-agent-sdk.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +5 -0
- package/dist/cli.js.map +1 -0
- package/dist/codex-app-server-ui.d.ts +39 -0
- package/dist/codex-app-server-ui.js +327 -0
- package/dist/codex-app-server-ui.js.map +1 -0
- package/dist/codex-app-server.d.ts +141 -0
- package/dist/codex-app-server.js +747 -0
- package/dist/codex-app-server.js.map +1 -0
- package/dist/command-failure.d.ts +29 -0
- package/dist/command-failure.js +54 -0
- package/dist/command-failure.js.map +1 -0
- package/dist/computer-use-actor.d.ts +52 -0
- package/dist/computer-use-actor.js +48 -0
- package/dist/computer-use-actor.js.map +1 -0
- package/dist/computer-use.d.ts +232 -0
- package/dist/computer-use.js +615 -0
- package/dist/computer-use.js.map +1 -0
- package/dist/concurrency.d.ts +1 -0
- package/dist/concurrency.js +27 -0
- package/dist/concurrency.js.map +1 -0
- package/dist/concurrent-shared-world-lab.d.ts +115 -0
- package/dist/concurrent-shared-world-lab.js +1092 -0
- package/dist/concurrent-shared-world-lab.js.map +1 -0
- package/dist/core/git-state.d.ts +31 -0
- package/dist/core/git-state.js +142 -0
- package/dist/core/git-state.js.map +1 -0
- package/dist/core/index.d.ts +4 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/run-primitives.d.ts +66 -0
- package/dist/core/run-primitives.js +120 -0
- package/dist/core/run-primitives.js.map +1 -0
- package/dist/cua-actor-lab.d.ts +620 -0
- package/dist/cua-actor-lab.js +2834 -0
- package/dist/cua-actor-lab.js.map +1 -0
- package/dist/device-presets.d.ts +67 -0
- package/dist/device-presets.js +50 -0
- package/dist/device-presets.js.map +1 -0
- package/dist/e2b-desktop-executor.d.ts +101 -0
- package/dist/e2b-desktop-executor.js +309 -0
- package/dist/e2b-desktop-executor.js.map +1 -0
- package/dist/e2b-desktop-launch.d.ts +144 -0
- package/dist/e2b-desktop-launch.js +59 -0
- package/dist/e2b-desktop-launch.js.map +1 -0
- package/dist/e2b-detached.d.ts +53 -0
- package/dist/e2b-detached.js +130 -0
- package/dist/e2b-detached.js.map +1 -0
- package/dist/e2b-terminal-lab.d.ts +345 -0
- package/dist/e2b-terminal-lab.js +1467 -0
- package/dist/e2b-terminal-lab.js.map +1 -0
- package/dist/env-file.d.ts +14 -0
- package/dist/env-file.js +108 -0
- package/dist/env-file.js.map +1 -0
- package/dist/feedback.d.ts +51 -0
- package/dist/feedback.js +313 -0
- package/dist/feedback.js.map +1 -0
- package/dist/image-evidence.d.ts +2 -0
- package/dist/image-evidence.js +33 -0
- package/dist/image-evidence.js.map +1 -0
- package/dist/index.d.ts +63 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/dist/init-templates.d.ts +12 -0
- package/dist/init-templates.js +369 -0
- package/dist/init-templates.js.map +1 -0
- package/dist/init.d.ts +26 -0
- package/dist/init.js +343 -0
- package/dist/init.js.map +1 -0
- package/dist/lab-app-runner.d.ts +78 -0
- package/dist/lab-app-runner.js +403 -0
- package/dist/lab-app-runner.js.map +1 -0
- package/dist/lab-config.d.ts +578 -0
- package/dist/lab-config.js +1751 -0
- package/dist/lab-config.js.map +1 -0
- package/dist/lab-engine.d.ts +77 -0
- package/dist/lab-engine.js +216 -0
- package/dist/lab-engine.js.map +1 -0
- package/dist/lab-preflight.d.ts +67 -0
- package/dist/lab-preflight.js +385 -0
- package/dist/lab-preflight.js.map +1 -0
- package/dist/labs.d.ts +53 -0
- package/dist/labs.js +176 -0
- package/dist/labs.js.map +1 -0
- package/dist/observer-assets.d.ts +2 -0
- package/dist/observer-assets.js +2630 -0
- package/dist/observer-assets.js.map +1 -0
- package/dist/observer-data.d.ts +63 -0
- package/dist/observer-data.js +145 -0
- package/dist/observer-data.js.map +1 -0
- package/dist/observer-static.d.ts +39 -0
- package/dist/observer-static.js +192 -0
- package/dist/observer-static.js.map +1 -0
- package/dist/observer.d.ts +46 -0
- package/dist/observer.js +353 -0
- package/dist/observer.js.map +1 -0
- package/dist/openai-responses-cu.d.ts +113 -0
- package/dist/openai-responses-cu.js +531 -0
- package/dist/openai-responses-cu.js.map +1 -0
- package/dist/oss-lab.d.ts +52 -0
- package/dist/oss-lab.js +299 -0
- package/dist/oss-lab.js.map +1 -0
- package/dist/oss-meta-lab-scoring.d.ts +15 -0
- package/dist/oss-meta-lab-scoring.js +145 -0
- package/dist/oss-meta-lab-scoring.js.map +1 -0
- package/dist/oss-meta-lab.d.ts +279 -0
- package/dist/oss-meta-lab.js +4889 -0
- package/dist/oss-meta-lab.js.map +1 -0
- package/dist/oss-remote-telemetry.d.ts +77 -0
- package/dist/oss-remote-telemetry.js +403 -0
- package/dist/oss-remote-telemetry.js.map +1 -0
- package/dist/persona.d.ts +35 -0
- package/dist/persona.js +133 -0
- package/dist/persona.js.map +1 -0
- package/dist/pi-agent-core.d.ts +77 -0
- package/dist/pi-agent-core.js +203 -0
- package/dist/pi-agent-core.js.map +1 -0
- package/dist/program.d.ts +54 -0
- package/dist/program.js +2347 -0
- package/dist/program.js.map +1 -0
- package/dist/redaction.d.ts +90 -0
- package/dist/redaction.js +292 -0
- package/dist/redaction.js.map +1 -0
- package/dist/run.d.ts +784 -0
- package/dist/run.js +4555 -0
- package/dist/run.js.map +1 -0
- package/dist/scripted-browser-actor.d.ts +294 -0
- package/dist/scripted-browser-actor.js +1286 -0
- package/dist/scripted-browser-actor.js.map +1 -0
- package/dist/scripted-browser-lab.d.ts +107 -0
- package/dist/scripted-browser-lab.js +738 -0
- package/dist/scripted-browser-lab.js.map +1 -0
- package/dist/shared-world-lab.d.ts +208 -0
- package/dist/shared-world-lab.js +1048 -0
- package/dist/shared-world-lab.js.map +1 -0
- package/dist/source-archive.d.ts +82 -0
- package/dist/source-archive.js +365 -0
- package/dist/source-archive.js.map +1 -0
- package/dist/stop-conditions.d.ts +26 -0
- package/dist/stop-conditions.js +66 -0
- package/dist/stop-conditions.js.map +1 -0
- package/dist/terminal-agent-actor.d.ts +36 -0
- package/dist/terminal-agent-actor.js +23 -0
- package/dist/terminal-agent-actor.js.map +1 -0
- package/docs/architecture/actor-contract.md +431 -0
- package/docs/architecture/github-feedback-loop.md +189 -0
- package/docs/architecture/local-codex-tui-actor.md +213 -0
- package/docs/architecture/observer.md +118 -0
- package/docs/architecture/oss-lab-poc.md +242 -0
- package/docs/architecture/project-layout.md +166 -0
- package/docs/architecture/state-driven-executor.md +203 -0
- package/docs/architecture/terminal-product-lane.md +148 -0
- package/docs/contracts/adapter-fixtures.md +80 -0
- package/docs/contracts/core.md +71 -0
- package/docs/contracts/feedback.md +145 -0
- package/docs/contracts/policy.md +305 -0
- package/docs/contracts/run-bundle.md +358 -0
- package/docs/contracts/schemas.md +984 -0
- package/docs/goals/current.md +409 -0
- package/docs/principles/invariants-and-defaults.md +135 -0
- package/docs/principles/self-driving-harness.md +129 -0
- package/docs/product/open-source-install-experience.md +229 -0
- package/docs/ramp/README.md +200 -0
- package/docs/release/open-source-readiness.md +208 -0
- package/docs/release/public-readiness-standard.md +205 -0
- package/docs/roadmap/world-class-open-source-v0.md +311 -0
- package/package.json +102 -8
- package/skills/humanish/SKILL.md +244 -0
- package/skills/humanish/agents/openai.yaml +7 -0
- package/index.js +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stop-conditions.js","sourceRoot":"","sources":["../src/stop-conditions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ActorPersonaRef, ActorStatus, ActorCompletionReason, ActorTrace } from "./actor-contract.js";
|
|
2
|
+
/** The fail-closed marker a SLICE-1 live invocation surfaces (never a raw crash). */
|
|
3
|
+
export declare const TERMINAL_AGENT_NOT_IMPLEMENTED_CODE: "HUMANISH_TERMINAL_AGENT_NOT_IMPLEMENTED";
|
|
4
|
+
/**
|
|
5
|
+
* Options the engine hands the terminal agent session. The transport is the captured
|
|
6
|
+
* non-interactive exec stream (stdin disabled), NOT an interactive PTY — see ActorProtocol
|
|
7
|
+
* "terminal-exec" and the goal packet's PTY ruling. Fields here are the SLICE-1 contract; SLICE 2
|
|
8
|
+
* consumes them to drive the in-sandbox `codex exec` and capture its redacted output.
|
|
9
|
+
*/
|
|
10
|
+
export interface TerminalAgentSessionOptions {
|
|
11
|
+
/** Where the session writes its native trace/transcript/event-stream artifacts. */
|
|
12
|
+
artifactRoot: string;
|
|
13
|
+
/** The composed prompt the agent runs (mission + persona + public-surface manifest). */
|
|
14
|
+
prompt: string;
|
|
15
|
+
/** Public-safe persona reference (id + composed-prompt digest); no plaintext beyond the mission. */
|
|
16
|
+
persona: ActorPersonaRef;
|
|
17
|
+
/** The product's declared PUBLIC surfaces (http(s) URLs / refs) — the only world the agent sees. */
|
|
18
|
+
publicSurfaces: string[];
|
|
19
|
+
/** Session wall-clock budget (also bounded by scenario.caps.maxMinutes in SLICE 2). */
|
|
20
|
+
timeoutMs: number;
|
|
21
|
+
/** Per-run verdict nonce: the agent must echo it so replayed text can't forge the verdict. */
|
|
22
|
+
verdictNonce: string;
|
|
23
|
+
}
|
|
24
|
+
export interface TerminalAgentSessionResult {
|
|
25
|
+
status: ActorStatus;
|
|
26
|
+
completionReason: ActorCompletionReason;
|
|
27
|
+
reason: string;
|
|
28
|
+
/** The provider-neutral evidence projection (humanish.actor-trace.v1, lane "terminal"). */
|
|
29
|
+
trace: ActorTrace;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Registry-facing session entry. SLICE 1: NOT yet live — the dry-run lab path never calls this,
|
|
33
|
+
* and a live engine call fails closed with a structured error before reaching here. Calling it
|
|
34
|
+
* directly throws the same fail-closed marker rather than pretending to drive a real agent.
|
|
35
|
+
*/
|
|
36
|
+
export declare function runTerminalAgentSession(_options: TerminalAgentSessionOptions): Promise<TerminalAgentSessionResult>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// The terminal agent actor: a real autonomous coding agent (Codex) discovering and using a
|
|
2
|
+
// CLI/product from PUBLIC SURFACES ONLY, running INSIDE an E2B shell with command-scoped runtime
|
|
3
|
+
// auth, capturing its non-interactive exec output (stdin disabled) as a redacted event stream +
|
|
4
|
+
// normalized transcript. This is the registry seam for the terminal-product lane.
|
|
5
|
+
//
|
|
6
|
+
// SLICE 1 SCOPE (honest): this module declares the session CONTRACT — the option/result shapes
|
|
7
|
+
// and the registry-facing `runTerminalAgentSession` entry — but the LIVE session is NOT
|
|
8
|
+
// implemented here yet. The dry-run lab path (src/e2b-terminal-lab.ts) never invokes runSession;
|
|
9
|
+
// it builds a contract-only bundle. A live (non-dry-run) call into the engine returns a
|
|
10
|
+
// structured "not yet implemented in this slice" failure, and calling runSession directly throws
|
|
11
|
+
// the same fail-closed marker. SLICE 2 implements the real create -> inject (command-scoped) ->
|
|
12
|
+
// run `codex exec --json` -> capture -> teardown session on the @e2b/desktop commands.run surface.
|
|
13
|
+
/** The fail-closed marker a SLICE-1 live invocation surfaces (never a raw crash). */
|
|
14
|
+
export const TERMINAL_AGENT_NOT_IMPLEMENTED_CODE = "HUMANISH_TERMINAL_AGENT_NOT_IMPLEMENTED";
|
|
15
|
+
/**
|
|
16
|
+
* Registry-facing session entry. SLICE 1: NOT yet live — the dry-run lab path never calls this,
|
|
17
|
+
* and a live engine call fails closed with a structured error before reaching here. Calling it
|
|
18
|
+
* directly throws the same fail-closed marker rather than pretending to drive a real agent.
|
|
19
|
+
*/
|
|
20
|
+
export async function runTerminalAgentSession(_options) {
|
|
21
|
+
throw new Error(`${TERMINAL_AGENT_NOT_IMPLEMENTED_CODE}: the live terminal-agent session (in-sandbox Codex exec + command-scoped runtime auth) is implemented in SLICE 2. SLICE 1 only produces the dry-run contract bundle.`);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=terminal-agent-actor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-agent-actor.js","sourceRoot":"","sources":["../src/terminal-agent-actor.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,iGAAiG;AACjG,gGAAgG;AAChG,kFAAkF;AAClF,EAAE;AACF,+FAA+F;AAC/F,wFAAwF;AACxF,iGAAiG;AACjG,wFAAwF;AACxF,iGAAiG;AACjG,gGAAgG;AAChG,mGAAmG;AAInG,qFAAqF;AACrF,MAAM,CAAC,MAAM,mCAAmC,GAAG,yCAAkD,CAAC;AA+BtG;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,QAAqC;IAErC,MAAM,IAAI,KAAK,CACb,GAAG,mCAAmC,uKAAuK,CAC9M,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
# Actor Contract
|
|
2
|
+
|
|
3
|
+
Date: 2026-06-06 (updated 2026-06-11)
|
|
4
|
+
|
|
5
|
+
Status: accepted design, partially implemented. Shipped: the evidence schema
|
|
6
|
+
`humanish.actor-trace.v1` (`src/actor-contract.ts`) and a registry of five
|
|
7
|
+
actors (`src/actor-registry.ts`: `codex-app-server`, `pi-agent-core`,
|
|
8
|
+
`claude-agent-sdk`, `openai-computer-use`, `scripted-browser`), with
|
|
9
|
+
`actors[0].type` a real dispatch key on the computer-use and scripted-browser
|
|
10
|
+
lab routes. Not yet shipped (roadmap, not near-term claims): the full
|
|
11
|
+
`Actor.run(input)` interface, `RedactionHooks` injection, `ApprovalPolicy`,
|
|
12
|
+
`StagehandCuaActor`, and the `persona-fidelity` verify check. Decision 6's
|
|
13
|
+
capture-time screenshot stance was recanted in 0.6.0; see the inline notes and
|
|
14
|
+
the capture-vs-publish rule in
|
|
15
|
+
[`docs/principles/invariants-and-defaults.md`](../principles/invariants-and-defaults.md).
|
|
16
|
+
|
|
17
|
+
## Context
|
|
18
|
+
|
|
19
|
+
> 2026-06-11: this section describes the world as it stood when the design was
|
|
20
|
+
> accepted (one real actor, hardcoded dispatch). That ceiling has since been
|
|
21
|
+
> removed — the registry now holds the four actors listed in the status note.
|
|
22
|
+
|
|
23
|
+
An actor is the thing that drives a persona scenario and produces evidence. Today
|
|
24
|
+
Humanish has exactly one real actor: the local Codex integration in
|
|
25
|
+
`src/codex-app-server.ts` (plus the `codex-exec` and `codex-tui` variants in
|
|
26
|
+
`src/run.ts`). The actor selection is a hardcoded `if (actor === ...)` dispatch,
|
|
27
|
+
`RunStream.codex` is Codex-shaped, and the evidence schema is
|
|
28
|
+
`humanish.codex-app-server-trace.v1`.
|
|
29
|
+
|
|
30
|
+
That is a ceiling. Humanish's value is being a public-safe harness for persona and
|
|
31
|
+
agent user-studies, and the agent harnesses our users actually run are plural:
|
|
32
|
+
OpenAI Codex, the pi stack (`@earendil-works/pi-agent-core`, `pi-coding-agent`,
|
|
33
|
+
OpenClaw), Claude Code and the Claude Agent SDK, and computer-use models that
|
|
34
|
+
drive a real screen. A neutral, public-safe way to run the same persona scenario
|
|
35
|
+
across these harnesses, and to compare how each fares against a product, is an
|
|
36
|
+
unoccupied position. To get there, the actor must become a pluggable contract,
|
|
37
|
+
not a hardcoded branch.
|
|
38
|
+
|
|
39
|
+
This document defines that contract and the decisions behind it. It is the API
|
|
40
|
+
surface the adapters depend on, so it is treated as durable: proof artifacts are
|
|
41
|
+
API surface.
|
|
42
|
+
|
|
43
|
+
## Decisions
|
|
44
|
+
|
|
45
|
+
1. **Transport-agnostic contract; Codex stays the reference implementation.** The
|
|
46
|
+
contract describes lifecycle and evidence, not transport. An adapter may be a
|
|
47
|
+
subprocess protocol (Codex stdio JSON-RPC, `pi --mode rpc`, `claude -p
|
|
48
|
+
--output-format stream-json`) or an in-process SDK (`pi-agent-core`, Claude
|
|
49
|
+
Agent SDK, Stagehand). The existing Codex app-server integration is the
|
|
50
|
+
reference adapter; `pi-agent-core` is the first in-process-SDK adapter, chosen
|
|
51
|
+
to prove both shapes early.
|
|
52
|
+
|
|
53
|
+
2. **One normalized evidence schema: `humanish.actor-trace.v1`.** Codex `item/*`
|
|
54
|
+
events, Claude `ToolUse`/`ToolResult` blocks, pi `tool_execution_*` events,
|
|
55
|
+
and computer-use `computer_call` cycles all map onto one `ActorTrace` with a
|
|
56
|
+
typed `items[]`. `humanish.codex-app-server-trace.v1` remains a back-compat
|
|
57
|
+
alias during migration.
|
|
58
|
+
|
|
59
|
+
3. **A run is multi-turn within one trace; it stops on goal, abandonment,
|
|
60
|
+
unrecoverable failure, or a wall-clock safety timeout, never on a turn cap.**
|
|
61
|
+
Turn count is explicitly rejected as a stop signal: many turns usually means
|
|
62
|
+
legitimate complex progress, so a turn budget truncates real work and rewards
|
|
63
|
+
early quitting (it is a proxy for "too complex," not for "this user would
|
|
64
|
+
quit"). Patience is modeled as **friction tolerance**, not a budget (see
|
|
65
|
+
Persona section). The only hard runaway guard is the existing `timeoutMs`.
|
|
66
|
+
One `ActorRunResult` covers a multi-step scenario.
|
|
67
|
+
|
|
68
|
+
4. **Redaction is injected once, never re-implemented per adapter.** Every
|
|
69
|
+
adapter receives `RedactionHooks` (the shared secret/path/prompt-digest
|
|
70
|
+
redaction, plus screenshot redaction) and must route all persisted evidence
|
|
71
|
+
through it. This is also where the remaining consolidation of #107 lands.
|
|
72
|
+
|
|
73
|
+
5. **The registry refuses capability mismatches.** Each adapter declares
|
|
74
|
+
`ActorCapabilities`. A scenario that needs `producesScreenshots` (a GUI
|
|
75
|
+
journey) will not be dispatched to a code-only actor that would fake success
|
|
76
|
+
via the shell. Coverage honesty over green-by-construction.
|
|
77
|
+
|
|
78
|
+
6. **Computer-use is one lane behind one adapter.** The shipped computer-use
|
|
79
|
+
actor is `openai-computer-use` (fronting the OpenAI Responses adapter); a
|
|
80
|
+
`StagehandCuaActor` fronting the other raw-pixel providers (Anthropic
|
|
81
|
+
computer-use, Gemini) is not-yet-shipped roadmap. Screenshots are the
|
|
82
|
+
largest new public-safety surface, and the original stance here
|
|
83
|
+
(field-blurred plus OCR-scrubbed before any public artifact, fail-closed)
|
|
84
|
+
enforced a default at capture-time; 0.6.0 recanted it. Current policy:
|
|
85
|
+
frames are retained raw and full-fidelity by default in the gitignored
|
|
86
|
+
`.humanish/` tree (never emitted by a publish command; this repo's CI
|
|
87
|
+
binary-asset scan additionally blocks them from commit), and
|
|
88
|
+
`policies.redactScreenshots: true` blurs at capture for share-as-is
|
|
89
|
+
bundles. See the capture-vs-publish rule in
|
|
90
|
+
[`docs/principles/invariants-and-defaults.md`](../principles/invariants-and-defaults.md).
|
|
91
|
+
|
|
92
|
+
## The contract
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
export const ACTOR_TRACE_SCHEMA = "humanish.actor-trace.v1";
|
|
96
|
+
|
|
97
|
+
export type ActorStatus = "passed" | "failed" | "blocked" | "timed_out";
|
|
98
|
+
|
|
99
|
+
export type ActorCompletionReason =
|
|
100
|
+
| "goal_satisfied" // scenario success predicate met
|
|
101
|
+
| "turn_completed" // harness saw an explicit done signal, no predicate
|
|
102
|
+
| "gave_up" // persona abandoned in character: friction exceeded its tolerance
|
|
103
|
+
| "blocked_approval" // an action was auto-declined and the actor could not proceed
|
|
104
|
+
| "timed_out"
|
|
105
|
+
| "actor_error"
|
|
106
|
+
| "step_failed" // a deterministic scripted step/expectation evaluated false: the
|
|
107
|
+
// SUBJECT failed the script; the harness executed faithfully
|
|
108
|
+
// (distinct from actor_error/harness_error)
|
|
109
|
+
| "harness_error";
|
|
110
|
+
|
|
111
|
+
// One normalized evidence row. Codex item/*, Claude ToolUse/ToolResult,
|
|
112
|
+
// pi tool_execution_*, and computer_call cycles all collapse onto this.
|
|
113
|
+
export interface ActorTraceItem {
|
|
114
|
+
id: string;
|
|
115
|
+
kind:
|
|
116
|
+
| "message" | "reasoning" | "tool_call" | "command" | "file_change"
|
|
117
|
+
| "approval" | "screenshot" | "ui_action" | "plan" | "notice";
|
|
118
|
+
lifecycle: "started" | "completed";
|
|
119
|
+
status?: string;
|
|
120
|
+
title: string; // redacted, <= 120 chars
|
|
121
|
+
tool?: { server?: string; name?: string };
|
|
122
|
+
command?: { text?: string; cwd?: string; exitCode?: number; outputTail?: string };
|
|
123
|
+
screenshotRef?: { path: string; redaction: "blurred" | "ocr_scrubbed" | "none" };
|
|
124
|
+
text?: string; // redacted
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface ActorCapabilities {
|
|
128
|
+
headless: boolean;
|
|
129
|
+
structuredTrace: boolean;
|
|
130
|
+
lanes: Array<"code" | "app" | "computer-use" | "scripted-browser">;
|
|
131
|
+
producesScreenshots: boolean;
|
|
132
|
+
byoModel: boolean;
|
|
133
|
+
preGrantableApprovals: boolean; // can run unattended without a human prompt
|
|
134
|
+
inProcessTools: boolean; // can inject product tools without a subprocess
|
|
135
|
+
license: "open" | "source-available" | "proprietary";
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface ActorTrace {
|
|
139
|
+
schema: typeof ACTOR_TRACE_SCHEMA;
|
|
140
|
+
provider: string; // "codex-app-server" | "pi-agent-core" | "claude-agent-sdk" | "openai-responses-cu" | "browser-persona"
|
|
141
|
+
providerVersion?: string;
|
|
142
|
+
protocol: "json-rpc" | "json-stream" | "in-process-sdk" | "cua-loop" | "scripted-steps";
|
|
143
|
+
lane: "code" | "app" | "computer-use" | "scripted-browser";
|
|
144
|
+
persona: { id: string; traitsApplied: string[]; promptDigest: string }; // proves traits were threaded
|
|
145
|
+
// "raw" = full-fidelity frames retained (valid for LOCAL use; redact before
|
|
146
|
+
// publishing); "blurred"/"ocr_scrubbed" = publish-safe; "n/a" = none captured.
|
|
147
|
+
redaction: { status: "passed"; screenshots: "n/a" | "raw" | "blurred" | "ocr_scrubbed"; notes: string };
|
|
148
|
+
startedAt: string; completedAt: string; durationMs: number;
|
|
149
|
+
status: ActorStatus; completionReason: ActorCompletionReason; reason: string;
|
|
150
|
+
ids: { sessionId?: string; threadId?: string; turnId?: string; model?: string };
|
|
151
|
+
counts: Record<string, number>;
|
|
152
|
+
items: ActorTraceItem[];
|
|
153
|
+
tokenUsage?: { input?: number; output?: number; total?: number; costUsd?: number };
|
|
154
|
+
capabilities: ActorCapabilities;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface ApprovalPolicy {
|
|
158
|
+
mode: "auto-decline" | "pre-grant-allowlist" | "deny-all";
|
|
159
|
+
allow?: string[]; // e.g. ["read:*", "bash:git diff *", "mcp__browser__*"]
|
|
160
|
+
onRequest(req: ApprovalRequest): ApprovalDecision; // adapter calls; harness records every call
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export interface RedactionHooks {
|
|
164
|
+
redactText(s: string): string;
|
|
165
|
+
publicPath(p: string, root: string): string;
|
|
166
|
+
redactScreenshot(buf: Buffer, meta: ScreenshotMeta): Promise<{ buf: Buffer; method: "blurred" | "ocr_scrubbed" }>;
|
|
167
|
+
promptForLog(raw: string): { placeholder: string; digest: string; length: number };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export interface ActorRunInput {
|
|
171
|
+
cwd: string;
|
|
172
|
+
runRoot: string; // where the adapter writes events/summary/transcript
|
|
173
|
+
timeoutMs: number;
|
|
174
|
+
persona: ResolvedPersona; // FULL traits, not just {id, name}
|
|
175
|
+
scenario: { id: string; title: string; goal: string; successText?: string[] };
|
|
176
|
+
laneFocus?: { id: string; label: string; instruction: string };
|
|
177
|
+
approval: ApprovalPolicy;
|
|
178
|
+
redaction: RedactionHooks;
|
|
179
|
+
model?: string;
|
|
180
|
+
actorCommand?: string[]; // override binary / transport
|
|
181
|
+
signal: AbortSignal;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface ActorRunResult {
|
|
185
|
+
status: ActorStatus;
|
|
186
|
+
completionReason: ActorCompletionReason;
|
|
187
|
+
reason: string;
|
|
188
|
+
durationMs: number;
|
|
189
|
+
trace: ActorTrace;
|
|
190
|
+
transcriptPath: string; tracePath: string; eventsPath: string;
|
|
191
|
+
tail: string;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export interface Actor {
|
|
195
|
+
readonly id: string; // "codex-app-server"
|
|
196
|
+
capabilities(): ActorCapabilities;
|
|
197
|
+
run(input: ActorRunInput): Promise<ActorRunResult>;
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Contract semantics (every adapter must guarantee)
|
|
202
|
+
|
|
203
|
+
- **Lifecycle.** connect/spawn, initialize, apply persona + scenario as system or
|
|
204
|
+
turn input, drive a bounded turn loop honoring `timeoutMs` and `signal`, emit a
|
|
205
|
+
single explicit `completionReason`, tear down. No adapter may block waiting on a
|
|
206
|
+
human.
|
|
207
|
+
- **Evidence.** Write the same three artifacts (`events.ndjson` redacted
|
|
208
|
+
envelopes, `summary.json` = `ActorTrace`, `transcript.txt` human view) so the
|
|
209
|
+
run-bundle wiring is provider-agnostic.
|
|
210
|
+
- **Approvals.** Call `approval.onRequest`; never embed adapter-local decline
|
|
211
|
+
strings. Every call is recorded as an `items[kind=approval]`.
|
|
212
|
+
- **Redaction.** Use the injected `RedactionHooks`. Never re-implement redaction
|
|
213
|
+
per adapter.
|
|
214
|
+
- **Diagnostics.** Unexpected actor-loop failures are recorded as
|
|
215
|
+
`items[kind=notice,status=error]`, not raw crash dumps. Keep diagnostic notices
|
|
216
|
+
public-safe: redacted message, coarse loop phase, last normalized UI action,
|
|
217
|
+
and last screenshot reference only. Do not persist raw stacks, env values,
|
|
218
|
+
target URLs, or unredacted provider payloads in the trace.
|
|
219
|
+
- **Capabilities.** Declare them honestly; the registry uses them to refuse
|
|
220
|
+
unsuitable dispatch.
|
|
221
|
+
|
|
222
|
+
## The scripted-browser lane (shipped)
|
|
223
|
+
|
|
224
|
+
`scripted-browser` is the deterministic, model-free browser-actuation lane — distinct from
|
|
225
|
+
`computer-use` (raw pixels + a model deciding actions) and `app`. The registered
|
|
226
|
+
`scripted-browser` actor (`src/scripted-browser-actor.ts`) replays a committed scenario's
|
|
227
|
+
browser steps with playwright against a loopback app; the steps ARE the behavior, so
|
|
228
|
+
`byoModel: false` means there is NO model, and `tokenUsage` records zeros as an affirmative
|
|
229
|
+
$0 declaration that is true by mechanism (no provider client is importable from that code
|
|
230
|
+
path). Its trace keeps the concrete driver name `provider: "browser-persona"` (matching the
|
|
231
|
+
native `humanish.browser-persona-trace.v1` it also emits) with `protocol: "scripted-steps"`.
|
|
232
|
+
|
|
233
|
+
Completion semantics: `goal_satisfied` means the scenario's `expect` blocks — the success
|
|
234
|
+
predicate — all held ("the app still affords this exact journey", nothing about user
|
|
235
|
+
behavior); `step_failed` means a deterministic step or expectation evaluated false (the
|
|
236
|
+
subject failed the script; the harness ran faithfully); `timed_out` is the journey wall-clock
|
|
237
|
+
budget; `harness_error` is a browser that could not launch. `gave_up` and `blocked_approval`
|
|
238
|
+
are unreachable — no persona patience, no approvals exist on a deterministic replay.
|
|
239
|
+
|
|
240
|
+
Actuation-vs-spend gate: on the scripted lab route `scenario.mode: live` is still required
|
|
241
|
+
even though provider spend is $0 by mechanism. The gate's justification there is ACTUATION,
|
|
242
|
+
not cost — a live scripted run drives a real browser against a real running app
|
|
243
|
+
(state-mutating effects on the operator's app), which deserves the same affirmative
|
|
244
|
+
declaration as spend. "Live" on this route must never silently come to mean "costs money";
|
|
245
|
+
this paragraph is the record of that decision.
|
|
246
|
+
|
|
247
|
+
## The state-driven executor seam (shipped — the transport-agnostic intent, made real)
|
|
248
|
+
|
|
249
|
+
The `CuaExecutor` / `CuaProvider` ports are the concrete realization of the "plural harnesses /
|
|
250
|
+
transport-agnostic" intent above: the computer-use loop does not require a screen or a vision
|
|
251
|
+
model. A library caller can drive an **already-running local app** through its in-process JS
|
|
252
|
+
contract (`window.app.getState()` etc.) with a custom `CuaExecutor` (screenshot optional,
|
|
253
|
+
`appState` as the progress signal) paired with a **non-vision** `CuaProvider` (`requiresFrame`
|
|
254
|
+
falsey), keeping the whole lab composition with NO E2B desktop and NO clone. See
|
|
255
|
+
[`state-driven-executor.md`](./state-driven-executor.md) for the port, both entry points
|
|
256
|
+
(`runComputerUseLoop` and `runLab` + `buildExecutor`/`buildProvider`), the `subject.source:
|
|
257
|
+
local-app` config surface, the `requiresFrame` provider-authoring contract, and the
|
|
258
|
+
appState-is-runtime-only stance.
|
|
259
|
+
|
|
260
|
+
## The product-adapter extension seam (shipped — terminal-product lane, layer 6)
|
|
261
|
+
|
|
262
|
+
The terminal-product lane carries the proof-roadmap layer-6 deliverable: a product
|
|
263
|
+
adopter attaches product-specific scoring + feedback as a THIN in-repo extension
|
|
264
|
+
WITHOUT forking core. The seam is exported contract types (`RunBundle`,
|
|
265
|
+
`RunFeedbackCandidate`, `RunAdapterScore`, `RunMeaningfulUseScore`, `ActorTrace`,
|
|
266
|
+
the terminal-lane `TerminalProductScoringContext` / `TerminalLedgers` / ...) plus a
|
|
267
|
+
registrable `score` / `deriveFeedback` DI hook on `TerminalProductLabHooks` (mirror
|
|
268
|
+
of the `CuaActorLabHooks` DI seam). The adapter records its product nouns ONLY under
|
|
269
|
+
an adapter-NAMESPACED block (`RunFeedbackCandidate.adapter` /
|
|
270
|
+
`RunAdapterScore.{namespace,data}`), so core's enums stay product-agnostic — no
|
|
271
|
+
adopter noun is hardcoded into a core enum. Default (no hook) behavior is unchanged.
|
|
272
|
+
See [`terminal-product-lane.md`](./terminal-product-lane.md#slice-4--the-product-adapter-extension-seam-layer-6)
|
|
273
|
+
for the full seam and the thin-adapter conformance proof.
|
|
274
|
+
|
|
275
|
+
## Making personas load-bearing
|
|
276
|
+
|
|
277
|
+
The bug, grounded in code: `loadDryRunSelection` (`src/run.ts`) parses persona
|
|
278
|
+
YAML down to `{ id, name, source, sourceDigest }` and discards `summary`,
|
|
279
|
+
`traits.{patience, technical_confidence, accessibility_needs}`, and `constraints`.
|
|
280
|
+
The prompt builders then inject one line: `Persona: ${name}`. The persona is a
|
|
281
|
+
label.
|
|
282
|
+
|
|
283
|
+
Plan:
|
|
284
|
+
|
|
285
|
+
1. **Parse the whole persona** into a `ResolvedPersona`:
|
|
286
|
+
`{ id, name, summary, goals[], traits: { patience, skill, accessibilityNeeds? }, constraints[], sourceDigest }`
|
|
287
|
+
(map `technical_confidence` to `skill`).
|
|
288
|
+
2. **Compile traits into actor-neutral directives**, not prose, via a pure
|
|
289
|
+
`personaToDirectives(p)`:
|
|
290
|
+
- patience -> `frictionTolerance`: how much failure, dead-end, or
|
|
291
|
+
no-forward-progress the persona absorbs before abandoning the task in
|
|
292
|
+
character. Expressed as an instruction the actor embodies ("you are
|
|
293
|
+
impatient: if you hit repeated friction or stop making progress toward your
|
|
294
|
+
goal, stop and report exactly what blocked you"), not a turn or tool-call
|
|
295
|
+
count.
|
|
296
|
+
- skill -> tool/strategy bias (low-skill avoids CLI/flags and narrates
|
|
297
|
+
confusion at ambiguous UI; high-skill uses shortcuts and recovery paths).
|
|
298
|
+
- accessibilityNeeds -> concrete behavior (keyboard_first navigates by
|
|
299
|
+
keyboard and fails a step that is mouse-only; clear_terminal_output flags
|
|
300
|
+
noisy output as a defect).
|
|
301
|
+
- goals + constraints become explicit success / forbidden lists for the
|
|
302
|
+
scenario predicate.
|
|
303
|
+
3. **Abandonment is persona-judged, harness-corroborated, never a counter.** The
|
|
304
|
+
persona-actor (an LLM embodying that user) decides in character when the
|
|
305
|
+
friction is no longer worth it and stops with `completionReason: "gave_up"`,
|
|
306
|
+
citing the specific friction. The harness corroborates with objective signals
|
|
307
|
+
it already sees in the stream (consecutive failed/blocked actions,
|
|
308
|
+
repeated-identical-action looping, no progress toward the success predicate)
|
|
309
|
+
and annotates the abandonment friction as a feedback candidate. The harness
|
|
310
|
+
imposes no turn cap; its only hard stop is the wall-clock `timeoutMs`.
|
|
311
|
+
4. **Bind the same directives per harness**: pi (`systemPrompt` +
|
|
312
|
+
`beforeToolCall` allow rules), Claude (`system_prompt`/`--append-system-prompt`
|
|
313
|
+
+ `allowedTools`), Codex (prepend to `turn/start` input), Stagehand (agent
|
|
314
|
+
context + action policy). Each binds the friction-tolerance, skill, and
|
|
315
|
+
accessibility directives identically; none uses a `max_turns`-style cap as the
|
|
316
|
+
persona stop condition.
|
|
317
|
+
5. **Prove it.** `ActorTrace.persona.traitsApplied` lists the injected
|
|
318
|
+
directives; a `persona-fidelity` verify check asserts that the friction and
|
|
319
|
+
accessibility directives reached the actor input and that a `gave_up` run
|
|
320
|
+
cites a concrete friction reason (not a turn count). "Did the persona drive
|
|
321
|
+
the run" becomes a verifiable artifact, not an assertion.
|
|
322
|
+
(Status 2026-06-11: `personaToDirectives` shipped in `src/persona.ts` and
|
|
323
|
+
`traitsApplied` is threaded on the codex routes, but the `persona-fidelity`
|
|
324
|
+
verify check is not-yet-shipped roadmap, and the computer-use route stubs
|
|
325
|
+
`persona.traitsApplied` to `[]` today — see `src/cua-actor-lab.ts`.)
|
|
326
|
+
|
|
327
|
+
## Decision: how abandonment is adjudicated
|
|
328
|
+
|
|
329
|
+
"When does a synthetic persona give up?" has no obvious best answer, so the
|
|
330
|
+
choice is recorded here rather than left implicit and silently re-litigated.
|
|
331
|
+
|
|
332
|
+
Options considered:
|
|
333
|
+
|
|
334
|
+
1. **Persona-judged only** (the LLM decides in character, uncorroborated).
|
|
335
|
+
Truest embodiment, but LLM stop behavior is erratic (often too stubborn,
|
|
336
|
+
sometimes too eager), non-reproducible, and hard to verify. A purely
|
|
337
|
+
model-judged stop can also run uselessly to the wall-clock timeout.
|
|
338
|
+
2. **Harness-adjudicated from objective signals only** (no-progress,
|
|
339
|
+
repeated-failure, looping); the persona just sets a numeric threshold.
|
|
340
|
+
Deterministic and reproducible, but mechanical, misses the subjective "this
|
|
341
|
+
is not worth it" judgment that is the whole point of a persona, and a fixed
|
|
342
|
+
threshold quietly drifts back toward a disguised counter.
|
|
343
|
+
3. **Persona-judged primary, harness-corroborated backstop.** The actor decides
|
|
344
|
+
in character and emits `gave_up` with the friction; the harness independently
|
|
345
|
+
tracks objective signals and (a) annotates the friction as a feedback
|
|
346
|
+
candidate, and (b) force-ends only on unambiguous pathology (e.g. repeated
|
|
347
|
+
identical failed actions = a loop, or no progress past a wall-clock
|
|
348
|
+
checkpoint) so a too-stubborn model cannot waste the entire timeout.
|
|
349
|
+
|
|
350
|
+
**Decision: option 3.** It keeps the behavior emergent and persona-faithful (the
|
|
351
|
+
value) while the objective backstop adds reproducibility and bounds a stubborn
|
|
352
|
+
model. Every backstop signal is progress- or friction-based, never a turn or
|
|
353
|
+
tool-call count.
|
|
354
|
+
|
|
355
|
+
Non-obvious tradeoffs to revisit with real-run data:
|
|
356
|
+
|
|
357
|
+
- The backstop thresholds (what counts as "looping" or "no progress") are
|
|
358
|
+
themselves judgment calls. Start conservative (fire only on unambiguous
|
|
359
|
+
pathology) and tune against real runs, logging when the backstop fires versus
|
|
360
|
+
when the persona self-abandons, so the split stays mostly persona-judged.
|
|
361
|
+
- Friction tolerance per patience level is qualitative in the prompt, not a
|
|
362
|
+
number. If "impatient" proves too soft, escalate by feeding the actor explicit
|
|
363
|
+
running friction context ("you have hit 3 dead-ends"), still never a turn
|
|
364
|
+
count.
|
|
365
|
+
- `persona-fidelity` treats a `gave_up` with no cited friction as a fidelity
|
|
366
|
+
failure, not an accepted stop, so the model cannot quietly quit for no reason.
|
|
367
|
+
|
|
368
|
+
This keeps patience load-bearing and reproducible without ever using elapsed
|
|
369
|
+
turns as a stop signal.
|
|
370
|
+
|
|
371
|
+
## Capability matrix (target adapters)
|
|
372
|
+
|
|
373
|
+
| Adapter | headless | structured trace | sandbox | BYO model | license | actor fit |
|
|
374
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
375
|
+
| codex-app-server (reference) | yes (stdio JSON-RPC) | typed item/* | OS Seatbelt/seccomp + approvalPolicy | OpenAI-first | Apache-2.0 | code |
|
|
376
|
+
| pi-agent-core (first new) | yes (SDK + rpc/json) | event stream + session JSONL + token/cost | BYO container + hook gating | 15+ providers, local | MIT | code, app |
|
|
377
|
+
| claude-agent-sdk | yes (SDK + `-p` stream-json) | typed ToolUse/ToolResult + cost | OS sandbox + dontAsk/allowedTools | Anthropic-centric | SDK MIT (CLI proprietary) | code, app |
|
|
378
|
+
| stagehand-cua (roadmap, not shipped; `openai-computer-use` is the shipped computer-use actor) | yes (SDK, mode:'cua') | structured results + replay | Playwright/Browserbase isolation | OpenAI/Anthropic/Google | MIT | computer-use |
|
|
379
|
+
|
|
380
|
+
## Sequencing
|
|
381
|
+
|
|
382
|
+
1. This document.
|
|
383
|
+
2. Shared `RedactionHooks` module (completes the remaining #107 criterion) plus
|
|
384
|
+
the `Actor` contract types, with the Codex integration refactored to implement
|
|
385
|
+
`Actor` and emit `ActorTrace` behind the back-compat alias. Add an
|
|
386
|
+
`actorRegistry`; generalize `RunStream.codex` to `RunStream.actor`.
|
|
387
|
+
3. Personas load-bearing: `ResolvedPersona`, `personaToDirectives`, harness turn
|
|
388
|
+
budget, and the `persona-fidelity` verify check.
|
|
389
|
+
4. `pi-agent-core` adapter (proves the contract against a non-Codex protocol;
|
|
390
|
+
local-model dogfood for ~$0). Landed in two slices: first the pure
|
|
391
|
+
`piSessionToActorTrace` mapper + registry generalization (discriminated
|
|
392
|
+
`ActorDescriptor` union + `getActor` overloads) + a fixture conformance test,
|
|
393
|
+
with no pi dependency and no model key required (proves the evidence contract
|
|
394
|
+
is provider-neutral); then a follow-up live SDK shim behind a DI seam, deferred
|
|
395
|
+
until the package identity (`@earendil-works/pi-agent-core` vs
|
|
396
|
+
`@mariozechner/pi-coding-agent`) and the Node `>=22.19` vs engines `>=20` gap
|
|
397
|
+
are pinned against an installed build.
|
|
398
|
+
5. `claude-agent-sdk` adapter (the `app` lane).
|
|
399
|
+
6. Computer-use lane. (Shipped as `openai-computer-use` — registered 0.3.0,
|
|
400
|
+
lab-dispatched 0.4.0; `stagehand-cua` as a multi-provider front remains
|
|
401
|
+
not-yet-shipped roadmap.)
|
|
402
|
+
7. Cross-harness conformance test: one persona x scenario through every adapter,
|
|
403
|
+
asserting identical trace shape, completion vocabulary, and redaction status.
|
|
404
|
+
8. The proof point: run the harness-plural loop against popular OSS repos and turn
|
|
405
|
+
real, merged issues into the receipt.
|
|
406
|
+
|
|
407
|
+
## Risks
|
|
408
|
+
|
|
409
|
+
- Protocol/version drift across four moving harnesses. Pin every binary/SDK and
|
|
410
|
+
assert the init handshake; the registry refuses an actor whose declared
|
|
411
|
+
capabilities do not satisfy the scenario.
|
|
412
|
+
- Screenshot PII in the computer-use lane. Redaction binds the PUBLISH
|
|
413
|
+
boundary, not capture (0.6.0): raw frames stay local in gitignored
|
|
414
|
+
`.humanish/` and are never emitted by a publish command (this repo's CI
|
|
415
|
+
binary-asset scan additionally blocks them from commit);
|
|
416
|
+
`policies.redactScreenshots: true` blurs at capture for share-as-is
|
|
417
|
+
bundles. The earlier fail-closed redacted-thumbnail default was recanted —
|
|
418
|
+
see the capture-vs-publish rule in
|
|
419
|
+
[`docs/principles/invariants-and-defaults.md`](../principles/invariants-and-defaults.md).
|
|
420
|
+
- Persona directives regressing into decoration. Friction tolerance and
|
|
421
|
+
accessibility must demonstrably reach the actor input and change step pass/fail,
|
|
422
|
+
enforced by the `persona-fidelity` check; a `gave_up` run must cite a concrete
|
|
423
|
+
friction, never an elapsed-turn count.
|
|
424
|
+
- License contamination. Proprietary harnesses (Claude CLI, Cursor) sit behind
|
|
425
|
+
adapters; only their open SDKs are depended on directly.
|
|
426
|
+
|
|
427
|
+
## References
|
|
428
|
+
|
|
429
|
+
- Self-driving harness principles: `docs/principles/self-driving-harness.md`.
|
|
430
|
+
- Observer architecture: `docs/architecture/observer.md`.
|
|
431
|
+
- Related issues: shared redaction module (#107), PII/PHI detector (#108).
|