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.
Files changed (156) hide show
  1. forge/__init__.py +35 -0
  2. forge/__main__.py +20 -0
  3. forge/_version.py +38 -0
  4. forge/action_derivation_canary_closure.py +129 -0
  5. forge/action_derivation_evidence.py +101 -0
  6. forge/action_derivation_qualification.py +71 -0
  7. forge/action_derivation_reattempt.py +113 -0
  8. forge/ai_architect/__init__.py +5 -0
  9. forge/ai_architect/provider_registry.py +82 -0
  10. forge/architecture/__init__.py +8 -0
  11. forge/architecture/advisor.py +37 -0
  12. forge/architecture/presentation.py +21 -0
  13. forge/architecture/workspace.py +169 -0
  14. forge/autonomous_orchestrator.py +201 -0
  15. forge/business/__init__.py +9 -0
  16. forge/business/advisor.py +44 -0
  17. forge/business/ingress.py +186 -0
  18. forge/business/presentation.py +74 -0
  19. forge/business/workspace.py +141 -0
  20. forge/canary.py +134 -0
  21. forge/capabilities/__init__.py +11 -0
  22. forge/capabilities/delegation.py +150 -0
  23. forge/completion/__init__.py +5 -0
  24. forge/completion/assessor.py +53 -0
  25. forge/constitutional/__init__.py +5 -0
  26. forge/constitutional/assessor.py +38 -0
  27. forge/core/__init__.py +5 -0
  28. forge/core/json_store.py +24 -0
  29. forge/decision_evidence/__init__.py +5 -0
  30. forge/decision_evidence/repository.py +68 -0
  31. forge/dispatcher/__init__.py +13 -0
  32. forge/dispatcher/dispatcher.py +194 -0
  33. forge/execution/__init__.py +13 -0
  34. forge/execution/loop.py +346 -0
  35. forge/execution/presentation.py +14 -0
  36. forge/foundation/__init__.py +10 -0
  37. forge/foundation/loader.py +378 -0
  38. forge/generation_two.py +89 -0
  39. forge/generation_two_execution.py +179 -0
  40. forge/governance/__init__.py +26 -0
  41. forge/governance/execution_policy.py +83 -0
  42. forge/governance/profiles.py +93 -0
  43. forge/governance_authority.py +199 -0
  44. forge/intake.py +136 -0
  45. forge/integration/__init__.py +7 -0
  46. forge/integration/coordinator.py +100 -0
  47. forge/integration/repository.py +54 -0
  48. forge/knowledge/__init__.py +6 -0
  49. forge/knowledge/consumer.py +39 -0
  50. forge/knowledge/registry.py +65 -0
  51. forge/lifecycle/__init__.py +17 -0
  52. forge/lifecycle/store.py +445 -0
  53. forge/mission_amendment.py +35 -0
  54. forge/mission_scheduler.py +264 -0
  55. forge/models/__init__.py +561 -0
  56. forge/models/action.py +53 -0
  57. forge/models/action_derivation.py +284 -0
  58. forge/models/agent_policy.py +252 -0
  59. forge/models/ai_architect_provider.py +230 -0
  60. forge/models/ai_architect_session.py +147 -0
  61. forge/models/ai_provider_registry.py +140 -0
  62. forge/models/architecture_mission.py +113 -0
  63. forge/models/architecture_reasoning.py +257 -0
  64. forge/models/architecture_review.py +232 -0
  65. forge/models/codex_runtime_prompt.py +227 -0
  66. forge/models/constitutional_validation.py +109 -0
  67. forge/models/decision_evidence.py +202 -0
  68. forge/models/execution_host.py +289 -0
  69. forge/models/foundation.py +192 -0
  70. forge/models/historical_intent.py +212 -0
  71. forge/models/integration.py +173 -0
  72. forge/models/intent.py +265 -0
  73. forge/models/intent_authoring.py +119 -0
  74. forge/models/mission.py +301 -0
  75. forge/models/mission_candidate.py +108 -0
  76. forge/models/mission_planner.py +219 -0
  77. forge/models/mission_recommendation.py +222 -0
  78. forge/models/phase_completion.py +162 -0
  79. forge/models/planning.py +162 -0
  80. forge/models/producer.py +164 -0
  81. forge/models/prompt_artifact.py +234 -0
  82. forge/models/proposal.py +148 -0
  83. forge/models/runtime_prompt.py +245 -0
  84. forge/models/solution_template.py +64 -0
  85. forge/operator_identity.py +135 -0
  86. forge/planner/__init__.py +28 -0
  87. forge/planner/action_derivation.py +265 -0
  88. forge/planner/engine.py +49 -0
  89. forge/planner/openai_responses.py +660 -0
  90. forge/planner/provider_adapter.py +81 -0
  91. forge/planning/__init__.py +6 -0
  92. forge/planning/loader.py +232 -0
  93. forge/planning/registry.py +38 -0
  94. forge/portfolio_intelligence.py +110 -0
  95. forge/programme_authorization.py +194 -0
  96. forge/prompts/__init__.py +6 -0
  97. forge/prompts/codex_cli.py +121 -0
  98. forge/prompts/generator.py +106 -0
  99. forge/proposals/__init__.py +5 -0
  100. forge/proposals/generator.py +90 -0
  101. forge/provider_security.py +210 -0
  102. forge/qualification/__init__.py +13 -0
  103. forge/qualification/bootstrap_sequence.py +266 -0
  104. forge/qualification/generation_one.py +103 -0
  105. forge/qualification/installed_smoke.py +29 -0
  106. forge/recommendations/__init__.py +5 -0
  107. forge/recommendations/engine.py +113 -0
  108. forge/repository_truth.py +105 -0
  109. forge/review/__init__.py +5 -0
  110. forge/review/engine.py +80 -0
  111. forge/runtime/__init__.py +34 -0
  112. forge/runtime/bootstrap.py +360 -0
  113. forge/runtime/database.py +2191 -0
  114. forge/runtime/evidence.py +355 -0
  115. forge/runtime/execution_context.py +173 -0
  116. forge/runtime/runner.py +402 -0
  117. forge/runtime/service.py +127 -0
  118. forge/scheduler/__init__.py +7 -0
  119. forge/scheduler/adapter.py +245 -0
  120. forge/scheduler/ep_http_adapter.py +231 -0
  121. forge/scheduler/ep_v12.py +182 -0
  122. forge/scheduler/scheduler.py +180 -0
  123. forge/schemas/capability-registry-1.0.schema.json +23 -0
  124. forge/schemas/capability.schema.json +17 -0
  125. forge/schemas/common.schema.json +9 -0
  126. forge/schemas/decision-evidence-1.0.schema.json +20 -0
  127. forge/schemas/engineering-goal-0.5.schema.json +16 -0
  128. forge/schemas/engineering-increment-proposal-0.5.schema.json +20 -0
  129. forge/schemas/engineering-mode.schema.json +8 -0
  130. forge/schemas/engineering-plan-0.5.schema.json +29 -0
  131. forge/schemas/engineering-prompt-artifact-0.7.schema.json +26 -0
  132. forge/schemas/engineering-proposal-0.6.schema.json +75 -0
  133. forge/schemas/evidence-reference-0.5.schema.json +16 -0
  134. forge/schemas/foundation-document.schema.json +26 -0
  135. forge/schemas/governance-profile.schema.json +8 -0
  136. forge/schemas/knowledge-source-0.4.schema.json +23 -0
  137. forge/schemas/knowledge-source.schema.json +18 -0
  138. forge/schemas/mission-candidate-1.0.schema.json +27 -0
  139. forge/schemas/mission-candidate-1.1.schema.json +15 -0
  140. forge/schemas/phase-completion-1.0.schema.json +60 -0
  141. forge/schemas/planning-document-0.5.schema.json +17 -0
  142. forge/schemas/repository-catalog.schema.json +24 -0
  143. forge/schemas/repository.schema.json +18 -0
  144. forge/schemas/solution-template-1.0.schema.json +14 -0
  145. forge/schemas/workspace.schema.json +18 -0
  146. forge/solutions/__init__.py +6 -0
  147. forge/solutions/catalogue.py +54 -0
  148. forge/solutions/generator.py +64 -0
  149. forge/state/__init__.py +19 -0
  150. forge/state/mission_state.py +429 -0
  151. forge/workspace/__init__.py +1 -0
  152. forge_autonomy-2.3.1.dist-info/METADATA +474 -0
  153. forge_autonomy-2.3.1.dist-info/RECORD +156 -0
  154. forge_autonomy-2.3.1.dist-info/WHEEL +5 -0
  155. forge_autonomy-2.3.1.dist-info/entry_points.txt +2 -0
  156. forge_autonomy-2.3.1.dist-info/top_level.txt +1 -0
forge/__init__.py ADDED
@@ -0,0 +1,35 @@
1
+ """Forge's local-only Foundation Model, knowledge, and planning contracts."""
2
+
3
+ from .models import (
4
+ ArchitectureMission,
5
+ Capability,
6
+ EngineeringMode,
7
+ GovernanceProfile,
8
+ KnowledgeSource,
9
+ MissionCandidate,
10
+ Repository,
11
+ RepositoryCatalog,
12
+ Workspace,
13
+ )
14
+ from .foundation import FoundationDocument, FoundationDocumentLoader, ValidationIssue, ValidationReport
15
+ from .planning import EngineeringPlanningDocument, PlanningDocumentLoader, PlanningRegistry, PlanningValidationReport
16
+
17
+ __all__ = [
18
+ "ArchitectureMission",
19
+ "Capability",
20
+ "EngineeringMode",
21
+ "GovernanceProfile",
22
+ "KnowledgeSource",
23
+ "MissionCandidate",
24
+ "Repository",
25
+ "RepositoryCatalog",
26
+ "Workspace",
27
+ "FoundationDocument",
28
+ "FoundationDocumentLoader",
29
+ "ValidationIssue",
30
+ "ValidationReport",
31
+ "EngineeringPlanningDocument",
32
+ "PlanningDocumentLoader",
33
+ "PlanningRegistry",
34
+ "PlanningValidationReport",
35
+ ]
forge/__main__.py ADDED
@@ -0,0 +1,20 @@
1
+ """Read-only command entrypoint for the packaged Forge runtime."""
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+
6
+ from ._version import canonical_version
7
+
8
+
9
+ def main(argv: list[str] | None = None) -> int:
10
+ parser = argparse.ArgumentParser(
11
+ prog="forge",
12
+ description="Forge mission planning and evidence interpretation runtime.",
13
+ )
14
+ parser.add_argument("--version", action="version", version=canonical_version())
15
+ parser.parse_args(argv)
16
+ return 0
17
+
18
+
19
+ if __name__ == "__main__":
20
+ raise SystemExit(main())
forge/_version.py ADDED
@@ -0,0 +1,38 @@
1
+ """Resolve Forge's canonical version without creating a second allocator."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ from importlib.metadata import PackageNotFoundError, version as installed_version
6
+ from pathlib import Path
7
+ import re
8
+
9
+
10
+ _VERSION = re.compile(r"^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$")
11
+
12
+
13
+ def _source_version() -> str | None:
14
+ """Read the sole source of authority when this is a source distribution."""
15
+ manifest = Path(__file__).resolve().parents[1] / "product-version.json"
16
+ try:
17
+ value = json.loads(manifest.read_text(encoding="utf-8"))
18
+ except (OSError, json.JSONDecodeError):
19
+ return None
20
+ candidate = value.get("version") if isinstance(value, dict) else None
21
+ return candidate if isinstance(candidate, str) and _VERSION.fullmatch(candidate) else None
22
+
23
+
24
+ def canonical_version() -> str:
25
+ """Return the source manifest version or installed distribution metadata."""
26
+ source = _source_version()
27
+ if source is not None:
28
+ return source
29
+ try:
30
+ value = installed_version("forge-autonomy")
31
+ except PackageNotFoundError as error:
32
+ raise RuntimeError("Forge version metadata is unavailable") from error
33
+ if _VERSION.fullmatch(value) is None:
34
+ raise RuntimeError("Forge version metadata is not a stable X.Y.Z version")
35
+ return value
36
+
37
+
38
+ __version__ = canonical_version()
@@ -0,0 +1,129 @@
1
+ """Canonical immutable closure for a bounded Action-Derivation qualification canary."""
2
+ from __future__ import annotations
3
+
4
+ from hashlib import sha256
5
+ import json
6
+
7
+ from forge.governance_authority import CanonicalGovernanceRepository, GovernanceCapability
8
+ from forge.operator_identity import OperatorContext
9
+ from forge.runtime.database import _timestamp
10
+
11
+
12
+ _NOT_QUALIFIED = (
13
+ "ACTION_MATERIALIZATION", "AUTONOMOUS_NEXT_MISSION_LOOP", "EP_DISPATCH",
14
+ "EP_RESULT_OBSERVATION", "EXECUTION_ADMISSION",
15
+ )
16
+
17
+
18
+ def _digest(value: object) -> str:
19
+ return "sha256:" + sha256(json.dumps(value, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
20
+
21
+
22
+ class CanonicalActionDerivationCanaryClosureService:
23
+ """Close a qualified planning-only canary without changing Mission lifecycle."""
24
+
25
+ def __init__(self, repository: CanonicalGovernanceRepository) -> None:
26
+ self.repository, self.database = repository, repository.database
27
+
28
+ @staticmethod
29
+ def _operator_id(context: OperatorContext) -> str:
30
+ return sha256(context.generated_uid.encode()).hexdigest()[:16]
31
+
32
+ def close(self, *, mission_id: str, successor_attempt_id: str,
33
+ qualification_decision_id: str, operator_context: OperatorContext) -> dict[str, object]:
34
+ if not self.repository.operators.authorize(operator_context):
35
+ raise PermissionError("trusted operator context is required")
36
+ state = self.database.get_document("mission_state", mission_id)
37
+ successor = self.database.get_document("action_derivations", successor_attempt_id)
38
+ required = (
39
+ "predecessor_attempt_id", "authorization_id", "preflight_receipt_id", "main_head",
40
+ "snapshot_digest", "evidence_digest", "effective_contract_digest",
41
+ "provider_configuration", "generation_request_digest", "validation_digest",
42
+ )
43
+ if (state.get("status") != "APPROVED_PLANNABLE" or successor.get("mission_id") != mission_id
44
+ or successor.get("lifecycle") != "VALIDATED" or successor.get("validation_result") != "PASS"
45
+ or successor.get("provider_output_untrusted") is not True
46
+ or successor.get("runtime_action_executed") is not False
47
+ or successor.get("action_materialized") is not False
48
+ or successor.get("engineering_side_effects", 0) != 0
49
+ or any(not isinstance(successor.get(key), str) or not successor[key] for key in required)):
50
+ raise PermissionError("canonical validated non-executing Action-Derivation evidence is incomplete")
51
+ predecessor = self.database.get_document("action_derivations", successor["predecessor_attempt_id"])
52
+ authorization = self.database.get_document(
53
+ "action_derivation_reattempt_authorizations", successor["authorization_id"]
54
+ )
55
+ if (predecessor.get("mission_id") != mission_id or predecessor.get("lifecycle") != "FAILED"
56
+ or authorization.get("successor_attempt_id") != successor_attempt_id
57
+ or authorization.get("predecessor_attempt_id") != successor["predecessor_attempt_id"]
58
+ or any(authorization.get(left) != successor.get(right) for left, right in (
59
+ ("planning_snapshot_digest", "snapshot_digest"), ("effective_contract_digest", "effective_contract_digest"),
60
+ ("evidence_digest", "evidence_digest"), ("g011_policy_digest", "provider_configuration"),
61
+ ("provider_request_digest", "generation_request_digest"), ("main_head", "main_head")))):
62
+ raise PermissionError("canonical successor lineage is stale or conflicting")
63
+ if self.database._connection.execute(
64
+ "SELECT 1 FROM action_derivation_reattempt_consumptions WHERE authorization_id=?",
65
+ (successor["authorization_id"],),
66
+ ).fetchone() is None:
67
+ raise PermissionError("validated successor lacks a consumed canonical reattempt authorization")
68
+ receipt = self.database.consumed_token_preflight_receipt(successor["preflight_receipt_id"])
69
+ if any(receipt.get(left) != successor.get(right) for left, right in (
70
+ ("mission_id", "mission_id"), ("main_head", "main_head"), ("policy_digest", "provider_configuration"),
71
+ ("request_digest", "generation_request_digest"), ("evidence_digest", "evidence_digest"),
72
+ ("effective_contract_digest", "effective_contract_digest"),
73
+ )):
74
+ raise PermissionError("canonical token-preflight receipt is stale or conflicting")
75
+ try:
76
+ decision = self.repository.decision(qualification_decision_id)
77
+ except ValueError as error:
78
+ raise PermissionError("post-canary Security qualification is absent") from error
79
+ summary = {
80
+ "mission_id": mission_id, "successor_attempt_id": successor_attempt_id,
81
+ "predecessor_attempt_id": successor["predecessor_attempt_id"],
82
+ "authorization_id": successor["authorization_id"], "main_head": successor["main_head"],
83
+ "snapshot_digest": successor["snapshot_digest"], "evidence_digest": successor["evidence_digest"],
84
+ "effective_contract_digest": successor["effective_contract_digest"],
85
+ "g011_policy_digest": successor["provider_configuration"],
86
+ "request_digest": successor["generation_request_digest"],
87
+ "preflight_receipt_id": successor["preflight_receipt_id"],
88
+ "validation_digest": successor["validation_digest"],
89
+ }
90
+ if (decision.get("subject_id") != successor_attempt_id
91
+ or decision.get("subject_revision") != successor["validation_digest"]
92
+ or decision.get("capability") != GovernanceCapability.SECURITY_APPROVAL.value
93
+ or decision.get("decision") != "approved"
94
+ or decision.get("scope") != ["ACTION_DERIVATION_QUALIFICATION"]
95
+ or decision.get("gates") != ["POST_CANARY_SECURITY_REVIEW"]
96
+ or decision.get("predecessor_digest") != _digest(summary)):
97
+ raise PermissionError("post-canary Security qualification is absent, stale, or conflicting")
98
+ closure_id = f"action-derivation-canary-closure:{mission_id}:{successor_attempt_id}"
99
+ document = {
100
+ "closure_id": closure_id, "mission_id": mission_id,
101
+ "successor_attempt_id": successor_attempt_id,
102
+ "predecessor_attempt_id": successor["predecessor_attempt_id"],
103
+ "qualification_decision_id": qualification_decision_id,
104
+ "qualification_decision_digest": _digest(decision),
105
+ "effective_contract_digest": successor["effective_contract_digest"],
106
+ "evidence_digest": successor["evidence_digest"],
107
+ "g011_policy_digest": successor["provider_configuration"],
108
+ "provider_request_digest": successor["generation_request_digest"],
109
+ "preflight_receipt_id": successor["preflight_receipt_id"],
110
+ "reattempt_authorization_id": successor["authorization_id"],
111
+ "main_head": successor["main_head"], "qualified_capability": "ACTION_DERIVATION",
112
+ "not_qualified_capabilities": list(_NOT_QUALIFIED),
113
+ "installation_id": operator_context.installation_id,
114
+ "operator_id": self._operator_id(operator_context),
115
+ "runtime_id": self.database.metadata["runtime_id"], "closed_at": _timestamp(),
116
+ }
117
+ existing = self.database._connection.execute(
118
+ "SELECT document FROM action_derivation_canary_closures WHERE successor_attempt_id=?",
119
+ (successor_attempt_id,),
120
+ ).fetchone()
121
+ if existing is not None:
122
+ persisted = json.loads(existing["document"])
123
+ expected = {key: value for key, value in document.items() if key != "closed_at"}
124
+ actual = {key: value for key, value in persisted.items() if key not in ("closed_at", "digest")}
125
+ if actual != expected:
126
+ raise PermissionError("existing Action-Derivation canary closure is stale or conflicting")
127
+ return persisted
128
+ document["digest"] = _digest(document)
129
+ return self.database.create_action_derivation_canary_closure(document)
@@ -0,0 +1,101 @@
1
+ """Canonical, pre-planning evidence producer for Action Derivation.
2
+
3
+ It deliberately derives only from a Mission's immutable admission contract;
4
+ it neither observes repositories, creates approvals, nor grants execution.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ from hashlib import sha256
9
+ import json
10
+
11
+ from forge.governance_authority import CanonicalGovernanceRepository
12
+ from forge.models.architecture_mission import ArchitectureMission
13
+ from forge.models.intent import IntentReference
14
+ from forge.models.mission_planner import (ApprovedScope, MissionPlannerInput, MissionPlanningState,
15
+ PlanningEvidence, PlanningInputKind)
16
+ from forge.runtime.database import RuntimeDatabase, RuntimeIntegrityError
17
+ from forge.mission_amendment import MissionAmendmentService
18
+
19
+
20
+ def _digest(value: object) -> str:
21
+ return "sha256:" + sha256(json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode()).hexdigest()
22
+
23
+
24
+ class ActionDerivationEvidenceError(ValueError):
25
+ """The immutable admission contract cannot produce a complete evidence set."""
26
+
27
+
28
+ class CanonicalActionDerivationEvidenceProducer:
29
+ """The sole runtime-owned producer for generic pre-planning evidence."""
30
+
31
+ _NO_REPOSITORY_TARGET = "NO_REPOSITORY_TARGET=TRUE"
32
+ _CAPABILITIES = ("action-derivation-planning", "deterministic-action-materialization")
33
+
34
+ def __init__(self, database: RuntimeDatabase, repository: CanonicalGovernanceRepository) -> None:
35
+ if repository.database is not database:
36
+ raise ValueError("evidence producer requires the canonical governance Runtime Database")
37
+ self.database, self.repository = database, repository
38
+
39
+ def produce(self, mission_id: str) -> dict[str, object]:
40
+ state = self.database.get_document("mission_state", mission_id)
41
+ contract, _ = MissionAmendmentService(self.repository).effective_contract(mission_id)
42
+ if not isinstance(contract, dict) or state.get("status") != "APPROVED_PLANNABLE":
43
+ raise ActionDerivationEvidenceError("Mission is not canonically approved/plannable")
44
+ required = ("installation_id", "candidate_id", "subject_revision", "business_decision_id",
45
+ "architecture_decision_id", "planning", "envelope_digest", "write_scope", "mission")
46
+ if any(item not in contract for item in required) or contract["write_scope"] != "NONE":
47
+ raise ActionDerivationEvidenceError("Mission admission contract is incomplete or has non-NONE write scope")
48
+ mission = ArchitectureMission.from_dict(contract["mission"])
49
+ if mission.id != mission_id or mission.candidate_id != contract["candidate_id"]:
50
+ raise RuntimeIntegrityError("Mission admission contract identity mismatch")
51
+ if contract["installation_id"] != self.repository.operators.installation_id():
52
+ raise ActionDerivationEvidenceError("Mission admission contract is cross-installation")
53
+ planning = contract["planning"]
54
+ if not isinstance(planning, dict) or planning.get("provenance_revision") != contract["subject_revision"]:
55
+ raise ActionDerivationEvidenceError("Mission planning evidence revision is stale")
56
+ for decision_id in (contract["business_decision_id"], contract["architecture_decision_id"]):
57
+ decision = self.repository.decision(decision_id)
58
+ if decision["subject_id"] != contract["candidate_id"] or decision["subject_revision"] != contract["subject_revision"]:
59
+ raise ActionDerivationEvidenceError("Mission approval lineage is inconsistent")
60
+ if self._NO_REPOSITORY_TARGET not in mission.engineering_constraints:
61
+ raise ActionDerivationEvidenceError("Mission lacks explicit approved NO_REPOSITORY_TARGET evidence")
62
+ context = {"kind": "NO_REPOSITORY_TARGET", "mission_id": mission_id,
63
+ "installation_id": contract["installation_id"], "candidate_id": contract["candidate_id"],
64
+ "subject_revision": contract["subject_revision"], "write_scope": contract["write_scope"],
65
+ "classification": "planning_only_no_repository_target",
66
+ "provenance": {"envelope_digest": contract["envelope_digest"], "mission_constraint": self._NO_REPOSITORY_TARGET}}
67
+ context["digest"] = _digest(context)
68
+ projection = {"kind": "PRE_PLANNING_CANONICAL_PROJECTION", "mission_id": mission_id,
69
+ "installation_id": contract["installation_id"], "candidate_id": contract["candidate_id"],
70
+ "subject_revision": contract["subject_revision"], "architecture_decision_id": contract["architecture_decision_id"],
71
+ "envelope_digest": contract["envelope_digest"], "planning": planning}
72
+ projection["digest"] = _digest(projection)
73
+ catalogue = {"kind": "RUNTIME_CAPABILITY_CATALOGUE", "mission_id": mission_id,
74
+ "installation_id": contract["installation_id"], "candidate_id": contract["candidate_id"],
75
+ "subject_revision": contract["subject_revision"], "envelope_digest": contract["envelope_digest"],
76
+ "capabilities": list(self._CAPABILITIES), "effective_write_scope": "NONE", "grants_authority": False}
77
+ catalogue["digest"] = _digest(catalogue)
78
+ mission_evidence = {"kind": "MISSION_STATE", "mission_id": mission_id, "digest": _digest(state)}
79
+ value = {"evidence_set_id": "action-derivation-evidence-" + contract["envelope_digest"][7:23],
80
+ "mission_id": mission_id, "installation_id": contract["installation_id"],
81
+ "candidate_id": contract["candidate_id"], "subject_revision": contract["subject_revision"],
82
+ "envelope_digest": contract["envelope_digest"], "mission_state": mission_evidence,
83
+ "repository_context": context, "architecture_review": projection, "capability_catalogue": catalogue}
84
+ value["digest"] = _digest(value)
85
+ return self.database.create_action_derivation_evidence_set(value)
86
+
87
+ def planner_input(self, mission_id: str) -> MissionPlannerInput:
88
+ evidence_set = self.produce(mission_id)
89
+ mission = ArchitectureMission.from_dict(self.database.get_document("mission_state", mission_id)["admission_contract"]["mission"])
90
+ reference = IntentReference("architecture-decision", str(evidence_set["subject_revision"]),
91
+ "runtime://governance/" + str(evidence_set["architecture_review"]["architecture_decision_id"]))
92
+ documents = ((PlanningInputKind.MISSION_STATE, evidence_set["mission_state"]),
93
+ (PlanningInputKind.REPOSITORY_CONTEXT, evidence_set["repository_context"]),
94
+ (PlanningInputKind.ARCHITECTURE_REVIEW, evidence_set["architecture_review"]),
95
+ (PlanningInputKind.CAPABILITY_CATALOGUE, evidence_set["capability_catalogue"]))
96
+ evidence = tuple(PlanningEvidence(kind, str(document["kind"]).lower(), str(evidence_set["subject_revision"]),
97
+ "runtime://action-derivation-evidence/" + str(evidence_set["evidence_set_id"]), str(document["digest"]))
98
+ for kind, document in documents)
99
+ capability = mission.required_capabilities[0]
100
+ scopes = tuple(ApprovedScope(scope, capability, (reference,), (), allow_provider_derivation=True) for scope in mission.scope)
101
+ return MissionPlannerInput(mission, MissionPlanningState(mission.id, int(self.database.get_document("mission_state", mission_id)["revision"])), evidence, scopes)
@@ -0,0 +1,71 @@
1
+ """Canonical post-canary qualification decision for validated derivations."""
2
+ from __future__ import annotations
3
+
4
+ from hashlib import sha256
5
+ import json
6
+
7
+ from forge.governance_authority import CanonicalGovernanceRepository, GovernanceCapability, GovernanceDecision
8
+ from forge.operator_identity import OperatorContext
9
+
10
+
11
+ def _digest(value: object) -> str:
12
+ return "sha256:" + sha256(json.dumps(value, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
13
+
14
+
15
+ class CanonicalActionDerivationQualificationService:
16
+ """Records no new authority: a typed projection of SECURITY_APPROVAL only."""
17
+ def __init__(self, repository: CanonicalGovernanceRepository) -> None:
18
+ self.repository, self.database = repository, repository.database
19
+
20
+ def qualify(self, *, mission_id: str, successor_attempt_id: str,
21
+ operator_context: OperatorContext, decision_id: str) -> str:
22
+ if not self.repository.operators.authorize(operator_context):
23
+ raise PermissionError("trusted operator context is required")
24
+ state = self.database.get_document("mission_state", mission_id)
25
+ successor = self.database.get_document("action_derivations", successor_attempt_id)
26
+ required = ("predecessor_attempt_id", "authorization_id", "preflight_receipt_id", "main_head", "evidence_digest",
27
+ "effective_contract_digest", "provider_configuration", "generation_request_digest",
28
+ "validation_digest")
29
+ if (state.get("status") != "APPROVED_PLANNABLE" or successor.get("mission_id") != mission_id
30
+ or successor.get("lifecycle") != "VALIDATED" or successor.get("validation_result") != "PASS"
31
+ or successor.get("provider_output_untrusted") is not True
32
+ or successor.get("runtime_action_executed") is not False
33
+ or successor.get("action_materialized") is not False
34
+ or any(not isinstance(successor.get(key), str) or not successor[key] for key in required)):
35
+ raise PermissionError("canonical validated non-executing Action-Derivation evidence is incomplete")
36
+ predecessor = self.database.get_document("action_derivations", successor["predecessor_attempt_id"])
37
+ authorization = self.database.get_document("action_derivation_reattempt_authorizations", successor["authorization_id"])
38
+ if (predecessor.get("lifecycle") != "FAILED"
39
+ or authorization.get("successor_attempt_id") != successor_attempt_id
40
+ or authorization.get("predecessor_attempt_id") != successor["predecessor_attempt_id"]
41
+ or any(authorization.get(left) != successor.get(right) for left, right in (
42
+ ("planning_snapshot_digest", "snapshot_digest"), ("g011_policy_digest", "provider_configuration"),
43
+ ("evidence_digest", "evidence_digest"), ("effective_contract_digest", "effective_contract_digest"),
44
+ ("provider_request_digest", "generation_request_digest"), ("main_head", "main_head")))):
45
+ raise PermissionError("canonical successor lineage is stale or conflicting")
46
+ consumed_authorization = self.database._connection.execute(
47
+ "SELECT 1 FROM action_derivation_reattempt_consumptions WHERE authorization_id = ?",
48
+ (successor["authorization_id"],),
49
+ ).fetchone()
50
+ if consumed_authorization is None:
51
+ raise PermissionError("validated successor lacks a consumed canonical reattempt authorization")
52
+ receipt = self.database.consumed_token_preflight_receipt(successor["preflight_receipt_id"])
53
+ if any(receipt.get(left) != successor.get(right) for left, right in (
54
+ ("mission_id", "mission_id"), ("main_head", "main_head"), ("policy_digest", "provider_configuration"),
55
+ ("request_digest", "generation_request_digest"), ("evidence_digest", "evidence_digest"),
56
+ ("effective_contract_digest", "effective_contract_digest"),
57
+ )):
58
+ raise PermissionError("canonical token-preflight receipt is stale or conflicting")
59
+ summary = {"mission_id": mission_id, "successor_attempt_id": successor_attempt_id,
60
+ "predecessor_attempt_id": successor["predecessor_attempt_id"],
61
+ "authorization_id": successor["authorization_id"],
62
+ "main_head": successor["main_head"], "snapshot_digest": successor["snapshot_digest"],
63
+ "evidence_digest": successor["evidence_digest"],
64
+ "effective_contract_digest": successor["effective_contract_digest"],
65
+ "g011_policy_digest": successor["provider_configuration"],
66
+ "request_digest": successor["generation_request_digest"],
67
+ "preflight_receipt_id": successor["preflight_receipt_id"],
68
+ "validation_digest": successor["validation_digest"]}
69
+ return self.repository.record(GovernanceDecision(
70
+ decision_id, successor_attempt_id, successor["validation_digest"], GovernanceCapability.SECURITY_APPROVAL,
71
+ "approved", ("ACTION_DERIVATION_QUALIFICATION",), ("POST_CANARY_SECURITY_REVIEW",), _digest(summary)), operator_context)
@@ -0,0 +1,113 @@
1
+ """Canonical, explicitly authorized successor lineage for Action Derivation.
2
+
3
+ This module allocates no provider work. It records the one durable authority
4
+ needed before a later, separately authorized token preflight may be attempted.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ from hashlib import sha256
9
+ import json
10
+ import re
11
+ import uuid
12
+
13
+ from forge.action_derivation_evidence import CanonicalActionDerivationEvidenceProducer
14
+ from forge.governance_authority import CanonicalGovernanceRepository
15
+ from forge.models.action_derivation import PlanningSnapshot
16
+ from forge.operator_identity import OperatorContext
17
+ from forge.planner.openai_responses import _G011PolicySnapshot, _digest
18
+ from forge.planner.provider_adapter import ProviderDerivationRequest
19
+ from forge.runtime.database import RuntimeIntegrityError, _timestamp
20
+
21
+
22
+ REQUEST_SEMANTICS_CHANGED = "REQUEST_SEMANTICS_CHANGED"
23
+
24
+
25
+ class NewPlanningRequired(RuntimeIntegrityError):
26
+ """The candidate drifted beyond the narrow reattempt authority."""
27
+
28
+
29
+ class CanonicalActionDerivationReattemptService:
30
+ """The sole application path for a governed Action-Derivation successor.
31
+
32
+ It accepts a canonical adapter/configuration rather than caller-provided
33
+ policy, evidence, database, or request digests. Persisting authorization
34
+ never calls a provider and never consumes a preflight receipt.
35
+ """
36
+ def __init__(self, adapter, governance_repository: CanonicalGovernanceRepository) -> None:
37
+ database = adapter.configuration.policy_service.db
38
+ if governance_repository.database is not database:
39
+ raise PermissionError("reattempt lineage requires the canonical governance RuntimeDatabase")
40
+ self.adapter, self.repository, self.database = adapter, governance_repository, database
41
+
42
+ @staticmethod
43
+ def _record_digest(value: dict[str, object]) -> str:
44
+ return "sha256:" + sha256(json.dumps(value, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
45
+
46
+ def authorize_successor(self, *, predecessor_attempt_id: str, operator_context: OperatorContext,
47
+ rationale: str, reason: str = REQUEST_SEMANTICS_CHANGED) -> dict[str, object]:
48
+ if reason != REQUEST_SEMANTICS_CHANGED:
49
+ raise PermissionError("unsupported Action-Derivation reattempt reason")
50
+ if not isinstance(rationale, str) or not rationale.strip() or len(rationale) > 512:
51
+ raise ValueError("a bounded explicit reattempt rationale is required")
52
+ if not self.repository.operators.authorize(operator_context):
53
+ raise PermissionError("trusted operator context is required for a reattempt")
54
+ predecessor = self.database.get_document("action_derivations", predecessor_attempt_id)
55
+ if predecessor.get("lifecycle") != "FAILED":
56
+ raise PermissionError("only a terminal failed Action-Derivation attempt may have a successor")
57
+ required = ("mission_id", "snapshot_digest", "effective_contract_digest", "evidence_digest",
58
+ "provider_configuration", "generation_request_digest", "main_head")
59
+ if any(not isinstance(predecessor.get(field), str) or not predecessor[field] for field in required):
60
+ raise RuntimeIntegrityError("predecessor lacks complete canonical reattempt provenance")
61
+
62
+ mission_id = predecessor["mission_id"]
63
+ producer = CanonicalActionDerivationEvidenceProducer(self.database, self.repository)
64
+ planning_input = producer.planner_input(mission_id)
65
+ snapshot = PlanningSnapshot.from_planner_input(planning_input)
66
+ policy = self.adapter.configuration.current_policy()
67
+ policy_digest = _G011PolicySnapshot.from_policy(policy).digest
68
+ boundary = self.adapter.configuration.preflight_authority.boundary_for(mission_id)
69
+ state = self.database.get_document("mission_state", mission_id)
70
+ if state.get("status") != "APPROVED_PLANNABLE":
71
+ raise NewPlanningRequired("Mission is no longer approved/plannable")
72
+ if any((snapshot.digest != predecessor["snapshot_digest"],
73
+ boundary.effective_contract_digest != predecessor["effective_contract_digest"],
74
+ boundary.evidence_digest != predecessor["evidence_digest"],
75
+ policy_digest != predecessor["provider_configuration"])):
76
+ raise NewPlanningRequired("Mission, planning, effective contract, evidence, or G011 policy drift requires new planning")
77
+
78
+ successor_attempt_id = f"reattempt-{uuid.uuid4()}"
79
+ request = ProviderDerivationRequest(successor_attempt_id, snapshot, policy.provider_id, policy.model)
80
+ request_digest = _digest(self.adapter._body(request, policy))
81
+ if request_digest == predecessor["generation_request_digest"]:
82
+ raise PermissionError("an unchanged request digest is an exact replay, not a successor attempt")
83
+ sequence_row = self.database._connection.execute(
84
+ "SELECT MAX(attempt_sequence) AS sequence FROM action_derivation_reattempt_authorizations WHERE predecessor_attempt_id=?",
85
+ (predecessor_attempt_id,),
86
+ ).fetchone()
87
+ if sequence_row is not None and sequence_row["sequence"] is not None:
88
+ # A second child of this predecessor is never silently given a new
89
+ # sequence; its different reason needs separate governance semantics.
90
+ raise PermissionError("a predecessor already has a governed successor attempt")
91
+ operator_id = self.repository._operator_id(operator_context)
92
+ document: dict[str, object] = {
93
+ "authorization_id": f"action-derivation-reattempt-{uuid.uuid4()}",
94
+ "successor_attempt_id": successor_attempt_id,
95
+ "mission_id": mission_id,
96
+ "predecessor_attempt_id": predecessor_attempt_id,
97
+ "predecessor_terminal_state": predecessor["lifecycle"],
98
+ "attempt_sequence": 2,
99
+ "planning_snapshot_digest": snapshot.digest,
100
+ "effective_contract_digest": boundary.effective_contract_digest,
101
+ "evidence_digest": boundary.evidence_digest,
102
+ "g011_policy_digest": policy_digest,
103
+ "provider_request_digest": request_digest,
104
+ "main_head": boundary.main_head,
105
+ "reattempt_reason": reason,
106
+ "rationale": rationale.strip(),
107
+ "authorization_identity": operator_id,
108
+ "installation_id": operator_context.installation_id,
109
+ "created_at": _timestamp(),
110
+ }
111
+ document["digest"] = self._record_digest(document)
112
+ return self.database.create_action_derivation_reattempt_authorization(document)
113
+
@@ -0,0 +1,5 @@
1
+ """Provider-neutral AI Architect registry and future provider namespace."""
2
+
3
+ from .provider_registry import AIProviderRegistry
4
+
5
+ __all__ = ["AIProviderRegistry"]
@@ -0,0 +1,82 @@
1
+ """Local deterministic registry for AI Architect Provider declarations only."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from forge.models import (
6
+ AIProviderMetadata,
7
+ ProviderQualification,
8
+ ProviderQualificationState,
9
+ ProviderSelection,
10
+ ProviderSelectionRequest,
11
+ ProviderStatus,
12
+ WorkspaceProviderConfiguration,
13
+ )
14
+
15
+
16
+ class AIProviderRegistry:
17
+ """Register and select declared providers without loading or invoking one."""
18
+
19
+ def __init__(self) -> None:
20
+ self._providers: dict[str, AIProviderMetadata] = {}
21
+ self._qualifications: dict[str, ProviderQualification] = {}
22
+
23
+ def register(self, provider: AIProviderMetadata, qualification: ProviderQualification) -> AIProviderMetadata:
24
+ """Register one repository-qualified declaration, rejecting duplicate ids."""
25
+ if provider.id in self._providers:
26
+ raise ValueError("provider id is already registered")
27
+ if qualification.provider_id != provider.id or qualification.provider_version != provider.version:
28
+ raise ValueError("provider qualification must match provider identity and version")
29
+ if qualification.state is not provider.qualification_state:
30
+ raise ValueError("provider qualification must match provider qualification state")
31
+ self._providers[provider.id] = provider
32
+ self._qualifications[provider.id] = qualification
33
+ return provider
34
+
35
+ def list(self) -> tuple[AIProviderMetadata, ...]:
36
+ """Return all declarations in stable provider-id and version order."""
37
+ return tuple(sorted(self._providers.values(), key=lambda item: (item.id, item.version)))
38
+
39
+ def qualification(self, provider_id: str) -> ProviderQualification | None:
40
+ """Return the repository-owned qualification record, if registered."""
41
+ return self._qualifications.get(provider_id)
42
+
43
+ def select(
44
+ self,
45
+ request: ProviderSelectionRequest,
46
+ configuration: WorkspaceProviderConfiguration,
47
+ ) -> ProviderSelection:
48
+ """Select an active qualified declaration by policy, never invoke it."""
49
+ if configuration.workspace_id != request.workspace_id:
50
+ raise ValueError("workspace provider configuration must match selection request")
51
+ qualified = tuple(
52
+ provider
53
+ for provider in self.list()
54
+ if provider.qualification_state is ProviderQualificationState.QUALIFIED
55
+ and provider.status is ProviderStatus.ACTIVE
56
+ and request.capability in provider.capabilities
57
+ and request.reasoning_mode in provider.reasoning_modes
58
+ )
59
+ if not qualified:
60
+ raise LookupError("no qualified provider supports the requested capability and reasoning mode")
61
+ by_id = {provider.id: provider for provider in qualified}
62
+ configured = self._ordered_configured_ids(configuration)
63
+ for provider_id, reason in configured:
64
+ provider = by_id.get(provider_id)
65
+ if provider is not None:
66
+ return ProviderSelection(request, provider, reason)
67
+ return ProviderSelection(request, qualified[0], "stable_provider_id_version_tiebreaker")
68
+
69
+ @staticmethod
70
+ def _ordered_configured_ids(configuration: WorkspaceProviderConfiguration) -> tuple[tuple[str, str], ...]:
71
+ candidates = [(provider_id, "workspace_provider_preference") for provider_id in configuration.provider_preferences]
72
+ if configuration.default_provider_id:
73
+ candidates.append((configuration.default_provider_id, "workspace_default_provider"))
74
+ if configuration.fallback_provider_id:
75
+ candidates.append((configuration.fallback_provider_id, "workspace_fallback_provider"))
76
+ seen: set[str] = set()
77
+ ordered: list[tuple[str, str]] = []
78
+ for provider_id, reason in candidates:
79
+ if provider_id not in seen:
80
+ seen.add(provider_id)
81
+ ordered.append((provider_id, reason))
82
+ return tuple(ordered)
@@ -0,0 +1,8 @@
1
+ """Architecture Workspace public contracts."""
2
+
3
+ from .advisor import ArchitectureAdvisor, ArchitectureAdvisorAdvice
4
+ from .presentation import render_architecture_decision_evidence, render_architecture_mission
5
+ from .workspace import ArchitectureMissionHistoryEntry, ArchitectureWorkspace, ArchitectureWorkspaceError
6
+ from forge.governance_authority import ArchitecturePlanningEvidence, CanonicalArchitectureWorkspace, MissionPlanningEvidenceEnvelope
7
+
8
+ __all__ = ["ArchitectureAdvisor", "ArchitectureAdvisorAdvice", "ArchitectureMissionHistoryEntry", "ArchitectureWorkspace", "ArchitectureWorkspaceError", "ArchitecturePlanningEvidence", "CanonicalArchitectureWorkspace", "MissionPlanningEvidenceEnvelope", "render_architecture_decision_evidence", "render_architecture_mission"]