Skip to content

Finalizer configuration

Configuration for commit hooks and post-run finalizers.

finalizers

Configures host-owned completion finalizers for SASE-launched agent runs. The bundled default selects the built-in commit instance, preserving ordinary commit enforcement without runtime-specific hooks.

finalizers:
  defaults: [commit]
  required: []
  instances:
    commit:
      use: builtin@commit
      after: []
      max_attempts: 2
      refusal: defer

defaults is the ordered selection used when a prompt omits %final. required instances cannot be removed by %final:none or %final:!name. instances is keyed by lowercase slug; each instance names a trusted provider with use, optional dependency edges in after, bounded attempts through max_attempts, refusal handling, and provider-specific config.

sase final submit is authoring-only: every dirty repository obligation takes a Conventional Commit message, because SASE agents work in ephemeral numbered workspace clones where uncommitted work is lost work. There is no free-text refusal action. The only escape hatch is sase final defer, a separate, deliberate command that names a typed reason from a closed set (protected_paths, foreign_work, unsafe_content, belongs_to_another_turn) and is adjudicated by the host against run evidence at submit time — a deferral the host can disprove is rejected with counter-evidence so the agent can repair and resubmit in the same turn. refusal controls what happens when the host upholds a deferral: fail (still fails the run; the historical behavior) or defer (the shipped default — the run completes with a distinct deferred status and the tree stays dirty, and a notification names the repository, reason, paths, and the command to finish the commit by hand).

Only trusted configuration can define providers, commands, cwd policy, environment allowlists, timeouts, or retry policy. Prompt text can only select configured instances: %final:lint adds lint, %final:none clears defaults unless blocked by required, and repeated/comma-separated %final operations replay left to right before dependency ordering. Plugin packages may advertise providers through the sase_finalizers entry-point group, but installation alone is inert; a trusted config layer must declare and select an instance. Plugin-contributed config layers cannot activate finalizers.

Use sase final list, sase final show <instance>, and sase final doctor to inspect effective policy, provider provenance, and diagnostics. During an active agent turn, generated agent instructions tell the model to finish with /sase_final. That skill uses sase final context -f json and sase final submit to publish the one turn-bound declaration required by selected finalizers, and it returns after reading context when no payload is required. If a required submission is missing or stale, the host opens one bounded recovery turn that explicitly requests /sase_final. The host executes and verifies finalizers after the model returns.

sase final prepare <wrapper.json> is the conditional alternative for a long verification handoff. It seals a completed declaration, exact verification argv, current context, and repository observations without submitting or executing them. Bind the returned single-use ref with sase monitor start -f <ref>; only an eligible successful monitor result lets the host install that declaration and run finalizers without another model turn. A plain monitor profile carries no completion authority. See Prepared host completion.

The built-in builtin@commit provider checks the main workspace, configured linked Git worktrees, and repositories opened through /sase_repo. Dirty enforced repositories become declaration obligations; each repository must receive exactly one commit decision with a Conventional Commit message, and commit is the only legal repository action. Typed deferrals can name explicit paths that must not be committed, using the adjudicated reasons protected_paths, foreign_work, unsafe_content, or belongs_to_another_turn. When the run has an assigned bead (SASE_BEAD_ID), the primary repository's commit decision also carries a keep or close bead action, which is forwarded to sase stitch create -B; see Explicit Bead Action. Commit decisions dispatch through sase stitch create sequentially, preserve protected pre-existing dirt, write stitch evidence, stop on the first conflict for repair/resume, and fail completion when a rejected deferral or unrepaired dirty state remains. Later mutating finalizers can reactivate commit until the controller reaches its bounded fixed point.

When $SASE_ARTIFACTS_DIR is set, new runs write generic finalizer artifacts: finalizer_baseline.json, final_context.json, final_submission.json, final_submission_attempts.jsonl, finalizer_result.json, and per-instance files under finalizers/<instance>/. The commit provider also keeps current stitch evidence in commit_results.json. Historical commit_finalizer_* artifacts are read only for archived run reporting and are not a supported output format for new runs.

commit.message

Configures the Conventional Commit subject gate that sase stitch create applies to every create_commit, create_proposal, and create_pull_request message before any side effect runs.

commit:
  message:
    require_conventional_subject: true
    allowed_types:
      [build, chore, ci, deps, docs, feat, fix, perf, refactor, revert, style, test]

Generated reference for commit.message

The subject must match <type>[(<scope>)][!]: <description>. The scope is optional, one or more spaces may follow the colon, and no length or capitalization rule is applied to the description. The type itself must be lowercase, because release tooling does not classify capitalized types reliably. Only the first line is inspected; nothing below the subject is validated.

Subjects beginning with Merge, Revert ", fixup!, squash!, or amend! are exempt and always pass — these are mechanical git-generated or rebase-directive subjects. An empty message is always rejected.

A rejection fails the workflow before beads are closed, plans are staged, or the before-commit hook runs, and the -M message file is preserved so the same command can be re-run after the subject is rewritten. There is no per-invocation bypass flag or environment variable; a project that does not use Conventional Commits sets require_conventional_subject: false.

Source: src/sase/finalizers/controller.py, src/sase/finalizers/commit.py, src/sase/commit_instructions.py, src/sase/workflows/commit/message_validation.py, src/sase/core/commit_subject_facade.py

commit_hooks

Shell commands that bracket commit-producing VCS dispatches. before runs in the repository root after bead and plan mutations but before diff capture and dispatch. after runs in the repository root only after create_commit or create_pull_request succeeds, including its push where applicable. Proposals run before but never run after because they save a diff without creating a commit.

Both fields default to an empty string. Because the object is deep-merged, a global before hook and project-local after hook compose without either configuration repeating the other phase.

commit_hooks:
  before: "just fix" # default: ""
  after: "chezmoi update -a --force" # default: ""

Generated reference for commit_hooks

Hook output is captured and a bounded stdout/stderr tail is printed on failure; each run also leaves per-run evidence files described in Commit Hook Evidence. A failing before hook aborts before dispatch. A failing after hook leaves the commit checkpoint in place and returns failure even though the commit may already be pushed; fix the command and run sase stitch create --resume. The completed after-hook step is checkpointed so a normal resume does not rerun it. A crash after the external command succeeds but before that checkpoint write can run it again, so after commands must be safe to repeat.

Source: src/sase/default_config.yml, src/sase/workflows/commit/commit_hooks.py, src/sase/workflows/commit/workflow.py

_[project]: A sase project is a named unit of work registered with SASE. _[workspace]: A sase workspace is a numbered clone of a project's primary repo, managed by the workspace store and tracked in that project's registry.json. _[agent]: A sase agent is an agent session or a single agent that does not belong to a session. _[prompt]: A prompt is the text SASE hands to a provider to start an agent turn. _[macro]: A macro is a reusable prompt triggered with #name in agent prompts. _[Patch]: A Patch is SASE's local unit of change. _[bead]: A bead is one tracked work item in SASE's git-native issue tracker. _[plan]: A plan is a persisted, approved artifact recording the intent behind agent work. _[TUI]: The TUI is SASE's terminal interface for running agents and reviewing results. _[epic]: An epic is an executable multi-phase plan run with sase bead work. _[gate]: A sase gate is a durable, command-backed request for one user decision, created when work must pause for the user. _[monitor]: A sase monitor is a monitor turn that runs one long command under a detached supervisor, so the command outlives the agent that started it. *[stitch]: A stitch is the lightweight ordered change record inside a Patch's STITCHES: section.