@arbiterforge/ca-pi 0.18.4 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -1
- package/plugins/ca-pi/CHANGELOG.md +54 -0
- package/plugins/ca-pi/COMMANDS.md +2 -2
- package/plugins/ca-pi/SKILLS.md +5 -5
- package/plugins/ca-pi/SPRINT.md +43 -230
- package/plugins/ca-pi/agents/backend-author.md +5 -5
- package/plugins/ca-pi/agents/checkpoint-aggregator.md +7 -0
- package/plugins/ca-pi/agents/frontend-author.md +5 -5
- package/plugins/ca-pi/agents/infra-author.md +6 -7
- package/plugins/ca-pi/agents/scout.md +25 -24
- package/plugins/ca-pi/arbiter.md +6 -4
- package/plugins/ca-pi/generated/command-catalog.json +5 -5
- package/plugins/ca-pi/helpers/artifacts/ca-artifact-darwin-amd64 +0 -0
- package/plugins/ca-pi/helpers/artifacts/ca-artifact-darwin-arm64 +0 -0
- package/plugins/ca-pi/helpers/artifacts/ca-artifact-linux-amd64 +0 -0
- package/plugins/ca-pi/helpers/artifacts/ca-artifact-linux-arm64 +0 -0
- package/plugins/ca-pi/helpers/artifacts/ca-artifact-windows-amd64.exe +0 -0
- package/plugins/ca-pi/helpers/artifacts/ca-artifact-windows-arm64.exe +0 -0
- package/plugins/ca-pi/helpers/artifacts/release.json +37 -37
- package/plugins/ca-pi/hooks/_approvallib.py +41 -0
- package/plugins/ca-pi/hooks/_artifactlib.py +61 -115
- package/plugins/ca-pi/hooks/_artifactpromptlib.py +12 -12
- package/plugins/ca-pi/hooks/_bashguardlib.py +66 -4
- package/plugins/ca-pi/hooks/_contextselectlib.py +38 -35
- package/plugins/ca-pi/hooks/_contextwriterlib.py +114 -0
- package/plugins/ca-pi/hooks/_host.py +1 -1
- package/plugins/ca-pi/hooks/_prerequisitelib.py +35 -0
- package/plugins/ca-pi/hooks/_provenancelib.py +107 -1
- package/plugins/ca-pi/hooks/_readinjectlib.py +21 -7
- package/plugins/ca-pi/hooks/_reconciliationlib.py +167 -2
- package/plugins/ca-pi/hooks/_releaselib.py +6 -6
- package/plugins/ca-pi/hooks/_sprintapprovallib.py +52 -2
- package/plugins/ca-pi/hooks/artifact-reconcile.py +13 -4
- package/plugins/ca-pi/hooks/hostapi.py +1 -1
- package/plugins/ca-pi/hooks/init-codearbiter.py +34 -5
- package/plugins/ca-pi/includes/artifacts-attestation.md +434 -0
- package/plugins/ca-pi/includes/artifacts.md +133 -492
- package/plugins/ca-pi/includes/author-tdd-workflow.md +32 -21
- package/plugins/ca-pi/includes/fresh-verification.md +14 -12
- package/plugins/ca-pi/includes/harvest.md +3 -3
- package/plugins/ca-pi/includes/reference-map.md +1 -1
- package/plugins/ca-pi/includes/routing-table.md +3 -3
- package/plugins/ca-pi/includes/sprint-authority.md +6 -1
- package/plugins/ca-pi/includes/verification-boundary.md +11 -2
- package/plugins/ca-pi/package.json +1 -1
- package/plugins/ca-pi/routines/INDEX.md +8 -8
- package/plugins/ca-pi/routines/brainstorming/SKILL.md +65 -202
- package/plugins/ca-pi/routines/commit-gate/SKILL.md +60 -25
- package/plugins/ca-pi/routines/context-check/SKILL.md +89 -53
- package/plugins/ca-pi/routines/context-creation/SKILL.md +114 -220
- package/plugins/ca-pi/routines/decompose/SKILL.md +139 -167
- package/plugins/ca-pi/routines/executing-plans/SKILL.md +38 -136
- package/plugins/ca-pi/routines/finishing-a-development-branch/SKILL.md +45 -55
- package/plugins/ca-pi/routines/refactor/SKILL.md +27 -12
- package/plugins/ca-pi/routines/release/SKILL.md +1 -1
- package/plugins/ca-pi/routines/subagent-driven-development/SKILL.md +80 -230
- package/plugins/ca-pi/routines/tdd/SKILL.md +115 -228
- package/plugins/ca-pi/routines/writing-plans/SKILL.md +104 -174
- package/plugins/ca-pi/skills/ca-audit/SKILL.md +2 -2
- package/plugins/ca-pi/skills/ca-checkpoint/SKILL.md +1 -1
- package/plugins/ca-pi/skills/ca-commit/SKILL.md +60 -25
- package/plugins/ca-pi/skills/ca-context-check/SKILL.md +89 -53
- package/plugins/ca-pi/skills/ca-create-context/SKILL.md +114 -220
- package/plugins/ca-pi/skills/ca-decompose/SKILL.md +139 -167
- package/plugins/ca-pi/skills/ca-feature/SKILL.md +21 -38
- package/plugins/ca-pi/skills/ca-fix/SKILL.md +15 -9
- package/plugins/ca-pi/skills/ca-init/SKILL.md +18 -40
- package/plugins/ca-pi/skills/ca-pr/SKILL.md +45 -55
- package/plugins/ca-pi/skills/ca-refactor/SKILL.md +27 -12
- package/plugins/ca-pi/skills/ca-release/SKILL.md +1 -1
- package/plugins/ca-pi/skills/ca-review/SKILL.md +5 -8
- package/plugins/ca-pi/skills/ca-sprint/SKILL.md +11 -25
- package/plugins/ca-pi/skills/ca-status/SKILL.md +15 -6
|
@@ -10,6 +10,7 @@ from __future__ import annotations
|
|
|
10
10
|
|
|
11
11
|
import hashlib
|
|
12
12
|
from functools import wraps
|
|
13
|
+
from itertools import islice
|
|
13
14
|
import json
|
|
14
15
|
import os
|
|
15
16
|
from pathlib import Path
|
|
@@ -278,12 +279,61 @@ def resume(root: str | Path, client: Any, plan_id: str) -> dict[str, Any]:
|
|
|
278
279
|
"reapproval_required": True}
|
|
279
280
|
|
|
280
281
|
|
|
282
|
+
|
|
283
|
+
def _published_reply_observed(root: Path, pending: dict[str, Any]) -> bool:
|
|
284
|
+
"""Bounded crash-gap check for a reply published before pair submission.
|
|
285
|
+
|
|
286
|
+
The old producer publishes a content-addressed observation *before* its
|
|
287
|
+
durable submission record. A crash between these writes is not an
|
|
288
|
+
unanswered request. This rare cleanup-only read uses existing bounded,
|
|
289
|
+
no-follow readers; it never scans source files or validates execution.
|
|
290
|
+
"""
|
|
291
|
+
relative = Path(".codearbiter/.artifacts/observations")
|
|
292
|
+
try:
|
|
293
|
+
directory = approval._safe_directory(root, relative, create=False)
|
|
294
|
+
except FileNotFoundError:
|
|
295
|
+
return False
|
|
296
|
+
expected_contexts = {
|
|
297
|
+
pending["spec_context"]["context_ref"],
|
|
298
|
+
pending["plan_context"]["context_ref"],
|
|
299
|
+
}
|
|
300
|
+
# Stop enumeration at the first over-limit entry; do not materialize an
|
|
301
|
+
# unbounded directory just to decide a rare legacy cleanup is uncertain.
|
|
302
|
+
entries = list(islice(directory.iterdir(), 513))
|
|
303
|
+
if len(entries) > 512:
|
|
304
|
+
raise approval.ApprovalError(
|
|
305
|
+
"SUBMISSION_RECOVERY_REQUIRED",
|
|
306
|
+
"too many historical observations to prove the paired prompt was unanswered",
|
|
307
|
+
)
|
|
308
|
+
for entry in entries:
|
|
309
|
+
if not approval.SHA256_RE.fullmatch(entry.stem) or entry.suffix != ".json":
|
|
310
|
+
# An incomplete publisher/temp or an unexpected file in immutable
|
|
311
|
+
# observation storage makes a zero-observed-reply claim unsafe.
|
|
312
|
+
raise approval.ApprovalError(
|
|
313
|
+
"SUBMISSION_RECOVERY_REQUIRED",
|
|
314
|
+
"observation storage contains an unrecognized entry; inspect before retiring",
|
|
315
|
+
)
|
|
316
|
+
observed = _read(root, relative / entry.name)
|
|
317
|
+
if _hash(observed) != entry.stem:
|
|
318
|
+
raise approval.ApprovalError(
|
|
319
|
+
"SUBMISSION_RECOVERY_REQUIRED",
|
|
320
|
+
"stored observation changed; do not discard the pending decision",
|
|
321
|
+
)
|
|
322
|
+
if (observed.get("format") == "codearbiter.observation/0.2.0"
|
|
323
|
+
and observed.get("kind") == "approval"
|
|
324
|
+
and observed.get("producer_profile") == "host-user-pair/0.1.0"
|
|
325
|
+
and observed.get("context_ref") in expected_contexts
|
|
326
|
+
and isinstance(observed.get("producer_result"), dict)
|
|
327
|
+
and observed["producer_result"].get("prompt_sha256") == pending["prompt_sha256"]):
|
|
328
|
+
return True
|
|
329
|
+
return False
|
|
330
|
+
|
|
281
331
|
@_serialized
|
|
282
332
|
def cancel(root: Path, pending: dict[str, Any]) -> dict[str, Any]:
|
|
283
333
|
if approval._load_pending(root) != pending:
|
|
284
334
|
raise approval.ApprovalError("PENDING_APPROVAL_MISMATCH", "pending pair changed before cancellation")
|
|
285
|
-
if _submission(root, pending) is not None:
|
|
286
|
-
raise approval.ApprovalError("SUBMITTED_APPROVAL", "recover the observed
|
|
335
|
+
if _submission(root, pending) is not None or _published_reply_observed(root, pending):
|
|
336
|
+
raise approval.ApprovalError("SUBMITTED_APPROVAL", "recover the observed user reply before cancelling its pending state")
|
|
287
337
|
_artifactpromptlib.unregister(root, "approval", pending["artifact_id"])
|
|
288
338
|
(root / approval.PENDING).unlink()
|
|
289
339
|
return {"artifact_id": pending["artifact_id"], "cancelled": True}
|
|
@@ -23,26 +23,35 @@ def main(argv=None):
|
|
|
23
23
|
parser.add_argument("--assessment")
|
|
24
24
|
parser.add_argument("--cancel", action="store_true", help="cancel an armed request before its mutation attempt")
|
|
25
25
|
parser.add_argument("--cancel-orphan", action="store_true", help="cancel a pending request whose prompt route was never registered")
|
|
26
|
-
parser.add_argument("--recover", action="store_true", help="replay an in-flight mutation before clearing its marker")
|
|
26
|
+
parser.add_argument("--recover", action="store_true", help="recover an observed reply or replay an in-flight mutation before clearing its marker")
|
|
27
|
+
parser.add_argument("--supersede", action="store_true", help="retire a verified unanswered legacy prompt without a code (never discards a durable attempt)")
|
|
27
28
|
parser.add_argument("--prompt", help="exact armed prompt required for --cancel")
|
|
28
29
|
args = parser.parse_args(argv)
|
|
29
30
|
root = Path(args.root).resolve(strict=True)
|
|
30
31
|
if args.cancel_orphan:
|
|
31
|
-
if args.cancel or args.recover or args.prompt or args.target_id or args.operation or args.target_state or args.assessment:
|
|
32
|
+
if args.cancel or args.recover or args.supersede or args.prompt or args.target_id or args.operation or args.target_state or args.assessment:
|
|
32
33
|
parser.error("--cancel-orphan excludes arming and exact-prompt cancellation fields")
|
|
33
34
|
print(json.dumps(_reconciliationlib.cancel_orphaned_reconciliation(root, args.artifact_id), sort_keys=True))
|
|
34
35
|
return 0
|
|
35
36
|
if args.cancel:
|
|
36
|
-
if args.recover or args.prompt is None or args.target_id or args.operation or args.target_state or args.assessment:
|
|
37
|
+
if args.recover or args.supersede or args.prompt is None or args.target_id or args.operation or args.target_state or args.assessment:
|
|
37
38
|
parser.error("--cancel requires --prompt and excludes arming fields")
|
|
38
39
|
print(json.dumps(_reconciliationlib.cancel_reconciliation(root, args.artifact_id, args.prompt), sort_keys=True))
|
|
39
40
|
return 0
|
|
40
41
|
if args.recover:
|
|
41
|
-
if args.prompt or args.target_id or args.operation or args.target_state or args.assessment:
|
|
42
|
+
if args.supersede or args.prompt or args.target_id or args.operation or args.target_state or args.assessment:
|
|
42
43
|
parser.error("--recover excludes arming and cancellation fields")
|
|
43
44
|
client = _artifactlib.ArtifactClient(root, _artifactlib.helper_installation(__file__))
|
|
44
45
|
print(json.dumps(_reconciliationlib.recover_reconciliation(root, client, args.artifact_id), sort_keys=True))
|
|
45
46
|
return 0
|
|
47
|
+
if args.supersede:
|
|
48
|
+
if (args.prompt is not None or args.target_id or args.operation or
|
|
49
|
+
args.target_state or args.assessment or not args.reason.strip()):
|
|
50
|
+
parser.error("--supersede requires --reason and excludes all arming and prompt fields")
|
|
51
|
+
print(json.dumps(_reconciliationlib.supersede_unanswered_reconciliation(
|
|
52
|
+
root, args.artifact_id, reason=args.reason,
|
|
53
|
+
), sort_keys=True))
|
|
54
|
+
return 0
|
|
46
55
|
if not args.target_id or not args.operation or not args.assessment or args.prompt is not None:
|
|
47
56
|
parser.error("arming requires --target-id, --operation, and --assessment, without --prompt")
|
|
48
57
|
client = _artifactlib.ArtifactClient(root, _artifactlib.helper_installation(__file__))
|
|
@@ -313,7 +313,7 @@ class Host:
|
|
|
313
313
|
|
|
314
314
|
name = "claude"
|
|
315
315
|
adapter_name = "ca"
|
|
316
|
-
adapter_version = "
|
|
316
|
+
adapter_version = "3.0.0"
|
|
317
317
|
|
|
318
318
|
# Update-notifier descriptor. Each independently versioned host overrides
|
|
319
319
|
# these three values in its per-plugin _host.py. Keeping the target,
|
|
@@ -222,27 +222,56 @@ def main(argv=None):
|
|
|
222
222
|
ap.add_argument("--passive", action="store_true", help=(
|
|
223
223
|
"inspect explicit root before host activation; no Git or network probes"))
|
|
224
224
|
ap.add_argument("--context-request", help=argparse.SUPPRESS)
|
|
225
|
+
ap.add_argument("--context-draft", help="preview an explicit whole-document context refresh from a UTF-8 draft")
|
|
226
|
+
ap.add_argument("--document", choices=tuple(_artifactlib._CONTEXT_TARGETS))
|
|
227
|
+
ap.add_argument("--source", action="append", default=[], help="repository-relative source supporting the draft; repeat as needed")
|
|
228
|
+
ap.add_argument("--context-apply", help="apply the exact context preview reference returned by the writer")
|
|
229
|
+
ap.add_argument("--context-finalize", action="store_true", help="preview initialization of the five populated context documents")
|
|
230
|
+
ap.add_argument("--preview-sha256", help="SHA-256 of the retained context preview to apply")
|
|
225
231
|
ap.add_argument("--repair-lock-exclusion", action="store_true", help=(
|
|
226
232
|
"repair only Git-local info/exclude for an existing scaffold; retain the "
|
|
227
233
|
"task-board OS lock, never rewrite project state (cannot combine with --check/--stage)"))
|
|
228
234
|
args = ap.parse_args(argv)
|
|
229
|
-
|
|
235
|
+
context_modes = sum(bool(value) for value in (
|
|
236
|
+
args.context_request, args.context_draft, args.context_apply, args.context_finalize))
|
|
237
|
+
if context_modes:
|
|
238
|
+
if context_modes != 1:
|
|
239
|
+
ap.error("select exactly one context request, draft, finalize, or apply")
|
|
230
240
|
if not args.root or args.stage is not None or args.check or args.passive or args.repair_lock_exclusion:
|
|
231
|
-
ap.error("
|
|
241
|
+
ap.error("context writing requires --root and excludes scaffold flags")
|
|
242
|
+
if (args.context_draft and (not args.document or not args.source or args.preview_sha256)
|
|
243
|
+
or args.context_apply and (not args.preview_sha256 or args.document or args.source)
|
|
244
|
+
or (args.context_request or args.context_finalize)
|
|
245
|
+
and (args.document or args.source or args.preview_sha256)):
|
|
246
|
+
ap.error("a draft requires --document and --source; apply requires only --preview-sha256")
|
|
232
247
|
try:
|
|
233
248
|
root = _artifactlib._trusted_directory(args.root, "UNSAFE_ROOT")
|
|
234
|
-
|
|
249
|
+
if args.context_draft:
|
|
250
|
+
from _contextwriterlib import prepare_document_preview
|
|
251
|
+
request = {"operation": "preview", "preview": prepare_document_preview(
|
|
252
|
+
root, args.document, args.context_draft, args.source)}
|
|
253
|
+
elif args.context_apply:
|
|
254
|
+
request = {"operation": "apply", "preview_ref": args.context_apply,
|
|
255
|
+
"preview_sha256": args.preview_sha256}
|
|
256
|
+
elif args.context_finalize:
|
|
257
|
+
from _contextwriterlib import prepare_finalization_preview
|
|
258
|
+
request = {"operation": "preview", "preview": prepare_finalization_preview(root)}
|
|
259
|
+
else:
|
|
260
|
+
request = _artifactlib.read_context_preview_request(root, args.context_request)
|
|
235
261
|
operation, payload = _artifactlib.validate_context_writer_request(request)
|
|
236
262
|
client = _artifactlib.context_writer_client(root)
|
|
237
263
|
if operation == "preview":
|
|
238
|
-
|
|
239
|
-
|
|
264
|
+
native_operation = ("context-finalize-preview" if payload["mode"] == "finalize"
|
|
265
|
+
else "context-preview")
|
|
266
|
+
result = client.call(native_operation, payload)
|
|
240
267
|
else:
|
|
241
268
|
result = client.call("context-apply", payload)
|
|
242
269
|
except RuntimeError as exc:
|
|
243
270
|
raise SystemExit(f"REFUSING context request: {exc}") from None
|
|
244
271
|
print(json.dumps(result, sort_keys=True))
|
|
245
272
|
return
|
|
273
|
+
if args.document or args.source or args.preview_sha256:
|
|
274
|
+
ap.error("context options require --context-draft or --context-apply")
|
|
246
275
|
if args.passive:
|
|
247
276
|
if not args.check or not args.root or args.stage is not None or args.repair_lock_exclusion:
|
|
248
277
|
ap.error("--passive requires --check and --root; excludes --stage and repair")
|
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
# Optional artifact attestation and historical recovery
|
|
2
|
+
|
|
3
|
+
**Cold reference, not a default workflow.** This file holds the existing
|
|
4
|
+
strict evidence-producer, receipt, older approval and recovery contracts for
|
|
5
|
+
explicit claims. Start ordinary authoring and implementation at
|
|
6
|
+
`<plugin-root>/includes/artifacts.md` instead. Do not turn these historical
|
|
7
|
+
protocols into a second permission system, a mandatory reviewer chain, or a
|
|
8
|
+
precondition for tracking progress. Real host permissions and user stops always
|
|
9
|
+
remain independently enforceable.
|
|
10
|
+
|
|
11
|
+
## Explicit verification-input and attestation contracts
|
|
12
|
+
|
|
13
|
+
The following input/receipt protocols describe explicit historical evidence and
|
|
14
|
+
attestation operations, not ordinary authoring or work-progress prerequisites.
|
|
15
|
+
Read only the portion needed for an actual evidence consumer. Do not create an
|
|
16
|
+
approval, method grant, or reviewer event just to satisfy a status transition.
|
|
17
|
+
Independent host permissions and genuinely requested user checkpoints still apply.
|
|
18
|
+
|
|
19
|
+
`verification_inputs.roots` and `exclude_directories` use exact slash-separated
|
|
20
|
+
repository-relative paths. Exclusions are directory paths, not globs or basename
|
|
21
|
+
matches and not relative to an individual root. For root `site`, a generated
|
|
22
|
+
dependency exclusion is `site/node_modules`, not `node_modules`. Review each
|
|
23
|
+
excluded output directory explicitly: the plan's roots and exclusions narrow
|
|
24
|
+
the ship set. In a git work tree `.gitignore` already keeps untracked ignored
|
|
25
|
+
files out of it; outside git nothing is filtered, so exclude generated
|
|
26
|
+
directories explicitly.
|
|
27
|
+
An exclusion outside all selected roots is rejected as ineffective. Overlapping
|
|
28
|
+
roots cannot reinclude an excluded directory. Roots must exist before execution;
|
|
29
|
+
use an existing containing directory for a planned new file so its creation
|
|
30
|
+
changes the snapshot. An oversized included file is reported with its path and
|
|
31
|
+
the 32 MiB per-file limit; revise the explicit policy rather than increasing the
|
|
32
|
+
limit or excluding source/test/governance inputs needed for the task.
|
|
33
|
+
|
|
34
|
+
Snapshot input identity is ship-set membership. In a git work tree the members
|
|
35
|
+
are the git index entries present on disk, plus untracked non-ignored files
|
|
36
|
+
within the plan's roots, minus its excludes. The one fixed exemption is
|
|
37
|
+
`.codearbiter/gate-events.log`: it is never counted, whatever the plan says, and
|
|
38
|
+
stays tracked as the audit trail; every other `.codearbiter` path still counts.
|
|
39
|
+
Outside git, membership is read from the filesystem (`membership: filesystem`);
|
|
40
|
+
the root must be the top level of its work tree, so a subdirectory root counts as
|
|
41
|
+
outside git. A failed git probe fails closed with `INPUT_MEMBERSHIP_UNAVAILABLE`. An unmerged
|
|
42
|
+
git index (conflicted paths) makes a snapshot fail closed until it is resolved.
|
|
43
|
+
The manifest format is `codearbiter.verification-inputs/0.2.0`; receipts bound to
|
|
44
|
+
a 0.1.0 manifest read as stale. Each evidence object is validated at most once
|
|
45
|
+
per operation and scans are bounded; raw reads are reused only up to a 64 MiB
|
|
46
|
+
per-operation retained-byte budget, past which an object can be read again, but
|
|
47
|
+
validation is never skipped.
|
|
48
|
+
|
|
49
|
+
The existing user, SMARTS, reviewer, or verification boundary must first persist
|
|
50
|
+
its actual policy-owned workflow event as canonical JSON in the reserved
|
|
51
|
+
content-addressed authority-source store. Interactive prompt approval currently
|
|
52
|
+
has production host seams in Claude Code and Codex. Pi 1.0.0 exposes an `input`
|
|
53
|
+
event, but codeArbiter has no Pi-native authority adapter. Under Pi do not arm this
|
|
54
|
+
adapter; the workflow remains blocked until that adapter is
|
|
55
|
+
implemented. On Claude Code or Codex, arm the exact current artifact immediately
|
|
56
|
+
before asking the approval question:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
python "<plugin-root>/hooks/_approvallib.py" arm --root "<project-root>" --artifact-id <artifact-id>
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
On Claude Code, when the result carries `ask_envelope`, pass it unchanged as the
|
|
63
|
+
entire AskUserQuestion input (no `answers`, `annotations` or other field): the
|
|
64
|
+
user approves by clicking its Approve option, and the host-observed selection is
|
|
65
|
+
applied with origin `claude:AskUserQuestion:<session>`. Otherwise, or if the
|
|
66
|
+
user prefers typing, present the returned `short_reply` (a four-character code
|
|
67
|
+
such as `approve K7MQ`); the full `reply` is also accepted, and you
|
|
68
|
+
require that exact reply: the hook tolerates surrounding whitespace, invisible characters, a code
|
|
69
|
+
fence or quotes and a closing period, but nothing else in the message, and it
|
|
70
|
+
reports a mismatch instead of staying silent. The `UserPromptSubmit` hook
|
|
71
|
+
rechecks the armed identity, records the full armed reply as the host-observed
|
|
72
|
+
policy-owned source, captures its receipt, and applies approval. An ordinary
|
|
73
|
+
`yes`, a changed artifact, a wrong or expired code, a reply inside other text, or
|
|
74
|
+
a model-authored event does not confer authority. On the next turn, verify `validate` at the `approved`
|
|
75
|
+
gate before continuing. If the user declines or the artifact changes, cancel the
|
|
76
|
+
exact pending request before arming another one:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
python "<plugin-root>/hooks/_approvallib.py" cancel --root "<project-root>" --artifact-id <artifact-id>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Cancellation records no authority. Other workflow authorities must use their
|
|
83
|
+
corresponding host-owned adapters. Legacy `capture` events remain readable for
|
|
84
|
+
inventory but are inspection-only: the engine refuses to wrap them in a current
|
|
85
|
+
receipt. Current authority requires `capture-observation` over a closed producer
|
|
86
|
+
profile and engine-issued evidence context; never create an authority source
|
|
87
|
+
merely to satisfy an artifact transition. `approve` checks the resulting receipt
|
|
88
|
+
against current ready content. A missing or changed receipt, captured event, or
|
|
89
|
+
still-present policy source returns `AUTHORITY_UNVERIFIED`. Receipt format 0.1.0
|
|
90
|
+
remains readable for inspection after upgrade but is not authority-bearing; the
|
|
91
|
+
applicable policy boundary must produce a fresh 0.2.0 attestation before further
|
|
92
|
+
approval, dispatch, acceptance, commit proof, or finalization. This is the
|
|
93
|
+
repository's cooperative same-user attestation model, not cryptographic identity
|
|
94
|
+
proof or protection from an unrestricted same-user filesystem writer.
|
|
95
|
+
|
|
96
|
+
For ordinary sequential planning, create a plan as `draft_preview`, consume existing criterion IDs,
|
|
97
|
+
then `plan-bind` to the approved spec and approve the plan through its existing workflow. Initial
|
|
98
|
+
combined HTML sprint review uses the explicit paired path below instead of separate approvals.
|
|
99
|
+
A normative change invalidates its approval. Rebinding is explicit and preserves
|
|
100
|
+
history; it does not waive coverage or permit silent reinterpretation.
|
|
101
|
+
|
|
102
|
+
When adversarial review revises a ready initial draft spec after its draft plan exists,
|
|
103
|
+
query both current identities. The installed engine's explicit `plan-bind` request with
|
|
104
|
+
`binding_mode: draft_preview` requires the plan's existing `expected` CAS and the spec's
|
|
105
|
+
`spec_expected` revision/model CAS. It rebinds only that same-ID, same-slug initial pair:
|
|
106
|
+
both artifacts must remain unapproved drafts, tasks pending and unevidenced, prerequisites
|
|
107
|
+
unsatisfied and unevidenced, and scopes empty. Optional typed plan corrections must leave
|
|
108
|
+
both drafts fully ready. The binding remains `draft_preview`; this operation grants no
|
|
109
|
+
approval, delegation, prerequisite satisfaction, execution or acceptance. Revalidate both
|
|
110
|
+
current drafts, repeat the affected review and arm the exact combined approval afterward.
|
|
111
|
+
Inspect the installed operation schema first. An engine without this explicit draft
|
|
112
|
+
request reports a capability gap; never edit HTML, recreate the pair, substitute sequential
|
|
113
|
+
approval or use ordinary approved-source `plan-bind` to repair it.
|
|
114
|
+
|
|
115
|
+
## Historical paired approval and delegated methods
|
|
116
|
+
|
|
117
|
+
`<plugin-root>/includes/sprint-authority.md` retains the old paired/method
|
|
118
|
+
attestation adapter contract for inspecting or recovering those records. It is
|
|
119
|
+
not required to create drafts, bind a source, or maintain an in-scope method.
|
|
120
|
+
A historical grant is not permission to bypass a real user stop or host denial.
|
|
121
|
+
|
|
122
|
+
## Explicit verification and review receipts
|
|
123
|
+
|
|
124
|
+
This is the existing attested-evidence path for a consumer that actually needs
|
|
125
|
+
that claim. It is not a mandatory author/reviewer sequence for ordinary work.
|
|
126
|
+
Do not apply the following arming protocol to receipt-free progress operations.
|
|
127
|
+
|
|
128
|
+
The orchestrator executes the declared verification through its existing governed
|
|
129
|
+
execution tool through the installed production authority adapter; it never
|
|
130
|
+
constructs a successful workflow event itself. Arm an exact task request first.
|
|
131
|
+
Verification and review authority below is supported on Codex and Claude Code.
|
|
132
|
+
On Pi, stop at this boundary rather than treating model-authored results as
|
|
133
|
+
authority; its installed prompt approval seam does not imply verification or
|
|
134
|
+
review support.
|
|
135
|
+
Every distinct `cwd` label in the engine context must be mapped once to an exact
|
|
136
|
+
linked Git worktree root. Mapped worktrees must share the artifact repository's
|
|
137
|
+
Git common directory, and the adapter freezes their filesystem identity, HEAD,
|
|
138
|
+
status digest, effective argv, and executable bytes:
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
python "<plugin-root>/hooks/artifact-authority.py" arm --root "<project-root>" --artifact-id <plan-id> --record-id <task-id> --activity verification --workspace "candidate worktree=<absolute-worktree>" --workspace "baseline-evidence worktree=<absolute-worktree>"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Use only the mappings actually named by that task; missing or extra labels fail
|
|
145
|
+
closed. Run the returned request through one plain governed exec invocation (no
|
|
146
|
+
compound shell expression), then publish only after the host's matching
|
|
147
|
+
`PostToolUse` has corroborated the wrapper's completed request-bound result.
|
|
148
|
+
For the `verify` invocation, use literal absolute paths for both the installed
|
|
149
|
+
script and repository root; do not submit an unexpanded plugin-root or project-root
|
|
150
|
+
variable. The selector rejects `$` and other shell metacharacters before shell
|
|
151
|
+
expansion on both supported hosts, so an exec shell exporting `PLUGIN_ROOT` does
|
|
152
|
+
not make that spelling valid. On Claude, run the exact `verify_command` returned
|
|
153
|
+
by `arm`. Otherwise substitute the resolved paths into this invocation:
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
python "<absolute-plugin-root>/hooks/artifact-authority.py" verify --root "<absolute-project-root>" --request-id <request-id>
|
|
157
|
+
python "<plugin-root>/hooks/artifact-authority.py" publish --root "<project-root>" --request-id <request-id>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The verifier launches exact engine-declared argv without a shell, supervises the
|
|
161
|
+
whole process tree, bounds output, requires exact named outcomes, and rejects
|
|
162
|
+
workspace or executable drift. If an attempt is interrupted, use the adapter's
|
|
163
|
+
`recover` operation to retain it as failed or abandoned; never rerun an uncertain
|
|
164
|
+
attempt under the same request.
|
|
165
|
+
|
|
166
|
+
Named Playwright commands require `test --reporter=json`; each required name must
|
|
167
|
+
equal a unique native `spec.title`. Named Vitest commands require
|
|
168
|
+
`run --reporter=verbose`; use the complete rendered `suite > case` identity after
|
|
169
|
+
the filename, not a leaf-name alias. Named unittest commands require `-v` or
|
|
170
|
+
`--verbose`. A passing row cannot conceal a duplicate, skip, failure or retry.
|
|
171
|
+
Plain exit checks cannot attest named tests.
|
|
172
|
+
|
|
173
|
+
Named npm commands support inspectable single-runner scripts, a contained
|
|
174
|
+
repository-relative `--prefix`, and arguments forwarded after `--`. A root script
|
|
175
|
+
may delegate once through `npm -w <name> run <script>` or
|
|
176
|
+
`npm --workspace <name> run <script>` when the name resolves uniquely in an array
|
|
177
|
+
of at most 32 explicit, contained workspace directories. The runner binds the root
|
|
178
|
+
manifest and every inspected workspace manifest. Globs, linked directories,
|
|
179
|
+
multiple workspace selectors and recursive npm delegation are unsupported.
|
|
180
|
+
Workspace delegation uses the declared cwd; a non-dot `--prefix` is unsupported
|
|
181
|
+
for this shape.
|
|
182
|
+
Preserve both npm argument boundaries when adding a reporter: for a root
|
|
183
|
+
`test:client` script containing `npm -w @example/client run test`, declare
|
|
184
|
+
`npm run test:client -- -- --reporter=verbose` to forward the reporter to Vitest.
|
|
185
|
+
The original root command still executes the full script. Compound
|
|
186
|
+
scripts and pre/post lifecycle scripts require separately declared direct-runner
|
|
187
|
+
commands; retain every check from the original suite when splitting commands.
|
|
188
|
+
On Windows, only the qualified standard `npm.cmd` layout is adapted: the runner
|
|
189
|
+
pins the wrapper, sibling `node.exe`, sibling `node_modules/npm/bin/npm-cli.js`,
|
|
190
|
+
and script manifest. It deliberately selects that sibling CLI rather than the
|
|
191
|
+
shim's optional global-prefix redirection. Unknown batch wrappers fail closed.
|
|
192
|
+
For workspace delegation, it also pins `npm-prefix.js` and checks that the nested
|
|
193
|
+
wrapper uses the same CLI, the default script shell, and no configured Node
|
|
194
|
+
options. Local executables shadowing nested npm are rejected.
|
|
195
|
+
Direct Node script entrypoints are also pinned. These launch-file bindings do
|
|
196
|
+
not establish transitive dependency closure for deliberately excluded inputs.
|
|
197
|
+
|
|
198
|
+
New observations use the closed `declared-command/0.3.0` producer profile to
|
|
199
|
+
retain collector and launch-file identities plus an engine-produced normalized
|
|
200
|
+
completion closure. Raw workspace fingerprints must still match before and
|
|
201
|
+
after command execution. Historical `0.1.0` and `0.2.0` bindings retain their
|
|
202
|
+
original contracts and cannot be relabelled as newer evidence.
|
|
203
|
+
|
|
204
|
+
For `spec_review` or `quality_review`, arm the corresponding activity without
|
|
205
|
+
workspace mappings and dispatch the returned `launch_envelope` unchanged to one
|
|
206
|
+
fresh host subagent. The bounded prompt names the retained, content-addressed
|
|
207
|
+
evidence context instead of embedding its full input manifest. The reviewer
|
|
208
|
+
must read that context and inspect its subject, coverage and source inputs.
|
|
209
|
+
The adapter verifies the context's canonical bytes and digest before launch and
|
|
210
|
+
publication; a missing or changed context blocks authority. Never shorten or
|
|
211
|
+
rewrite the returned envelope yourself.
|
|
212
|
+
The host hooks bind the exact launch call, child start and
|
|
213
|
+
the child's first stop; a pasted or coordinator-authored decision is not review
|
|
214
|
+
authority.
|
|
215
|
+
|
|
216
|
+
When reviewing completed work that relies on a verification run, linked
|
|
217
|
+
worktrees or supporting evidence, explicitly select the completion path. Add
|
|
218
|
+
`--completion-receipt <exact-published-receipt>` once per task and
|
|
219
|
+
`--supporting-file <absolute-regular-file>` for each supporting document to the
|
|
220
|
+
review `arm` command. A task review selects one verification receipt; a scope
|
|
221
|
+
review selects the exact receipt for every member. Supporting files require a
|
|
222
|
+
receipt selection. The existing review-before-verification path remains
|
|
223
|
+
available; it does not satisfy an explicitly requested completion review.
|
|
224
|
+
|
|
225
|
+
The native engine validates the selected published verification chain and
|
|
226
|
+
derives the worktree mappings from its qualified observation. It retains the
|
|
227
|
+
selected identities, worktree closure and supporting bytes in the immutable
|
|
228
|
+
context. Supporting material is data for review, not authority. Selection is
|
|
229
|
+
explicit: no newest-receipt search, directory import or attachment execution.
|
|
230
|
+
At most 16 regular files are admitted, each at most 64 KiB and together at most
|
|
231
|
+
256 KiB; links, reparse indirection, duplicate paths and changed content fail
|
|
232
|
+
closed. Read canonical spec and plan artifacts through the native artifact API;
|
|
233
|
+
they cannot be imported as supporting files. The generated prompt directs the reviewer to assess performed work
|
|
234
|
+
against each criterion, step and `done_when` obligation, using selected evidence
|
|
235
|
+
and source references. A consistent plan or an empty diff is insufficient.
|
|
236
|
+
|
|
237
|
+
Use the returned `completion_sha256` in the native `task-review` or
|
|
238
|
+
`accept-scope` request. The completed review and its exact verification run must
|
|
239
|
+
agree; a definition-only review or a different successful run is rejected.
|
|
240
|
+
A task review retains the exact run it actually inspected; never relabel it as
|
|
241
|
+
having reviewed another run. A new scope review may select a separately validated
|
|
242
|
+
passing rerun without redispatching that task reviewer when the original task
|
|
243
|
+
review remains current, the reviewed workspaces match the new run's normalized
|
|
244
|
+
closure, and all supporting material is retained. The engine checks those facts;
|
|
245
|
+
a new receipt ID alone is not a reason to repeat the task review. Different
|
|
246
|
+
source, definitions, or supporting evidence still needs the affected assessment.
|
|
247
|
+
This does not infer equivalence from a caller-supplied label or a matching exit
|
|
248
|
+
code, and does not make a later failed run pass.
|
|
249
|
+
|
|
250
|
+
Creating a completion context for a `0.3.0` verification recomputes the normalized
|
|
251
|
+
worktree closure bound during that verification. Only validated native outputs
|
|
252
|
+
and execution bookkeeping are normalized; their publication must not invalidate
|
|
253
|
+
unchanged source. Historical `0.2.0` verification retains its initial exact raw
|
|
254
|
+
fingerprint comparison. Later freshness checks allow native task-status and
|
|
255
|
+
execution bookkeeping updates in validated canonical artifacts while retaining
|
|
256
|
+
their normative content hashes.
|
|
257
|
+
Changes to task definitions, other HTML or other governance files still affect
|
|
258
|
+
freshness under the normal snapshot policy. The closure covers
|
|
259
|
+
tracked working-tree bytes and non-ignored untracked files, not ignored
|
|
260
|
+
dependencies or outputs. The completion assessment is still an independent
|
|
261
|
+
reviewer's judgment; schema validation does not prove that every file was read
|
|
262
|
+
or every conclusion is correct. Preserve the unchanged launch envelope and
|
|
263
|
+
genuine host capture throughout this path.
|
|
264
|
+
Publish the completed request through the same `publish` command. `task-review`
|
|
265
|
+
records the separate verification and spec-review receipts. After every task in
|
|
266
|
+
the scope reaches `REVIEW`, reverify against one current source snapshot, run the
|
|
267
|
+
combined quality review, and use `accept-scope` once. Do not accept tasks
|
|
268
|
+
individually.
|
|
269
|
+
|
|
270
|
+
Current Codex native V1 completion reviews retain the full frozen context in
|
|
271
|
+
its immutable, content-addressed file and keep only its closed reference in
|
|
272
|
+
mutable request state. Loading validates the exact context locator, hash,
|
|
273
|
+
canonical bytes and review kind. The native context budget remains 8 MiB;
|
|
274
|
+
mutable request state remains bounded at 1 MiB. Before dispatch, the adapter
|
|
275
|
+
reserves the exact fixed result identities, six times the 64 KiB decision
|
|
276
|
+
budget for canonical JSON escaping, and 32 bounded 256-byte lifecycle slots.
|
|
277
|
+
A request without that capacity is refused before child launch. Historical
|
|
278
|
+
inline requests retain their existing limits and abandonment-only recovery.
|
|
279
|
+
|
|
280
|
+
An ARMED review that never launched can be abandoned through the adapter:
|
|
281
|
+
|
|
282
|
+
```sh
|
|
283
|
+
python "<plugin-root>/hooks/artifact-authority.py" recover --root "<project-root>" --request-id <request-id> --disposition abandoned
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
The adapter requires no attempt, launch, observation, receipt or prior recovery.
|
|
287
|
+
It retains the request and its evidence, creates no receipt, and prohibits
|
|
288
|
+
dispatching that request again. This also covers bounded legacy requests whose
|
|
289
|
+
inline manifests made them too large for normal dispatch. Launched or captured
|
|
290
|
+
requests remain subject to their existing recovery rules. Arm a new request
|
|
291
|
+
only after the retained request is terminal.
|
|
292
|
+
|
|
293
|
+
## Assistive recovery of an unanswered legacy reconciliation prompt
|
|
294
|
+
|
|
295
|
+
After a session restart, **inspect the active task and any workers first**.
|
|
296
|
+
Ordinary `task-reconcile` records a reasoned progress update through the
|
|
297
|
+
native engine without a prompt token; it never clears an explicit user stop.
|
|
298
|
+
The following repair applies **only** when an older installed adapter has left
|
|
299
|
+
an *unanswered* reconciliation prompt that should no longer be re-armed:
|
|
300
|
+
|
|
301
|
+
```sh
|
|
302
|
+
python "<plugin-root>/hooks/artifact-reconcile.py" --root "<project-root>" --artifact-id <plan-id> --supersede --reason "Old unanswered artifact prompt; the actual task was inspected and no user decision is being overridden."
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
This calls the existing host adapter's locked, read-validated pending-state
|
|
306
|
+
handler. It requires the original prompt-route binding to match the pending
|
|
307
|
+
record, rejects ambiguous legacy/current markers, and **refuses any durable
|
|
308
|
+
mutation attempt**. It removes only an unanswered prompt/route, does not edit
|
|
309
|
+
spec/plan state, report tests passed, cancel a user stop, or fabricate an
|
|
310
|
+
approval. No existing receipt or evidence history is rewritten. If it fails
|
|
311
|
+
because a mutation may have started, use the exact `--recover` path below;
|
|
312
|
+
**never** reissue the work under a different operation ID. After superseding,
|
|
313
|
+
read `identity` and `eligible`, then reconcile only what actual observed
|
|
314
|
+
work requires. A cancelled question is not an accomplished task.
|
|
315
|
+
|
|
316
|
+
An **unanswered** old paired-sprint approval can be retired through the
|
|
317
|
+
existing approval adapter. The paired format persists a submitted reply before attempting a native
|
|
318
|
+
write. Its old producer publishes an immutable observation **before** that
|
|
319
|
+
submission record, so a crash between those two writes must not make the
|
|
320
|
+
reply appear unanswered. The transition lock rechecks both the exact
|
|
321
|
+
submission and a **bounded, content-hash-checked observation scan**.
|
|
322
|
+
It refuses retirement if either proves an observed reply or if historical
|
|
323
|
+
observation storage cannot be safely assessed. Recover confirmed/submitted
|
|
324
|
+
decisions with the existing `resume-sprint` path or investigate an
|
|
325
|
+
interrupted pre-submission observation; never discard its source:
|
|
326
|
+
|
|
327
|
+
```sh
|
|
328
|
+
python "<plugin-root>/hooks/_approvallib.py" retire --root "<project-root>" --artifact-id <plan-id> --reason "The old paired approval was never answered; the current authorized work does not require that question."
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
Old **single-artifact** approval format 0.1 cannot prove that a submitted
|
|
332
|
+
reply was never captured or committed. Its no-code retirement therefore
|
|
333
|
+
**fails closed** as `LEGACY_APPROVAL_RECOVERY_REQUIRED`; inspect the
|
|
334
|
+
current artifact, actual authority history and journal instead of deleting
|
|
335
|
+
the marker. A historical approval, refusal, or genuine user stop is not a
|
|
336
|
+
routine progress permission and must not be silently inverted.
|
|
337
|
+
|
|
338
|
+
An unanswered old prerequisite can use the same existing prerequisite
|
|
339
|
+
helper when its validated state is exactly `ARMED`:
|
|
340
|
+
|
|
341
|
+
```sh
|
|
342
|
+
python "<plugin-root>/hooks/_prerequisitelib.py" retire --root "<project-root>" --artifact-id <plan-id> --prerequisite-id <prerequisite-id> --reason "No human reply was observed; use the real factual observation path if this condition still matters."
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
Its per-plan transition lock rejects `CONFIRMED` or `CAPTURED` requests:
|
|
346
|
+
preserve the source and recover the actual submission instead. Either
|
|
347
|
+
retirement reports `unanswered_prompt_superseded`,
|
|
348
|
+
`authority_granted: false`, and `mutation_attempted: false`.
|
|
349
|
+
Neither is an automatic prompt sweep or a native permission operation.
|
|
350
|
+
No canonical task state, receipt history, or explicit user denial changes.
|
|
351
|
+
**Inspect workers and actual state first**; do not invoke these commands
|
|
352
|
+
for an actively handled request. Other older interrupted decisions use
|
|
353
|
+
their specific recovery contract. Do not bulk-delete markers, receipts,
|
|
354
|
+
or Git-admin attempt state. Actual host permissions remain final.
|
|
355
|
+
|
|
356
|
+
## Historical pending-request recovery
|
|
357
|
+
|
|
358
|
+
Ordinary progress uses the receipt-free reconciliation path above. The following
|
|
359
|
+
legacy adapter operations describe retained armed requests, not a requirement to
|
|
360
|
+
create new approval prompts after every interruption. Do not delete an uncertain
|
|
361
|
+
mutation or relabel its history. Full upgrade retirement of obsolete prompts is
|
|
362
|
+
separate from ordinary task progress.
|
|
363
|
+
|
|
364
|
+
For an explicit historical authority-backed reconciliation, arm
|
|
365
|
+
the installed reconciliation adapter for the exact task or scope, present its
|
|
366
|
+
returned `short_reply` (`reconcile <STATE> <code>` for a task, `reconcile <code>`
|
|
367
|
+
for a scope; the full `reply` also works), and let `UserPromptSubmit` capture and apply the observed
|
|
368
|
+
user decision across repositories:
|
|
369
|
+
|
|
370
|
+
```sh
|
|
371
|
+
python "<plugin-root>/hooks/artifact-reconcile.py" --root "<project-root>" --artifact-id <plan-id> --target-id <task-id> --operation task-reconcile --target-state PENDING --reason "<reviewed reason>" --assessment "<reviewed assessment>"
|
|
372
|
+
python "<plugin-root>/hooks/artifact-reconcile.py" --root "<project-root>" --artifact-id <plan-id> --target-id <checkpoint-id> --operation scope-reconcile --assessment "<reviewed assessment>"
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Ambiguous armed requests, changed identities, stale inputs, wrong replies, or a
|
|
376
|
+
foreign Git repository all fail closed. Re-run `eligible` after reconciliation;
|
|
377
|
+
the prompt reply itself is not proof that the lifecycle mutation committed.
|
|
378
|
+
If a mutation response is lost, submit the same exact reply again: the adapter
|
|
379
|
+
replays its durable operation ID and receipt without recapturing authority. A
|
|
380
|
+
request that has not reached mutation can be cancelled with:
|
|
381
|
+
|
|
382
|
+
```sh
|
|
383
|
+
python "<plugin-root>/hooks/artifact-reconcile.py" --root "<project-root>" --artifact-id <plan-id> --cancel --prompt "<exact returned reply>"
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
If a crash left a complete pending request before its prompt route was
|
|
387
|
+
registered, use this command instead. It refuses an active route or any
|
|
388
|
+
in-flight mutation:
|
|
389
|
+
|
|
390
|
+
```sh
|
|
391
|
+
python "<plugin-root>/hooks/artifact-reconcile.py" --root "<project-root>" --artifact-id <plan-id> --cancel-orphan
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
If a durable mutation attempt exists, neither cancellation path can clear it.
|
|
395
|
+
Replay the exact stored operation and receipt under the reconciliation lock:
|
|
396
|
+
|
|
397
|
+
```sh
|
|
398
|
+
python "<plugin-root>/hooks/artifact-reconcile.py" --root "<project-root>" --artifact-id <plan-id> --recover
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
Recovery clears the request only on a committed replay or a closed-set engine
|
|
402
|
+
result proving no commit (`REVISION_CONFLICT` or `OPERATION_ROLLED_BACK`). Other
|
|
403
|
+
errors retain it for investigation. Re-arm only after cancellation or recovery
|
|
404
|
+
succeeds; a committed recovery is not permission to repeat the mutation.
|
|
405
|
+
|
|
406
|
+
An `IN_PROGRESS` task after interruption requires inspecting the current
|
|
407
|
+
worktree, outstanding tools and actual task results, **not a new approval code**.
|
|
408
|
+
Use receipt-free `task-reconcile` only after establishing its real state; do
|
|
409
|
+
not assume completion or duplicate dispatch. `REVIEW` is implementation-ready,
|
|
410
|
+
not passing verification. Source changes affect applicable proof; presentation
|
|
411
|
+
and routine bookkeeping do not by themselves justify reviewer cascades.
|
|
412
|
+
The legacy `scope-reconcile` proof form remains an explicitly attested claim,
|
|
413
|
+
not the default method of resuming independent tasks.
|
|
414
|
+
|
|
415
|
+
## Legacy transition and blocked capabilities
|
|
416
|
+
|
|
417
|
+
Legacy Markdown remains readable through the old workflow. Conversion is explicit:
|
|
418
|
+
`migration-preview` receives typed spec/plan models and a complete nonoverlapping
|
|
419
|
+
line-disposition map for each original file. Review the models and mappings, then
|
|
420
|
+
`migration-apply` binds to the exact preview digest and records the review. Both
|
|
421
|
+
new artifacts are drafts. Original approval/status prose is not transferred.
|
|
422
|
+
Cutover is recoverable as a pair; completed cutovers use `migration-rollback`, which
|
|
423
|
+
refuses any intervening candidate edit. Original bytes remain in transaction history.
|
|
424
|
+
|
|
425
|
+
**HTML farm dispatch remains disabled and out of scope.** The
|
|
426
|
+
engine can derive an immutable base-bound projection, verify it before canary,
|
|
427
|
+
permit only `meta.model`/`meta.apiBaseUrl` enrichment, and seal exact execution
|
|
428
|
+
bytes plus caller-supplied provider-selection assertions. Those assertions are
|
|
429
|
+
auditable correspondence, not authenticated provenance. The shared built dispatcher enforces the checks
|
|
430
|
+
before side effects. Keep the HTML `--farm` path disabled until real authority
|
|
431
|
+
adapters, native packaged payloads and the fresh frontier/low-tier qualification
|
|
432
|
+
matrix all pass; the June 2026 run earns no current credit. Existing legacy farm
|
|
433
|
+
behavior is unchanged. Enabling normal premium-path HTML authoring does not count
|
|
434
|
+
as farm qualification or authorize farm dispatch.
|