agent-lifecycle-kit 1.50.1__py3-none-any.whl
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.
- agent_lifecycle/__init__.py +5 -0
- agent_lifecycle/__main__.py +5 -0
- agent_lifecycle/_version.py +3 -0
- agent_lifecycle/adapter_sessions/__init__.py +41 -0
- agent_lifecycle/adapter_sessions/contracts.py +133 -0
- agent_lifecycle/adapter_sessions/env.py +44 -0
- agent_lifecycle/adapter_sessions/launcher.py +101 -0
- agent_lifecycle/adapter_sessions/process.py +39 -0
- agent_lifecycle/adapter_sessions/redaction.py +30 -0
- agent_lifecycle/adapter_sessions/session_store.py +64 -0
- agent_lifecycle/adapter_sessions/task_intake.py +450 -0
- agent_lifecycle/adapter_sessions/workflow_bridge.py +154 -0
- agent_lifecycle/audit/__init__.py +67 -0
- agent_lifecycle/audit/bug_forensics.py +94 -0
- agent_lifecycle/audit/implementation.py +516 -0
- agent_lifecycle/audit/ownership.py +119 -0
- agent_lifecycle/audit/proof_integrity.py +814 -0
- agent_lifecycle/audit/review_verdict.py +116 -0
- agent_lifecycle/changesets/__init__.py +5 -0
- agent_lifecycle/changesets/git.py +35 -0
- agent_lifecycle/cli/__init__.py +5 -0
- agent_lifecycle/cli/adapter.py +360 -0
- agent_lifecycle/cli/dispatch.py +1220 -0
- agent_lifecycle/cli/followup.py +89 -0
- agent_lifecycle/cli/main.py +41 -0
- agent_lifecycle/cli/metrics_parser.py +49 -0
- agent_lifecycle/cli/parsers.py +618 -0
- agent_lifecycle/cli/policy.py +101 -0
- agent_lifecycle/cli/progress_hooks.py +117 -0
- agent_lifecycle/cli/worktree.py +74 -0
- agent_lifecycle/compiler/__init__.py +17 -0
- agent_lifecycle/compiler/small_model_packets.py +322 -0
- agent_lifecycle/compiler/task_packets.py +229 -0
- agent_lifecycle/context/__init__.py +20 -0
- agent_lifecycle/context/episode_retrieval.py +30 -0
- agent_lifecycle/context/external_memory.py +71 -0
- agent_lifecycle/context/profiles.py +104 -0
- agent_lifecycle/context/rendering.py +226 -0
- agent_lifecycle/contracts/__init__.py +25 -0
- agent_lifecycle/contracts/adapter_contract_schemas.py +130 -0
- agent_lifecycle/contracts/adapter_event_schemas.py +118 -0
- agent_lifecycle/contracts/adapter_session_schemas.py +130 -0
- agent_lifecycle/contracts/adapter_task_schemas.py +90 -0
- agent_lifecycle/contracts/audit_schemas.py +133 -0
- agent_lifecycle/contracts/bug_forensics_schemas.py +400 -0
- agent_lifecycle/contracts/canonical.py +55 -0
- agent_lifecycle/contracts/compatibility.py +242 -0
- agent_lifecycle/contracts/context_model_schemas.py +265 -0
- agent_lifecycle/contracts/core_schemas.py +252 -0
- agent_lifecycle/contracts/cross_check_schemas.py +121 -0
- agent_lifecycle/contracts/errors.py +26 -0
- agent_lifecycle/contracts/evidence_import_schemas.py +152 -0
- agent_lifecycle/contracts/host_capability_schemas.py +64 -0
- agent_lifecycle/contracts/import_dialect_schemas.py +166 -0
- agent_lifecycle/contracts/metric_schemas.py +315 -0
- agent_lifecycle/contracts/paths.py +31 -0
- agent_lifecycle/contracts/plan_contract_schemas.py +148 -0
- agent_lifecycle/contracts/policy_schemas.py +350 -0
- agent_lifecycle/contracts/progress_bridge_schemas.py +114 -0
- agent_lifecycle/contracts/progress_hook_schemas.py +119 -0
- agent_lifecycle/contracts/proof_integrity_schemas.py +240 -0
- agent_lifecycle/contracts/release_contract_schemas.py +476 -0
- agent_lifecycle/contracts/review_mesh_recommendation_schemas.py +64 -0
- agent_lifecycle/contracts/review_mesh_schemas.py +222 -0
- agent_lifecycle/contracts/review_quality_schemas.py +109 -0
- agent_lifecycle/contracts/runner_schemas.py +91 -0
- agent_lifecycle/contracts/runner_worktree_schemas.py +389 -0
- agent_lifecycle/contracts/sandbox_schemas.py +159 -0
- agent_lifecycle/contracts/schema_builders.py +23 -0
- agent_lifecycle/contracts/schemas.py +93 -0
- agent_lifecycle/contracts/status_goal_schemas.py +469 -0
- agent_lifecycle/contracts/task_template_schemas.py +96 -0
- agent_lifecycle/contracts/usage_export_schemas.py +68 -0
- agent_lifecycle/diagnostics/__init__.py +17 -0
- agent_lifecycle/diagnostics/bundles.py +124 -0
- agent_lifecycle/diagnostics/readiness.py +733 -0
- agent_lifecycle/evidence_index/__init__.py +39 -0
- agent_lifecycle/evidence_index/core.py +272 -0
- agent_lifecycle/evidence_index/episode_index.py +329 -0
- agent_lifecycle/evidence_index/external_context.py +276 -0
- agent_lifecycle/followup/__init__.py +23 -0
- agent_lifecycle/followup/records.py +404 -0
- agent_lifecycle/freeze/__init__.py +5 -0
- agent_lifecycle/freeze/locks.py +23 -0
- agent_lifecycle/goal/__init__.py +15 -0
- agent_lifecycle/goal/records.py +244 -0
- agent_lifecycle/goal/view.py +109 -0
- agent_lifecycle/host_protocol/__init__.py +65 -0
- agent_lifecycle/host_protocol/acp_capability.py +195 -0
- agent_lifecycle/host_protocol/capabilities.py +268 -0
- agent_lifecycle/host_protocol/contracts.py +214 -0
- agent_lifecycle/host_protocol/event_capture.py +390 -0
- agent_lifecycle/host_protocol/events.py +196 -0
- agent_lifecycle/host_protocol/inspection.py +194 -0
- agent_lifecycle/host_protocol/inspection_common.py +310 -0
- agent_lifecycle/host_protocol/inspection_cursor.py +164 -0
- agent_lifecycle/host_protocol/inspection_gemini_cli.py +163 -0
- agent_lifecycle/host_protocol/inspection_hermes.py +161 -0
- agent_lifecycle/host_protocol/inspection_kimi_code.py +169 -0
- agent_lifecycle/host_protocol/inspection_opencode.py +147 -0
- agent_lifecycle/host_protocol/inspection_qwen_code.py +143 -0
- agent_lifecycle/host_protocol/receipts.py +58 -0
- agent_lifecycle/host_protocol/scaffold.py +329 -0
- agent_lifecycle/host_protocol/validation.py +229 -0
- agent_lifecycle/imports/__init__.py +76 -0
- agent_lifecycle/imports/agentskills_profile.py +54 -0
- agent_lifecycle/imports/bmad_profile.py +49 -0
- agent_lifecycle/imports/constitution_adr.py +91 -0
- agent_lifecycle/imports/dialect_profiles.py +121 -0
- agent_lifecycle/imports/external_agent.py +28 -0
- agent_lifecycle/imports/external_dialects.py +486 -0
- agent_lifecycle/imports/external_workflow.py +28 -0
- agent_lifecycle/imports/multi_markdown.py +111 -0
- agent_lifecycle/imports/openspec_profile.py +49 -0
- agent_lifecycle/imports/planning.py +536 -0
- agent_lifecycle/imports/spec_kit_profile.py +49 -0
- agent_lifecycle/imports/spec_kitty_profile.py +49 -0
- agent_lifecycle/metrics/__init__.py +60 -0
- agent_lifecycle/metrics/cost_collection.py +315 -0
- agent_lifecycle/metrics/costs.py +203 -0
- agent_lifecycle/metrics/outcome_index.py +330 -0
- agent_lifecycle/metrics/phase_resources.py +208 -0
- agent_lifecycle/metrics/recommendations.py +507 -0
- agent_lifecycle/metrics/regression_signals.py +64 -0
- agent_lifecycle/metrics/usage_export.py +390 -0
- agent_lifecycle/model_routing/__init__.py +19 -0
- agent_lifecycle/model_routing/profiles.py +323 -0
- agent_lifecycle/model_routing/receipts.py +140 -0
- agent_lifecycle/model_routing/resolver.py +566 -0
- agent_lifecycle/neutrality/__init__.py +12 -0
- agent_lifecycle/neutrality/__main__.py +6 -0
- agent_lifecycle/neutrality/authority.py +265 -0
- agent_lifecycle/neutrality/canonical.py +28 -0
- agent_lifecycle/neutrality/cli.py +294 -0
- agent_lifecycle/neutrality/ed25519.py +124 -0
- agent_lifecycle/neutrality/errors.py +89 -0
- agent_lifecycle/neutrality/gate.py +269 -0
- agent_lifecycle/neutrality/paths.py +92 -0
- agent_lifecycle/neutrality/policy.py +76 -0
- agent_lifecycle/neutrality/receipt.py +135 -0
- agent_lifecycle/neutrality/scanner.py +310 -0
- agent_lifecycle/neutrality/test_runner.py +116 -0
- agent_lifecycle/planning/__init__.py +47 -0
- agent_lifecycle/planning/acceptance_markdown.py +159 -0
- agent_lifecycle/planning/completeness.py +362 -0
- agent_lifecycle/planning/continuity.py +255 -0
- agent_lifecycle/planning/templates.py +294 -0
- agent_lifecycle/planning/tiering.py +87 -0
- agent_lifecycle/planning/validation.py +72 -0
- agent_lifecycle/policy/__init__.py +37 -0
- agent_lifecycle/policy/adaptive_lifecycle.py +416 -0
- agent_lifecycle/policy/apply.py +44 -0
- agent_lifecycle/policy/proposals.py +189 -0
- agent_lifecycle/policy/quality_floor.py +144 -0
- agent_lifecycle/policy/runtime_receipts.py +160 -0
- agent_lifecycle/py.typed +1 -0
- agent_lifecycle/quality/__init__.py +89 -0
- agent_lifecycle/quality/bug_forensics.py +727 -0
- agent_lifecycle/quality/bug_forensics_advisor.py +88 -0
- agent_lifecycle/quality/bug_forensics_recipes.py +239 -0
- agent_lifecycle/quality/cross_check.py +437 -0
- agent_lifecycle/quality/failure_classification.py +318 -0
- agent_lifecycle/quality/packs.py +265 -0
- agent_lifecycle/reporting/__init__.py +37 -0
- agent_lifecycle/reporting/change_summary.py +156 -0
- agent_lifecycle/reporting/event_feed.py +101 -0
- agent_lifecycle/reporting/progress_bridge.py +153 -0
- agent_lifecycle/reporting/progress_hooks.py +161 -0
- agent_lifecycle/reporting/progress_terminal.py +95 -0
- agent_lifecycle/reporting/progress_view.py +284 -0
- agent_lifecycle/reporting/status_view.py +125 -0
- agent_lifecycle/reporting/usage_export.py +59 -0
- agent_lifecycle/review/__init__.py +5 -0
- agent_lifecycle/review/validation.py +29 -0
- agent_lifecycle/review_mesh/__init__.py +83 -0
- agent_lifecycle/review_mesh/assignments.py +160 -0
- agent_lifecycle/review_mesh/contracts.py +685 -0
- agent_lifecycle/review_mesh/operator_templates.py +320 -0
- agent_lifecycle/review_mesh/quorum.py +38 -0
- agent_lifecycle/review_mesh/recommendation.py +487 -0
- agent_lifecycle/review_mesh/results.py +98 -0
- agent_lifecycle/review_mesh/synthesis.py +102 -0
- agent_lifecycle/runner/__init__.py +47 -0
- agent_lifecycle/runner/attempt_snapshots.py +244 -0
- agent_lifecycle/runner/core.py +653 -0
- agent_lifecycle/runner/sandbox_receipts.py +628 -0
- agent_lifecycle/specification/__init__.py +23 -0
- agent_lifecycle/specification/completion_check.py +135 -0
- agent_lifecycle/specification/completion_gate.py +501 -0
- agent_lifecycle/specification/completion_signal.py +75 -0
- agent_lifecycle/specification/validation.py +48 -0
- agent_lifecycle/workflow/__init__.py +79 -0
- agent_lifecycle/workflow/artifacts.py +74 -0
- agent_lifecycle/workflow/budget_decisions.py +370 -0
- agent_lifecycle/workflow/budget_policy.py +140 -0
- agent_lifecycle/workflow/budget_receipts.py +70 -0
- agent_lifecycle/workflow/bug_forensics_gates.py +284 -0
- agent_lifecycle/workflow/controller.py +45 -0
- agent_lifecycle/workflow/events.py +50 -0
- agent_lifecycle/workflow/final_proof_integrity.py +74 -0
- agent_lifecycle/workflow/finalization.py +422 -0
- agent_lifecycle/workflow/gates.py +185 -0
- agent_lifecycle/workflow/implementation_audit_gate.py +126 -0
- agent_lifecycle/workflow/leases.py +220 -0
- agent_lifecycle/workflow/lineage.py +108 -0
- agent_lifecycle/workflow/managed_runner.py +167 -0
- agent_lifecycle/workflow/model_usage.py +106 -0
- agent_lifecycle/workflow/next_action.py +86 -0
- agent_lifecycle/workflow/operation_kernel.py +98 -0
- agent_lifecycle/workflow/plan_adoption.py +439 -0
- agent_lifecycle/workflow/query.py +52 -0
- agent_lifecycle/workflow/review_mesh_gate.py +93 -0
- agent_lifecycle/workflow/reviews.py +168 -0
- agent_lifecycle/workflow/run_transitions.py +163 -0
- agent_lifecycle/workflow/sandbox_policy.py +214 -0
- agent_lifecycle/workflow/selectors.py +57 -0
- agent_lifecycle/workflow/state.py +130 -0
- agent_lifecycle/workflow/task_transitions.py +421 -0
- agent_lifecycle/worktree/__init__.py +17 -0
- agent_lifecycle/worktree/isolation.py +407 -0
- agent_lifecycle_kit-1.50.1.dist-info/METADATA +201 -0
- agent_lifecycle_kit-1.50.1.dist-info/RECORD +227 -0
- agent_lifecycle_kit-1.50.1.dist-info/WHEEL +5 -0
- agent_lifecycle_kit-1.50.1.dist-info/entry_points.txt +3 -0
- agent_lifecycle_kit-1.50.1.dist-info/licenses/LICENSE +201 -0
- agent_lifecycle_kit-1.50.1.dist-info/licenses/NOTICE +5 -0
- agent_lifecycle_kit-1.50.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
"""Provider-neutral worktree isolation receipts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import UTC, datetime
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from agent_lifecycle.contracts import LifecycleError, canonical_digest
|
|
9
|
+
from agent_lifecycle.contracts.paths import is_under_repo_path, normalize_repo_path
|
|
10
|
+
|
|
11
|
+
LINEAGE_KEYS = ("runId", "packageId", "planRevision", "planDigest", "sourceRevision")
|
|
12
|
+
OUTCOMES = {"PASS", "FAILED", "BLOCKED"}
|
|
13
|
+
CLEANUP_DECISIONS = {"PRESERVE", "REMOVE"}
|
|
14
|
+
WRITEBACK_DECISIONS = {"APPLY", "DISCARD"}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def validate_worktree_policy(policy: dict[str, Any]) -> dict[str, Any]:
|
|
18
|
+
if not isinstance(policy, dict):
|
|
19
|
+
raise LifecycleError("invalid-worktree-policy", "worktree policy must be an object")
|
|
20
|
+
if policy.get("schemaVersion") != "agent-worktree-isolation-policy.v1":
|
|
21
|
+
raise LifecycleError("invalid-worktree-policy", "worktree policy schemaVersion is unsupported")
|
|
22
|
+
worktree_root = normalize_repo_path(_required_string(policy.get("worktreeRoot"), label="worktreeRoot"), label="worktreeRoot")
|
|
23
|
+
allowed_write_roots = _path_list(policy.get("allowedWriteRoots", []), label="allowedWriteRoots", allow_empty=True)
|
|
24
|
+
preserve_failed = _required_bool(policy.get("preserveFailedAttempts"), label="preserveFailedAttempts")
|
|
25
|
+
cleanup_requires_operator = _required_bool(policy.get("cleanupRequiresOperator"), label="cleanupRequiresOperator")
|
|
26
|
+
body = {
|
|
27
|
+
"schemaVersion": "agent-worktree-isolation-policy-validation.v1",
|
|
28
|
+
"status": "PASS",
|
|
29
|
+
"worktreeRoot": worktree_root,
|
|
30
|
+
"allowedWriteRoots": allowed_write_roots,
|
|
31
|
+
"preserveFailedAttempts": preserve_failed,
|
|
32
|
+
"cleanupRequiresOperator": cleanup_requires_operator,
|
|
33
|
+
"policyDigest": canonical_digest(policy),
|
|
34
|
+
}
|
|
35
|
+
return body
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def build_attempt_isolation_receipt(
|
|
39
|
+
workflow_state: dict[str, Any],
|
|
40
|
+
*,
|
|
41
|
+
task_id: str,
|
|
42
|
+
attempt: int,
|
|
43
|
+
policy: dict[str, Any],
|
|
44
|
+
worktree_path: str,
|
|
45
|
+
baseline_ref: str,
|
|
46
|
+
baseline_sha: str,
|
|
47
|
+
changed_files: list[str],
|
|
48
|
+
outcome: str,
|
|
49
|
+
cleanup_decision: str | None = None,
|
|
50
|
+
operator_authorization: dict[str, Any] | None = None,
|
|
51
|
+
reason: str,
|
|
52
|
+
) -> dict[str, Any]:
|
|
53
|
+
validation = validate_worktree_policy(policy)
|
|
54
|
+
task = _workflow_task(workflow_state, task_id)
|
|
55
|
+
decision = cleanup_decision or "PRESERVE"
|
|
56
|
+
body = {
|
|
57
|
+
"schemaVersion": "agent-worktree-attempt-receipt.v1",
|
|
58
|
+
"status": "PASS",
|
|
59
|
+
"lineage": _lineage_from_workflow(workflow_state),
|
|
60
|
+
"taskId": task_id,
|
|
61
|
+
"attempt": _positive_int(attempt, label="attempt"),
|
|
62
|
+
"policyDigest": validation["policyDigest"],
|
|
63
|
+
"isolation": {
|
|
64
|
+
"strategy": "git-worktree",
|
|
65
|
+
"worktreePath": normalize_repo_path(worktree_path, label="worktreePath"),
|
|
66
|
+
"worktreeRoot": validation["worktreeRoot"],
|
|
67
|
+
"baselineRef": _required_string(baseline_ref, label="baselineRef"),
|
|
68
|
+
"baselineSha": _required_string(baseline_sha, label="baselineSha"),
|
|
69
|
+
"mainWorktreeClean": True,
|
|
70
|
+
"allowedWriteRoots": _allowed_roots(validation["allowedWriteRoots"], task),
|
|
71
|
+
"changedFiles": _path_list(changed_files, label="changedFiles", allow_empty=True),
|
|
72
|
+
},
|
|
73
|
+
"outcome": _enum(outcome, OUTCOMES, label="outcome"),
|
|
74
|
+
"cleanup": {
|
|
75
|
+
"decision": _enum(decision, CLEANUP_DECISIONS, label="cleanupDecision"),
|
|
76
|
+
"operatorAuthorization": operator_authorization,
|
|
77
|
+
"reason": reason,
|
|
78
|
+
},
|
|
79
|
+
"createdAt": _now_iso(),
|
|
80
|
+
}
|
|
81
|
+
result = validate_attempt_isolation_receipt(body, workflow_state=workflow_state, policy=policy)
|
|
82
|
+
return {**body, "receiptDigest": result["receiptDigest"]}
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def validate_attempt_isolation_receipt(
|
|
86
|
+
receipt: dict[str, Any],
|
|
87
|
+
*,
|
|
88
|
+
workflow_state: dict[str, Any] | None = None,
|
|
89
|
+
policy: dict[str, Any] | None = None,
|
|
90
|
+
) -> dict[str, Any]:
|
|
91
|
+
if not isinstance(receipt, dict):
|
|
92
|
+
raise LifecycleError("invalid-worktree-receipt", "worktree receipt must be an object")
|
|
93
|
+
if receipt.get("schemaVersion") != "agent-worktree-attempt-receipt.v1":
|
|
94
|
+
raise LifecycleError("invalid-worktree-receipt", "worktree receipt schemaVersion is unsupported")
|
|
95
|
+
if receipt.get("status") != "PASS":
|
|
96
|
+
raise LifecycleError("worktree-isolation-not-pass", "worktree isolation receipt must be PASS")
|
|
97
|
+
lineage = _lineage(receipt.get("lineage"))
|
|
98
|
+
if workflow_state is not None:
|
|
99
|
+
_validate_workflow_binding(lineage, workflow_state)
|
|
100
|
+
task_id = _required_string(receipt.get("taskId"), label="taskId")
|
|
101
|
+
attempt = _positive_int(receipt.get("attempt"), label="attempt")
|
|
102
|
+
isolation = _isolation(receipt.get("isolation"))
|
|
103
|
+
cleanup = _cleanup(receipt.get("cleanup"))
|
|
104
|
+
if policy is not None:
|
|
105
|
+
policy_validation = validate_worktree_policy(policy)
|
|
106
|
+
if receipt.get("policyDigest") != policy_validation["policyDigest"]:
|
|
107
|
+
raise LifecycleError("worktree-policy-mismatch", "worktree receipt policyDigest mismatch")
|
|
108
|
+
if not is_under_repo_path(isolation["worktreePath"], policy_validation["worktreeRoot"]):
|
|
109
|
+
raise LifecycleError("worktree-path-outside-root", "worktree path is outside policy root")
|
|
110
|
+
if policy_validation["cleanupRequiresOperator"] and cleanup["decision"] == "REMOVE" and cleanup["operatorAuthorization"] is None:
|
|
111
|
+
raise LifecycleError("worktree-cleanup-authorization-required", "worktree cleanup requires operator authorization")
|
|
112
|
+
if policy_validation["preserveFailedAttempts"] and receipt.get("outcome") == "FAILED" and cleanup["decision"] == "REMOVE":
|
|
113
|
+
authorization = cleanup["operatorAuthorization"]
|
|
114
|
+
if not isinstance(authorization, dict) or authorization.get("allowFailedAttemptRemoval") is not True:
|
|
115
|
+
raise LifecycleError("worktree-failed-attempt-preserved", "failed attempt cleanup requires explicit removal authorization")
|
|
116
|
+
if workflow_state is not None:
|
|
117
|
+
task = _workflow_task(workflow_state, task_id)
|
|
118
|
+
_validate_changed_files_in_scope(isolation["changedFiles"], _allowed_roots(isolation["allowedWriteRoots"], task))
|
|
119
|
+
stored_digest = receipt.get("receiptDigest")
|
|
120
|
+
receipt_digest = _receipt_digest(receipt)
|
|
121
|
+
if stored_digest is not None and stored_digest != receipt_digest:
|
|
122
|
+
raise LifecycleError("worktree-receipt-digest-mismatch", "worktree receiptDigest does not match receipt")
|
|
123
|
+
body = {
|
|
124
|
+
"schemaVersion": "agent-worktree-attempt-receipt-validation.v1",
|
|
125
|
+
"status": "PASS",
|
|
126
|
+
"lineage": lineage,
|
|
127
|
+
"taskId": task_id,
|
|
128
|
+
"attempt": attempt,
|
|
129
|
+
"outcome": receipt.get("outcome"),
|
|
130
|
+
"cleanupDecision": cleanup["decision"],
|
|
131
|
+
"receiptDigest": receipt_digest,
|
|
132
|
+
}
|
|
133
|
+
return body
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def build_worktree_writeback_receipt(
|
|
137
|
+
workflow_state: dict[str, Any],
|
|
138
|
+
*,
|
|
139
|
+
task_id: str,
|
|
140
|
+
attempt: int,
|
|
141
|
+
overlay_digest: str,
|
|
142
|
+
changed_files: list[str],
|
|
143
|
+
decision: str,
|
|
144
|
+
operator_authorization: dict[str, Any],
|
|
145
|
+
reason: str,
|
|
146
|
+
applied_files: list[str] | None = None,
|
|
147
|
+
discarded_files: list[str] | None = None,
|
|
148
|
+
isolation_receipt_digest: str | None = None,
|
|
149
|
+
) -> dict[str, Any]:
|
|
150
|
+
selected_decision = _enum(decision, WRITEBACK_DECISIONS, label="decision.action")
|
|
151
|
+
changed = _path_list(changed_files, label="changedFiles", allow_empty=True)
|
|
152
|
+
applied = _path_list(
|
|
153
|
+
applied_files if applied_files is not None else (changed if selected_decision == "APPLY" else []),
|
|
154
|
+
label="appliedFiles",
|
|
155
|
+
allow_empty=True,
|
|
156
|
+
)
|
|
157
|
+
discarded = _path_list(
|
|
158
|
+
discarded_files if discarded_files is not None else (changed if selected_decision == "DISCARD" else []),
|
|
159
|
+
label="discardedFiles",
|
|
160
|
+
allow_empty=True,
|
|
161
|
+
)
|
|
162
|
+
body = {
|
|
163
|
+
"schemaVersion": "agent-worktree-writeback-receipt.v1",
|
|
164
|
+
"status": "PASS",
|
|
165
|
+
"lineage": _lineage_from_workflow(workflow_state),
|
|
166
|
+
"taskId": task_id,
|
|
167
|
+
"attempt": _positive_int(attempt, label="attempt"),
|
|
168
|
+
"boundary": {
|
|
169
|
+
"kind": "isolated-overlay",
|
|
170
|
+
"isolationReceiptDigest": isolation_receipt_digest,
|
|
171
|
+
"overlayDigest": _digest(overlay_digest, label="overlayDigest"),
|
|
172
|
+
"runtimeAgnostic": True,
|
|
173
|
+
},
|
|
174
|
+
"decision": {
|
|
175
|
+
"action": selected_decision,
|
|
176
|
+
"operatorAuthorization": _operator_authorization(operator_authorization),
|
|
177
|
+
"reason": _required_string(reason, label="decision.reason"),
|
|
178
|
+
},
|
|
179
|
+
"changedFiles": changed,
|
|
180
|
+
"appliedFiles": applied,
|
|
181
|
+
"discardedFiles": discarded,
|
|
182
|
+
"createdAt": _now_iso(),
|
|
183
|
+
"productionPromotionClaimed": False,
|
|
184
|
+
}
|
|
185
|
+
result = validate_worktree_writeback_receipt(body, workflow_state=workflow_state)
|
|
186
|
+
return {**body, "receiptDigest": result["receiptDigest"]}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def validate_worktree_writeback_receipt(
|
|
190
|
+
receipt: dict[str, Any],
|
|
191
|
+
*,
|
|
192
|
+
workflow_state: dict[str, Any] | None = None,
|
|
193
|
+
) -> dict[str, Any]:
|
|
194
|
+
if not isinstance(receipt, dict):
|
|
195
|
+
raise LifecycleError("invalid-worktree-writeback-receipt", "worktree write-back receipt must be an object")
|
|
196
|
+
if receipt.get("schemaVersion") != "agent-worktree-writeback-receipt.v1":
|
|
197
|
+
raise LifecycleError("invalid-worktree-writeback-receipt", "worktree write-back schemaVersion is unsupported")
|
|
198
|
+
if receipt.get("status") != "PASS":
|
|
199
|
+
raise LifecycleError("worktree-writeback-not-pass", "worktree write-back receipt must be PASS")
|
|
200
|
+
lineage = _lineage(receipt.get("lineage"))
|
|
201
|
+
if workflow_state is not None:
|
|
202
|
+
_validate_workflow_binding(lineage, workflow_state)
|
|
203
|
+
task_id = _required_string(receipt.get("taskId"), label="taskId")
|
|
204
|
+
attempt = _positive_int(receipt.get("attempt"), label="attempt")
|
|
205
|
+
boundary = _writeback_boundary(receipt.get("boundary"))
|
|
206
|
+
decision = _writeback_decision(receipt.get("decision"))
|
|
207
|
+
changed = _path_list(receipt.get("changedFiles", []), label="changedFiles", allow_empty=True)
|
|
208
|
+
applied = _path_list(receipt.get("appliedFiles", []), label="appliedFiles", allow_empty=True)
|
|
209
|
+
discarded = _path_list(receipt.get("discardedFiles", []), label="discardedFiles", allow_empty=True)
|
|
210
|
+
_validate_writeback_paths(changed, applied=applied, discarded=discarded, decision=decision["action"])
|
|
211
|
+
if workflow_state is not None:
|
|
212
|
+
task = _workflow_task(workflow_state, task_id)
|
|
213
|
+
_validate_changed_files_in_scope(changed, _allowed_roots([], task))
|
|
214
|
+
if receipt.get("productionPromotionClaimed") is not False:
|
|
215
|
+
raise LifecycleError("worktree-writeback-production-claim", "write-back receipt must not claim production promotion")
|
|
216
|
+
stored_digest = receipt.get("receiptDigest")
|
|
217
|
+
receipt_digest = _receipt_digest(receipt)
|
|
218
|
+
if stored_digest is not None and stored_digest != receipt_digest:
|
|
219
|
+
raise LifecycleError("worktree-writeback-receipt-digest-mismatch", "worktree write-back receiptDigest does not match receipt")
|
|
220
|
+
return {
|
|
221
|
+
"schemaVersion": "agent-worktree-writeback-receipt-validation.v1",
|
|
222
|
+
"status": "PASS",
|
|
223
|
+
"lineage": lineage,
|
|
224
|
+
"taskId": task_id,
|
|
225
|
+
"attempt": attempt,
|
|
226
|
+
"decision": decision["action"],
|
|
227
|
+
"overlayDigest": boundary["overlayDigest"],
|
|
228
|
+
"changedFileCount": len(changed),
|
|
229
|
+
"appliedFileCount": len(applied),
|
|
230
|
+
"discardedFileCount": len(discarded),
|
|
231
|
+
"receiptDigest": receipt_digest,
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _isolation(value: Any) -> dict[str, Any]:
|
|
236
|
+
if not isinstance(value, dict):
|
|
237
|
+
raise LifecycleError("invalid-worktree-receipt", "worktree isolation is required")
|
|
238
|
+
strategy = value.get("strategy")
|
|
239
|
+
if strategy != "git-worktree":
|
|
240
|
+
raise LifecycleError("invalid-worktree-receipt", "worktree isolation strategy is unsupported")
|
|
241
|
+
if value.get("mainWorktreeClean") is not True:
|
|
242
|
+
raise LifecycleError("worktree-main-dirty", "main worktree must be clean before isolated attempt")
|
|
243
|
+
return {
|
|
244
|
+
"strategy": strategy,
|
|
245
|
+
"worktreePath": normalize_repo_path(_required_string(value.get("worktreePath"), label="isolation.worktreePath"), label="isolation.worktreePath"),
|
|
246
|
+
"worktreeRoot": normalize_repo_path(_required_string(value.get("worktreeRoot"), label="isolation.worktreeRoot"), label="isolation.worktreeRoot"),
|
|
247
|
+
"baselineRef": _required_string(value.get("baselineRef"), label="isolation.baselineRef"),
|
|
248
|
+
"baselineSha": _required_string(value.get("baselineSha"), label="isolation.baselineSha"),
|
|
249
|
+
"allowedWriteRoots": _path_list(value.get("allowedWriteRoots", []), label="isolation.allowedWriteRoots", allow_empty=True),
|
|
250
|
+
"changedFiles": _path_list(value.get("changedFiles", []), label="isolation.changedFiles", allow_empty=True),
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _cleanup(value: Any) -> dict[str, Any]:
|
|
255
|
+
if not isinstance(value, dict):
|
|
256
|
+
raise LifecycleError("invalid-worktree-receipt", "worktree cleanup is required")
|
|
257
|
+
decision = _enum(value.get("decision"), CLEANUP_DECISIONS, label="cleanup.decision")
|
|
258
|
+
authorization = value.get("operatorAuthorization")
|
|
259
|
+
if authorization is not None and not isinstance(authorization, dict):
|
|
260
|
+
raise LifecycleError("invalid-worktree-receipt", "operatorAuthorization must be an object")
|
|
261
|
+
_required_string(value.get("reason"), label="cleanup.reason")
|
|
262
|
+
return {"decision": decision, "operatorAuthorization": authorization}
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _writeback_boundary(value: Any) -> dict[str, Any]:
|
|
266
|
+
if not isinstance(value, dict):
|
|
267
|
+
raise LifecycleError("invalid-worktree-writeback-receipt", "write-back boundary is required")
|
|
268
|
+
if value.get("kind") != "isolated-overlay":
|
|
269
|
+
raise LifecycleError("invalid-worktree-writeback-receipt", "write-back boundary kind is unsupported")
|
|
270
|
+
if value.get("runtimeAgnostic") is not True:
|
|
271
|
+
raise LifecycleError("invalid-worktree-writeback-receipt", "write-back boundary must be runtime agnostic")
|
|
272
|
+
isolation_digest = value.get("isolationReceiptDigest")
|
|
273
|
+
if isolation_digest is not None:
|
|
274
|
+
_digest(isolation_digest, label="boundary.isolationReceiptDigest")
|
|
275
|
+
return {
|
|
276
|
+
"kind": "isolated-overlay",
|
|
277
|
+
"isolationReceiptDigest": isolation_digest,
|
|
278
|
+
"overlayDigest": _digest(value.get("overlayDigest"), label="boundary.overlayDigest"),
|
|
279
|
+
"runtimeAgnostic": True,
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def _writeback_decision(value: Any) -> dict[str, Any]:
|
|
284
|
+
if not isinstance(value, dict):
|
|
285
|
+
raise LifecycleError("invalid-worktree-writeback-receipt", "write-back decision is required")
|
|
286
|
+
action = _enum(value.get("action"), WRITEBACK_DECISIONS, label="decision.action")
|
|
287
|
+
authorization = _operator_authorization(value.get("operatorAuthorization"))
|
|
288
|
+
_required_string(value.get("reason"), label="decision.reason")
|
|
289
|
+
return {"action": action, "operatorAuthorization": authorization}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _operator_authorization(value: Any) -> dict[str, Any]:
|
|
293
|
+
if not isinstance(value, dict):
|
|
294
|
+
raise LifecycleError("invalid-worktree-writeback-receipt", "operatorAuthorization must be an object")
|
|
295
|
+
if not isinstance(value.get("operatorIdentityHash"), str) or not value["operatorIdentityHash"]:
|
|
296
|
+
raise LifecycleError("invalid-worktree-writeback-receipt", "operatorIdentityHash is required")
|
|
297
|
+
return dict(value)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _validate_writeback_paths(
|
|
301
|
+
changed: list[str],
|
|
302
|
+
*,
|
|
303
|
+
applied: list[str],
|
|
304
|
+
discarded: list[str],
|
|
305
|
+
decision: str,
|
|
306
|
+
) -> None:
|
|
307
|
+
changed_set = set(changed)
|
|
308
|
+
applied_set = set(applied)
|
|
309
|
+
discarded_set = set(discarded)
|
|
310
|
+
overlap = sorted(applied_set.intersection(discarded_set))
|
|
311
|
+
if overlap:
|
|
312
|
+
raise LifecycleError("worktree-writeback-path-overlap", "applied and discarded paths overlap", {"paths": overlap})
|
|
313
|
+
outside = sorted(applied_set.union(discarded_set) - changed_set)
|
|
314
|
+
if outside:
|
|
315
|
+
raise LifecycleError("worktree-writeback-path-outside-overlay", "write-back path is outside overlay changes", {"paths": outside})
|
|
316
|
+
if decision == "DISCARD" and applied:
|
|
317
|
+
raise LifecycleError("worktree-writeback-discard-applied-paths", "discard decision must not apply files")
|
|
318
|
+
if decision == "APPLY" and changed and not applied:
|
|
319
|
+
raise LifecycleError("worktree-writeback-apply-empty", "apply decision must apply at least one changed file")
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def _validate_changed_files_in_scope(changed_files: list[str], allowed_roots: list[str]) -> None:
|
|
323
|
+
for path in changed_files:
|
|
324
|
+
if not any(is_under_repo_path(path, root) for root in allowed_roots):
|
|
325
|
+
raise LifecycleError("worktree-write-scope-violation", "worktree changed file is outside task write scope", {"path": path, "allowedWriteRoots": allowed_roots})
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _allowed_roots(policy_roots: list[str], task: dict[str, Any]) -> list[str]:
|
|
329
|
+
task_writes = _path_list(task.get("writes", []), label="task.writes", allow_empty=False)
|
|
330
|
+
if not policy_roots:
|
|
331
|
+
return task_writes
|
|
332
|
+
return [root for root in policy_roots if any(is_under_repo_path(root, task_root) or is_under_repo_path(task_root, root) for task_root in task_writes)] or task_writes
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _workflow_task(workflow_state: dict[str, Any], task_id: str) -> dict[str, Any]:
|
|
336
|
+
for task in workflow_state.get("tasks", []):
|
|
337
|
+
if isinstance(task, dict) and task.get("id") == task_id:
|
|
338
|
+
return task
|
|
339
|
+
raise LifecycleError("worktree-task-not-found", "worktree task is missing from workflow state", {"taskId": task_id})
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def _lineage_from_workflow(state: dict[str, Any]) -> dict[str, Any]:
|
|
343
|
+
return {key: state.get(key) for key in LINEAGE_KEYS}
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _lineage(value: Any) -> dict[str, Any]:
|
|
347
|
+
if not isinstance(value, dict):
|
|
348
|
+
raise LifecycleError("invalid-worktree-receipt", "worktree lineage is required")
|
|
349
|
+
result = {key: value.get(key) for key in LINEAGE_KEYS}
|
|
350
|
+
for key, item in result.items():
|
|
351
|
+
if key == "planRevision":
|
|
352
|
+
_positive_int(item, label="lineage.planRevision")
|
|
353
|
+
elif not isinstance(item, str) or not item:
|
|
354
|
+
raise LifecycleError("invalid-worktree-receipt", f"worktree lineage.{key} is required")
|
|
355
|
+
return result
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def _validate_workflow_binding(lineage: dict[str, Any], workflow_state: dict[str, Any]) -> None:
|
|
359
|
+
for key in LINEAGE_KEYS:
|
|
360
|
+
if lineage.get(key) != workflow_state.get(key):
|
|
361
|
+
raise LifecycleError("worktree-lineage-mismatch", f"worktree {key} mismatch")
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def _path_list(value: Any, *, label: str, allow_empty: bool) -> list[str]:
|
|
365
|
+
if not isinstance(value, list) or not all(isinstance(item, str) and item for item in value):
|
|
366
|
+
raise LifecycleError("invalid-worktree-receipt", f"{label} must be a list of non-empty paths")
|
|
367
|
+
if not value and not allow_empty:
|
|
368
|
+
raise LifecycleError("invalid-worktree-receipt", f"{label} must not be empty")
|
|
369
|
+
return [normalize_repo_path(item, label=label) for item in value]
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _enum(value: Any, allowed: set[str], *, label: str) -> str:
|
|
373
|
+
if value not in allowed:
|
|
374
|
+
raise LifecycleError("invalid-worktree-receipt", f"{label} is unsupported")
|
|
375
|
+
return str(value)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _required_string(value: Any, *, label: str) -> str:
|
|
379
|
+
if not isinstance(value, str) or not value:
|
|
380
|
+
raise LifecycleError("invalid-worktree-receipt", f"{label} is required")
|
|
381
|
+
return value
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def _required_bool(value: Any, *, label: str) -> bool:
|
|
385
|
+
if not isinstance(value, bool):
|
|
386
|
+
raise LifecycleError("invalid-worktree-policy", f"{label} must be boolean")
|
|
387
|
+
return value
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _digest(value: Any, *, label: str) -> str:
|
|
391
|
+
if not isinstance(value, str) or len(value) != 64:
|
|
392
|
+
raise LifecycleError("invalid-worktree-writeback-receipt", f"{label} must be a sha256 digest")
|
|
393
|
+
return value
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def _positive_int(value: Any, *, label: str) -> int:
|
|
397
|
+
if not isinstance(value, int) or isinstance(value, bool) or value < 1:
|
|
398
|
+
raise LifecycleError("invalid-worktree-receipt", f"{label} must be a positive integer")
|
|
399
|
+
return value
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def _receipt_digest(receipt: dict[str, Any]) -> str:
|
|
403
|
+
return canonical_digest({key: value for key, value in receipt.items() if key != "receiptDigest"})
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def _now_iso() -> str:
|
|
407
|
+
return datetime.now(UTC).replace(microsecond=0).isoformat().replace("+00:00", "Z")
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agent-lifecycle-kit
|
|
3
|
+
Version: 1.50.1
|
|
4
|
+
Summary: Provider-neutral lifecycle control layer that helps coding agents plan, execute, validate, and prove software tasks to completion.
|
|
5
|
+
Author: Agent Lifecycle Kit contributors
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Keywords: adapter-conformance,agent-lifecycle,agent-workflows,agents,ai-agents,audit,coding-agents,lifecycle,model-routing,planning,sdd,software-delivery,task-orchestration,workflow
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Typing :: Typed
|
|
16
|
+
Requires-Python: <3.14,>=3.11
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
License-File: NOTICE
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<img src="docs/assets/agent-lifecycle-kit-banner.svg?v=2026-08-02-1" alt="Agent Lifecycle Kit - plan, execute, prove, finish agent work" width="100%">
|
|
24
|
+
</p>
|
|
25
|
+
|
|
26
|
+
# Agent Lifecycle Kit
|
|
27
|
+
|
|
28
|
+
[](LICENSE)
|
|
29
|
+
[](https://github.com/avksp/agent-lifecycle-kit/releases)
|
|
30
|
+

|
|
31
|
+
|
|
32
|
+
**Agent Lifecycle Kit (ALK)** gives coding agents one delivery workflow from
|
|
33
|
+
request to verified result. It turns a user task into a reviewed specification,
|
|
34
|
+
an approved plan, bounded implementation work, review, and final proof, so
|
|
35
|
+
agents finish the job instead of stopping at a patch.
|
|
36
|
+
|
|
37
|
+
The workflow is provider-neutral. Codex, Claude Code, Qwen Code, Goose,
|
|
38
|
+
OpenInterpreter, Pi, Grok Build, or another CLI can follow the same lifecycle,
|
|
39
|
+
while provider commands, model choice, and secret handling stay in adapters or
|
|
40
|
+
host-local profiles.
|
|
41
|
+
|
|
42
|
+
## Quick start
|
|
43
|
+
|
|
44
|
+
From a source checkout:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python -m pip install -e .
|
|
48
|
+
agent-lifecycle version
|
|
49
|
+
agent-lifecycle diagnose --no-install-plans
|
|
50
|
+
agent-lifecycle adapter validate --descriptor adapters/codex/adapter.descriptor.json
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Package install, when available for the release:
|
|
54
|
+
`python -m pip install agent-lifecycle-kit==1.50.1`.
|
|
55
|
+
|
|
56
|
+
For a short walkthrough, use [Quickstart](docs/guides/quickstart.md) and the [Lifecycle cookbook](docs/guides/lifecycle-cookbook.md). For structure and positioning, see [System architecture](docs/architecture/system-architecture.md) and [Project comparison](docs/reference/project-comparison.md). Russian documentation starts at [Документация на русском](docs/ru/README.md).
|
|
57
|
+
|
|
58
|
+
## What it gives you
|
|
59
|
+
|
|
60
|
+
- A finish-oriented lifecycle: plan, execute, review, and prove the result.
|
|
61
|
+
- One process for different CLIs: adapter-specific commands stay outside the
|
|
62
|
+
core, and new adapters can be added without changing lifecycle schemas.
|
|
63
|
+
- Small-model friendly packets: compact context, clear next actions, and
|
|
64
|
+
deterministic checks for local or cheaper models.
|
|
65
|
+
- Quality without overengineering: optional profiles turn on only when risk or
|
|
66
|
+
task type requires them.
|
|
67
|
+
- Usage visibility: tokens, time, and resource counters are native; monetary
|
|
68
|
+
cost is optional and only used when a metered host reports it.
|
|
69
|
+
- Read-only progress and managed sessions: host UIs can show lifecycle state,
|
|
70
|
+
attested tokens and Git-style change counters; `adapter task start` accepts
|
|
71
|
+
task text, Markdown or frozen run requests while preserving review/freeze
|
|
72
|
+
boundaries.
|
|
73
|
+
|
|
74
|
+
## Feature areas
|
|
75
|
+
|
|
76
|
+
### Plan and execute
|
|
77
|
+
|
|
78
|
+
- Reviewed specification and plan flow before implementation starts.
|
|
79
|
+
- Deterministic task packets for splitting work across agents.
|
|
80
|
+
- Execution records capture completion checks, blockers, retries, and final
|
|
81
|
+
proof; missing required evidence fails the step.
|
|
82
|
+
- Ready-made task templates cover bug fixes, idea-to-PR flow, PR review,
|
|
83
|
+
merge-conflict repair, and release-readiness work.
|
|
84
|
+
|
|
85
|
+
### Quality and proof
|
|
86
|
+
|
|
87
|
+
- Implementation audits compare results with the frozen plan and acceptance
|
|
88
|
+
evidence.
|
|
89
|
+
- For explicit bug and regression repair, the optional Bug Forensics profile
|
|
90
|
+
records reproduction, fingerprint, failure class, hypotheses, minimal patch,
|
|
91
|
+
regression proof, and reusable recipes.
|
|
92
|
+
- Optional proof-integrity evidence for high-risk final proofs: stable findings,
|
|
93
|
+
root-cause digests, fix-impact receipts, and hash chains.
|
|
94
|
+
- Cross-checks and runtime-policy receipts are opt-in controls; plans can
|
|
95
|
+
require proof for an external system update before acceptance.
|
|
96
|
+
- Optional Review Mesh covers leader-draft review, parallel research synthesis
|
|
97
|
+
and audit panels. The advisor can suggest a mode; `review-mesh prepare`
|
|
98
|
+
creates local reviewer packets; opted-in plans can require
|
|
99
|
+
assignment/result/synthesis/quorum receipts, but ALK core does not start hosts.
|
|
100
|
+
|
|
101
|
+
### Routing and resource control
|
|
102
|
+
|
|
103
|
+
- Compact context profiles, small-model packets, objective snapshots, and
|
|
104
|
+
local quality-cost learning help choose the lightest safe mode.
|
|
105
|
+
- Phase resource measurements reuse the usage export envelope for tokens,
|
|
106
|
+
duration, and resource counters without mandatory monetary accounting.
|
|
107
|
+
- Usage/session exports include tokens, resources, receipt digests, and optional
|
|
108
|
+
host-reported `cost_usd`.
|
|
109
|
+
|
|
110
|
+
### Security and containment
|
|
111
|
+
|
|
112
|
+
- Neutrality filters use host-supplied deny rules to keep local paths, secrets, trust roots, and signing keys out of portable artifacts.
|
|
113
|
+
- Host env files require explicit `--host-env-allow`; receipts store redacted metadata, not secret values or private env-file paths.
|
|
114
|
+
- Imports, diagnostics, and usage exports redact local paths and common secret markers before validation.
|
|
115
|
+
- Sandbox receipts separate runtime filesystem, network, process, and environment containment from git write-scope.
|
|
116
|
+
- Release security gates reject leaked local paths, credentials, and unsupported adapter or production claims.
|
|
117
|
+
|
|
118
|
+
### Adapters and interop
|
|
119
|
+
|
|
120
|
+
- Adapter contracts keep host-specific projections separate from lifecycle
|
|
121
|
+
schemas.
|
|
122
|
+
- New CLI hosts start as adapters: descriptor, command projection, environment
|
|
123
|
+
boundary, and verification evidence live outside the lifecycle core.
|
|
124
|
+
- Adapter capability checks and progress bridge support compare live receipts
|
|
125
|
+
and display lifecycle state without automatic maturity changes.
|
|
126
|
+
- Import mappers and issue-to-spec intake treat external workflows, agent
|
|
127
|
+
dialects, and tickets as untrusted draft inputs.
|
|
128
|
+
- Lightweight episode retrieval over receipt/session summaries keeps digest
|
|
129
|
+
provenance and explicit `chainVerified` or `chainUnchecked` state.
|
|
130
|
+
|
|
131
|
+
### Operations
|
|
132
|
+
|
|
133
|
+
- Runner recovery receipts cover attempt snapshot, restore, abandon, selected
|
|
134
|
+
attempt, worker lease, and heartbeat state.
|
|
135
|
+
- Read-only diagnostics, event feeds, managed lifecycle steps, progress watch
|
|
136
|
+
receipts, and change summaries inspect checkout and workflow state without
|
|
137
|
+
model calls.
|
|
138
|
+
|
|
139
|
+
## Daily flow
|
|
140
|
+
|
|
141
|
+
Spec -> frozen plan -> bounded work -> implementation audit -> final proof.
|
|
142
|
+
Core commands cover specification, plan, workflow, audit, adapters, imports, metrics,
|
|
143
|
+
policy, diagnostics, runner state, and adapter task intake. See [CLI reference](docs/reference/cli.md) and [Source of truth](docs/reference/source-of-truth.md).
|
|
144
|
+
|
|
145
|
+
## Adapter maturity
|
|
146
|
+
|
|
147
|
+
`EXPERIMENTAL` means offline checks exist; bounded live host conformance and
|
|
148
|
+
usage/resource calibration are still required before `VERIFIED`. The verified
|
|
149
|
+
claim is host-specific and also requires accepted redacted evidence and
|
|
150
|
+
lifecycle final proof for the tested host range. New hosts can be added as
|
|
151
|
+
adapters first and promoted only for the exact CLI/version/provider binding that
|
|
152
|
+
has evidence. Monetary accounting is required only for metered modes.
|
|
153
|
+
|
|
154
|
+
| Host | Current claim |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| Codex | `VERIFIED` for Codex CLI 0.145.0. Public Plugins Directory approval is not claimed. |
|
|
157
|
+
| Claude Code | `VERIFIED` for Claude Code 2.1.220. Official directory approval is not claimed. |
|
|
158
|
+
| OpenCode | `VERIFIED` for OpenCode CLI 1.18.9. npm publication is not claimed. |
|
|
159
|
+
| Hermes | `VERIFIED` for Hermes Agent v0.19.0. Public directory approval is not claimed. |
|
|
160
|
+
| Qwen Code | `VERIFIED` for Qwen Code 0.21.0 on the tested host-local provider/model binding. Public package approval is not claimed. |
|
|
161
|
+
| Cursor | `EXPERIMENTAL`; local safe inspection passed, but accepted live receipts are incomplete. |
|
|
162
|
+
| Gemini CLI | `EXPERIMENTAL`; local live canary is blocked by the current Gemini Code Assist tier. |
|
|
163
|
+
| Goose | `VERIFIED` for Goose 1.45.0 on the tested host-local provider/model binding. Public directory approval is not claimed. |
|
|
164
|
+
| Kimi Code | `EXPERIMENTAL`; live proof requires a configured provider and model alias. |
|
|
165
|
+
| Grok Build | `VERIFIED` for Grok Build 0.2.117 on the tested host-local provider/model binding. Public directory approval is not claimed. |
|
|
166
|
+
| OpenInterpreter | `VERIFIED` for `interpreter` 0.0.34 on the tested host-local provider/model binding. Public directory approval is not claimed. |
|
|
167
|
+
| Pi | `VERIFIED` for Pi 0.83.0 on the tested host-local provider/model binding. Public directory approval is not claimed. |
|
|
168
|
+
|
|
169
|
+
Adapter installation and maturity details live in [Adapter install](docs/adapters/install.md) and [Adapter support matrix](docs/adapters/support-matrix.md).
|
|
170
|
+
|
|
171
|
+
## Contract map
|
|
172
|
+
|
|
173
|
+
The public lifecycle surface is schema-backed. Full stable schema ids, compatibility rules, runner recovery, cross-check, Review Mesh, Bug Forensics and usage export details are listed in [Public contracts](docs/reference/public-contracts.md).
|
|
174
|
+
|
|
175
|
+
## Design boundaries
|
|
176
|
+
|
|
177
|
+
- The core stays provider-neutral. Concrete host commands and model bindings
|
|
178
|
+
belong to adapters or host-local profiles.
|
|
179
|
+
- New CLIs are integrated through adapters: descriptor, command projection,
|
|
180
|
+
environment boundary, and verification evidence. The lifecycle schemas remain
|
|
181
|
+
stable while host support grows.
|
|
182
|
+
- Small models get compact packets, deterministic checks, and explicit
|
|
183
|
+
next-action lists instead of long narrative state.
|
|
184
|
+
- Larger models keep the same gates; better reasoning does not bypass evidence.
|
|
185
|
+
- Public release claims are limited to tracked source files and redacted
|
|
186
|
+
evidence summaries.
|
|
187
|
+
- External dialect imports and retrieved episodes are context aids only; they
|
|
188
|
+
do not replace reviewed ALK source-of-truth artifacts.
|
|
189
|
+
- Optional cross-check, Review Mesh and runner recovery receipts add evidence
|
|
190
|
+
only when a task or plan requests them; they are not default multi-model execution.
|
|
191
|
+
|
|
192
|
+
## Documentation
|
|
193
|
+
|
|
194
|
+
- Start: [English documentation](docs/README.md), [Русская документация](docs/ru/README.md), [Quickstart](docs/guides/quickstart.md), [Lifecycle cookbook](docs/guides/lifecycle-cookbook.md), and [Code review workflows](docs/guides/code-review-workflows.md).
|
|
195
|
+
- Planning and adapters: [Issue to specification drafts](docs/guides/issue-to-spec.md), [Adapter install](docs/adapters/install.md), and [Adapter support matrix](docs/adapters/support-matrix.md).
|
|
196
|
+
- Reference: [System architecture](docs/architecture/system-architecture.md), [CLI reference](docs/reference/cli.md), [Source of truth](docs/reference/source-of-truth.md), [Managed lifecycle runner](docs/reference/managed-lifecycle-runner.md), [Managed adapter sessions](docs/reference/managed-adapter-sessions.md), [Implementation audit](docs/reference/implementation-audit.md), [Plan completeness](docs/reference/plan-completeness.md), [Public contracts](docs/reference/public-contracts.md), and [Readiness diagnostics](docs/reference/readiness-diagnostics.md).
|
|
197
|
+
- Quality and resources: [Small-model packets](docs/reference/small-model-packets.md), [model routing](docs/reference/model-routing.md), [adaptive policy](docs/reference/adaptive-lifecycle-policy.md), [quality-cost learning](docs/reference/quality-cost-learning.md), [lifecycle cost accounting](docs/reference/lifecycle-cost.md), [Usage export](docs/reference/usage-export.md), and [Evidence integrity](docs/reference/evidence-integrity.md).
|
|
198
|
+
- Profiles and operations: [Read-only status views](docs/reference/read-only-status-view.md), [Adapter progress bridge](docs/reference/automatic-progress-bridge.md), [Sandbox boundaries](docs/reference/sandbox-boundaries.md), [Import mappers](docs/reference/import-mappers.md), [Episode retrieval](docs/reference/episode-retrieval.md), [Runner recovery](docs/reference/runner-recovery.md), [Cross-check profile](docs/reference/cross-check-profile.md), [Review Mesh](docs/reference/review-mesh.md), [Bug Forensics profile](docs/reference/bug-forensics.md), and [Bug Forensics context budget](docs/reference/bug-forensics-context-budget.md).
|
|
199
|
+
- Release assets: [Task templates](templates/tasks/README.md) and [Release security](docs/security/release-security.md).
|
|
200
|
+
|
|
201
|
+
Apache-2.0. See [LICENSE](LICENSE).
|