techtree 0.1.0__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.
- techtree/__init__.py +35 -0
- techtree/__main__.py +14 -0
- techtree/canonical.py +239 -0
- techtree/catalog/__init__.py +25 -0
- techtree/catalog/repository.py +400 -0
- techtree/catalog/service.py +419 -0
- techtree/cli/__init__.py +1 -0
- techtree/cli/app.py +416 -0
- techtree/cli/commands/__init__.py +1 -0
- techtree/cli/commands/climb.py +1223 -0
- techtree/cli/commands/doctor.py +147 -0
- techtree/cli/commands/engine.py +207 -0
- techtree/cli/commands/proof.py +556 -0
- techtree/cli/commands/publish.py +447 -0
- techtree/cli/commands/release.py +303 -0
- techtree/cli/commands/run.py +1067 -0
- techtree/cli/commands/setup.py +181 -0
- techtree/cli/commands/skill.py +221 -0
- techtree/cli/commands/uplift.py +698 -0
- techtree/cli/commands/withdraw.py +212 -0
- techtree/cli/confirm.py +47 -0
- techtree/cli/context.py +96 -0
- techtree/cli/invoke.py +220 -0
- techtree/cli/output.py +280 -0
- techtree/constants.py +138 -0
- techtree/crypto.py +128 -0
- techtree/doctor/__init__.py +1 -0
- techtree/doctor/checks.py +675 -0
- techtree/doctor/execution_checks.py +435 -0
- techtree/doctor/service.py +326 -0
- techtree/drafts/__init__.py +32 -0
- techtree/drafts/source.py +146 -0
- techtree/drafts/store.py +992 -0
- techtree/engines/__init__.py +1 -0
- techtree/engines/bundle.py +251 -0
- techtree/engines/installer.py +679 -0
- techtree/engines/registry.py +235 -0
- techtree/engines/runner.py +170 -0
- techtree/errors.py +262 -0
- techtree/fs.py +234 -0
- techtree/harness.py +108 -0
- techtree/identity/__init__.py +41 -0
- techtree/identity/models.py +113 -0
- techtree/identity/service.py +199 -0
- techtree/identity/store.py +263 -0
- techtree/ids.py +85 -0
- techtree/manifests/__init__.py +39 -0
- techtree/manifests/builder.py +433 -0
- techtree/manifests/compare.py +376 -0
- techtree/models/__init__.py +282 -0
- techtree/models/base.py +201 -0
- techtree/models/campaign.py +484 -0
- techtree/models/catalog.py +227 -0
- techtree/models/cli.py +151 -0
- techtree/models/climb.py +254 -0
- techtree/models/data_policy.py +130 -0
- techtree/models/engine.py +156 -0
- techtree/models/episode_receipt.py +130 -0
- techtree/models/evaluation_backend.py +113 -0
- techtree/models/experiment.py +154 -0
- techtree/models/run.py +214 -0
- techtree/models/skill.py +156 -0
- techtree/models/uplift_report.py +158 -0
- techtree/models/validation.py +299 -0
- techtree/paths.py +116 -0
- techtree/presentation/__init__.py +31 -0
- techtree/presentation/build.py +1242 -0
- techtree/presentation/compact.py +246 -0
- techtree/presentation/evidence.py +169 -0
- techtree/presentation/models.py +358 -0
- techtree/presentation/rich.py +312 -0
- techtree/presentation/sanitize.py +156 -0
- techtree/publication/__init__.py +44 -0
- techtree/publication/address.py +180 -0
- techtree/publication/coordinates.py +26 -0
- techtree/publication/journal.py +212 -0
- techtree/publication/keccak.py +183 -0
- techtree/publication/models.py +209 -0
- techtree/publication/offer.py +35 -0
- techtree/publication/service.py +618 -0
- techtree/publication/transport.py +296 -0
- techtree/publication/verify.py +242 -0
- techtree/publication/withdraw.py +156 -0
- techtree/py.typed +0 -0
- techtree/receipts/__init__.py +52 -0
- techtree/receipts/bundle.py +578 -0
- techtree/receipts/compare.py +1065 -0
- techtree/receipts/episode.py +672 -0
- techtree/receipts/execution.py +630 -0
- techtree/receipts/observed.py +474 -0
- techtree/receipts/set.py +336 -0
- techtree/receipts/uplift.py +655 -0
- techtree/receipts/verify.py +1055 -0
- techtree/release/__init__.py +9 -0
- techtree/release/bootstrap.py +509 -0
- techtree/release/checks.py +376 -0
- techtree/release/document.py +125 -0
- techtree/release/generate.py +221 -0
- techtree/release/models.py +293 -0
- techtree/release/provenance.py +109 -0
- techtree/resources/catalog/campaigns/hello-world-climb.json +1 -0
- techtree/resources/catalog/catalog.json +32 -0
- techtree/resources/catalog/climbs/hello-world-climb.json +1 -0
- techtree/resources/catalog/data-policies/hello-world-climb.json +1 -0
- techtree/resources/catalog/taskset-validations/hello-world-climb.json +1 -0
- techtree/resources/catalog/validation-evidence/hello-world-climb.json +1 -0
- techtree/resources/engines/default/engine.json +20 -0
- techtree/resources/engines/default/packages/procedure-transfer-v1/procedure_transfer_v1/__init__.py +7 -0
- techtree/resources/engines/default/packages/procedure-transfer-v1/procedure_transfer_v1/algorithm.py +136 -0
- techtree/resources/engines/default/packages/procedure-transfer-v1/procedure_transfer_v1/dataset.py +156 -0
- techtree/resources/engines/default/packages/procedure-transfer-v1/procedure_transfer_v1/env.py +48 -0
- techtree/resources/engines/default/packages/procedure-transfer-v1/procedure_transfer_v1/taskset.py +163 -0
- techtree/resources/engines/default/packages/procedure-transfer-v1/pyproject.toml +13 -0
- techtree/resources/engines/default/pyproject.toml +23 -0
- techtree/resources/engines/default/tools/inspect_taskset.py +124 -0
- techtree/resources/engines/default/tools/normalize_eval_output.py +470 -0
- techtree/resources/engines/default/tools/normalize_validation.py +222 -0
- techtree/resources/engines/default/uv.lock +1758 -0
- techtree/resources/harness/hermes-agent-0.19.0.json +69 -0
- techtree/resources/release/build-provenance.json +4 -0
- techtree/resources/release/release-core.json +24 -0
- techtree/runs/__init__.py +31 -0
- techtree/runs/artifacts.py +750 -0
- techtree/runs/child_registry.py +228 -0
- techtree/runs/events.py +478 -0
- techtree/runs/executor.py +140 -0
- techtree/runs/fake.py +741 -0
- techtree/runs/launcher.py +253 -0
- techtree/runs/machine.py +489 -0
- techtree/runs/real.py +789 -0
- techtree/runs/service.py +616 -0
- techtree/runs/store.py +555 -0
- techtree/runs/validation.py +259 -0
- techtree/runs/variants.py +684 -0
- techtree/settings.py +143 -0
- techtree/skills/__init__.py +14 -0
- techtree/skills/archive.py +282 -0
- techtree/skills/policy.py +62 -0
- techtree/skills/scanner.py +394 -0
- techtree/skills/service.py +752 -0
- techtree/skills/starter.py +434 -0
- techtree/tasksets/__init__.py +1 -0
- techtree/tasksets/membership.py +269 -0
- techtree/tasksets/provider.py +207 -0
- techtree/tasksets/resolver.py +311 -0
- techtree/tasksets/service.py +484 -0
- techtree/tasksets/verifiers_cli.py +538 -0
- techtree/uplift/__init__.py +20 -0
- techtree/uplift/context.py +544 -0
- techtree/uplift/derive.py +203 -0
- techtree/uplift/public_tasks.py +151 -0
- techtree/uplift/service.py +719 -0
- techtree/uplift/source.py +160 -0
- techtree/verifiers/__init__.py +31 -0
- techtree/verifiers/budget.py +219 -0
- techtree/verifiers/child.py +633 -0
- techtree/verifiers/compiler.py +432 -0
- techtree/verifiers/config.py +365 -0
- techtree/verifiers/credentials.py +321 -0
- techtree/verifiers/image.py +126 -0
- techtree/verifiers/models.py +527 -0
- techtree/verifiers/outputs.py +368 -0
- techtree/verifiers/progress.py +192 -0
- techtree/verifiers/supervisor.py +341 -0
- techtree/verifiers/verify.py +782 -0
- techtree/version.py +39 -0
- techtree/worker/__init__.py +18 -0
- techtree/worker/execute.py +487 -0
- techtree/worker/main.py +57 -0
- techtree-0.1.0.dist-info/METADATA +344 -0
- techtree-0.1.0.dist-info/RECORD +174 -0
- techtree-0.1.0.dist-info/WHEEL +4 -0
- techtree-0.1.0.dist-info/entry_points.txt +3 -0
- techtree-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""What executes a run, and where it may be interrupted. Spec PR8 §8.5.
|
|
2
|
+
|
|
3
|
+
:class:`RunExecutor` is the seam the whole slice exists to establish. A future
|
|
4
|
+
real executor — Verifiers evaluation, a Hermes harness, a Docker subject —
|
|
5
|
+
implements this one method and inherits the entire lifecycle around it: the
|
|
6
|
+
request, the staged inputs, the validation provider, the event journal, the
|
|
7
|
+
cancellation boundaries, and the CLI that watches all of it. Only the body
|
|
8
|
+
changes.
|
|
9
|
+
|
|
10
|
+
Cancellation is cooperative and deliberately so. A worker that could be killed
|
|
11
|
+
mid-write would leave a run whose journal and artifacts disagreed, so the CLI
|
|
12
|
+
never kills it: it appends ``cancel.requested`` and signals the process, and
|
|
13
|
+
the executor notices at a boundary of its own choosing and unwinds. The two
|
|
14
|
+
things it notices are here.
|
|
15
|
+
|
|
16
|
+
:func:`raise_if_cancel_requested` reads the run's projected phase, which is how
|
|
17
|
+
a cancellation requested by a *different process* reaches this one.
|
|
18
|
+
:func:`request_local_cancellation` is the other direction: a signal handler in
|
|
19
|
+
this process cannot safely read a file or take a lock, so it sets a flag that
|
|
20
|
+
the same check consults. Either is enough to stop the run, and both funnel into
|
|
21
|
+
one :class:`~techtree.errors.CancellationError` so that no executor has to know
|
|
22
|
+
which of them happened.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import threading
|
|
28
|
+
import time
|
|
29
|
+
from collections.abc import Callable
|
|
30
|
+
from dataclasses import dataclass
|
|
31
|
+
from datetime import datetime
|
|
32
|
+
from typing import Final, Protocol
|
|
33
|
+
|
|
34
|
+
from techtree.errors import CancellationError
|
|
35
|
+
from techtree.models.run import RunPhase, RunRequest
|
|
36
|
+
from techtree.models.uplift_report import UpliftReport
|
|
37
|
+
from techtree.runs.artifacts import RunArtifactStore
|
|
38
|
+
from techtree.runs.store import RunStore
|
|
39
|
+
from techtree.runs.validation import TasksetValidationProvider
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"DEFAULT_CANCELLATION_QUANTUM_SECONDS",
|
|
43
|
+
"ExecutionContext",
|
|
44
|
+
"RunExecutor",
|
|
45
|
+
"clear_local_cancellation",
|
|
46
|
+
"interruptible_sleep",
|
|
47
|
+
"local_cancellation_requested",
|
|
48
|
+
"raise_if_cancel_requested",
|
|
49
|
+
"request_local_cancellation",
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
#: How often an interruptible delay looks up from what it is doing. Short
|
|
53
|
+
#: enough that a cancelled run stops promptly, long enough that waiting is not
|
|
54
|
+
#: itself the work.
|
|
55
|
+
DEFAULT_CANCELLATION_QUANTUM_SECONDS: Final = 0.05
|
|
56
|
+
|
|
57
|
+
#: Set by this process's signal handlers. Process-wide rather than passed
|
|
58
|
+
#: around because a signal arrives at the process, not at an object, and the
|
|
59
|
+
#: handler must do nothing but set it.
|
|
60
|
+
_LOCAL_CANCELLATION: Final[threading.Event] = threading.Event()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@dataclass(frozen=True)
|
|
64
|
+
class ExecutionContext:
|
|
65
|
+
"""Everything one execution of one run is given."""
|
|
66
|
+
|
|
67
|
+
request: RunRequest
|
|
68
|
+
run_store: RunStore
|
|
69
|
+
artifact_store: RunArtifactStore
|
|
70
|
+
validation_provider: TasksetValidationProvider
|
|
71
|
+
clock: Callable[[], datetime]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class RunExecutor(Protocol):
|
|
75
|
+
"""Executes one run to a terminal scientific artifact."""
|
|
76
|
+
|
|
77
|
+
def execute(self, context: ExecutionContext) -> UpliftReport:
|
|
78
|
+
"""Execute one run and return the report it produced."""
|
|
79
|
+
...
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def request_local_cancellation() -> None:
|
|
83
|
+
"""Record that this process has been asked to stop.
|
|
84
|
+
|
|
85
|
+
Safe to call from a signal handler: it sets an event and returns.
|
|
86
|
+
"""
|
|
87
|
+
_LOCAL_CANCELLATION.set()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def clear_local_cancellation() -> None:
|
|
91
|
+
"""Forget any local cancellation request.
|
|
92
|
+
|
|
93
|
+
A worker process handles exactly one run, so this exists for tests, which
|
|
94
|
+
share one process across many runs.
|
|
95
|
+
"""
|
|
96
|
+
_LOCAL_CANCELLATION.clear()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def local_cancellation_requested() -> bool:
|
|
100
|
+
"""Return whether this process has been signalled to stop."""
|
|
101
|
+
return _LOCAL_CANCELLATION.is_set()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def raise_if_cancel_requested(run_store: RunStore, run_id: str) -> None:
|
|
105
|
+
"""Raise when this run has been asked to stop, from anywhere."""
|
|
106
|
+
if local_cancellation_requested():
|
|
107
|
+
raise CancellationError(
|
|
108
|
+
f"run {run_id} was signalled to stop",
|
|
109
|
+
details={"run_id": run_id, "requested_by": "signal"},
|
|
110
|
+
)
|
|
111
|
+
if run_store.state(run_id).phase is RunPhase.CANCEL_REQUESTED:
|
|
112
|
+
raise CancellationError(
|
|
113
|
+
f"run {run_id} was asked to stop",
|
|
114
|
+
details={"run_id": run_id, "requested_by": "run_journal"},
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def interruptible_sleep(
|
|
119
|
+
*,
|
|
120
|
+
seconds: float,
|
|
121
|
+
check: Callable[[], None],
|
|
122
|
+
quantum: float = DEFAULT_CANCELLATION_QUANTUM_SECONDS,
|
|
123
|
+
) -> None:
|
|
124
|
+
"""Wait, looking up every ``quantum`` seconds to see whether to stop.
|
|
125
|
+
|
|
126
|
+
``check`` is called before the first wait and after every one of them, so
|
|
127
|
+
a delay of zero still costs one check and a cancellation never has to wait
|
|
128
|
+
out a whole interval to be noticed.
|
|
129
|
+
"""
|
|
130
|
+
if quantum <= 0:
|
|
131
|
+
raise ValueError("an interruptible delay needs a positive quantum")
|
|
132
|
+
|
|
133
|
+
check()
|
|
134
|
+
deadline = time.monotonic() + max(seconds, 0.0)
|
|
135
|
+
while True:
|
|
136
|
+
remaining = deadline - time.monotonic()
|
|
137
|
+
if remaining <= 0:
|
|
138
|
+
return
|
|
139
|
+
time.sleep(min(quantum, remaining))
|
|
140
|
+
check()
|