tobiishi-seal
PM-posture terminal pipeline step. Validate the developer marker and real deferrals, take main's advance and run the suite for one repository, and join the audit set on both topologies. Invoke the del
Files6
View on GitHubSKILL.mdskills/pm/.claude/skills/tobiishi-seal/SKILL.md
GitHubTobiishi Seal
Purpose
Seal the caller's change through <CORE>/tools/deliver_change.py, using the public interface in
<CORE>/tools/deliver_change.md § Invoke the executable and § Supply merge metadata and a literal
seal action. This skill owns readiness, advance/suite policy, audit joins, bookkeeping, terminal
validation, recovery reporting and audit cleanup. The executable owns delivery merges and removal
of delivery worktrees and their exact branch refs.
A real developer deferral is recorded verbatim as sealed with deferrals. Step 1 checks that it
is real; it does not decide whether it is right. The bookkeeping seal travels into instance main
through its delivery merge. Make no further commit on main after delivery.
Produces: the sealed carving and findings on instance main, delivery merges and proved
worktree/ref absence, followed by joined-audit cleanup on success; an existing-folder
halt_report.md with retained terminal and recovery evidence on halt.
Input
PM role only. It runs under the pm agent (<CORE>/tools/chain_development.py:160, the seal step). If invoked outside a PM session,
stop and say: "tobiishi-seal is PM work — the flow's terminal seal+merge step."
- The change key
<YYYY-MM-DD>_<slug>— the branch name, worktree suffix, and Tobiishi folder name. You know it from the invocation (the change you were asked to deliver). - The project name
<project>— thesystem/projects/<project>/folder (passed by the orchestrator). Needed to locate the right Tobiishi at Step 2 (system/projects/<project>/journey/tobiishi/<key>/).
One repo or two — resolve once, retain the complete contract
After locating the instance change worktree in Step 1, capture this command's complete JSON in a stable directory outside every worktree scheduled for removal:
python3 "<CORE>/tools/development_path.py" resolve --strict --format json \
--worktree "$WORKTREE" --project "<project>" --key "<key>"
Use the returned object whole in the configuration below, including every alias and
delivery_repos. The contract is owned by <CORE>/tools/development_path.py build_contract;
the accepted delivery domain is <CORE>/tools/deliver_change.md § Supply all contract fields.
A nonzero resolver exit halts deliver-precondition-failed before joining, sealing or delivery.
- Relative: run Steps 1, 1a and 1b, then the common caller. The executable runs seal → instance delivery → instance cleanup.
- Fixed: run Steps 1, 1a's no-suite call and 1b, with no advance or suite on either repository, then the same caller.
The executable runs product delivery → product cleanup → seal → instance delivery → instance
cleanup. Use separate metadata for each role; no cross-repository merge occurs. When delivery
declares the product change branch empty, the product step removes it without a merge, and the
seal then proves
delivered: instancewithempty: product.
Both paths delegate their complete delivery sequence to the executable. Its ordering and recovery
boundary are <CORE>/tools/deliver_change.md § Validate targets, observe landing, then remove only
delivered worktrees and § Read the terminal result and retained recovery state.
Workflow
<key> below is the change key; <project> the project, both passed by the orchestrator.
Step 1 — Derive paths and confirm preconditions
Read git worktree list --porcelain and locate the entry whose branch is
refs/heads/<key>. Record that entry's literal absolute worktree path as WORKTREE, including
spaces; never reconstruct it from a guessed directory. Resolve the contract above from that path.
Set REPO_ROOT to its instance_repo, and TOBIISHI to
system/projects/<project>/journey/tobiishi. Keep these absolute paths for all preceding gates.
Move an earlier halt's uncommitted report aside first. A seal that halted before its seal commit
wrote halt_report.md into $WORKTREE/$TOBIISHI/<key>/ and left it uncommitted (§ Where a halt
writes its report); an earlier step's own report at that path is uncommitted too. Delivery
refuses a change worktree holding any untracked file, so a report left there halts every re-run of
this seal on its own report. Before any gate below, when
git -C "$WORKTREE" status --porcelain -- "$TOBIISHI/<key>/halt_report.md" prints ?? …, move that
file to $REPO_ROOT/system/logs/seal-halt-<key>-<UTC timestamp>.md (ignored by git, kept for
reading) and name the new path in the outcome. A halt_report.md git tracks — a step
committed it — is left where it is.
The caller action runs in the instance change worktree on <key>. The executable runs from its
stable invocation directory and targets the contract's main checkouts. Confirm:
REPO_ROOThasmainchecked out (git -C "$REPO_ROOT" rev-parse --abbrev-ref HEADismain) — the merge target.The recorded worktree exists and is registered on
refs/heads/<key>ingit -C "$REPO_ROOT" worktree list --porcelain; the exact branch ref exists (git -C "$REPO_ROOT" show-ref --verify --quiet refs/heads/<key>).The active Tobiishi folder exists on the branch (
$WORKTREE/$TOBIISHI/<key>/change_request.md).The developer's closing marker commit is on the branch:
git -C "$WORKTREE" log main..<key> --format='%s' | grep -q "implementation complete"development-implementation§ Step 3 owns that commit and its subject (developer: <key> — implementation complete). It is an empty marker commit whose body carries the run's whole record — the language resolution, the Step 1b review, and the developer's divergence notes — so its absence is not a missing formality but the loss of everything the developer stage produced besides code. It lands on the pipeline worktree for an external product too — the instance branch (development-implementation§ Where each commit goes) — so this one check covers both paths.Every deferral on the marker is real. Read the marker's
Deferred:lines — the bare lines of the body of the newestdeveloper: <key> — implementation completecommit (git -C "$WORKTREE" log -1 main..<key> --grep='<key> — implementation complete' --format=%B). The-1and the<key>are both load-bearing. The key keeps out another change's marker carried onto the branch by a merge, which the chain also ignores. A re-closed branch carries more than one marker, and the newest supersedes the rest (development-implementation§ Step 3, A re-close makes a new marker), so every mention of "the marker" in this skill means that newest one, which is also the one the chain read. A marker with none has nothing to check. Run the same four checksdevelopment-code-integrity§ Deferrals runs, on each line, reading the same repositories — this is a check that the deferral is real, never that it is right:- task
<N>is a### Task <N>intl_instruction.md; - no delivered task is deferred, checked by
development-code-integrity§ Deferrals, check 2, including the evidence required when an earlier landing subject is corrected; - a blocked task's case is in the tree as
change_spec.mdandtl_instruction.mdstate it, and a dependent task'stask <M>is itself named by anotherDeferred:line; - the
<record>path exists on the branch.
- task
Halt (deliver-precondition-failed) if main isn't checked out, or the branch/worktree/Tobiishi
folder is missing, or a Deferred: line on the marker fails one of the four checks → write
halt_report.md (for a deferral, naming the Deferred: line and the check it failed), do not seal, do
not merge, return status: halt. No new halt code: an unreal deferral is a precondition the branch does
not meet.
Halt (missing-implementation-marker) if the marker commit is absent → same treatment: no join,
no seal, no merge, no teardown. It carries its own reason code rather than
deliver-precondition-failed because the two are repaired by different people in different places —
a missing worktree is re-run upstream, a missing marker is the developer stage's record to reconstruct
— and the code is what the orchestrator hands the human. Sealing anyway is what happened once: the
substance sat scattered across per-task commit bodies in the product repository while this branch
carried nothing from the developer, and nothing between the two stages noticed.
Step 1a — Take main's advance, then run the suite
The advance and the suite run in $WORKTREE, on branch <key>, on the one-repo path only. The
gate-tool call at the end of this step is made on both paths.
The advance. Take main's advance onto the branch through the builder:
python3 "<CORE>/tools/commit_builder.py" --repo "$WORKTREE" advance --from main --actor pm \
--surface "<supplied surface>" --co-authored-by "<supplied model identity>" \
--reasoning-effort "<resolved runtime effort>"
An exit of 0 — with or without a commit — goes on to the suite below. It carries no --bump: a
branch never edits a version marker, the same reason Step 1b gives.
A failed advance. On a non-zero exit, read two things before aborting anything:
git -C "$WORKTREE" rev-parse -q --verify MERGE_HEAD # a merge in progress?
git -C "$WORKTREE" diff --name-only --diff-filter=U # its unmerged paths
Then, when a merge is in progress, git -C "$WORKTREE" merge --abort. Halt:
merge-conflict— unmerged paths are present.merge-hook-failed— a merge is in progress with no unmerged paths.deliver-precondition-failed— no merge is in progress; quote the builder's stderr.
Write halt_report.md, return status: halt, and join, seal, merge and tear down nothing. It aborts
rather than leaving the merge in progress because the seal is halt-don't-ask — the branch owner
repairs the branch with the same advance.
The suite. Run project.md's Test command: once, through the gate tool (§ The call, below),
from $WORKTREE — never from <name>/, since a card like this project's collects nothing under its
product root. Exit 0 passes. Any other exit from an issued suite halts
deliver-precondition-failed, naming the command, the exit code, and each failing case. A command
that collects no tests is a non-zero exit, and the advance commit stays on the branch either way.
Deferrals. When the marker reads Suite: red, a non-zero exit passes only if every failing case is
named by a marker Failing: line. Any case no line names halts as above.
The call. The tool runs the card's command in the environment the product's CI calls for, and
prints that environment as its first line. Read <CORE>/prompts/gate_suite.md and follow it: it owns
how the evidence is read, where the owner's ask comes from, and the three calls. Call it once, after
the advance and not before: the advance is what changes the tree the suite runs on. This skill's own
call, with the evidence file that recipe's Step 3 builds, is:
python3 "<CORE>/tools/gate_suite.py" run \
--worktree "<instance change worktree>" --project "<project>" --key "<key>" \
--gate seal --inputs "<inputs file>" \
> "<output file>" 2> "<error file>"
Where the owner's ask applies, the recipe's second call replaces it, with --gate seal. Write the
output and error files in the stable directory Step 2 names, outside every delivery worktree.
- The line is the tool's, whole. Copy the output file's first line,
Environment: …, as printed. Never write one from the evidence you read, and never reuse the developer's or the integrity run's line: this run's suite is its own. - A refusal or an unreachable environment is no suite. When the exit is non-zero and standard error
ends with
The test command was not run, on the owner's Mac or anywhere else., nothing was issued: a refusal or an unreachable environment haltsdeliver-precondition-failed, quoting the first line of standard error and theEnvironment:line, with no join, no seal and no merge. Never run the card's command by hand, and never fall back to another environment. A failed suite keeps the rule above: the tool's exit is the suite's, and the deferral matching applies to it unchanged. - An environment that was not ready is no suite result either. Read the run's lines by
<CORE>/prompts/gate_suite.md§ Reading the run's lines. When any of them beginsEnvironment: not-ready, the run is not a failed suite, whatever its exit, and the deferral matching never applies to it. Haltdeliver-precondition-failed, quoting that line, with no join, no seal and no merge. The environment line rule below does not change for such a run, because a run that was not ready seals nothing.
The environment line. The printed line is kept as environment_line in Step 2's bookkeeping,
written to the caller configuration in the stable directory before any delivery worktree can be
removed: for a product in its own repository the product worktree is removed before the instance seal
is made, and the line has to outlast it. That holds the line outside every worktree.
The fixed path. A product in its own repository runs no advance and no suite, and the call is
still made once, from the same place, as invocation-no-inputs. Nothing is probed or issued: the tool
prints Environment: none — no suite ran and exits 0, and that line is the one kept.
python3 "<CORE>/tools/gate_suite.py" run \
--worktree "<instance change worktree>" --project "<project>" --key "<key>" \
--gate seal \
> "<output file>" 2> "<error file>"
The same call, on the one-repo path, serves a card with no command: none, N/A, a validated by use
verdict or a skill name (§ No command, below). The line is the same Environment: none — no suite ran.
No command. A card whose Test command: names no command owes the advance alone, and no suite
runs: none, N/A, a validated by use verdict, or a skill name such as skill-evals. The gate tool is
still called, for its Environment: none — no suite ran line. Of these,
only none lets a Suite: none marker past the chain's implementation gate, so a product with no
code has its card set to none, not any of the others. On a none card with a Behaviour: preserving change on this path, re-run instead
every after-check in tl_instruction.md's #### Invariant gate blocks that is a command, in its between-two-commits form (the
before-checks are meant to fail once the change has landed), from the product root
tl_instruction.md names ($WORKTREE/<Product path>), after the advance. A product in its own repo
takes no advance here, so code-integrity's re-check is its last. A reading-only check is not re-run. A failing command halts
deliver-precondition-failed as a failing suite would, naming the command and the invariant
(<CORE>/doctrine/flow/change_classification.md § A product with no code).
Why. The developer's Suite: line was measured before the advance, on a tree that is no longer the
one that merges. A conflict found here sits on the branch, where the work is, not on main's checkout
after the seal. commit_format.md § A branch takes main's advance through advance states the
rule.
Step 1b — Join the audit branches
Runs on both paths (§ One repo or two). Run in the worktree ($WORKTREE, the instance
worktree, on branch <key>), before Step 2's folder move — every
development-audit branch carries flow_findings/<step>.md at the change-work folder's pre-move
path ($TOBIISHI/<key>/flow_findings/<step>.md); joining after the move would land it at a path that
no longer exists on the change branch.
Glob the set from the key alone — nothing is handed to this step that it cannot recompute (the same
convention development-audit's own Step 0 names):
AUDIT_BRANCHES=$(git -C "$WORKTREE" for-each-ref --format='%(refname:short)' "refs/heads/<key>__audit-*")
A missing audit branch is not a failure. development-audit is not a gate
(<CORE>/tools/chain_development.py gates=False, :157) — an auditor may have died and AUDIT_BRANCHES may glob fewer than the audit launched (or
none). Join whatever the glob finds; an empty set skips this step entirely.
For each branch in $AUDIT_BRANCHES, merge it into <key> through the builder — one call per
branch (commit_builder.py merge takes one --branch; one sequential call per branch is the shape, never an
octopus merge, never a cherry-pick):
git -C "$WORKTREE" log <key>..<branch> --oneline # branch commits for the merge body
python3 "<CORE>/tools/commit_builder.py" --repo "$WORKTREE" merge \
--branch "<branch>" --target-branch "<key>" \
--description "development-audit findings — <step audited>" \
--branch-commits "<commit line 1>" ... \
--actor pm \
--merge-context "tobiishi-seal <key> — audit fan-out join" \
--surface "<supplied surface>" --co-authored-by "<supplied model identity>" \
--reasoning-effort "<resolved runtime effort>"
No --bump on any of these merges. The target is the change branch <key>, never main — a
branch never edits a version marker (<CORE>/doctrine/governance/doctrine_structure.md
§Versioning); the commit that lands a version bump on main is the executable's delivery merge, not this one.
A merge conflict here is not expected — each auditor writes only its own flow_findings/<step>.md
(development-audit's own Critical Rules) — but is handled like any other merge failure:
git -C "$WORKTREE" merge --abort, then halt (merge-conflict / merge-hook-failed) — write
halt_report.md, leave every worktree and branch intact, return status: halt. Do not proceed to
Step 2 on a failed join.
Step 2 — Prepare the common delivery invocation
Steps 1/1a/1b must finish before this recipe runs. Its bookkeeping input receives only the ordered
Deferred: lines already validated in Step 1; acceptance supplies an empty array.
Use one existing absolute directory outside every delivery worktree for the caller configuration
and a materialized caller-delivery.py. Write the exact caller-delivery block below into that
script, retaining its bytes. The configuration is a JSON object with these fields:
| Field | Supplied value |
|---|---|
core |
The executing run's resolved absolute <CORE>, outside delivery cleanup; its tools/deliver_change.py, tools/commit_builder.py and dependencies remain reachable. |
caller_source |
Absolute path of the adopted SKILL.md actually read, not an older pinned caller. Preparation extracts its caller-seal-action bytes once. |
contract |
The entire resolver JSON object from above. No projection or alias removal. |
merges |
An object keyed by exactly the contract roles: instance, or product and instance. Each record has the public shape below. |
bookkeeping |
The object below, using the executing run's supplied values. |
Each independent merge record requires string description, merge_context, surface, and
exactly one string actor or authorizer. The automated PM run supplies actor: "pm".
Optional strings are co_authored_by, reasoning_effort and claude_session. Optional arrays of
strings are branch_commits and bump. Retain supplied values and order, empty arrays and optional
absence. Do not copy attribution from an example or fill omitted fields with defaults. These
fields and the builder's path=patch|minor|major bump format are owned by
<CORE>/tools/deliver_change.md § Supply merge metadata and a literal seal action.
Read preparation-time commit entries separately for each role with
git -C "<that role's repo>" log main..<key> --oneline. Choose each role's merge description and
context from its verified change; when real deferrals are present, the instance context says
sealed with deferrals and makes no green-suite claim. Obtain changed versioned files with
git -C "<that role's repo>" diff --name-only main...<key> and supply a bump entry for each one
under core/, .claude/, skills/ or system/ per
<CORE>/doctrine/governance/versioning.md § Bump semantics; skill levels follow
<CORE>/doctrine/kaizen/skills.md § The version's three digits. Keep branch markers unchanged.
No bump names a product's file — not in the product record, and not under products/ in an
instance record. A product's version lines are its own content, which its tasks set, and the
builder refuses a bump on them (<CORE>/doctrine/governance/versioning.md § Whose artifacts).
No supplied commit list predicts the seal SHA: the action records its actual SHA after committing,
and the executable observes the new instance tip for landing.
Bookkeeping requires date (the run's seal date), deferred (the validated ordered line array),
environment_line (the first line this run's Step 1a gate-tool call printed, copied whole — never the
developer's or the integrity run's), directional (the full resolved directional line), surface, co_authored_by and
reasoning_effort (the supplied resolved runtime effort). Include claude_session only when the
run supplies one. Resolve directional under <CORE>/doctrine/governance/commit_format.md
§ Directional trailer; preserve its literal value. Preparation hands it to the builder's
check-directional before the tool runs, because the seal commits only after a product merge: a
line the builder would refuse halts deliver-precondition-failed with nothing merged.
Invoke the materialized block with the configuration path as one literal argument:
python3 "/absolute/stable/caller-delivery.py" "/absolute/stable/configuration.json"
The script creates and retains one absolute temporary directory. It copies the complete contract
and independent merge records without schema defaults, materializes the adopted action and action
configuration, and creates seal.json with an absolute Python executable and literal action argv.
Preparation executes no action; its one builder call is the read-only directional check. The actual tool invocation in Caller.prepare supplies
--contract, --merges, --seal-action and explicit --builder; Caller.deliver runs it once
from the stable temporary directory. Keep that directory, its input/configuration/action files,
before.json, invocation.json, seal-evidence.json and result.json through outcome handling.
No caller value is shell source.
The caller-seal-action block owns bookkeeping only: refuse a missing or empty environment_line
before it creates or moves anything, create the trail parent if absent, move the
whole folder before appending the result to the destination request — the **Sealed:** line, the
environment_line directly after it, then any deferral lines — retain every carving/finding,
mark only the existing current PM-context item Sealed, and use the builder to commit on the instance
branch. It declares both move prefixes and adds the edited request and existing context. It creates
no context when one is absent. The action emits the actual clean seal commit SHA. The executable
verifies that it extends the recorded instance tip before merging
(<CORE>/tools/deliver_change.md § Supply merge metadata and a literal seal action).
Step 3 — Read the terminal result and retained recovery state
Read result.json and the returned JSON outcome from the actual process. Success requires exit zero
and the exact topology-appropriate three-line block from <CORE>/tools/deliver_change.md
§ Read the terminal result and retained recovery state. For a product in its own repository, one
second complete result is also proved: the four-line block delivery: complete /
delivered: instance / empty: product / cleanup: complete, each line ending in a newline, with
exit zero. empty: product is delivery's declaration that the product change branch carried no
commits of its own and was removed without a merge. A product inside the instance has no such second
result: the four-line block there halts delivery-failed, unproved. Only proved success makes audit
cleanup eligible. A valid halt retains its actual public code, delivered-role prefix, incomplete cleanup,
stdout, stderr and process exit. A missing, malformed or exit-inconsistent result halts
delivery-failed, marks landing unproved, and retains raw observations; launch failure has no
invented exit status.
On halt, return the actual halt_report path. The report retains recorded main/change tips and
read-only post-call disk, registration, exact-ref, merge-head and unmerged-index evidence, plus
observed main SHAs and their ordered parents. Failed observations are unavailable with diagnostics;
they do not prove absence. Intended landing is proved only when the observed ordered parents match
recorded old main and intended product tip or observed seal tip, with no merge in progress.
These observations never replace the tool's terminal verdict.
Stop the delivery path on any halt. Do not abort conflicts, roll back a landed product, retry the
seal/tool call, remove recovery worktrees/branches, or clean up audits. Preserve teardown-failed
after product landing. Advance and audit-join failure policy remains confined to Steps 1a/1b.
Step 4 — Clean up joined audits only after proved delivery success
The executable has proved delivery worktree/ref absence. Remove only the exact audit branches
successfully joined in Step 1b; keep that list through delivery, never rediscover it with a glob.
Save the executable recipe below in the stable invocation directory and run it with REPO_ROOT,
<key>, and that joined list as separate argv elements. It reads literal registered paths, including
spaces and nested checkouts. It validates the entire set before removing anything, and rechecks each
entry immediately before removal. No joined audits means an empty list and no cleanup effects.
#!/usr/bin/env python3
from __future__ import annotations
import subprocess
import sys
__version__ = "1.0.0"
def git(repo: str, *args: str) -> str:
result = subprocess.run(["git", "-C", repo, *args], capture_output=True,
text=True, encoding="utf-8", errors="surrogateescape")
if result.returncode:
raise ValueError(f"{repo}: git {args!r}: {result.stderr.strip()}")
return result.stdout
def registrations(repo: str) -> list[dict[str, str]]:
records = []
for block in git(repo, "worktree", "list", "--porcelain", "-z").split("\0\0"):
if block:
records.append(dict(field.partition(" ")[::2] for field in block.split("\0")
if field))
return records
def checked_path(repo: str, branch: str) -> str:
matches = [record for record in registrations(repo)
if record.get("branch") == f"refs/heads/{branch}"]
if len(matches) != 1 or any(flag in matches[0] for flag in ("locked", "prunable")):
raise ValueError(f"{branch}: expected one usable registered audit worktree")
path = matches[0]["worktree"]
if git(path, "symbolic-ref", "--quiet", "HEAD").strip() != f"refs/heads/{branch}":
raise ValueError(f"{path}: audit branch identity changed")
if git(path, "status", "--porcelain", "--untracked-files=all"):
raise ValueError(f"{path}: audit worktree has uncommitted changes")
git(repo, "merge-base", "--is-ancestor", f"refs/heads/{branch}", "refs/heads/main")
return path
def cleanup(repo: str, key: str, branches: list[str]) -> None:
if len(set(branches)) != len(branches) or any(
not branch.startswith(f"{key}__audit-") for branch in branches
):
raise ValueError("expected unique joined audit branches for this change")
if git(repo, "symbolic-ref", "--quiet", "HEAD").strip() != "refs/heads/main":
raise ValueError("audit cleanup requires the delivered main checkout")
paths = [(branch, checked_path(repo, branch)) for branch in branches]
for branch, path in paths:
if checked_path(repo, branch) != path:
raise ValueError(f"{branch}: registered path changed before cleanup")
git(repo, "worktree", "remove", path)
git(repo, "branch", "-d", branch)
if any(record.get("worktree") == path or
record.get("branch") == f"refs/heads/{branch}" for record in registrations(repo)):
raise ValueError(f"{branch}: audit registration survived cleanup")
if git(repo, "for-each-ref", "--format=%(refname)", f"refs/heads/{branch}"):
raise ValueError(f"{branch}: audit ref survived cleanup")
def main(argv: list[str] | None = None) -> int:
arguments = sys.argv[1:] if argv is None else argv
try:
if len(arguments) < 2:
raise ValueError("supply the main checkout, change key and joined audit branches")
cleanup(arguments[0], arguments[1], arguments[2:])
return 0
except (OSError, ValueError, KeyError) as error:
print(f"error: teardown-failed: {error}", file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())
A nonzero exit halts teardown-failed; record stderr and observed survivors in the halt report.
Preserve already-delivered history and remaining audit state; never force deletion. Delivery-worktree
cleanup is never repeated here. This recipe replaces the reconstructed-path shell loop.
Step 5 — Return the outcome
Return the step-outcome message under <CORE>/doctrine/flow/step_outcome_contract.md, using the
actual report path for halt. Make no further commit on main after delivery.
Where a halt writes its report
Use existing destinations only: the retained instance worktree's active change folder before the
move, or its trail folder after the move. If the delivery worktree is absent, use the primary
instance's existing active folder; if that is absent too, use the existing project's tobiishi root.
Recreate neither a removed worktree nor an active change folder. A report in the worktree's active
change folder is not committed, so a re-run of this seal moves it aside before its first gate (Step 1). The Caller._report_directory
recipe below implements this order and returns the path actually written. The same placement
applies to pre-delivery halts in Steps 1/1a/1b, and Caller.refuse applies it when preparation
refuses before the executable runs.
Outputs
| Effect | Produced when |
|---|---|
| Main's advance and suite result on the branch | Relative path, Step 1a |
| Audit findings joined on the instance branch | Both topologies, Step 1b |
| Whole-folder seal, acceptance or verbatim deferrals, existing PM item Sealed, clean new branch commit | Caller action invoked by the executable |
| Delivery merges and proved delivery disk/registration/ref absence | Executable complete result |
| Joined audit cleanup | Proved delivery success only, Step 4 |
| Existing-folder halt report, raw terminal observations and read-only recovery evidence | Any halt; delivery halts perform no automatic recovery or audit cleanup |
Critical Rules
- PM posture only; no seal without the newest developer closing marker or over an unreal deferral.
- Resolve the complete development-path contract once. Both topologies use the same tool invocation.
- Keep relative advance/suite policy and both-topology audit joins before invocation. The suite goes
through the gate tool, and its printed
Environment:line is carried in the bookkeeping to the seal's record; a refusal or an unreachable environment is a halt, never a pass. - Keep the builder, inputs, action and its dependencies outside delivery cleanup. Execute from a stable absolute directory; materialize the adopted action once before its source can disappear.
- Keep supplied runtime attribution, optional absence, role-specific values and ordered entries.
- Caller bookkeeping moves the whole carving before editing the destination; it commits only on the instance branch and emits its actual seal SHA. It performs no delivery merge or cleanup.
- Delivery merges and delivery-worktree/ref removal belong exclusively to the executable.
- Read the complete terminal block together with process exit. Keep every public halt code distinct and preserve raw unproved results and unavailable evidence.
- Delivery halt means no abort, rollback, retry, recovery teardown or audit cleanup. Report partial landings without rewriting the tool verdict.
- Audit cleanup follows proved delivery success only. No post-delivery commit on
main. - Halt without prompting, write in an existing destination, and return the actual report path.
Runnable caller recipes
These are the executed subject, not illustrative pseudocode. Materialize caller-delivery as
specified in Step 2. It extracts and materializes caller-seal-action from caller_source.
The process and filesystem ports are supplied to their callers; production builds them only at the
entry point, and controlled-result tests supply recording fakes through those same parameters.
#!/usr/bin/env python3
"""Materialize a literal bookkeeping action and retain delivery observations outside cleanup."""
from __future__ import annotations
import json
import re
import subprocess
import sys
import tempfile
from dataclasses import dataclass
from enum import Enum
from pathlib import Path
__version__ = "1.0.0"
class State(str, Enum):
COMPLETE = "complete"
HALT = "halt"
class Role(str, Enum):
PRODUCT = "product"
INSTANCE = "instance"
class Halt(str, Enum):
PRECONDITION = "deliver-precondition-failed"
CONFLICT = "merge-conflict"
HOOK = "merge-hook-failed"
PARTIAL = "instance-merge-after-product"
TEARDOWN = "teardown-failed"
DELIVERY = "delivery-failed"
def terminal_result(observation: dict, roles: tuple[Role, ...]) -> dict:
"""Recognize only a complete public block for these roles and its matching process exit."""
delivered = ",".join(role.value for role in roles)
completes = {f"delivery: complete\ndelivered: {delivered}\ncleanup: complete\n": delivered}
if roles == (Role.PRODUCT, Role.INSTANCE):
completes["delivery: complete\ndelivered: instance\nempty: product\n"
"cleanup: complete\n"] = Role.INSTANCE.value
if observation["exit"] == 0 and observation["stdout"] in completes:
return {"state": State.COMPLETE, "delivered": completes[observation["stdout"]],
"cleanup": "complete", "code": None, "proved": True}
prefixes = ["none"] + [",".join(role.value for role in roles[:size])
for size in range(1, len(roles) + 1)]
if observation["exit"] == 1:
for code in Halt:
for prefix in prefixes:
block = (f"delivery: halt\nhalt: {code.value}\ndelivered: {prefix}\n"
"cleanup: incomplete\n")
if observation["stdout"] == block:
return {"state": State.HALT, "delivered": prefix, "cleanup": "incomplete",
"code": code, "proved": True}
return {"state": State.HALT, "delivered": "unproved", "cleanup": "unproved",
"code": Halt.DELIVERY, "proved": False}
class Files:
def __init__(self, root: Path) -> None:
self.root = root.resolve()
def read(self, path: Path) -> str:
return path.read_text(encoding="utf-8")
def write(self, path: Path, content: str) -> None:
path.write_text(content, encoding="utf-8")
def directory(self) -> Path:
return Path(tempfile.mkdtemp(prefix="seal-caller-", dir=self.root)).resolve()
def exists(self, path: Path) -> bool:
return path.exists()
def is_directory(self, path: Path) -> bool:
return path.is_dir()
class Processes:
def run(self, argv: list[str], *, cwd: Path) -> subprocess.CompletedProcess[str]:
return subprocess.run(argv, cwd=cwd, capture_output=True, text=True)
@dataclass(frozen=True)
class Prepared:
directory: Path
configuration: dict
argv: list[str]
before: dict
class Caller:
def __init__(self, *, files: Files, processes: Processes) -> None:
self.files = files
self.processes = processes
def _json(self, path: Path, value: object) -> None:
self.files.write(path, json.dumps(value, indent=2, ensure_ascii=False) + "\n")
def _observe(self, repo: Path, *arguments: str) -> dict:
argv = ["git", "-C", str(repo), *arguments]
try:
result = self.processes.run(argv, cwd=self.files.root)
absence_check = arguments[:3] == ("show-ref", "--verify", "--quiet") or (
arguments == ("rev-parse", "-q", "--verify", "MERGE_HEAD")
)
available = result.returncode == 0 or (absence_check and result.returncode == 1)
return {"argv": argv, "available": available,
"exit": result.returncode, "stdout": result.stdout, "stderr": result.stderr}
except OSError as error:
return {"argv": argv, "available": False, "exit": None,
"stdout": "", "stderr": str(error)}
def _snapshot(self, contract: dict) -> dict:
records = {}
for role in ("product", "instance") if contract.get("kind") == "fixed" else ("instance",):
if not all(isinstance(contract.get(f"{role}_{name}"), str)
for name in ("repo", "worktree")):
continue
repo, worktree = Path(contract[f"{role}_repo"]), Path(contract[f"{role}_worktree"])
try:
disk = {"available": True, "present": self.files.exists(worktree)}
except OSError as error:
disk = {"available": False, "present": None, "diagnostics": str(error)}
records[role] = {
"repo": str(repo), "worktree": str(worktree), "disk": disk,
"main_head": self._observe(repo, "rev-parse", "--verify", "refs/heads/main"),
"change_head": self._observe(worktree, "rev-parse", "--verify", "HEAD"),
"main_parents": self._observe(repo, "show", "-s", "--format=%P", "main"),
"registrations": self._observe(repo, "worktree", "list", "--porcelain"),
"change_ref": self._observe(repo, "show-ref", "--verify", "--quiet",
"refs/heads/" + str(contract.get("key", ""))),
"primary_merge_head": self._observe(
repo, "rev-parse", "-q", "--verify", "MERGE_HEAD"
),
"primary_unmerged": self._observe(repo, "ls-files", "-u"),
"change_merge_head": self._observe(
worktree, "rev-parse", "-q", "--verify", "MERGE_HEAD"
),
"change_unmerged": self._observe(worktree, "ls-files", "-u"),
}
return records
def prepare(self, path: Path) -> Prepared:
"""Copy public inputs unchanged; extract the adopted action once without executing it."""
configuration = json.loads(self.files.read(path.resolve()))
contract = configuration["contract"]
directory = self.files.directory()
core = Path(configuration["core"]).resolve()
builder = core / "tools/commit_builder.py"
delivery = core / "tools/deliver_change.py"
for name in ("instance_worktree", "product_worktree"):
value = contract.get(name)
if isinstance(value, str):
removed = Path(value).resolve()
if any(location.is_relative_to(removed) for location in (directory, core)):
raise ValueError("caller inputs, executable and builder must survive cleanup")
# The seal commits after the product merge, so a directional the builder would refuse
# is refused here, while nothing has landed.
check = self.processes.run(
[sys.executable, str(builder), "--repo", contract["instance_worktree"],
"check-directional", "--directional", configuration["bookkeeping"]["directional"]],
cwd=directory,
)
if check.returncode:
raise ValueError(f"bookkeeping.directional refused: {check.stderr.strip()}")
for name in ("contract", "merges"):
self._json(directory / f"{name}.json", configuration[name])
source = self.files.read(Path(configuration["caller_source"]))
matches = re.findall(r"<!-- caller-seal-action -->\n```python\n(.*?)\n```", source, re.S)
if len(matches) != 1:
raise ValueError("adopted source must contain exactly one caller-seal-action block")
self.files.write(directory / "action.py", matches[0] + "\n")
self._json(directory / "action-config.json", {
"contract": contract, "bookkeeping": configuration["bookkeeping"],
"builder": str(builder), "evidence": str(directory / "seal-evidence.json"),
})
self._json(directory / "seal.json", {"argv": [
sys.executable, str(directory / "action.py"), str(directory / "action-config.json"),
]})
argv = [sys.executable, str(delivery), "--contract", str(directory / "contract.json"),
"--merges", str(directory / "merges.json"),
"--seal-action", str(directory / "seal.json"), "--builder", str(builder)]
self._json(directory / "invocation.json", {"argv": argv, "cwd": str(directory),
"caller_source": configuration["caller_source"]})
before = self._snapshot(contract)
self._json(directory / "before.json", before)
return Prepared(directory, configuration, argv, before)
def _seal_evidence(self, prepared: Prepared) -> dict:
path = prepared.directory / "seal-evidence.json"
try:
if not self.files.exists(path):
return {"available": False, "diagnostics": "no action evidence recorded"}
return {"available": True, **json.loads(self.files.read(path))}
except (OSError, ValueError, TypeError) as error:
return {"available": False, "diagnostics": str(error)}
def _recovery(self, prepared: Prepared, evidence: dict) -> dict:
records = self._snapshot(prepared.configuration["contract"])
for role, record in records.items():
prior = prepared.before.get(role, {})
old = prior.get("main_head", {})
intended = (prior.get("change_head", {}).get("stdout", "").strip()
if role == "product" else evidence.get("seal_sha"))
head, parents = record["main_head"], record["main_parents"]
merge_head = record["primary_merge_head"]
known = (old.get("exit") == 0 and head["exit"] == 0 and parents["exit"] == 0
and bool(intended) and merge_head["available"])
record["intended_landing"] = (
head["stdout"].strip() != old.get("stdout", "").strip()
and parents["stdout"].split() == [old["stdout"].strip(), intended]
and merge_head["exit"] == 1
) if known else None
record["intended_tip"] = intended
return records
def _report_directory(self, contract: dict) -> Path:
worktree = Path(contract["instance_worktree"])
root = Path("system/projects") / contract["project"] / "journey/tobiishi"
key = contract["key"]
if self.files.is_directory(worktree):
candidates = [worktree / root / key, worktree / root / "trail" / key]
else:
candidates = [Path(contract["instance_repo"]) / root / key]
candidates.append(Path(contract["instance_repo"]) / root)
for candidate in candidates:
if self.files.is_directory(candidate):
return candidate
raise OSError(
"no existing change folder or primary tobiishi root can hold halt_report.md"
)
def _report(self, contract: dict, outcome: dict) -> None:
path = self._report_directory(contract) / "halt_report.md"
outcome["halt_report"] = str(path)
content = (f"# Delivery halt — {outcome['halt_reason']}\n\n"
f"Delivered: {outcome['delivered']}\nCleanup: {outcome['cleanup']}\n"
f"Terminal landing proved: {outcome['landing_proved']}\n\n"
"The captured terminal verdict is retained. Recovery observations below are "
"read-only; unavailable observations carry their diagnostics.\n\n```json\n")
content += json.dumps(outcome, indent=2, ensure_ascii=False) + "\n```\n"
self.files.write(path, content)
def refuse(self, path: Path, error: Exception) -> dict:
"""Report a preparation refusal where a delivery halt would go; no tool ran."""
contract = json.loads(self.files.read(path.resolve()))["contract"]
outcome = {"step": "tobiishi-seal", "key": contract.get("key"), "artifact": None,
"status": "halt", "halt_reason": Halt.PRECONDITION.value,
"observations": {"exit": None, "stdout": "",
"stderr": f"preparation refused before delivery: {error}"},
"delivered": "none", "cleanup": "incomplete",
"audit_cleanup_eligible": False, "landing_proved": False}
self._report(contract, outcome)
return outcome
def deliver(self, prepared: Prepared) -> dict:
"""Invoke once; accept proved terminal results and perform no recovery effects on halt."""
try:
result = self.processes.run(prepared.argv, cwd=prepared.directory)
observation = {
"exit": result.returncode, "stdout": result.stdout, "stderr": result.stderr,
}
except OSError as error:
observation = {"exit": None, "stdout": "", "stderr": str(error)}
contract = prepared.configuration["contract"]
roles = (
(Role.PRODUCT, Role.INSTANCE) if contract.get("kind") == "fixed" else (Role.INSTANCE,)
)
terminal = terminal_result(observation, roles)
success = terminal["state"] == State.COMPLETE
outcome = {"step": "tobiishi-seal", "key": contract.get("key"), "artifact": None,
"status": "ok" if success else "halt", "observations": observation,
"delivered": terminal["delivered"], "cleanup": terminal["cleanup"],
"audit_cleanup_eligible": success,
"landing_proved": terminal["proved"]}
evidence = self._seal_evidence(prepared)
outcome["seal_evidence"] = evidence
if not success:
outcome["halt_reason"] = terminal["code"].value
outcome["recorded_before"] = prepared.before
outcome["recovery"] = self._recovery(prepared, evidence)
self._report(prepared.configuration["contract"], outcome)
self._json(prepared.directory / "result.json", outcome)
return outcome
def main(argv: list[str] | None = None) -> int:
arguments = sys.argv[1:] if argv is None else argv
if len(arguments) != 1:
print("error: supply the caller configuration JSON path", file=sys.stderr)
return 1
caller = Caller(files=Files(Path(tempfile.gettempdir())), processes=Processes())
try:
prepared = caller.prepare(Path(arguments[0]))
except (OSError, ValueError, KeyError, TypeError) as error:
try:
outcome = caller.refuse(Path(arguments[0]), error)
except (OSError, ValueError, KeyError, TypeError, AttributeError) as unplaced:
print(f"error: {error}; no halt report placed: {unplaced}", file=sys.stderr)
return 1
print(json.dumps(outcome, ensure_ascii=False))
print(f"error: {outcome['halt_reason']}; inspect {outcome['halt_report']}",
file=sys.stderr)
return 1
try:
outcome = caller.deliver(prepared)
print(json.dumps(outcome, ensure_ascii=False))
if outcome["status"] != "ok":
print(f"error: {outcome['halt_reason']}; inspect {outcome['halt_report']}",
file=sys.stderr)
return 1
return 0
except (OSError, ValueError, KeyError, TypeError) as error:
print(f"error: {error}", file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())
<!-- caller-seal-action -->
#!/usr/bin/env python3
"""Caller-owned bookkeeping; delivery and recovery remain outside this action."""
from __future__ import annotations
import json
import re
import subprocess
import sys
from pathlib import Path
__version__ = "1.0.0"
class Processes:
def run(self, argv: list[str], *, cwd: Path) -> subprocess.CompletedProcess[str]:
return subprocess.run(argv, cwd=cwd, capture_output=True, text=True)
class Files:
def read(self, path: Path) -> str:
return path.read_text(encoding="utf-8")
def write(self, path: Path, content: str) -> None:
path.write_text(content, encoding="utf-8")
def exists(self, path: Path) -> bool:
return path.is_file()
def ensure_directory(self, path: Path) -> None:
path.mkdir(parents=True, exist_ok=True)
class SealAction:
def __init__(self, *, files: Files, processes: Processes) -> None:
self.files = files
self.processes = processes
def _run(self, worktree: Path, argv: list[str]) -> str:
result = self.processes.run(argv, cwd=worktree)
if result.returncode:
raise OSError(f"{argv!r} exited {result.returncode}: {result.stderr}")
return result.stdout
def seal(self, configuration: Path) -> dict:
supplied = json.loads(self.files.read(configuration))
contract, bookkeeping = supplied["contract"], supplied["bookkeeping"]
worktree = Path(contract["instance_worktree"])
project, key = contract["project"], contract["key"]
environment = bookkeeping.get("environment_line")
if not isinstance(environment, str) or not environment.strip():
raise ValueError("bookkeeping.environment_line is required: the seal's suite run "
"printed no environment line")
root = Path("system/projects") / project / "journey/tobiishi"
source, destination = root / key, root / "trail" / key
self.files.ensure_directory(worktree / destination.parent)
self._run(worktree, ["git", "-C", str(worktree), "mv", str(source), str(destination)])
request = destination / "change_request.md"
deferred = bookkeeping["deferred"]
verdict = "sealed with deferrals" if deferred else "accepted"
record = (f"\n**Sealed:** {bookkeeping['date']} — {verdict}; "
"merged to `main` via `--no-ff`.\n")
record += environment.rstrip("\n") + "\n"
record += "".join(line + "\n" for line in deferred)
self.files.write(worktree / request, self.files.read(worktree / request) + record)
edited = [str(request)]
context = Path("system/projects") / project / "contexts/CONTEXT_PM.md"
if self.files.exists(worktree / context):
content = self.files.read(worktree / context)
heading = "## Current change — PM view"
position = content.find(heading)
if position < 0:
raise ValueError("existing PM context has no current change item")
end = content.find("\n## ", position + len(heading))
end = len(content) if end < 0 else end
item = content[position:end]
updated, count = re.subn(r"(?m)^\*\*Status:\*\* Open$", "**Status:** Sealed", item)
if count != 1:
raise ValueError("existing current change must have exactly one Open status")
self.files.write(worktree / context, content[:position] + updated + content[end:])
edited.append(str(context))
argv = [sys.executable, supplied["builder"], "--repo", str(worktree), "commit",
"--staged", str(source), str(destination), "--add", *edited,
"--subject", f"seal: {key} — {verdict}, moved to trail/", "--actor", "pm",
"--directional", bookkeeping["directional"]]
for name in ("surface", "co_authored_by", "reasoning_effort", "claude_session"):
if name in bookkeeping:
argv.extend(["--" + name.replace("_", "-"), bookkeeping[name]])
self._run(worktree, argv)
seal_sha = self._run(worktree, ["git", "-C", str(worktree), "rev-parse", "HEAD"]).strip()
if self._run(worktree, ["git", "-C", str(worktree), "status", "--porcelain"]):
raise OSError("seal commit left an unclean worktree")
evidence = {"seal_sha": seal_sha}
self.files.write(Path(supplied["evidence"]), json.dumps(evidence, indent=2) + "\n")
return evidence
def main(argv: list[str] | None = None) -> int:
arguments = sys.argv[1:] if argv is None else argv
if len(arguments) != 1:
print("error: supply the action configuration JSON path", file=sys.stderr)
return 1
try:
action = SealAction(files=Files(), processes=Processes())
print(json.dumps(action.seal(Path(arguments[0]))))
return 0
except (OSError, ValueError, KeyError, TypeError) as error:
print(f"error: {error}", file=sys.stderr)
return 1
if __name__ == "__main__":
sys.exit(main())