You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
What precise domain model and lifecycle should Crucible use for Workflow Bundles, catalog entries, projects, Runs, workflow steps, Step Attempts, Harnesses, Harness Sessions, Human Gates, Harness Requests, Bundle Assets, Run Artifacts, and related states, ownership, identity, and transitions?
Verified facts when defining Harness, Harness Session, Harness Request, and recovery concepts
Resolved research decisions
Required methods
/grilling
/domain-modeling
/codebase-design
Reading rule
Read the map, current glossary, and Scope first. Consult transport research only when a proposed domain relationship depends on native Harness lifecycle facts. Treat the development plan as a design hypothesis.
Crucible's domain is a small entity set with one dataflow currency, one live Run per Workspace, and a hard boundary at the Harness. The governing principle, which settles more downstream ambiguity than any single entity:
Crucible orchestrates around the Harness, never inside it. The Harness owns its own questions, tool approvals, and turn mechanics. Crucible owns everything between and around steps.
Entities
Entity
Identity
Owned by
Workflow Bundle
(bundle id, version), plus a human-facing name and description
—
Catalog Entry
a Bundle available on this machine, with its origin: built-in, local path, or later a portal
Crucible
Bundle Snapshot
the exact Bundle content one Run launched with
the Run
Step
an author-chosen name, unique within its Bundle, opaque to Crucible
the Bundle
Bundle Asset
a path within the Bundle, plus a declared kind
the Bundle
Run
an opaque id
Crucible
Step Attempt
(Step id, iteration index, attempt number)
the Run
Run Artifact
a name and a type, logically versioned
the Run
Harness Session
a name supplied by the routing
the Run
Human Gate
a Step of gate kind
the Bundle
Harness Request
none — it is ephemeral
the Harness
Deliberately not entities
Project. Replaced by Workspace: the resolved absolute directory a Run executes against, carried as a value on the Run. Run history is filtered by it. Nothing on this map needs Project identity, and an entity with no behaviour becomes a magnet for state we have not justified.
Queue. A queue is an artifact plus a step kind. A Queue entity would put "what counts as an item" and "what done means" inside the orchestrator, which are the Matt workflow's answers, and #11's ratchet forbids the orchestrator holding them. A generic list step kind later is not ruled out; that is #13's call.
Run History. All Runs, filterable by Workspace, Bundle, and state.
Iteration. A numbered scope on a Step Attempt, not a thing with its own identity.
Workspace changes. Source edits, patches, and new files that are not declared artifacts. Owned by the world and by Git, never by Crucible's artifact graph.
What a Run pins at launch
Workspace, Bundle Snapshot, Harness, default model, and launch inputs. That set is the Run's identity of record.
The Bundle Snapshot exists so that upgrading an installed Bundle mid-Run cannot rewrite a Run's history: a Run launched on v1.2 finishes as v1.2, and the upgrade applies to the next Run. This is why Step ids are author-declared names rather than positions — positional ids would renumber history whenever a step is inserted, defeating the pin.
Run lifecycle
States: running, blocked, halted, failed, and the two terminal states succeeded and cancelled.
State
Meaning
Terminal
running
a Step Attempt is executing
no
blocked
a Human Gate or a Harness Request is waiting on the human
no
halted
stopped for a reason outside the workflow's logic — quota exhausted, Crucible closed, Ctrl+C, an interrupted attempt
no
failed
the workflow concluded negatively — bounds exhausted, or a step failed with no path the routing allows
no
succeeded
the routing completed
yes
cancelled
the user explicitly ended the Run
yes
failed and halted are resting states, not terminal ones. Both resume from the failed step on the Run's own id, with no work redone. They differ in what resume must do: a halted Run simply continues, because nothing in the workflow's logic concluded anything; a failed Run concluded negatively, so resuming it resets that step's attempt and iteration counters to their declared bounds rather than granting extra on top. The human's decision to resume is the reset. Resetting rather than incrementing means resume behaves identically however many times it is invoked. The rule for which failures land in which state: the Harness Adapter reports why an attempt ended (#8) and the step kind decides whether that is fatal or retryable (#13).
An earlier draft of this decision made failed terminal and had a retry create a new Run with a parent link and inherited bindings. That was withdrawn. The argument for it — that reviving a Run makes "did this Run succeed?" unanswerable — does not hold, because the Step Attempt log is append-only: resuming appends attempts and rewrites nothing, so the current state answers "how is it now" and the log answers "what happened". Parent links and binding inheritance are dropped entirely: a fresh re-run is a plain new Run that inherits nothing.
blocked, and the "interrupted" condition of a Run marked live with no process running it, are derived from the current Step Attempt, never stored separately. Storing them twice creates two sources of truth that can disagree after a crash, which is exactly when the answer matters.
Closing Crucible or pressing Ctrl+C halts; it never cancels.cancelled is reachable only through an explicit command meaning "I am done with this Run". If cancellation were resumable it would be halted under another name.
Step Attempts
A Step Attempt is identified by (Step id, iteration index, attempt number). Retries and loop iterations are distinct, with independent bounds: an iteration is a new logical occurrence of a repeated group, an attempt is a re-execution of one Step within one iteration after failure. Collapsing them makes #11's "until step S succeeds, or N attempts" ambiguous and leaves the TUI unable to distinguish "failed twice" from "went round twice". Loops do not nest until a workflow demands it.
Outcomes are succeeded, failed, indeterminate, and cancelled.
indeterminate is the honest outcome when Crucible started an attempt and never saw a result — killed mid-attempt, host rebooted. A Git commit step that died after committing is not "failed", and retrying it as though it were invites a double commit. Naming the outcome forces each step kind to state whether it may be retried from there, which is #13's to write.
Each Step Attempt records the model it actually ran under, because a Run halted on one model may resume on another.
Three ways execution stops, and one that does not
Steer — the human injects a turn into a live Harness Session, the agent takes it and carries on. The Step Attempt keeps running and its state is untouched. This needs no domain concept: it is Harness-owned, and Crucible only relays input to a session it already owns.
Interrupt — the current Step Attempt ends with outcome cancelled and the Run goes halted. Resume re-attempts that step. Whether a given Harness can actually interrupt a turn mid-flight is #8's to report truthfully.
Cancel — the Run ends, terminally.
No step is ever skippable. The routing advances only by a step completing. A workflow whose steps can be skipped is not a workflow.
Run Artifacts and the single dataflow currency
A Run Artifact is a named, typed value in the routing's dataflow: produced by a Step Attempt, consumable by later steps. Two independent axes describe it.
Role — is it in the declared dataflow? If yes it is a Run Artifact; if no it is a Workspace change.
Home — where the bytes live. Crucible's own store is the default; a Workspace home is the opt-in exception, declared when the thing genuinely belongs to the repository.
Both axes are needed. A generated document can be a named, typed artifact that later steps consume and live in the repo; location alone cannot express that, and role alone cannot say where it goes. Most artifacts, including a workflow's generated issue files, belong in Crucible's store and are read from there.
Launch inputs and Human Gate answers are Run Artifacts. The Run holds a binding environment of named typed values: launch inputs seed it at Run creation, a Human Gate step produces its answer into it, step outputs land in it the same way. One currency makes "the routing composes when every step's requirements are satisfied" a single uniform check instead of three, and it gives #11's parked subset-selection gate an obvious representation: the selection is an artifact the gate produced.
Artifacts are logically versioned per Step Attempt. The name binds to the latest good version and superseded versions stay reachable, so a retried step always starts from the last good version rather than from its own partial write. This is a logical requirement only — whether it is physically a copy, a hard link, or content-addressed dedup, and how long old versions are kept, is #16's to choose. Nothing here forces copies.
Harness Sessions
A Harness Session belongs to exactly one Run and is never shared or reused across Runs, even in the same Workspace. Its name is supplied by the routing: each agent step names the session it runs in, Crucible opens it on first use and reuses it after, and a step may demand a fresh one. Continuity is the workflow's decision; managing the sessions is Crucible's job.
A session is open (a next turn can be sent now), detached (not live, but holding a native id worth reattaching), or unusable (reattach failed). A step names a session and requires it usable now; whether Crucible got there by keeping it open or by reattaching is an optimization, not a domain distinction, so the routing never has to know whether its steps happened to be adjacent. How well each Harness reattaches is #8's to establish.
The Harness-native session id is advisory recovery data, not truth. Crucible's own record says what the Run did; the native id is a handle to try to reattach. If reattaching fails — Harness upgraded, session expired, local state wiped — the Run remains fully readable, and a step naming that session fails cleanly with "cannot resume session X" rather than silently starting a fresh one.
Human Gate versus Harness Request versus interactive agent step
Three concepts that look alike in a TUI and behave completely differently. They are never merged.
A Human Gate is a Crucible-owned pause with a Bundle-authored question — approve/reject, select a subset, free text. Its answer is a durable Run Artifact, so a Run can sit blocked on a gate for days and still be answered.
A Harness Request is Harness-originated: a tool approval or a clarification raised mid-turn. It is ephemeral, a property of a live turn inside a Step Attempt. If Crucible dies while one is outstanding, the attempt ends indeterminate and the request is simply gone — there is nothing left to answer to.
An interactive agent step hands the Harness Session to the human for turn-taking: the agent asks, the human answers into the same session, the agent asks again. Crucible authors nothing and interprets nothing; it relays turns until a termination condition. The Matt workflow's grill is this. Its content does not exist until the agent writes it, so it can never be a Bundle-authored question, and it is not a permission request. Merging it into either would make the orchestrator understand conversation, which is precisely what #11's ratchet forbids.
Bundle Assets
One entity carrying a declared kind, not a separate entity per kind — a fourth asset type later is a new kind value, not a new domain concept.
Assets are static, read-only content of the pinned Bundle Snapshot, addressable by path and readable by any step. They are not Run Artifacts: no producer, no versioning per attempt, not in the dataflow. Bindings exist to express "step 4 needs what step 2 produced"; an asset has no producer and is unconditionally available, so putting it in the binding environment would add entries answering a question nobody asked.
Delivery is the Harness Adapter's job. Assets are not uniform: a skill must be materialized where the Harness will actually look for it, a prompt is injected as the step's input, a schema augments a prompt without replacing it. Where Claude Code, Codex, and Gemini each keep their skills is Harness-native knowledge, and a workflow author must never need to know it — the same boundary the governing principle draws. The set of kinds and each kind's delivery contract belong to #9 and #8.
Workspace concurrency and preflight
One live Run per Workspace, where live means running or blocked. A second launch against such a Workspace is refused with a clear message. The map already rules out parallel workflow execution, and two Runs editing one repository would have each one's Git steps observing the other's half-finished work.
A halted Run holds no Workspace claim. A shelved Run must not lock the user out of their own repository. The consequence, accepted explicitly: Crucible does not promise a halted Run's Workspace is unchanged when it resumes. Other Runs may have committed and files may have moved. Resume is best-effort, and steps re-read the world when they run.
A launch that fails its preconditions creates no Run. Preconditions — "requires a Git repository", a missing required launch input, a Harness lacking a capability the Bundle needs — are checked before the Run exists. A Run that never ran has no attempts, no artifacts, and nothing to resume; it would be an empty record whose only content is an error the user is already reading. Run history stays a record of things that actually ran.
Handed onward
Question
Ticket
Step vocabulary, routing composition, bounds and reset rules, whether an indeterminate attempt may be retried, a generic list step kind
Resolved: Crucible's domain is the entity set, identity scheme, and lifecycle above, governed by the principle that Crucible orchestrates around the Harness and never inside it.
Activity
Sandstorm831 commentedon Aug 16, 2026
Starting context
.agent/Scope.md.agent/workflow-orchestrator-development-plan.mdRequired methods
/grilling/domain-modeling/codebase-designReading rule
Read the map, current glossary, and Scope first. Consult transport research only when a proposed domain relationship depends on native Harness lifecycle facts. Treat the development plan as a design hypothesis.
Sandstorm831 commentedon Aug 23, 2026
Resolution
Crucible's domain is a small entity set with one dataflow currency, one live Run per Workspace, and a hard boundary at the Harness. The governing principle, which settles more downstream ambiguity than any single entity:
Crucible orchestrates around the Harness, never inside it. The Harness owns its own questions, tool approvals, and turn mechanics. Crucible owns everything between and around steps.
Entities
(bundle id, version), plus a human-facing name and descriptionkind(Step id, iteration index, attempt number)Deliberately not entities
What a Run pins at launch
Workspace, Bundle Snapshot, Harness, default model, and launch inputs. That set is the Run's identity of record.
The Bundle Snapshot exists so that upgrading an installed Bundle mid-Run cannot rewrite a Run's history: a Run launched on v1.2 finishes as v1.2, and the upgrade applies to the next Run. This is why Step ids are author-declared names rather than positions — positional ids would renumber history whenever a step is inserted, defeating the pin.
Run lifecycle
States:
running,blocked,halted,failed, and the two terminal statessucceededandcancelled.runningblockedhaltedfailedsucceededcancelledfailedandhaltedare resting states, not terminal ones. Both resume from the failed step on the Run's own id, with no work redone. They differ in what resume must do: ahaltedRun simply continues, because nothing in the workflow's logic concluded anything; afailedRun concluded negatively, so resuming it resets that step's attempt and iteration counters to their declared bounds rather than granting extra on top. The human's decision to resume is the reset. Resetting rather than incrementing means resume behaves identically however many times it is invoked. The rule for which failures land in which state: the Harness Adapter reports why an attempt ended (#8) and the step kind decides whether that is fatal or retryable (#13).An earlier draft of this decision made
failedterminal and had a retry create a new Run with a parent link and inherited bindings. That was withdrawn. The argument for it — that reviving a Run makes "did this Run succeed?" unanswerable — does not hold, because the Step Attempt log is append-only: resuming appends attempts and rewrites nothing, so the current state answers "how is it now" and the log answers "what happened". Parent links and binding inheritance are dropped entirely: a fresh re-run is a plain new Run that inherits nothing.blocked, and the "interrupted" condition of a Run marked live with no process running it, are derived from the current Step Attempt, never stored separately. Storing them twice creates two sources of truth that can disagree after a crash, which is exactly when the answer matters.Closing Crucible or pressing Ctrl+C halts; it never cancels.
cancelledis reachable only through an explicit command meaning "I am done with this Run". If cancellation were resumable it would behaltedunder another name.Step Attempts
A Step Attempt is identified by
(Step id, iteration index, attempt number). Retries and loop iterations are distinct, with independent bounds: an iteration is a new logical occurrence of a repeated group, an attempt is a re-execution of one Step within one iteration after failure. Collapsing them makes #11's "until step S succeeds, or N attempts" ambiguous and leaves the TUI unable to distinguish "failed twice" from "went round twice". Loops do not nest until a workflow demands it.Outcomes are
succeeded,failed,indeterminate, andcancelled.indeterminateis the honest outcome when Crucible started an attempt and never saw a result — killed mid-attempt, host rebooted. A Git commit step that died after committing is not "failed", and retrying it as though it were invites a double commit. Naming the outcome forces each step kind to state whether it may be retried from there, which is #13's to write.Each Step Attempt records the model it actually ran under, because a Run halted on one model may resume on another.
Three ways execution stops, and one that does not
cancelledand the Run goeshalted. Resume re-attempts that step. Whether a given Harness can actually interrupt a turn mid-flight is #8's to report truthfully.No step is ever skippable. The routing advances only by a step completing. A workflow whose steps can be skipped is not a workflow.
Run Artifacts and the single dataflow currency
A Run Artifact is a named, typed value in the routing's dataflow: produced by a Step Attempt, consumable by later steps. Two independent axes describe it.
Both axes are needed. A generated document can be a named, typed artifact that later steps consume and live in the repo; location alone cannot express that, and role alone cannot say where it goes. Most artifacts, including a workflow's generated issue files, belong in Crucible's store and are read from there.
Launch inputs and Human Gate answers are Run Artifacts. The Run holds a binding environment of named typed values: launch inputs seed it at Run creation, a Human Gate step produces its answer into it, step outputs land in it the same way. One currency makes "the routing composes when every step's requirements are satisfied" a single uniform check instead of three, and it gives #11's parked subset-selection gate an obvious representation: the selection is an artifact the gate produced.
Artifacts are logically versioned per Step Attempt. The name binds to the latest good version and superseded versions stay reachable, so a retried step always starts from the last good version rather than from its own partial write. This is a logical requirement only — whether it is physically a copy, a hard link, or content-addressed dedup, and how long old versions are kept, is #16's to choose. Nothing here forces copies.
Harness Sessions
A Harness Session belongs to exactly one Run and is never shared or reused across Runs, even in the same Workspace. Its name is supplied by the routing: each agent step names the session it runs in, Crucible opens it on first use and reuses it after, and a step may demand a fresh one. Continuity is the workflow's decision; managing the sessions is Crucible's job.
A session is open (a next turn can be sent now), detached (not live, but holding a native id worth reattaching), or unusable (reattach failed). A step names a session and requires it usable now; whether Crucible got there by keeping it open or by reattaching is an optimization, not a domain distinction, so the routing never has to know whether its steps happened to be adjacent. How well each Harness reattaches is #8's to establish.
The Harness-native session id is advisory recovery data, not truth. Crucible's own record says what the Run did; the native id is a handle to try to reattach. If reattaching fails — Harness upgraded, session expired, local state wiped — the Run remains fully readable, and a step naming that session fails cleanly with "cannot resume session X" rather than silently starting a fresh one.
Human Gate versus Harness Request versus interactive agent step
Three concepts that look alike in a TUI and behave completely differently. They are never merged.
blockedon a gate for days and still be answered.indeterminateand the request is simply gone — there is nothing left to answer to.Bundle Assets
One entity carrying a declared
kind, not a separate entity per kind — a fourth asset type later is a new kind value, not a new domain concept.Assets are static, read-only content of the pinned Bundle Snapshot, addressable by path and readable by any step. They are not Run Artifacts: no producer, no versioning per attempt, not in the dataflow. Bindings exist to express "step 4 needs what step 2 produced"; an asset has no producer and is unconditionally available, so putting it in the binding environment would add entries answering a question nobody asked.
Delivery is the Harness Adapter's job. Assets are not uniform: a skill must be materialized where the Harness will actually look for it, a prompt is injected as the step's input, a schema augments a prompt without replacing it. Where Claude Code, Codex, and Gemini each keep their skills is Harness-native knowledge, and a workflow author must never need to know it — the same boundary the governing principle draws. The set of kinds and each kind's delivery contract belong to #9 and #8.
Workspace concurrency and preflight
One live Run per Workspace, where live means
runningorblocked. A second launch against such a Workspace is refused with a clear message. The map already rules out parallel workflow execution, and two Runs editing one repository would have each one's Git steps observing the other's half-finished work.A
haltedRun holds no Workspace claim. A shelved Run must not lock the user out of their own repository. The consequence, accepted explicitly: Crucible does not promise a halted Run's Workspace is unchanged when it resumes. Other Runs may have committed and files may have moved. Resume is best-effort, and steps re-read the world when they run.A launch that fails its preconditions creates no Run. Preconditions — "requires a Git repository", a missing required launch input, a Harness lacking a capability the Bundle needs — are checked before the Run exists. A Run that never ran has no attempts, no artifacts, and nothing to resume; it would be an empty record whose only content is an error the user is already reading. Run history stays a record of things that actually ran.
Handed onward
indeterminateattempt may be retried, a generic list step kindResolved: Crucible's domain is the entity set, identity scheme, and lifecycle above, governed by the principle that Crucible orchestrates around the Harness and never inside it.
Sandstorm831 commentedon Aug 23, 2026
Resolved. The domain model, identity scheme, and Run lifecycle are settled above; #13 and #24 are now unblocked.
docs(#4): record Crucible's domain model and Run lifecycle