Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions docs/research.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,20 @@ Autonomy contracts are not set-and-forget. The agent should re-confirm autonomy

Beyond per-product autonomy settings, Anthropic positions its own model behavior on a "disposition dial" ranging from _fully corrigible_ (does whatever instructed) to _fully autonomous_ (acts on its own values). Current Claude models sit closer to the corrigible end — a deliberate safety choice with direct UX implications: agents defer to the principal hierarchy (Anthropic → operator → user) rather than override it. The autonomy contract a product designs sits on top of this model-level disposition.

### 4.5 Levels are analysis vocabulary, not Send-time UI (July 2026 reframe)

> **Position (ratified 2026-07-05).** The level ladder in §4.1 is how we _analyze_ autonomy. It is not what the user sees at Send. At the dispatch moment the user sees a **behavioral posture badge** plus **verification defensibility gaps** — and, for complex tasks, a **plan→execute gate** that picks execution posture after plan review.

The July 2026 web audits (`audits/`, 2026-07-03 series) converged on three findings that reshape how §4 applies to composer UI:

1. **No product ships L0–L5 as a numbered control at Send** ([`audits/dispatch-moment-ui-web-2026-07-03.md`](../audits/dispatch-moment-ui-web-2026-07-03.md)). Mode, model, and placement are visible; Run Mode, sandbox, allowlists, and stopping conditions stay hidden. Where level surfaces at all, it works as behavior language, not a number.
2. **The strongest pre-dispatch gap is defensibility** ([`audits/verification-defensibility-web-2026-07-03.md`](../audits/verification-defensibility-web-2026-07-03.md)). Nothing on the market answers "is this autonomy level defensible for _this_ task?" before Send. The eight-field run contract (Addy Osmani, July 2026) is one safeguard among many — not the organizing UI concept; its fields ship shredded across products ([`audits/contract-field-reality-2026-07-03.md`](../audits/contract-field-reality-2026-07-03.md)).
3. **Plan gates that also pick execution posture are the strongest shipping pattern** — Claude Code's plan-approval options, Factory's Spec approval. The plan review moment is where autonomy gets renegotiated, not a settings page.

**Canonical Send-time vocabulary.** agentic-craft ships posture as three behavioral badges — **"Asks first"** (pauses before edits and external actions), **"Edits freely"** (applies file edits without per-step approval), **"On its own"** (runs until done or a gate fires) — roughly the behavior of L2/L3/L4 in §4.1's terms. Before Send, the composer surfaces five defensibility gaps: **stopping condition, verification method, scope boundary, spend cap, risky actions**. Source of truth: `src/views/patterns/dispatch-moment-data.ts`; reference sketch: `/patterns/dispatch-moment`.

**How this composes with the rest of §4.** The matrix view (§4.2) and renegotiation (§4.3) still hold — the badge is the compressed, legible face of the effective policy, and the plan→execute gate is a renegotiation moment. What changes is the UI claim: the ladder informs design analysis and this document's taxonomy; the badge, the gap checklist, and the plan gate are what earn pixels at Send.

## 5. Trust through provenance

> **Position.** Trust in agentic systems is not earned through confident prose. It is earned through _provenance_ — visible links between every claim and its source. An agent that says less but cites everything beats an agent that says more without sources.
Expand Down
32 changes: 21 additions & 11 deletions sessions/2026-07-03-eve-chat-composer-autonomy.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,25 @@ The Claude Code exploration may have been **solving the wrong problem well**: po

---

## Open composer questions (pick one to pursue)
## Composer direction — decided 2026-07-05

| ID | Question | Research signal |
|----|----------|-----------------|
| **A** | Level selection legibility at Send | No product ships L0–L5 as a numbered control; level works better as behavioral badge |
| **B** | Verification defensibility before dispatch | Strongest gap — nothing answers "is this autonomy level defensible for *this* task?" pre-Send |
| **C** | Plan→execute transition | Strongest pre-dispatch patterns: Claude Code 5-option plan approval, Factory Spec approval, Plan→Implement |
**Decision: pursue B + C.** Verification defensibility before dispatch plus
the plan→execute transition are the composer problems agentic-craft works on.
Contract read-back is no longer the main problem.

Research points strongest at **B + C**, with level as behavioral badge rather than ladder UI.
| ID | Question | Resolution |
|----|----------|------------|
| **A** | Level selection legibility at Send | **Resolved as design position** — level ships as a behavioral badge ("Asks first" / "Edits freely" / "On its own"), never a numbered L0–L5 control |
| **B** | Verification defensibility before dispatch | **Chosen** — surface missing proof, unclear stopping conditions, missing scope, cost/budget ambiguity, and risky actions before Send |
| **C** | Plan→execute transition | **Chosen** — complex tasks gate on plan review, then explicitly pick execution posture |

Canonical vocabulary (three postures, five defensibility gaps) lives in
`src/views/patterns/dispatch-moment-data.ts`; the pattern page at
[`/patterns/dispatch-moment`](../app/(reference)/patterns/dispatch-moment/page.tsx)
is the reference sketch. `docs/research.md` §4.5 carries the reframe.

**Bare/B/C/D stay retired** unless B+C work surfaces a concrete need for a
contract-variant comparison.

---

Expand Down Expand Up @@ -130,9 +140,9 @@ Fixture ask: *"migrate session-chat-page to the shared adapter — don't touch d

---

## Suggested next steps in agentic-craft
## Next steps in agentic-craft

1. Choose composer problem **A**, **B**, or **C** (recommend B + C) — **started:** [`/patterns/dispatch-moment`](../app/(reference)/patterns/dispatch-moment/page.tsx) sketches B+C on the registry composer
1. ~~Choose composer problem **A**, **B**, or **C**~~ — **done 2026-07-05:** B + C chosen (see decision record above); [`/patterns/dispatch-moment`](../app/(reference)/patterns/dispatch-moment/page.tsx) sketches B+C on the registry composer
2. Sketch against registry composer demos and/or compound-composer slots — not contract read-back
3. Cross-check [`docs/research.md`](../docs/research.md) §4 "Autonomy as a contract" — may need revision to match reframe
4. If promoting compound composer into the site, wire a playground route under agentic-craft (don't fork registry composer)
3. ~~Cross-check [`docs/research.md`](../docs/research.md) §4~~ — **done 2026-07-05:** §4.5 carries the reframe (behavioral badge, defensibility at Send)
4. Wire the compound-composer playground at `/playground/compound-composer` (URL-only lab; don't fork registry composer) — icons injectable per consumer shadcn config, `attachment` added via CLI
136 changes: 9 additions & 127 deletions src/components/DispatchMomentDemo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
ComposerPlan,
ComposerDoneWhen,
ComposerReceipt,
type ComposerDoneWhenItem,
type ComposerTask,
} from "@/components/ui/composer"
import {
Expand All @@ -31,40 +30,20 @@ import {
HumanGateTitle,
} from "@/components/ui/human-gate"
import { cn } from "@/lib/utils"

const FIXTURE_PROMPT =
"Migrate session-chat-page to the shared adapter — don't touch deps, stop when tests pass, keep it under $5"
import {
AUTONOMY_POSTURES,
FIXTURE_PROMPT,
evaluateDefensibility,
parseDispatchPrompt,
type AutonomyPosture,
} from "@/views/patterns/dispatch-moment-data"

type ExecutionPosture =
| "auto-review"
| "accept-edits"
| "review-each"
| "keep-planning"

type AutonomyPosture = {
id: string
label: string
description: string
}

const AUTONOMY_POSTURES: AutonomyPosture[] = [
{
id: "asks-first",
label: "Asks first",
description: "Pauses before edits and external actions.",
},
{
id: "edits-freely",
label: "Edits freely",
description: "Applies file edits without per-step approval.",
},
{
id: "on-its-own",
label: "On its own",
description: "Runs until done or a gate fires.",
},
]

const EXECUTION_OPTIONS: {
id: ExecutionPosture
label: string
Expand Down Expand Up @@ -111,103 +90,6 @@ const PLAN_TASKS: ComposerTask[] = [
},
]

type ParsedDispatch = {
summary: string
doneWhen: ComposerDoneWhenItem[]
exclusions: string[]
budget?: string
needsPlanGate: boolean
}

function parseDispatchPrompt(text: string): ParsedDispatch {
const trimmed = text.trim()
const lower = trimmed.toLowerCase()

const doneWhen: ComposerDoneWhenItem[] = []
const exclusions: string[] = []
let budget: string | undefined

const stopMatch = lower.match(
/(?:stop|done)\s+when\s+([^—–.;]+(?:pass|green|complete)[^—–.;]*)/i
)
if (stopMatch) {
doneWhen.push({
id: "stop",
label: stopMatch[1].trim(),
})
}

const budgetMatch = trimmed.match(
/(?:under|below|keep\s+it\s+under)\s+\$?\d+/i
)
if (budgetMatch) {
budget = budgetMatch[0].replace(/^keep it /i, "")
doneWhen.push({ id: "budget", label: budget })
}

const dontTouchMatch = trimmed.match(/don't touch\s+([^—–.;]+)/i)
if (dontTouchMatch) {
exclusions.push(dontTouchMatch[1].trim())
doneWhen.push({
id: "exclude",
label: `${dontTouchMatch[1].trim()} locked`,
})
}

const summary =
trimmed.length > 72 ? `${trimmed.slice(0, 69).trim()}…` : trimmed || "…"

const needsPlanGate =
/migrate|refactor|implement|across|shared adapter/i.test(trimmed) &&
trimmed.length > 40

return {
summary,
doneWhen,
exclusions,
budget,
needsPlanGate,
}
}

type VerificationGap = {
id: string
label: string
met: boolean
}

function buildVerificationGaps(
parsed: ParsedDispatch,
fullText: string
): VerificationGap[] {
const hasStop = parsed.doneWhen.some((item) => item.id === "stop")
const hasBudget = Boolean(parsed.budget)
const hasScope = parsed.exclusions.length > 0

return [
{
id: "stop",
label: "Stopping condition",
met: hasStop,
},
{
id: "proof",
label: "Verification method",
met: hasStop && /test|check|pass|green|ci/i.test(fullText),
},
{
id: "scope",
label: "Scope boundary",
met: hasScope,
},
{
id: "budget",
label: "Spend cap",
met: hasBudget,
},
]
}

function DispatchMomentDemo() {
const [draft, setDraft] = React.useState("")
const [postureIndex, setPostureIndex] = React.useState(0)
Expand All @@ -222,7 +104,7 @@ function DispatchMomentDemo() {

const parsed = React.useMemo(() => parseDispatchPrompt(draft), [draft])
const verificationGaps = React.useMemo(
() => buildVerificationGaps(parsed, draft),
() => evaluateDefensibility(parsed, draft),
[parsed, draft]
)
const missingVerification = verificationGaps.filter((gap) => !gap.met)
Expand Down Expand Up @@ -466,4 +348,4 @@ function DispatchMomentDemo() {
)
}

export { DispatchMomentDemo, FIXTURE_PROMPT, parseDispatchPrompt }
export { DispatchMomentDemo }
84 changes: 69 additions & 15 deletions src/views/patterns/dispatch-moment-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@ import { PatternDemo } from "@/components/reference/pattern-demo"
import { PatternPage } from "@/components/reference/pattern-page"
import { PatternSection } from "@/components/reference/pattern-section"
import { PatternSpecTable } from "@/components/reference/pattern-spec-table"
import {
AUTONOMY_POSTURES,
DEFENSIBILITY_GAPS,
} from "@/views/patterns/dispatch-moment-data"

const PRINCIPLES = [
{
title: "Behavioral badge, not a ladder",
description:
"Ship posture as language users recognize — asks first, edits freely — not L0–L5 numbers.",
description: `Ship posture as language users recognize — ${AUTONOMY_POSTURES.map(
(posture) => posture.label.toLowerCase()
).join(", ")} — not L0–L5 numbers.`,
},
{
title: "Verification before posture",
Expand Down Expand Up @@ -62,13 +67,13 @@ function DispatchMomentContent() {
className="mb-10 grid scroll-mt-20 gap-4 border-y border-border/60 py-5 lg:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)]"
>
<div>
<p className="section-label mb-2">Reframe</p>
<p className="section-label mb-2">Direction — ratified 2026-07-05</p>
<p className="font-serif text-2xl font-light tracking-tight text-balance">
Verification defensibility + plan→execute — not contract read-back.
</p>
</div>
<div className="min-w-0">
<p className="section-label mb-2">Open questions pursued</p>
<p className="section-label mb-2">Questions chosen</p>
<div className="flex flex-wrap gap-2">
<span className="rounded-md border border-border/70 px-2.5 py-1 text-xs font-medium text-muted-foreground">
B · Verification defensibility
Expand All @@ -78,12 +83,13 @@ function DispatchMomentContent() {
</span>
</div>
<p className="mt-3 text-xs leading-5 text-muted-foreground">
Continues the July 3 composer/autonomy session handoff in{" "}
Decision recorded in{" "}
<code className="text-xs">
sessions/2026-07-03-eve-chat-composer-autonomy.md
</code>
. Supersedes the Bare/B/C/D contract-variant framing until the
problem is reframed.
. Question A resolves as behavioral badge, not ladder UI; the
Bare/B/C/D contract-variant framing is retired unless B+C work
surfaces a concrete need for it.
</p>
</div>
</section>
Expand Down Expand Up @@ -119,6 +125,52 @@ function DispatchMomentContent() {
</div>
</PatternSection>

<PatternSection
id="vocabulary"
eyebrow="Canonical vocabulary"
title="Three postures, five gaps"
description="The posture badge labels and the pre-Send defensibility checklist are defined once, in dispatch-moment-data.ts, and shared by every surface that renders them."
>
<div className="mt-8 grid gap-6 md:grid-cols-2">
<div>
<p className="section-label mb-3">Posture badge</p>
<ul className="flex flex-col gap-2" role="list">
{AUTONOMY_POSTURES.map((posture) => (
<li
key={posture.id}
className="border-l border-border/70 bg-muted/20 py-2 pl-4"
>
<span className="text-sm font-medium text-foreground">
{posture.label}
</span>
<span className="mt-0.5 block text-xs leading-5 text-muted-foreground">
{posture.description}
</span>
</li>
))}
</ul>
</div>
<div>
<p className="section-label mb-3">Defensibility checklist</p>
<ul className="flex flex-col gap-2" role="list">
{DEFENSIBILITY_GAPS.map((gap) => (
<li
key={gap.id}
className="border-l border-border/70 bg-muted/20 py-2 pl-4"
>
<span className="text-sm font-medium text-foreground">
{gap.label}
</span>
<span className="mt-0.5 block text-xs leading-5 text-muted-foreground">
{gap.hint}
</span>
</li>
))}
</ul>
</div>
</div>
</PatternSection>

<PatternSection
id="principles"
eyebrow="Principles"
Expand Down Expand Up @@ -159,18 +211,20 @@ function DispatchMomentContent() {
>
<ul className="mt-6 flex flex-col gap-2 text-sm leading-6 text-muted-foreground">
<li>
Cross-check <code className="text-xs">docs/research.md</code> §4
against the agency×orchestration reframe (behavioral badge vs
numbered ladder).
<code className="text-xs">docs/research.md</code> §4.5 now carries
the reframe: levels are analysis vocabulary; Send shows a behavioral
badge plus defensibility gaps.
</li>
<li>
Promote compound-composer ideas from{" "}
<code className="text-xs">references/eve-chat/</code> only where
they strengthen B+C — not contract document UI.
Compound-composer lab at{" "}
<code className="text-xs">/playground/compound-composer</code> —
slot compositions, fixture controls, and the same B+C ideas on the
eve-chat compound API for comparison against this registry-composer
sketch.
</li>
<li>
Wire eve-chat playground variant picker if comparing integrator
compositions — after the problem statement holds.
Promotion of compound parts into the registry stays a separate,
explicit decision — the comparison writeup is the output for now.
</li>
</ul>
</PatternSection>
Expand Down
Loading
Loading