[ai] Write out the model name finishing the task.

This commit is contained in:
John Preston
2026-08-14 17:17:03 +04:00
parent 5e7d11593b
commit d4319b82ff
4 changed files with 40 additions and 1 deletions

View File

@@ -744,9 +744,19 @@ Publish final AI state only after the Telegram commit and result are final:
```bash
python3 SOURCE_ROOT/.agents/skills/process-inbox/scripts/workspace.py \
finish --source-root SOURCE_ROOT --task TASK_ID \
--status approved|blocked
--status approved|blocked --model MODEL_SHORT_NAME
```
`--model` is required and records which model finished the task, into the
`model` field of its `state.yaml`. Self-report the model you are actually
running as, as a lowercase short name — `claude-opus-5`, `claude-fable-5`,
`gpt-5.6-sol`, `glm-5.3`, `kimi-k3`, `grok-4.6`. Report the model running the
performer that reaches this boundary, not a leaf's model and not whichever model
happened to start the task: the field answers "who finished it", so a task
resumed by a different model after an interruption records the model that
actually completed it. Never guess or copy the value from another task; if you
cannot tell what you are, say so and stop rather than recording a wrong name.
The helper verifies a clean source checkout, local task refs, current `HEAD`,
and the retained implementation's exact three-line commit message. It commits
all task-scoped local artifacts and final state as `Approve <TASK_ID>` or the

View File

@@ -200,6 +200,10 @@ phase: null
inbox_receipt: receipts/YYYY/MM/DD/<receipt>.md
```
Do not write a `model` field. It records which model finished the task, so only
`finish` writes it, at the canonical `Approve` or `Block` boundary; a task
carrying one before it is claimed is malformed.
Use a project slug instead of `null` when routed to a project. Use a YAML list
of task identifiers for dependencies. Dependencies record code lineage as well
as readiness: keep an approved source task in `depends_on` when the new task's

View File

@@ -22,6 +22,7 @@ SHA256_PATTERN = re.compile(r"[0-9a-f]{64}")
VALID_STATUSES = {"todo", "in-progress", "approved", "blocked"}
DEFAULT_TASK_TYPE = "implement"
VALID_TASK_TYPES = {DEFAULT_TASK_TYPE, "verify", "minimal"}
MODEL_PATTERN = re.compile(r"[a-z0-9][a-z0-9.-]{0,39}")
VALID_FINDINGS = {"confirmed", "deviation", "inconclusive"}
CONSOLIDATION_PENDING = "work/consolidation-pending.md"
CONSOLIDATION_COMPLETE = "work/consolidation-complete.md"
@@ -43,6 +44,7 @@ STATE_FIELD_ORDER = [
"claim_order",
"lease_until",
"phase",
"model",
"inbox_receipt",
]
PORTABLE_GOLDEN = "test_TelegramForcePortable"
@@ -383,6 +385,9 @@ def load_state(root, path):
order = int(order)
except ValueError as error:
raise WorkspaceError(f"Invalid claim_order in {path}: {order!r}") from error
model = parse_scalar(values.get("model", "null"))
if model is not None and not MODEL_PATTERN.fullmatch(str(model)):
raise WorkspaceError(f"Invalid model name {model!r} in {path}")
task_id = task_id_for_state(root, path)
task_file = path.with_name("task.md")
title = task_id.rsplit("/", 1)[-1]
@@ -404,6 +409,7 @@ def load_state(root, path):
"claim_order": order,
"lease_until": parse_scalar(values.get("lease_until", "null")),
"phase": parse_scalar(values.get("phase", "null")),
"model": model,
"inbox_receipt": parse_scalar(values.get("inbox_receipt", "null")),
"state_path": str(path),
}
@@ -2658,6 +2664,13 @@ def validate_verify_result(lines, result_path, approved):
def command_finish(args):
model = args.model.strip()
if not MODEL_PATTERN.fullmatch(model):
raise WorkspaceError(
f"Invalid --model short name {args.model!r}. Report the model you are "
"actually running as, lowercase, for example claude-opus-5, "
"gpt-5.6-sol, glm-5.3."
)
config, slot = task_action_config(args, allow_project=True)
ensure_clean(Path(config["source_root"]), "Telegram source checkout")
kind = task_type(slot, args.task)
@@ -2689,6 +2702,7 @@ def command_finish(args):
"status": args.status,
"phase": "complete" if args.status == "approved" else "blocked",
"lease_until": None,
"model": model,
})
verb = "Approve" if args.status == "approved" else "Block"
paths = [task_relative_dir(args.task)]
@@ -3707,6 +3721,7 @@ def parse_args():
add_common_arguments(finish)
finish.add_argument("--task", required=True)
finish.add_argument("--status", choices=("approved", "blocked"), required=True)
finish.add_argument("--model", required=True)
finish.set_defaults(handler=command_finish)
publish = subparsers.add_parser("publish")

View File

@@ -55,6 +55,16 @@ This file adapts harness mechanics and removes unnecessary text normalization.
leaf rules (no delegation, no commits, progress and reply contracts); an
adapter read there is wasted context.
## Model self-reporting
`workspace.py finish` requires `--model` and records it in the task's
`state.yaml`. In Claude Code, report the Claude model actually running the
performer, lowercase, without a context-window or date suffix:
`claude-opus-5`, `claude-sonnet-5`, `claude-fable-5`, `claude-haiku-4-5`. A
model id such as `claude-opus-5[1m]` becomes `claude-opus-5`. Take the value
from the model you are running as, never from the task, the queue, or another
checkout's records.
## Text handling
Do not run a dedicated line-ending or BOM check, normalization, repair, phase,