forge-autonomy 2.3.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.
- forge/__init__.py +35 -0
- forge/__main__.py +20 -0
- forge/_version.py +38 -0
- forge/action_derivation_canary_closure.py +129 -0
- forge/action_derivation_evidence.py +101 -0
- forge/action_derivation_qualification.py +71 -0
- forge/action_derivation_reattempt.py +113 -0
- forge/ai_architect/__init__.py +5 -0
- forge/ai_architect/provider_registry.py +82 -0
- forge/architecture/__init__.py +8 -0
- forge/architecture/advisor.py +37 -0
- forge/architecture/presentation.py +21 -0
- forge/architecture/workspace.py +169 -0
- forge/autonomous_orchestrator.py +201 -0
- forge/business/__init__.py +9 -0
- forge/business/advisor.py +44 -0
- forge/business/ingress.py +186 -0
- forge/business/presentation.py +74 -0
- forge/business/workspace.py +141 -0
- forge/canary.py +134 -0
- forge/capabilities/__init__.py +11 -0
- forge/capabilities/delegation.py +150 -0
- forge/completion/__init__.py +5 -0
- forge/completion/assessor.py +53 -0
- forge/constitutional/__init__.py +5 -0
- forge/constitutional/assessor.py +38 -0
- forge/core/__init__.py +5 -0
- forge/core/json_store.py +24 -0
- forge/decision_evidence/__init__.py +5 -0
- forge/decision_evidence/repository.py +68 -0
- forge/dispatcher/__init__.py +13 -0
- forge/dispatcher/dispatcher.py +194 -0
- forge/execution/__init__.py +13 -0
- forge/execution/loop.py +346 -0
- forge/execution/presentation.py +14 -0
- forge/foundation/__init__.py +10 -0
- forge/foundation/loader.py +378 -0
- forge/generation_two.py +89 -0
- forge/generation_two_execution.py +179 -0
- forge/governance/__init__.py +26 -0
- forge/governance/execution_policy.py +83 -0
- forge/governance/profiles.py +93 -0
- forge/governance_authority.py +199 -0
- forge/intake.py +136 -0
- forge/integration/__init__.py +7 -0
- forge/integration/coordinator.py +100 -0
- forge/integration/repository.py +54 -0
- forge/knowledge/__init__.py +6 -0
- forge/knowledge/consumer.py +39 -0
- forge/knowledge/registry.py +65 -0
- forge/lifecycle/__init__.py +17 -0
- forge/lifecycle/store.py +445 -0
- forge/mission_amendment.py +35 -0
- forge/mission_scheduler.py +264 -0
- forge/models/__init__.py +561 -0
- forge/models/action.py +53 -0
- forge/models/action_derivation.py +284 -0
- forge/models/agent_policy.py +252 -0
- forge/models/ai_architect_provider.py +230 -0
- forge/models/ai_architect_session.py +147 -0
- forge/models/ai_provider_registry.py +140 -0
- forge/models/architecture_mission.py +113 -0
- forge/models/architecture_reasoning.py +257 -0
- forge/models/architecture_review.py +232 -0
- forge/models/codex_runtime_prompt.py +227 -0
- forge/models/constitutional_validation.py +109 -0
- forge/models/decision_evidence.py +202 -0
- forge/models/execution_host.py +289 -0
- forge/models/foundation.py +192 -0
- forge/models/historical_intent.py +212 -0
- forge/models/integration.py +173 -0
- forge/models/intent.py +265 -0
- forge/models/intent_authoring.py +119 -0
- forge/models/mission.py +301 -0
- forge/models/mission_candidate.py +108 -0
- forge/models/mission_planner.py +219 -0
- forge/models/mission_recommendation.py +222 -0
- forge/models/phase_completion.py +162 -0
- forge/models/planning.py +162 -0
- forge/models/producer.py +164 -0
- forge/models/prompt_artifact.py +234 -0
- forge/models/proposal.py +148 -0
- forge/models/runtime_prompt.py +245 -0
- forge/models/solution_template.py +64 -0
- forge/operator_identity.py +135 -0
- forge/planner/__init__.py +28 -0
- forge/planner/action_derivation.py +265 -0
- forge/planner/engine.py +49 -0
- forge/planner/openai_responses.py +660 -0
- forge/planner/provider_adapter.py +81 -0
- forge/planning/__init__.py +6 -0
- forge/planning/loader.py +232 -0
- forge/planning/registry.py +38 -0
- forge/portfolio_intelligence.py +110 -0
- forge/programme_authorization.py +194 -0
- forge/prompts/__init__.py +6 -0
- forge/prompts/codex_cli.py +121 -0
- forge/prompts/generator.py +106 -0
- forge/proposals/__init__.py +5 -0
- forge/proposals/generator.py +90 -0
- forge/provider_security.py +210 -0
- forge/qualification/__init__.py +13 -0
- forge/qualification/bootstrap_sequence.py +266 -0
- forge/qualification/generation_one.py +103 -0
- forge/qualification/installed_smoke.py +29 -0
- forge/recommendations/__init__.py +5 -0
- forge/recommendations/engine.py +113 -0
- forge/repository_truth.py +105 -0
- forge/review/__init__.py +5 -0
- forge/review/engine.py +80 -0
- forge/runtime/__init__.py +34 -0
- forge/runtime/bootstrap.py +360 -0
- forge/runtime/database.py +2191 -0
- forge/runtime/evidence.py +355 -0
- forge/runtime/execution_context.py +173 -0
- forge/runtime/runner.py +402 -0
- forge/runtime/service.py +127 -0
- forge/scheduler/__init__.py +7 -0
- forge/scheduler/adapter.py +245 -0
- forge/scheduler/ep_http_adapter.py +231 -0
- forge/scheduler/ep_v12.py +182 -0
- forge/scheduler/scheduler.py +180 -0
- forge/schemas/capability-registry-1.0.schema.json +23 -0
- forge/schemas/capability.schema.json +17 -0
- forge/schemas/common.schema.json +9 -0
- forge/schemas/decision-evidence-1.0.schema.json +20 -0
- forge/schemas/engineering-goal-0.5.schema.json +16 -0
- forge/schemas/engineering-increment-proposal-0.5.schema.json +20 -0
- forge/schemas/engineering-mode.schema.json +8 -0
- forge/schemas/engineering-plan-0.5.schema.json +29 -0
- forge/schemas/engineering-prompt-artifact-0.7.schema.json +26 -0
- forge/schemas/engineering-proposal-0.6.schema.json +75 -0
- forge/schemas/evidence-reference-0.5.schema.json +16 -0
- forge/schemas/foundation-document.schema.json +26 -0
- forge/schemas/governance-profile.schema.json +8 -0
- forge/schemas/knowledge-source-0.4.schema.json +23 -0
- forge/schemas/knowledge-source.schema.json +18 -0
- forge/schemas/mission-candidate-1.0.schema.json +27 -0
- forge/schemas/mission-candidate-1.1.schema.json +15 -0
- forge/schemas/phase-completion-1.0.schema.json +60 -0
- forge/schemas/planning-document-0.5.schema.json +17 -0
- forge/schemas/repository-catalog.schema.json +24 -0
- forge/schemas/repository.schema.json +18 -0
- forge/schemas/solution-template-1.0.schema.json +14 -0
- forge/schemas/workspace.schema.json +18 -0
- forge/solutions/__init__.py +6 -0
- forge/solutions/catalogue.py +54 -0
- forge/solutions/generator.py +64 -0
- forge/state/__init__.py +19 -0
- forge/state/mission_state.py +429 -0
- forge/workspace/__init__.py +1 -0
- forge_autonomy-2.3.1.dist-info/METADATA +474 -0
- forge_autonomy-2.3.1.dist-info/RECORD +156 -0
- forge_autonomy-2.3.1.dist-info/WHEEL +5 -0
- forge_autonomy-2.3.1.dist-info/entry_points.txt +2 -0
- forge_autonomy-2.3.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Forge-owned Business Governance Ingress for canonical recommendations.
|
|
2
|
+
|
|
3
|
+
This composition service deliberately accepts only records in the canonical
|
|
4
|
+
RecommendationLifecycleStore. It has no Engineering Platform dependency and
|
|
5
|
+
delegates identity allocation to RuntimeDatabase and downstream work to the
|
|
6
|
+
existing MissionRuntimeScheduler.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from hashlib import sha256
|
|
13
|
+
from typing import Protocol
|
|
14
|
+
|
|
15
|
+
from forge.governance import GovernanceRole, ResolvedGovernanceProfile
|
|
16
|
+
from forge.lifecycle import (
|
|
17
|
+
LifecycleError,
|
|
18
|
+
MissionCandidate,
|
|
19
|
+
RecommendationLifecycleStore,
|
|
20
|
+
RecommendationStatus,
|
|
21
|
+
)
|
|
22
|
+
from forge.mission_scheduler import MissionRuntimeScheduler, SchedulerResult
|
|
23
|
+
from forge.runtime import RuntimeDatabase
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class BusinessGovernanceIngressResult:
|
|
28
|
+
recommendation_id: str
|
|
29
|
+
result: str
|
|
30
|
+
business_decision_id: str | None
|
|
31
|
+
business_approval_status: str | None
|
|
32
|
+
architecture_state: str
|
|
33
|
+
mission_candidate_id: str | None
|
|
34
|
+
mission_id: str | None
|
|
35
|
+
mission_runtime_state: str | None
|
|
36
|
+
scheduler_state: str | None
|
|
37
|
+
decision_evidence_references: tuple[str, ...]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class SchedulerHandoff(Protocol):
|
|
41
|
+
"""The scheduler's existing handoff surface; ingress owns no submission."""
|
|
42
|
+
|
|
43
|
+
def evaluate(self, mission_id: str) -> SchedulerResult: ...
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class BusinessGovernanceIngress:
|
|
47
|
+
"""Approve and reconcile one existing recommendation through canonical state."""
|
|
48
|
+
|
|
49
|
+
def __init__(self, lifecycle: RecommendationLifecycleStore, runtime: RuntimeDatabase,
|
|
50
|
+
governance: ResolvedGovernanceProfile, *, scheduler: SchedulerHandoff | None = None) -> None:
|
|
51
|
+
self._lifecycle = lifecycle
|
|
52
|
+
self._runtime = runtime
|
|
53
|
+
self._governance = governance
|
|
54
|
+
self._scheduler = scheduler
|
|
55
|
+
|
|
56
|
+
def approve_recommendation(self, recommendation_id: str, *, actor: str | None,
|
|
57
|
+
occurred_at: str, rationale: str) -> BusinessGovernanceIngressResult:
|
|
58
|
+
if not actor:
|
|
59
|
+
return self._result(recommendation_id, "AUTHORIZATION_REQUIRED", architecture_state="NOT_REACHED")
|
|
60
|
+
allowed = self._governance.role_assignments.get(GovernanceRole.BUSINESS_OWNER, ())
|
|
61
|
+
if actor not in allowed:
|
|
62
|
+
return self._result(recommendation_id, "FORBIDDEN", architecture_state="NOT_REACHED")
|
|
63
|
+
try:
|
|
64
|
+
recommendation = self._lifecycle.resolve_recommendation(recommendation_id)
|
|
65
|
+
except LifecycleError as error:
|
|
66
|
+
result = "AMBIGUOUS" if "ambiguous" in str(error) else "NOT_FOUND"
|
|
67
|
+
return self._result(recommendation_id, result, architecture_state="NOT_REACHED")
|
|
68
|
+
recommendation_id = recommendation.id
|
|
69
|
+
if recommendation.status is RecommendationStatus.RECOMMENDED:
|
|
70
|
+
recommendation = self._lifecycle.transition(
|
|
71
|
+
recommendation_id, RecommendationStatus.BUSINESS_APPROVED, actor=actor,
|
|
72
|
+
occurred_at=occurred_at, rationale=rationale,
|
|
73
|
+
references=(recommendation.decision_evidence_reference,),
|
|
74
|
+
)
|
|
75
|
+
elif recommendation.status not in {
|
|
76
|
+
RecommendationStatus.BUSINESS_APPROVED, RecommendationStatus.ARCHITECTURE_APPROVED,
|
|
77
|
+
RecommendationStatus.MISSION_ALLOCATED,
|
|
78
|
+
}:
|
|
79
|
+
return self._result(recommendation_id, "INVALID_LIFECYCLE_STATE",
|
|
80
|
+
architecture_state=recommendation.status.value)
|
|
81
|
+
if recommendation.status is RecommendationStatus.BUSINESS_APPROVED:
|
|
82
|
+
return self._current(recommendation_id, "WAITING_ARCHITECTURE_APPROVAL",
|
|
83
|
+
architecture_state="WAITING_ARCHITECTURE_APPROVAL")
|
|
84
|
+
return self._reconcile_approved(recommendation_id, occurred_at)
|
|
85
|
+
|
|
86
|
+
def _reconcile_approved(self, recommendation_id: str, occurred_at: str) -> BusinessGovernanceIngressResult:
|
|
87
|
+
allocation = self._lifecycle.allocation_for_recommendation(recommendation_id)
|
|
88
|
+
candidate = self._lifecycle.candidate_for_recommendation(recommendation_id)
|
|
89
|
+
if allocation is None:
|
|
90
|
+
if candidate is None:
|
|
91
|
+
recommendation = self._lifecycle.get_recommendation(recommendation_id)
|
|
92
|
+
candidate = MissionCandidate(
|
|
93
|
+
id="candidate:" + sha256(recommendation_id.encode()).hexdigest()[:20],
|
|
94
|
+
recommendation_id=recommendation_id, title=recommendation.title,
|
|
95
|
+
objective=recommendation.engineering_summary,
|
|
96
|
+
scope=(recommendation.business_summary,),
|
|
97
|
+
acceptance_criteria=(recommendation.engineering_value,),
|
|
98
|
+
architecture_constraints=(recommendation.architectural_value,),
|
|
99
|
+
dependencies=recommendation.dependencies,
|
|
100
|
+
)
|
|
101
|
+
self._lifecycle.create_candidate(candidate)
|
|
102
|
+
allocation = self._lifecycle.allocate(
|
|
103
|
+
candidate.id, actor="forge", occurred_at=occurred_at,
|
|
104
|
+
rationale="Canonical Business and Architecture approvals permit Mission allocation.",
|
|
105
|
+
allocate_mission_id=lambda source, timestamp: self._runtime.allocate_next_mission_id(
|
|
106
|
+
source=f"recommendation:{source}", allocated_at=timestamp),
|
|
107
|
+
)
|
|
108
|
+
self._initialize_runtime(allocation.mission_id, recommendation_id, candidate.id, occurred_at, allocation)
|
|
109
|
+
scheduler_state = "SCHEDULER_ELIGIBLE"
|
|
110
|
+
if self._scheduler is not None:
|
|
111
|
+
scheduler_state = self._scheduler.evaluate(allocation.mission_id).state
|
|
112
|
+
return self._current(recommendation_id, "APPROVED_AND_ACTIVATED",
|
|
113
|
+
architecture_state="ARCHITECTURE_APPROVED", scheduler_state=scheduler_state)
|
|
114
|
+
|
|
115
|
+
def _initialize_runtime(self, mission_id: str, recommendation_id: str, candidate_id: str,
|
|
116
|
+
occurred_at: str, allocation: object) -> None:
|
|
117
|
+
try:
|
|
118
|
+
self._runtime.get_document("mission_state", mission_id)
|
|
119
|
+
runtime_exists = True
|
|
120
|
+
except Exception:
|
|
121
|
+
runtime_exists = False
|
|
122
|
+
# Runtime Decision Evidence has a foreign-key relationship to its
|
|
123
|
+
# Mission. Reserve only the canonical operational record first;
|
|
124
|
+
# the complete initialized graph is written below.
|
|
125
|
+
self._runtime.save_mission_state({
|
|
126
|
+
"mission_id": mission_id, "lifecycle": "ALLOCATED", "status": "ALLOCATED",
|
|
127
|
+
"progress": {"percent_complete": 0}, "resume": {"phase": "governance_activation"},
|
|
128
|
+
"execution_policy": {"mode": "mission_review", "execution_authorized": False},
|
|
129
|
+
})
|
|
130
|
+
if not self._runtime.has_document("decision_evidence", f"{mission_id}-governance-activation-1"):
|
|
131
|
+
self._runtime.record_decision_evidence({
|
|
132
|
+
"id": f"{mission_id}-governance-activation-1", "decision_type": "mission_planning",
|
|
133
|
+
"timestamp": occurred_at, "mission_context": {"artifact_id": mission_id},
|
|
134
|
+
"repository_context": {"artifact_id": "forge-governance"},
|
|
135
|
+
"reasoning_summary": "Allocated canonical Mission is activated from immutable governance evidence.",
|
|
136
|
+
"evidence_references": [{"artifact_id": item} for item in (
|
|
137
|
+
recommendation_id, candidate_id, allocation.business_decision_evidence_id,
|
|
138
|
+
allocation.architecture_decision_evidence_id, allocation.allocation_decision_evidence_id)],
|
|
139
|
+
"alternatives_considered": [{"id": "wait", "reason": "Both required approvals are already recorded."}],
|
|
140
|
+
"chosen_alternative": "activate", "confidence": {"score": 100, "mission_state": {"artifact_id": mission_id}},
|
|
141
|
+
"execution_receipt_references": [],
|
|
142
|
+
})
|
|
143
|
+
if not runtime_exists:
|
|
144
|
+
intent_id, action_id = f"{mission_id}-intent-1", f"{mission_id}-action-1"
|
|
145
|
+
intent = {"id": intent_id, "title": "Approved Mission activation", "objective": "Execute the approved bounded Mission.", "status": "APPROVED"}
|
|
146
|
+
action = {"id": action_id, "intent_id": intent_id, "objective": "Start the approved Mission according to its canonical scope.", "status": "READY", "dependencies": []}
|
|
147
|
+
self._runtime.save_mission_state({
|
|
148
|
+
"mission_id": mission_id, "mission_title": self._lifecycle.get_recommendation(recommendation_id).title,
|
|
149
|
+
"mission_recommendation_status": "MISSION_ALLOCATED", "business_summary": self._lifecycle.get_recommendation(recommendation_id).business_summary,
|
|
150
|
+
"engineering_summary": self._lifecycle.get_recommendation(recommendation_id).engineering_summary,
|
|
151
|
+
"lifecycle": "ACTIVE", "status": "ACTIVE", "progress": {"percent_complete": 0},
|
|
152
|
+
"resume": {"phase": "autonomous_scheduler"}, "execution_policy": {"mode": "mission_review", "execution_authorized": True},
|
|
153
|
+
"governance": {"business_approval": "approved", "architecture_approval": "approved"},
|
|
154
|
+
"engineering_intents": [intent], "engineering_actions": [action],
|
|
155
|
+
"current_engineering_intent": intent, "current_engineering_action": action,
|
|
156
|
+
"runtime_prompts": [{"id": f"scheduler-prompt:{action_id}:1", "action_id": action_id, "intent_id": intent_id, "status": "READY_FOR_ENGINEERING_PLATFORM"}],
|
|
157
|
+
"decision_evidence_ids": [f"{mission_id}-governance-activation-1"], "execution_receipt_references": [],
|
|
158
|
+
})
|
|
159
|
+
self._runtime.record_mission_lifecycle(mission_id, "allocated", occurred_at)
|
|
160
|
+
self._runtime.record_mission_lifecycle(mission_id, "active", occurred_at)
|
|
161
|
+
self._runtime.save_dispatcher_state(status="ACTIVE", mission_sequence=(mission_id,), active_mission_id=mission_id)
|
|
162
|
+
self._runtime.save_planning_state({"planner_version": "business-governance-ingress-1", "current_queue": [mission_id],
|
|
163
|
+
"pending_engineering_actions": [action_id], "blocked_engineering_actions": [],
|
|
164
|
+
"execution_policy": {"mode": "mission_review", "execution_authorized": True},
|
|
165
|
+
"planner_runtime_metadata": {"scheduler_state": "READY"}})
|
|
166
|
+
self._runtime.runtime_evidence().mission_runtime_projection(mission_id)
|
|
167
|
+
|
|
168
|
+
def _current(self, recommendation_id: str, result: str, *, architecture_state: str,
|
|
169
|
+
scheduler_state: str | None = None) -> BusinessGovernanceIngressResult:
|
|
170
|
+
recommendation = self._lifecycle.get_recommendation(recommendation_id)
|
|
171
|
+
history = self._lifecycle.history(recommendation_id)
|
|
172
|
+
decisions = tuple(item.id for item in history)
|
|
173
|
+
business = next((item.id for item in history if item.kind == "business_decision"), None)
|
|
174
|
+
candidate = self._lifecycle.candidate_for_recommendation(recommendation_id)
|
|
175
|
+
allocation = self._lifecycle.allocation_for_recommendation(recommendation_id)
|
|
176
|
+
runtime_state = None
|
|
177
|
+
if allocation is not None:
|
|
178
|
+
runtime_state = self._runtime.get_document("mission_state", allocation.mission_id).get("lifecycle")
|
|
179
|
+
return BusinessGovernanceIngressResult(recommendation_id, result, business,
|
|
180
|
+
"APPROVED" if business else None, architecture_state, None if candidate is None else candidate.id,
|
|
181
|
+
None if allocation is None else allocation.mission_id, runtime_state, scheduler_state, decisions)
|
|
182
|
+
|
|
183
|
+
@staticmethod
|
|
184
|
+
def _result(recommendation_id: str, result: str, *, architecture_state: str) -> BusinessGovernanceIngressResult:
|
|
185
|
+
return BusinessGovernanceIngressResult(recommendation_id, result, None, None, architecture_state,
|
|
186
|
+
None, None, None, None, ())
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Minimal deterministic view models for the human-facing Business Workspace."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from forge.models.mission_recommendation import MissionRecommendation
|
|
8
|
+
from forge.models.decision_evidence import DecisionEvidence
|
|
9
|
+
from forge.lifecycle import LifecycleDecisionEvidence, MissionRecommendation as LifecycleMissionRecommendation
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def render_mission_recommendation(recommendation: MissionRecommendation) -> dict[str, Any]:
|
|
13
|
+
"""Render advisory details without changing recommendation or Portfolio state."""
|
|
14
|
+
return {
|
|
15
|
+
"id": recommendation.id,
|
|
16
|
+
"title": recommendation.title,
|
|
17
|
+
"rationale": recommendation.rationale,
|
|
18
|
+
"business_value": recommendation.business_value,
|
|
19
|
+
"expected_engineering_value": recommendation.expected_engineering_value,
|
|
20
|
+
"risk_if_deferred": recommendation.risk_if_deferred,
|
|
21
|
+
"estimated_engineering_effort": recommendation.estimated_effort.value,
|
|
22
|
+
"confidence": recommendation.confidence.to_dict(),
|
|
23
|
+
"required_disciplines": [item.value for item in recommendation.required_disciplines],
|
|
24
|
+
"missing_disciplines": [item.value for item in recommendation.missing_disciplines],
|
|
25
|
+
"dependencies": recommendation.dependencies.to_dict(),
|
|
26
|
+
"origin": recommendation.origin.value,
|
|
27
|
+
"repository_evidence": [item.to_dict() for item in recommendation.repository_evidence],
|
|
28
|
+
"recommendation_source": recommendation.recommendation_source,
|
|
29
|
+
"decision_evidence_references": list(recommendation.decision_evidence_references),
|
|
30
|
+
"architecture_review_reference": recommendation.architecture_review_id,
|
|
31
|
+
"advisory": True,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def render_business_decision_evidence(evidence: DecisionEvidence) -> dict[str, Any]:
|
|
36
|
+
"""Expose recommendation rationale without granting or automating approval."""
|
|
37
|
+
return {"id": evidence.id, "decision_type": evidence.decision_type.value, "decision": evidence.decision,
|
|
38
|
+
"reasoning_summary": evidence.reasoning_summary, "alternatives": [item.to_dict() for item in evidence.alternatives_considered],
|
|
39
|
+
"chosen_alternative": evidence.chosen_alternative, "confidence": evidence.confidence.to_dict(),
|
|
40
|
+
"approval_state": evidence.approval_state.value, "advisory": True}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def render_persisted_mission_recommendation(recommendation: LifecycleMissionRecommendation,
|
|
44
|
+
*, selection_decision_evidence_id: str | None = None) -> dict[str, Any]:
|
|
45
|
+
"""Render lifecycle-owned recommendation fields required for Business review."""
|
|
46
|
+
return {
|
|
47
|
+
"id": recommendation.id, "recommendation_set_id": recommendation.recommendation_set_id,
|
|
48
|
+
"rank": recommendation.rank, "title": recommendation.title,
|
|
49
|
+
"mission_origin": recommendation.mission_origin, "business_value": recommendation.business_value,
|
|
50
|
+
"architectural_value": recommendation.architectural_value, "engineering_value": recommendation.engineering_value,
|
|
51
|
+
"confidence": recommendation.confidence, "dependencies": list(recommendation.dependencies),
|
|
52
|
+
"risk_if_deferred": recommendation.risk_if_deferred, "rationale": recommendation.business_summary,
|
|
53
|
+
"decision_evidence_reference": selection_decision_evidence_id or recommendation.decision_evidence_reference,
|
|
54
|
+
"recommendation_status": recommendation.status.value, "approval_status": "NOT_YET_APPROVED",
|
|
55
|
+
"advisory": True,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def render_persisted_recommendation_set(recommendations: tuple[LifecycleMissionRecommendation, ...],
|
|
60
|
+
decision_evidence: LifecycleDecisionEvidence) -> tuple[dict[str, Any], ...]:
|
|
61
|
+
"""Render a complete current set for Business review without inferring Mission state."""
|
|
62
|
+
if not recommendations or decision_evidence.decision_type != "MISSION_RECOMMENDATION":
|
|
63
|
+
raise ValueError("a recommendation selection Decision Evidence record is required")
|
|
64
|
+
if decision_evidence.selected_recommendation_id != recommendations[0].id:
|
|
65
|
+
raise ValueError("selection Decision Evidence must identify rank one")
|
|
66
|
+
if decision_evidence.ranked_alternatives != tuple(item.id for item in recommendations):
|
|
67
|
+
raise ValueError("selection Decision Evidence must preserve the ranked alternatives")
|
|
68
|
+
rendered = tuple(
|
|
69
|
+
render_persisted_mission_recommendation(item, selection_decision_evidence_id=decision_evidence.id)
|
|
70
|
+
for item in recommendations
|
|
71
|
+
)
|
|
72
|
+
if sum(item["recommendation_status"] == "RECOMMENDED" for item in rendered) != 1:
|
|
73
|
+
raise ValueError("Business Workspace requires exactly one recommended recommendation")
|
|
74
|
+
return rendered
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Local, auditable Business Workspace storage and business-only transitions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import sqlite3
|
|
7
|
+
from dataclasses import dataclass, replace
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from forge.models.mission_candidate import MissionCandidate, MissionCandidateStatus
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BusinessWorkspaceError(ValueError):
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class MissionCandidateHistoryEntry:
|
|
19
|
+
revision: int
|
|
20
|
+
event: str
|
|
21
|
+
actor: str
|
|
22
|
+
occurred_at: str
|
|
23
|
+
rationale: str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BusinessWorkspace:
|
|
27
|
+
"""Legacy local workspace storage for offline/tests; not production authority.
|
|
28
|
+
|
|
29
|
+
Supported Forge governance mutations use :meth:`for_runtime`, which is
|
|
30
|
+
bound to the resolved Runtime Instance and canonical evidence repository.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
@classmethod
|
|
34
|
+
def for_runtime(cls, database: object, repository: object, context: object) -> object:
|
|
35
|
+
"""Return the canonical runtime-bound Business approval service.
|
|
36
|
+
|
|
37
|
+
No filesystem path is accepted on this supported production route.
|
|
38
|
+
"""
|
|
39
|
+
from forge.governance_authority import CanonicalBusinessWorkspace
|
|
40
|
+
if getattr(repository, "database", None) is not database:
|
|
41
|
+
raise BusinessWorkspaceError("Business governance repository must use the resolved Runtime Instance")
|
|
42
|
+
return CanonicalBusinessWorkspace(repository, context)
|
|
43
|
+
|
|
44
|
+
def __init__(self, path: Path) -> None:
|
|
45
|
+
self._connection = sqlite3.connect(path)
|
|
46
|
+
self._connection.row_factory = sqlite3.Row
|
|
47
|
+
self._connection.executescript("""
|
|
48
|
+
CREATE TABLE IF NOT EXISTS mission_candidate (
|
|
49
|
+
candidate_id TEXT PRIMARY KEY, revision INTEGER NOT NULL, status TEXT NOT NULL, document TEXT NOT NULL
|
|
50
|
+
);
|
|
51
|
+
CREATE TABLE IF NOT EXISTS mission_candidate_history (
|
|
52
|
+
candidate_id TEXT NOT NULL, revision INTEGER NOT NULL, event TEXT NOT NULL, actor TEXT NOT NULL,
|
|
53
|
+
occurred_at TEXT NOT NULL, rationale TEXT NOT NULL, PRIMARY KEY(candidate_id, revision),
|
|
54
|
+
FOREIGN KEY(candidate_id) REFERENCES mission_candidate(candidate_id)
|
|
55
|
+
);
|
|
56
|
+
CREATE TRIGGER IF NOT EXISTS mission_candidate_history_no_update BEFORE UPDATE ON mission_candidate_history
|
|
57
|
+
BEGIN SELECT RAISE(ABORT, 'mission candidate history is append-only'); END;
|
|
58
|
+
CREATE TRIGGER IF NOT EXISTS mission_candidate_history_no_delete BEFORE DELETE ON mission_candidate_history
|
|
59
|
+
BEGIN SELECT RAISE(ABORT, 'mission candidate history is append-only'); END;
|
|
60
|
+
""")
|
|
61
|
+
self._connection.commit()
|
|
62
|
+
|
|
63
|
+
def close(self) -> None:
|
|
64
|
+
self._connection.close()
|
|
65
|
+
|
|
66
|
+
def __enter__(self) -> "BusinessWorkspace":
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
def __exit__(self, *_: object) -> None:
|
|
70
|
+
self.close()
|
|
71
|
+
|
|
72
|
+
def create(self, candidate: MissionCandidate, *, actor: str, occurred_at: str) -> MissionCandidate:
|
|
73
|
+
if candidate.status is not MissionCandidateStatus.BUSINESS_REVIEW:
|
|
74
|
+
raise BusinessWorkspaceError("new candidates must enter business_review")
|
|
75
|
+
self._write(candidate, 1, "created", actor, occurred_at, candidate.business_rationale, insert=True)
|
|
76
|
+
return self.get(candidate.id)
|
|
77
|
+
|
|
78
|
+
def get(self, candidate_id: str) -> MissionCandidate:
|
|
79
|
+
row = self._connection.execute("SELECT document FROM mission_candidate WHERE candidate_id = ?", (candidate_id,)).fetchone()
|
|
80
|
+
if row is None:
|
|
81
|
+
raise BusinessWorkspaceError(f"unknown mission candidate: {candidate_id}")
|
|
82
|
+
return MissionCandidate.from_dict(json.loads(row["document"]))
|
|
83
|
+
|
|
84
|
+
def list(self, *, status: MissionCandidateStatus | None = None) -> tuple[MissionCandidate, ...]:
|
|
85
|
+
query = "SELECT document FROM mission_candidate" + (" WHERE status = ?" if status else "") + " ORDER BY candidate_id"
|
|
86
|
+
rows = self._connection.execute(query, () if status is None else (status.value,)).fetchall()
|
|
87
|
+
return tuple(MissionCandidate.from_dict(json.loads(row["document"])) for row in rows)
|
|
88
|
+
|
|
89
|
+
def refine(self, candidate_id: str, *, actor: str, occurred_at: str, rationale: str, **changes: object) -> MissionCandidate:
|
|
90
|
+
candidate, revision = self._current(candidate_id)
|
|
91
|
+
if candidate.status is not MissionCandidateStatus.BUSINESS_REVIEW:
|
|
92
|
+
raise BusinessWorkspaceError("only candidates in business_review may be refined")
|
|
93
|
+
allowed = {"title", "summary", "business_objective", "business_value", "priority", "business_rationale", "maturity", "dependencies", "required_disciplines"}
|
|
94
|
+
if not changes or set(changes) - allowed:
|
|
95
|
+
raise BusinessWorkspaceError("business refinement may update only business candidate fields")
|
|
96
|
+
refined = replace(candidate, **changes)
|
|
97
|
+
self._write(refined, revision + 1, "refined", actor, occurred_at, rationale)
|
|
98
|
+
return self.get(candidate_id)
|
|
99
|
+
|
|
100
|
+
def approve_for_architecture(self, candidate_id: str, *, actor: str, occurred_at: str, rationale: str) -> MissionCandidate:
|
|
101
|
+
return self._transition(candidate_id, MissionCandidateStatus.APPROVED_FOR_ARCHITECTURE, "approved_for_architecture", actor, occurred_at, rationale)
|
|
102
|
+
|
|
103
|
+
def reject(self, candidate_id: str, *, actor: str, occurred_at: str, rationale: str) -> MissionCandidate:
|
|
104
|
+
return self._transition(candidate_id, MissionCandidateStatus.REJECTED, "rejected", actor, occurred_at, rationale)
|
|
105
|
+
|
|
106
|
+
def archive(self, candidate_id: str, *, actor: str, occurred_at: str, rationale: str) -> MissionCandidate:
|
|
107
|
+
return self._transition(candidate_id, MissionCandidateStatus.ARCHIVED, "archived", actor, occurred_at, rationale)
|
|
108
|
+
|
|
109
|
+
def history(self, candidate_id: str) -> tuple[MissionCandidateHistoryEntry, ...]:
|
|
110
|
+
self.get(candidate_id)
|
|
111
|
+
rows = self._connection.execute("SELECT revision, event, actor, occurred_at, rationale FROM mission_candidate_history WHERE candidate_id = ? ORDER BY revision", (candidate_id,)).fetchall()
|
|
112
|
+
return tuple(MissionCandidateHistoryEntry(**dict(row)) for row in rows)
|
|
113
|
+
|
|
114
|
+
def _transition(self, candidate_id: str, target: MissionCandidateStatus, event: str, actor: str, occurred_at: str, rationale: str) -> MissionCandidate:
|
|
115
|
+
candidate, revision = self._current(candidate_id)
|
|
116
|
+
if candidate.status is not MissionCandidateStatus.BUSINESS_REVIEW:
|
|
117
|
+
raise BusinessWorkspaceError(f"mission candidate transition {candidate.status.value} -> {target.value} is not permitted")
|
|
118
|
+
if not all((actor, occurred_at, rationale)):
|
|
119
|
+
raise BusinessWorkspaceError("business decision requires actor, time, and rationale")
|
|
120
|
+
self._write(replace(candidate, status=target), revision + 1, event, actor, occurred_at, rationale)
|
|
121
|
+
return self.get(candidate_id)
|
|
122
|
+
|
|
123
|
+
def _current(self, candidate_id: str) -> tuple[MissionCandidate, int]:
|
|
124
|
+
row = self._connection.execute("SELECT revision, document FROM mission_candidate WHERE candidate_id = ?", (candidate_id,)).fetchone()
|
|
125
|
+
if row is None:
|
|
126
|
+
raise BusinessWorkspaceError(f"unknown mission candidate: {candidate_id}")
|
|
127
|
+
return MissionCandidate.from_dict(json.loads(row["document"])), row["revision"]
|
|
128
|
+
|
|
129
|
+
def _write(self, candidate: MissionCandidate, revision: int, event: str, actor: str, occurred_at: str, rationale: str, *, insert: bool = False) -> None:
|
|
130
|
+
if not all((actor, occurred_at, rationale)):
|
|
131
|
+
raise BusinessWorkspaceError("business change requires actor, time, and rationale")
|
|
132
|
+
document = json.dumps(candidate.to_dict(), sort_keys=True, separators=(",", ":"), ensure_ascii=False)
|
|
133
|
+
try:
|
|
134
|
+
with self._connection:
|
|
135
|
+
if insert:
|
|
136
|
+
self._connection.execute("INSERT INTO mission_candidate VALUES (?, ?, ?, ?)", (candidate.id, revision, candidate.status.value, document))
|
|
137
|
+
else:
|
|
138
|
+
self._connection.execute("UPDATE mission_candidate SET revision = ?, status = ?, document = ? WHERE candidate_id = ?", (revision, candidate.status.value, document, candidate.id))
|
|
139
|
+
self._connection.execute("INSERT INTO mission_candidate_history VALUES (?, ?, ?, ?, ?, ?)", (candidate.id, revision, event, actor, occurred_at, rationale))
|
|
140
|
+
except sqlite3.IntegrityError as error:
|
|
141
|
+
raise BusinessWorkspaceError(f"mission candidate already exists: {candidate.id}") from error
|
forge/canary.py
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""Deterministic End-to-End Bootstrap Mission Canary qualification scenario."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from forge.intake import MissionIntake
|
|
9
|
+
from forge.models import (
|
|
10
|
+
CodexCliRuntimePromptRequest, EngineeringAction, EngineeringIntent,
|
|
11
|
+
EngineeringActionStatus, ExecutionHostCompatibility, IntentApproval,
|
|
12
|
+
IntentCategory, IntentReference, IntentStatus, IntentTraceability,
|
|
13
|
+
RepositoryState,
|
|
14
|
+
)
|
|
15
|
+
from forge.models.execution_host import ExecutionEvidenceOutcome
|
|
16
|
+
from forge.models.mission import EngineeringMission, MissionIntentMembership, MissionScope, MissionStatus
|
|
17
|
+
from forge.prompts import CodexCliRuntimePromptRenderer
|
|
18
|
+
from forge.runtime import BootstrapMissionRunner, RuntimeDatabase
|
|
19
|
+
from forge.scheduler import BootstrapMissionScheduler
|
|
20
|
+
from forge.scheduler.adapter import (
|
|
21
|
+
BootstrapExecutionHostAdapter, EngineeringPlatformInboxReceipt, EngineeringPlatformReport,
|
|
22
|
+
EngineeringPlatformReportOutcome, ExecutionHostConfiguration,
|
|
23
|
+
)
|
|
24
|
+
from forge.state import MissionExecutionStatus, MissionStateStore
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
CANARY_MISSION_ID = "forge-bootstrap-mission-canary"
|
|
28
|
+
CANARY_INTENT_ID = "forge-bootstrap-mission-canary-intent"
|
|
29
|
+
CANARY_ACTION_ID = "forge-bootstrap-mission-canary-action"
|
|
30
|
+
CANARY_CORRELATION_ID = "forge-bootstrap-mission-canary-correlation"
|
|
31
|
+
CANARY_HOST_RUN_ID = "forge-bootstrap-mission-canary-host-run"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _reference(name: str) -> IntentReference:
|
|
35
|
+
return IntentReference(name, "1", f"local://{name}")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True)
|
|
39
|
+
class CanaryQualificationReport:
|
|
40
|
+
answer: str
|
|
41
|
+
mission_id: str
|
|
42
|
+
intent_id: str
|
|
43
|
+
action_id: str
|
|
44
|
+
runtime_prompt_id: str
|
|
45
|
+
execution_host_run_id: str
|
|
46
|
+
correlation_id: str
|
|
47
|
+
evidence_kinds: tuple[str, ...]
|
|
48
|
+
admission_levels: tuple[str, ...]
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class _Configuration:
|
|
52
|
+
def resolve(self, host_id: str) -> ExecutionHostConfiguration:
|
|
53
|
+
return ExecutionHostConfiguration(host_id, "2.4", ("GENESIS",), ("codex_cli", "local_git"),
|
|
54
|
+
"engineering-platform>=1.5.0", "canary://engineering-platform-1.5")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class _Preflight:
|
|
58
|
+
def __init__(self) -> None:
|
|
59
|
+
self.levels: list[str] = []
|
|
60
|
+
|
|
61
|
+
def admit(self, compatibility: ExecutionHostCompatibility, configuration: ExecutionHostConfiguration) -> None:
|
|
62
|
+
if compatibility.execution_host_contract_version != configuration.host_contract_version:
|
|
63
|
+
raise ValueError("Execution Host Preflight Level 1 failed")
|
|
64
|
+
self.levels.append("execution_host_level_1")
|
|
65
|
+
if compatibility.execution_mode not in configuration.supported_execution_modes:
|
|
66
|
+
raise ValueError("Workspace Preflight Level 2 failed")
|
|
67
|
+
self.levels.append("workspace_level_2")
|
|
68
|
+
if not set(compatibility.required_capabilities).issubset(configuration.supported_capabilities):
|
|
69
|
+
raise ValueError("Capability Preflight Level 3 failed")
|
|
70
|
+
self.levels.append("capability_level_3")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class _Inbox:
|
|
74
|
+
def __init__(self) -> None:
|
|
75
|
+
self.request = None
|
|
76
|
+
|
|
77
|
+
def submit(self, request: object) -> EngineeringPlatformInboxReceipt:
|
|
78
|
+
self.request = request
|
|
79
|
+
return EngineeringPlatformInboxReceipt(CANARY_HOST_RUN_ID, "forge-bootstrap-mission-canary-receipt", "engineering-platform-1.5", "2026-08-03T23:30:00Z")
|
|
80
|
+
|
|
81
|
+
def receipt_for(self, correlation_id: str) -> EngineeringPlatformInboxReceipt | None:
|
|
82
|
+
return EngineeringPlatformInboxReceipt(CANARY_HOST_RUN_ID, "forge-bootstrap-mission-canary-receipt", "engineering-platform-1.5", "2026-08-03T23:30:00Z") if self.request and correlation_id == CANARY_CORRELATION_ID else None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class _Reports:
|
|
86
|
+
def __init__(self, inbox: _Inbox) -> None: self._inbox = inbox
|
|
87
|
+
|
|
88
|
+
def report_for(self, run_id: str) -> EngineeringPlatformReport | None:
|
|
89
|
+
request = self._inbox.request
|
|
90
|
+
if run_id != CANARY_HOST_RUN_ID or request is None:
|
|
91
|
+
return None
|
|
92
|
+
return EngineeringPlatformReport(CANARY_HOST_RUN_ID, "forge-bootstrap-mission-canary-report",
|
|
93
|
+
EngineeringPlatformReportOutcome.COMPLETE, "canary-repository-revision", "sha256:" + "c" * 64,
|
|
94
|
+
("validation:bootstrap-mission-canary",), ("evidence:engineering-platform-1.5",),
|
|
95
|
+
"2026-08-03T23:30:00Z", "2026-08-03T23:31:00Z", "forge-bootstrap-mission-canary-receipt",
|
|
96
|
+
request.execution_host_id, request.correlation_id, request.runtime_prompt_id, request.mission_id,
|
|
97
|
+
request.intent_id, request.intent_revision, request.action_id, execution_duration_ms=60_000)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def run_bootstrap_mission_canary(state_path: Path) -> CanaryQualificationReport:
|
|
101
|
+
"""Run the complete adapter pipeline once with deterministic host evidence."""
|
|
102
|
+
reference = _reference("canary-approval")
|
|
103
|
+
intent = EngineeringIntent(CANARY_INTENT_ID, "1", "Bootstrap Mission Canary Intent", "Qualify the pipeline.",
|
|
104
|
+
IntentCategory.IMPLEMENTATION, IntentTraceability((reference,), (reference,), (reference,), (reference,), (reference,)),
|
|
105
|
+
approval=IntentApproval("architecture", "2026-08-03T23:29:00Z", reference), status=IntentStatus.APPROVED)
|
|
106
|
+
mission = EngineeringMission(CANARY_MISSION_ID, "1", "Bootstrap Mission Canary", "Qualify one bounded Mission.",
|
|
107
|
+
MissionScope(("one deterministic execution",), ("continuous execution",)),
|
|
108
|
+
(MissionIntentMembership(1, intent.id, intent.revision),), status=MissionStatus.ACTIVE)
|
|
109
|
+
action = EngineeringAction(1, CANARY_ACTION_ID, intent.id, intent.revision, "Execute the one qualified canary Action.",
|
|
110
|
+
("repository evidence",), status=EngineeringActionStatus.READY)
|
|
111
|
+
preflight, inbox = _Preflight(), _Inbox()
|
|
112
|
+
adapter = BootstrapExecutionHostAdapter(_Configuration(), preflight, inbox, _Reports(inbox))
|
|
113
|
+
with MissionStateStore(RuntimeDatabase(state_path.parent)) as store:
|
|
114
|
+
intake = MissionIntake(store, lambda: "2026-08-03T23:29:30Z")
|
|
115
|
+
intake.admit(mission, (intent,), (action,))
|
|
116
|
+
def render(_intent: object, active: EngineeringAction):
|
|
117
|
+
return CodexCliRuntimePromptRenderer().render(CodexCliRuntimePromptRequest(
|
|
118
|
+
mission, intent, active, RepositoryState("forge", "canary-repository-revision", "sha256:" + "c" * 64, "2026-08-03T23:29:30Z"),
|
|
119
|
+
("one action only",), ("bootstrap canary validation",),
|
|
120
|
+
ExecutionHostCompatibility("2.4", "GENESIS", ("codex_cli", "local_git"), "engineering-platform>=1.5.0"),
|
|
121
|
+
))
|
|
122
|
+
runner = BootstrapMissionRunner(store, BootstrapMissionScheduler(), adapter, render, host_id="engineering-platform-1.5",
|
|
123
|
+
workspace_id="forge", repository_id="forge", clock=lambda: "2026-08-03T23:29:30Z",
|
|
124
|
+
correlation_id_factory=lambda: CANARY_CORRELATION_ID)
|
|
125
|
+
# Intake owns state creation; the Runner advances that same durable state.
|
|
126
|
+
result = runner.run(CANARY_MISSION_ID)
|
|
127
|
+
if result.status is not MissionExecutionStatus.COMPLETED:
|
|
128
|
+
raise ValueError("Bootstrap Mission Canary did not complete")
|
|
129
|
+
request = result.execution_correlation["request"]
|
|
130
|
+
evidence = result.execution_evidence
|
|
131
|
+
assert evidence is not None
|
|
132
|
+
return CanaryQualificationReport("YES", mission.id, intent.id, action.id, request["runtime_prompt"]["id"],
|
|
133
|
+
result.execution_correlation["host_run_id"], request["correlation_id"],
|
|
134
|
+
("execution_host", "engineering", "repository", "validation", "mission_completion"), tuple(preflight.levels))
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Capability registry and delegation framework."""
|
|
2
|
+
|
|
3
|
+
from .delegation import (
|
|
4
|
+
CAPABILITY_REGISTRY_SCHEMA_VERSION, DELEGATION_SCHEMA_VERSION, CapabilityAssessment,
|
|
5
|
+
CapabilityAvailability, CapabilityExecutionMode, CapabilityOwner, CapabilityRegistration,
|
|
6
|
+
CapabilityRegistry, DelegationApprovalState, DelegationRequest, DelegationResultState,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
__all__ = ["CAPABILITY_REGISTRY_SCHEMA_VERSION", "DELEGATION_SCHEMA_VERSION", "CapabilityAssessment",
|
|
10
|
+
"CapabilityAvailability", "CapabilityExecutionMode", "CapabilityOwner", "CapabilityRegistration",
|
|
11
|
+
"CapabilityRegistry", "DelegationApprovalState", "DelegationRequest", "DelegationResultState"]
|