Harness spec 1.1.0: one plan, prototypes and the library shared; the CLI implements it

The spec: plan_write and plan_update (new, shared, offered in every mode) and
plan_submit (now shared) keep one structured plan — findings, objectives,
phases of tasks — in place of the todo list, which lives on as a former name
of plan_write. The shared system text is one text for both projects, with
variables for what differs; the mode texts, a prototypes block and a library
block are shared. acp.md: the client hands the device what only it knows
(context, library, prototypes), the plan travels as a structure (plan_state),
library calls carry the chat's scope, and a client may rewind a session.
loop.json says its numbers as numbers. Four conformance areas: plan,
prototype, library_scope, toolcall_index.

The CLI: the plan is kept per session in the store and drawn above the prompt
and on the approval card; the prompt is assembled from the spec's texts with
their variables filled; the link reads the instance's instructions,
personalization, knowledge scope and prototype offer, sends the plan as a
structure and answers rewind; library calls to the instance carry the session
as scope; a prototype written in the terminal is saved as a file and named in
the transcript; a tool-call fragment without an index continues the last call;
the default theme is Moria; dead exports and Hermes leftovers removed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
HomerandClaude Fable 5.1 committed 2026-10-18 04:02:12 +02:00
1 parent 6e70ff46d0
commit 837f38c744
96 files changed
+3259 -527

No files matched your search

+19 -5
View File
@@ -3,6 +3,7 @@
// them; the code validates with its own zod schema, and tests/harness.test.ts keeps the two equal.
import VERSION from "../harness/VERSION" with { type: "text" }
import PURPOSE from "../harness/purpose.json"
import LOOP_JSON from "../harness/loop.json"
import t_apply_patch from "../harness/tools/apply_patch.json"
import t_ask_user from "../harness/tools/ask_user.json"
import t_bash from "../harness/tools/bash.json"
@@ -22,6 +23,8 @@ import t_note_manage from "../harness/tools/note_manage.json"
import t_note_view from "../harness/tools/note_view.json"
import t_notes_search from "../harness/tools/notes_search.json"
import t_plan_submit from "../harness/tools/plan_submit.json"
import t_plan_update from "../harness/tools/plan_update.json"
import t_plan_write from "../harness/tools/plan_write.json"
import t_read from "../harness/tools/read.json"
import t_session_search from "../harness/tools/session_search.json"
import t_settings from "../harness/tools/settings.json"
@@ -30,7 +33,6 @@ import t_skill_view from "../harness/tools/skill_view.json"
import t_skills_list from "../harness/tools/skills_list.json"
import t_task from "../harness/tools/task.json"
import t_tasks from "../harness/tools/tasks.json"
import t_todo from "../harness/tools/todo.json"
import t_view_image from "../harness/tools/view_image.json"
import t_web_fetch from "../harness/tools/web_fetch.json"
import t_web_search from "../harness/tools/web_search.json"
@@ -44,9 +46,10 @@ export interface ToolSpecDef {
risk: "read" | "write" | "execute" | "interact"
exclusive: boolean
/** How a call is shown in a transcript: a shell command, an edit with its diff, a
* file read, a search with its hits, a web lookup, a delegated task, hidden (the todo list has a
* panel of its own), or a plain tool line. Both projects draw the same kind the same way. */
block: "shell" | "edit" | "read" | "search" | "web" | "task" | "hidden" | "tool"
* file read, a search with its hits, a web lookup, a delegated task, a plan change (not drawn:
* the plan card refreshes), hidden, or a plain tool line. Both projects draw the same kind the
* same way. */
block: "shell" | "edit" | "read" | "search" | "web" | "task" | "plan" | "hidden" | "tool"
/** Where a call says what it is for: the injected `purpose`, a field of its own, or none. */
purpose: string | false
/** Names each project used before spec v1; a call under one is read as this tool. */
@@ -60,6 +63,17 @@ export interface ToolSpecDef {
export const HARNESS_VERSION = VERSION.trim()
/** How one prompt runs (harness/loop.json): the step backstop, the budgets' defaults, the wrap-up
* message, how often a reply that says nothing is asked again, how many calls run together. */
export const LOOP: {
steps: { default: number }
budgets: { wall_seconds: { cli: number | null; llembas: number | null }; output_bytes: { cli: number | null; llembas: number | null }; completion_tokens: { cli: number | null; llembas: number | null } }
/** `message` holds `{{limit}}`: which limit was reached, as a phrase. */
wrap_up: { message: string }
nudges: { max: number }
parallel: { max: number }
} = LOOP_JSON
/** The `purpose` argument (harness/purpose.json). */
export const PURPOSE_DEF: { name: string; schema: Record<string, unknown> } = PURPOSE
@@ -73,7 +87,7 @@ export function fillDescription(description: string, vars: Record<string, string
}
export const TOOL_SPECS: Record<string, ToolSpecDef> = Object.fromEntries(
([t_apply_patch, t_ask_user, t_bash, t_bash_kill, t_bash_list, t_bash_output, t_decisions, t_edit, t_glob, t_grep, t_knowledge_get, t_knowledge_search, t_list, t_memory, t_multiedit, t_note_manage, t_note_view, t_notes_search, t_plan_submit, t_read, t_session_search, t_settings, t_skill_manage, t_skill_view, t_skills_list, t_task, t_tasks, t_todo, t_view_image, t_web_fetch, t_web_search, t_write] as ToolSpecDef[]).map((t) => [t.name, t]),
([t_apply_patch, t_ask_user, t_bash, t_bash_kill, t_bash_list, t_bash_output, t_decisions, t_edit, t_glob, t_grep, t_knowledge_get, t_knowledge_search, t_list, t_memory, t_multiedit, t_note_manage, t_note_view, t_notes_search, t_plan_submit, t_plan_update, t_plan_write, t_read, t_session_search, t_settings, t_skill_manage, t_skill_view, t_skills_list, t_task, t_tasks, t_view_image, t_web_fetch, t_web_search, t_write] as ToolSpecDef[]).map((t) => [t.name, t]),
)
/** A tool's name and description as the spec has them, for its definition: `...described("read")`. */