Skip to content

Isolated workspaces

Shared checkout execution is Harnery’s default. An isolated workspace is an optional boundary for workflows that should keep intermediate edits away from the host checkout.

The built-in provider creates local Git worktrees. The provider owns the worktree resource, but it does not decide whether the work succeeded or whether its changes may enter the target branch. Harnery’s existing work, proof, review, approval, and policy records keep those decisions.

Supply both the isolation mode and an explicit writable parent:

Terminal window
harn run ./workflow.mjs \
--isolation worktree \
--workspace-root . \
--policy ./workflow-policy.json

The explicit root is part of the authority record. Harnery resolves it, records its filesystem identity, and refuses a later resume if the path or identity no longer matches.

The writable root must contain the repository’s Git directory

Section titled “The writable root must contain the repository’s Git directory”

Allocating a worktree makes Git write to the repository’s administrative directory as well as to the new checkout, so that directory has to sit inside the root you declared. A sibling directory does not qualify, however tidy it looks:

repository_authority_outside_writable_root:
Git keeps this checkout's administrative files in /repo/.git,
which is outside the declared writable root

For an ordinary repository, the repository itself is the right answer, and its parent also works. For a submodule, Git keeps the administrative files in the superproject’s .git/modules/…, so the writable root must be the superproject, not the submodule. The same applies to a linked worktree, whose common directory belongs to the repository it was created from.

Workspaces are allocated under <workspace-root>/.harnery-workspaces/<binding-id>. That directory holds real checkouts rather than coordination state, so it is a sibling of .harnery/ and never a child of it. harn deinit --purge-state deletes .harnery/ outright, and a preserved worktree carrying unintegrated work has no business somewhere a purge is allowed to reach.

The provider writes a .gitignore containing * into that parent the first time it allocates. The rule covers the ignore file itself, so the directory stays out of git status in whatever repository happens to contain it, and you never have to maintain an ignore rule of your own.

Workspaces allocated by an earlier version live under harnery-workspaces/, without the leading dot. Those paths are frozen in their bindings and keep working, so nothing has to move. Only new allocations use the hidden parent.

You do not need to clone a repository by hand to get an isolated run. If you find yourself making a journal copy so agents have somewhere safe to write, the provider is what you actually want.

When Git or the required filesystem behavior is unavailable, the provider reports an unsupported capability. Shared fallback happens only when the frozen policy allows it. A bound run never falls back.

Allocation creates two related records:

  • the workflow manifest binds the run, requested isolation, provider, paths, script, policy, and work-attempt owner;
  • the provider stores an immutable ownership claim and binding, followed by a digest-chained event history.

The provider cannot update work outcomes, proof, review decisions, approvals, governor state, or budgets. It reports workspace facts to workflow core.

Harnery reattaches the exact binding before importing the workflow module and again when a parked run resumes. Reattachment checks path containment, filesystem identity, Git registration, branch ownership, commits, ancestry, dirty files, conflicts, and Git operations in progress.

Missing or contradictory authority becomes blocked or lost evidence. Harnery does not silently move execution back to the shared checkout.

The terminal proof keeps workflow outcome separate from workspace resource state. A workflow can fail while its worktree remains healthy and available for inspection. It can also finish successfully while integration remains pending.

Workflow success does not merge changes. Integration has two explicit calls:

import {
applyIntegration,
prepareIntegration,
} from "harnery/core/workflow";
const plan = await prepareIntegration({
coordRoot,
runId,
provider,
review: { actor: "reviewer" },
policy,
acceptedUnknowns: [],
});
const receipt = await applyIntegration({
coordRoot,
runId,
provider,
plan,
});

Preparation requires a successful proof with satisfied acceptance, a review bound to that exact proof, a current attestation, and policy authorization. Application rereads the durable plan and authority before mutation. The local provider supports fast-forward integration only. Divergence, target movement, dirty state, or conflicts block the operation.

An exact replay returns the existing receipt. It does not apply the same change twice.

Cleanup runs separately:

import { cleanupWorkspace } from "harnery/core/workflow";
await cleanupWorkspace({ coordRoot, runId, provider });

The cleanup intent freezes the exact worktree, provider ref, and target state. A digest-chained attempt record is written before mutation. The final receipt is written only after Harnery verifies that the worktree registration and provider branch are gone.

Dirty work is preserved. Ambiguous ancestry, a live run, changed ownership, or an incomplete release blocks cleanup and leaves evidence for another attempt.

Use the CLI:

Terminal window
harn run workspace <run-id>
harn run workspaces

Or use the product-tier reader:

import {
inspectWorkflowWorkspace,
readWorkflowWorkspaceStatus,
} from "harnery/core/workflow";
const status = readWorkflowWorkspaceStatus(coordRoot, runId);
const safeResult = inspectWorkflowWorkspace(coordRoot, runId);

readWorkflowWorkspaceStatus() throws when authority is corrupt or contradictory. inspectWorkflowWorkspace() returns an explicit { ok: false, error } result for dashboards and other read surfaces that must stay available while showing the defect.

The local dashboard uses this same reader. Its workflow pages show allocation, verification, integration, conflicts, cleanup, and the current resource state.