dos-kernel 0.22.0__py3-none-win_amd64.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 (178) hide show
  1. dos/__init__.py +261 -0
  2. dos/_bin/dos-hook.exe +0 -0
  3. dos/_filelock.py +255 -0
  4. dos/_job_policy.py +97 -0
  5. dos/_tree.py +145 -0
  6. dos/admission.py +433 -0
  7. dos/answer_shape.py +299 -0
  8. dos/arbiter.py +859 -0
  9. dos/archive_lock.py +266 -0
  10. dos/arg_provenance.py +814 -0
  11. dos/attest.py +472 -0
  12. dos/breaker.py +311 -0
  13. dos/churn.py +226 -0
  14. dos/claim_extract.py +229 -0
  15. dos/claim_ttl.py +150 -0
  16. dos/cli.py +8721 -0
  17. dos/commit_audit.py +666 -0
  18. dos/completion.py +466 -0
  19. dos/concurrency_class.py +154 -0
  20. dos/config.py +1380 -0
  21. dos/config_lint.py +464 -0
  22. dos/cooldown.py +390 -0
  23. dos/coverage.py +387 -0
  24. dos/dangling_intent.py +287 -0
  25. dos/data_class.py +397 -0
  26. dos/decisions.py +1274 -0
  27. dos/decisions_tui.py +251 -0
  28. dos/dispatch_top.py +740 -0
  29. dos/dispatch_top_tui.py +116 -0
  30. dos/drivers/__init__.py +40 -0
  31. dos/drivers/ci_status.py +630 -0
  32. dos/drivers/citation_resolve.py +703 -0
  33. dos/drivers/decision_stop.py +98 -0
  34. dos/drivers/export_file.py +173 -0
  35. dos/drivers/export_otlp.py +275 -0
  36. dos/drivers/export_statsd.py +242 -0
  37. dos/drivers/hook_dialects.py +391 -0
  38. dos/drivers/job.py +47 -0
  39. dos/drivers/llm_judge.py +360 -0
  40. dos/drivers/memory_recall.py +1231 -0
  41. dos/drivers/notify_slack.py +373 -0
  42. dos/drivers/notify_webhook.py +251 -0
  43. dos/drivers/operator_judge.py +114 -0
  44. dos/drivers/os_acceptance.py +228 -0
  45. dos/drivers/paste_log.py +132 -0
  46. dos/drivers/plan_scope.py +133 -0
  47. dos/drivers/self_improve.py +375 -0
  48. dos/drivers/similarity_judge.py +249 -0
  49. dos/drivers/state_diff.py +274 -0
  50. dos/drivers/supervisor.py +347 -0
  51. dos/drivers/watchdog.py +363 -0
  52. dos/drivers/workshop.py +160 -0
  53. dos/durable_schema.py +344 -0
  54. dos/effect_witness.py +393 -0
  55. dos/efficiency.py +318 -0
  56. dos/enforce.py +414 -0
  57. dos/enumerate.py +776 -0
  58. dos/env_print.py +378 -0
  59. dos/event_severity.py +258 -0
  60. dos/evidence.py +692 -0
  61. dos/exec_capability.py +256 -0
  62. dos/export_cursor.py +143 -0
  63. dos/exporter.py +320 -0
  64. dos/firing_label.py +353 -0
  65. dos/fleet_roll.py +226 -0
  66. dos/gate_classify.py +827 -0
  67. dos/gh4_coverage.py +179 -0
  68. dos/git_delta.py +122 -0
  69. dos/guard.py +215 -0
  70. dos/health.py +552 -0
  71. dos/help_summary.py +519 -0
  72. dos/home.py +934 -0
  73. dos/hook_binary.py +194 -0
  74. dos/hook_dialect.py +271 -0
  75. dos/hook_exit.py +191 -0
  76. dos/hook_install.py +437 -0
  77. dos/id_alloc.py +304 -0
  78. dos/improve.py +499 -0
  79. dos/intent_ledger.py +635 -0
  80. dos/interpret.py +176 -0
  81. dos/intervention.py +769 -0
  82. dos/intervention_eval.py +371 -0
  83. dos/journal_delta.py +308 -0
  84. dos/judge_eval.py +328 -0
  85. dos/judges.py +366 -0
  86. dos/lane_infer.py +127 -0
  87. dos/lane_journal.py +1001 -0
  88. dos/lane_lease.py +952 -0
  89. dos/lane_overlap.py +228 -0
  90. dos/lease_health.py +282 -0
  91. dos/lifecycle.py +211 -0
  92. dos/liveness.py +352 -0
  93. dos/lock_modes.py +185 -0
  94. dos/log_source.py +395 -0
  95. dos/loop_decide.py +1746 -0
  96. dos/marker_gate.py +254 -0
  97. dos/marker_sensor.py +396 -0
  98. dos/noop_streak.py +280 -0
  99. dos/notify.py +479 -0
  100. dos/observe.py +175 -0
  101. dos/oracle.py +1661 -0
  102. dos/overlap_eval.py +214 -0
  103. dos/overlap_policy.py +342 -0
  104. dos/packet_sidecar.py +267 -0
  105. dos/phase_shipped.py +1985 -0
  106. dos/pick_priority.py +225 -0
  107. dos/pickable.py +369 -0
  108. dos/picker_oracle.py +1037 -0
  109. dos/plan_board.py +513 -0
  110. dos/plan_board_tui.py +113 -0
  111. dos/plan_source.py +455 -0
  112. dos/posttool_sensor.py +528 -0
  113. dos/precursor_gate.py +499 -0
  114. dos/precursor_gate_eval.py +239 -0
  115. dos/preflight.py +825 -0
  116. dos/pretool_sensor.py +490 -0
  117. dos/proc_delta.py +181 -0
  118. dos/productivity.py +296 -0
  119. dos/provider_limit.py +242 -0
  120. dos/py.typed +4 -0
  121. dos/reason_morphology.py +299 -0
  122. dos/reasons.py +449 -0
  123. dos/reconcile.py +173 -0
  124. dos/recurring_wedge.py +206 -0
  125. dos/render.py +393 -0
  126. dos/result_state.py +468 -0
  127. dos/resume.py +578 -0
  128. dos/resume_evidence.py +293 -0
  129. dos/retention.py +344 -0
  130. dos/reward.py +372 -0
  131. dos/rewind.py +587 -0
  132. dos/rewind_evidence.py +168 -0
  133. dos/rewind_tokens.py +252 -0
  134. dos/run_id.py +342 -0
  135. dos/scope.py +520 -0
  136. dos/scope_source.py +382 -0
  137. dos/scout.py +982 -0
  138. dos/self_modify.py +209 -0
  139. dos/sibling_scan.py +569 -0
  140. dos/skills/EXAMPLES.md +584 -0
  141. dos/skills/dos-class-cycle/SKILL.md +107 -0
  142. dos/skills/dos-dispatch/SKILL.md +177 -0
  143. dos/skills/dos-dispatch-loop/SKILL.md +254 -0
  144. dos/skills/dos-goal-gate/SKILL.md +269 -0
  145. dos/skills/dos-next-up/SKILL.md +231 -0
  146. dos/skills/dos-promote/SKILL.md +114 -0
  147. dos/skills/dos-replan/SKILL.md +159 -0
  148. dos/skills/dos-replan-loop/SKILL.md +114 -0
  149. dos/skills/dos-self-improve/SKILL.md +213 -0
  150. dos/skills/dos-supervise-loop/SKILL.md +180 -0
  151. dos/skills/dos-unstick/SKILL.md +108 -0
  152. dos/skills/dos-witness-claim/SKILL.md +251 -0
  153. dos/stamp.py +1002 -0
  154. dos/state_health.py +387 -0
  155. dos/status.py +114 -0
  156. dos/stop_policy.py +334 -0
  157. dos/supervise.py +1014 -0
  158. dos/testwitness.py +392 -0
  159. dos/timeline.py +1027 -0
  160. dos/tokens.py +485 -0
  161. dos/tool_stream.py +393 -0
  162. dos/tool_stream_eval.py +226 -0
  163. dos/trace.py +524 -0
  164. dos/verdict.py +140 -0
  165. dos/verdict_cli.py +189 -0
  166. dos/verdict_journal.py +497 -0
  167. dos/verdict_rollup.py +217 -0
  168. dos/verdicts.py +181 -0
  169. dos/wedge_reason.py +282 -0
  170. dos_kernel-0.22.0.dist-info/METADATA +859 -0
  171. dos_kernel-0.22.0.dist-info/RECORD +178 -0
  172. dos_kernel-0.22.0.dist-info/WHEEL +5 -0
  173. dos_kernel-0.22.0.dist-info/entry_points.txt +39 -0
  174. dos_kernel-0.22.0.dist-info/licenses/LICENSE +21 -0
  175. dos_kernel-0.22.0.dist-info/top_level.txt +2 -0
  176. dos_mcp/__init__.py +52 -0
  177. dos_mcp/py.typed +2 -0
  178. dos_mcp/server.py +779 -0
dos/self_modify.py ADDED
@@ -0,0 +1,209 @@
1
+ """The SELF_MODIFY built-in admission predicate — the self-modification guard (ADM Phase 2, docs/73).
2
+
3
+ This is the first *real* second built-in predicate (the first, `DisjointnessPredicate`,
4
+ was a behavior-preserving refactor of the existing rule). It refuses a lease whose
5
+ requested file tree intersects the **orchestrator's own running code** — the kernel
6
+ modules that sit in a live dispatch loop's execution path.
7
+
8
+ Why this is a kernel concern (`project-dos-self-modification-hazard`, mechanism (a)):
9
+ editing the arbiter / classifiers / token rules *while a loop that depends on them
10
+ is live* is a T1 hazard — a packet that rewrites `arbiter.py` between two Step-0
11
+ admission checks changes the very logic deciding whether the next packet may run,
12
+ silently, mid-flight. The "natural DOS-kernel realization" of guarding against it is
13
+ a new typed arbiter refuse: intersect the requested tree with a frozen
14
+ `_DISPATCH_RUNTIME_FILES` set and refuse on a hit. ADM's predicate seam is the
15
+ vehicle; this is its first safety payload.
16
+
17
+ The override is `--force` and ONLY `--force` — the operator's explicit "yes, I am
18
+ editing the kernel between loop runs, I know what I'm doing" (the safe, human-in-loop
19
+ path the hazard memo calls for). A predicate can never force itself; `--force` skips
20
+ predicate refusals exactly as it skips the disjointness refuse (see `arbiter.arbitrate`).
21
+
22
+ Pure stdlib + the `_tree` prefix algebra — no I/O, no host names. The set is data,
23
+ pinned with a comment tying each entry to *why* it is runtime-critical, so a reviewer
24
+ can audit the blast radius of the guard at a glance.
25
+ """
26
+
27
+ from __future__ import annotations
28
+
29
+ from dos._tree import norm_tree_prefix as _norm_tree_prefix
30
+ from dos._tree import prefixes_collide as _prefixes_collide
31
+ from dos.admission import AdmissionRequest, AdmissionVerdict
32
+
33
+ # The typed reason a SELF_MODIFY refusal carries — declared in
34
+ # `dos.reasons.BASE_REASONS` so it is simultaneously emittable (here), verifiable
35
+ # (the registry's `category_for`), refusable (`is_refusal`), and `dos man
36
+ # wedge SELF_MODIFY`-documented (the Axis-1 completeness rail).
37
+ SELF_MODIFY_REASON = "SELF_MODIFY"
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # The T1 runtime set — the kernel modules in a LIVE dispatch loop's own decision
41
+ # path. Editing any of these mid-flight changes the logic that admits the NEXT
42
+ # packet, silently. Repo-relative POSIX paths (the form a lane tree carries). Each
43
+ # entry is annotated with WHY it is runtime-critical so the guard's blast radius
44
+ # is auditable. This is the T1 set from `project-dos-self-modification-hazard`,
45
+ # scoped to the kernel's *adjudication* path — NOT every kernel file (a lease
46
+ # editing `timeline.py`, pure post-hoc assembly, is not a live-decision hazard).
47
+ # ---------------------------------------------------------------------------
48
+ _DISPATCH_RUNTIME_FILES: tuple[str, ...] = (
49
+ # The admission kernel itself — the code that runs THIS very check. A packet
50
+ # rewriting it between two Step-0 calls changes whether the next loop admits.
51
+ "src/dos/arbiter.py",
52
+ # The admission-predicate seam + the self-modify guard — the conjunction
53
+ # runner and this predicate. Editing them mid-flight can disable the guard
54
+ # that is protecting the edit (the most direct self-defeat).
55
+ "src/dos/admission.py",
56
+ "src/dos/self_modify.py",
57
+ # The soft-overlap / tree-disjointness algebra the arbiter delegates its
58
+ # collision check to — the substance of "may these two lanes coexist."
59
+ "src/dos/lane_overlap.py",
60
+ "src/dos/_tree.py",
61
+ # The loop's go/no-go classifiers: whether a phase is gated, whether the loop
62
+ # should continue, the token-budget accounting those read. A live loop calls
63
+ # these every iteration; rewriting them mid-loop reshapes its control flow.
64
+ "src/dos/gate_classify.py",
65
+ "src/dos/loop_decide.py",
66
+ "src/dos/tokens.py",
67
+ # The structured-refusal vocabulary the loop emits/verifies against. Changing
68
+ # the reason set under a live loop desyncs producer↔oracle (the exact drift
69
+ # the registry exists to forbid), so it is a runtime-path edit.
70
+ "src/dos/wedge_reason.py",
71
+ "src/dos/reasons.py",
72
+ # The config seam — the lane taxonomy + paths a live arbiter reads on every
73
+ # admission. Re-pointing the workspace or rewriting the taxonomy mid-loop
74
+ # changes what "disjoint" even means for the next packet.
75
+ "src/dos/config.py",
76
+ )
77
+
78
+ # Pre-normalized prefixes (truncated at the first glob `*`, `\\`→`/`), computed
79
+ # once at import. The intersection test compares against these — the same
80
+ # normalization `lane_overlap._shared_count` / `_tree.lane_trees_disjoint` use,
81
+ # so "does this lane touch a runtime file" is decided by the identical algebra
82
+ # the rest of the kernel trusts for collision detection.
83
+ _RUNTIME_PREFIXES: tuple[str, ...] = tuple(
84
+ _norm_tree_prefix(p) for p in _DISPATCH_RUNTIME_FILES
85
+ )
86
+
87
+
88
+ def _tree_touches_runtime(
89
+ requested_tree: list[str],
90
+ runtime_files: tuple[str, ...] = _DISPATCH_RUNTIME_FILES,
91
+ ) -> list[str]:
92
+ """Return the runtime files a requested tree would touch (empty = none).
93
+
94
+ Prefix-collision in BOTH directions (a requested `src/dos/` glob contains
95
+ `src/dos/arbiter.py`; a requested `src/dos/arbiter.py` IS a runtime file) —
96
+ the same rule `_tree.lane_trees_disjoint` uses, now shared verbatim via
97
+ `_tree.prefixes_collide`. Returns the offending runtime paths (the original,
98
+ un-normalized entries) so the refusal can name exactly what was hit, in
99
+ declaration order.
100
+
101
+ A **leading-glob** request (`**/*`) normalizes to the empty (universal)
102
+ prefix, which collides with EVERY runtime file — so a whole-repo lease is
103
+ correctly flagged as touching the kernel's own code, not waved through as
104
+ "touches nothing." Only literally-blank entries are filtered (no path
105
+ information); the empty prefix from a real glob is kept. (This is the
106
+ self-modify half of the `**/*`-normalizes-to-empty bug.)
107
+
108
+ ``runtime_files`` is the kernel-source set to check against. It defaults to
109
+ the full static `_DISPATCH_RUNTIME_FILES`, but a boundary caller hands in the
110
+ subset that actually EXISTS under the served workspace (`existing_runtime_files`)
111
+ — so a `**/*` lane in a *foreign* repo (which has no `src/dos/*.py`) collides
112
+ with the empty set and is admitted, while the same lane in the DOS repo itself
113
+ is refused. The default keeps the pure, workspace-unaware behavior for direct
114
+ callers and tests.
115
+ """
116
+ hits: list[str] = []
117
+ req_prefixes = [_norm_tree_prefix(p) for p in (requested_tree or []) if p]
118
+ if not req_prefixes:
119
+ return hits
120
+ for original in runtime_files:
121
+ rp = _norm_tree_prefix(original)
122
+ if any(_prefixes_collide(nr, rp) for nr in req_prefixes):
123
+ hits.append(original)
124
+ return hits
125
+
126
+
127
+ class SelfModifyPredicate:
128
+ """Refuse a lease whose tree includes the orchestrator's own running code.
129
+
130
+ Always-on, like `DisjointnessPredicate` (`admission.built_in_predicates`).
131
+ Unlike disjointness, it is **request-absolute**: it does NOT depend on the
132
+ live lease — self-modification is a hazard regardless of what else is
133
+ running — so it answers from the REQUEST alone and ignores ``live_lease``.
134
+ (It still implements the per-lease predicate signature so it composes in the
135
+ same conjunction; it returns the same verdict for every live lease, which is
136
+ harmless: `run_predicates` short-circuits on the first refusal.)
137
+
138
+ It fires on EVERY admit path, including an otherwise-idle repo with NO live
139
+ leases: `run_predicates` runs the conjunction once against a synthetic
140
+ empty-lease sentinel exactly so request-absolute predicates are not skipped
141
+ when nothing else is live, and `arbiter.arbitrate` gates its cluster /
142
+ exclusive-lane / keyword fast-paths through that conjunction (it does not
143
+ return `acquire` before consulting the predicates). So a self-modifying lease
144
+ is refused whether the repo is busy or idle, whether the request is a cluster,
145
+ an exclusive lane, or a keyword. `--force` is the sole override. (This closes
146
+ the idle-repo / fast-path gaps an adversarial review caught; see
147
+ `test_self_modify_*` and `TestSelfModifyGatesEveryAdmitPath` for the pinned
148
+ contract across all paths.)
149
+ """
150
+
151
+ name = "self-modify"
152
+
153
+ def __init__(self, runtime_files: tuple[str, ...] = _DISPATCH_RUNTIME_FILES):
154
+ """``runtime_files`` is the kernel-source set this guard protects.
155
+
156
+ Defaults to the full static `_DISPATCH_RUNTIME_FILES` (pure, workspace-
157
+ unaware — the safe default for a direct/test caller). A boundary builder
158
+ (`admission.built_in_predicates(workspace=…)`) constructs the predicate
159
+ with the subset that actually EXISTS under the served workspace
160
+ (`existing_runtime_files`), so the guard fires only where the kernel
161
+ source it protects is genuinely present — the DOS repo serving itself,
162
+ not a foreign repo whose `**/*` lane cannot edit a `src/dos/` file that
163
+ isn't there. Stored verbatim; no I/O here (the existence probe already
164
+ ran at the boundary).
165
+ """
166
+ self._runtime_files = tuple(runtime_files)
167
+
168
+ def __call__(self, request: AdmissionRequest, live_lease: dict,
169
+ config: object) -> AdmissionVerdict:
170
+ hits = _tree_touches_runtime(list(request.tree), self._runtime_files)
171
+ if not hits:
172
+ return AdmissionVerdict.admit()
173
+ shown = ", ".join(hits[:3]) + ("…" if len(hits) > 3 else "")
174
+ return AdmissionVerdict.refuse(
175
+ f"lane {request.lane!r} would edit the orchestrator's own running "
176
+ f"code ({shown}) — refusing to let a live loop rewrite the kernel "
177
+ f"that is adjudicating it (SELF_MODIFY). Pass --force only if you "
178
+ f"are deliberately editing the kernel between loop runs.",
179
+ reason_class=SELF_MODIFY_REASON,
180
+ )
181
+
182
+
183
+ # ---------------------------------------------------------------------------
184
+ # Boundary helper — the ONE place the existence I/O lives. Mirrors
185
+ # `admission.active_predicates` (entry-point discovery) and the liveness/arbitrate
186
+ # pattern: I/O is gathered at the CALL BOUNDARY and the result is handed to a pure
187
+ # predicate, never run inside `arbitrate` itself.
188
+ # ---------------------------------------------------------------------------
189
+ def existing_runtime_files(workspace) -> tuple[str, ...]:
190
+ """The `_DISPATCH_RUNTIME_FILES` that actually exist under ``workspace``.
191
+
192
+ This is what makes the SELF_MODIFY guard **workspace-aware**: the static set
193
+ is the kernel's own source paths (`src/dos/arbiter.py`, …), which exist only
194
+ when DOS is serving its OWN repo. Against a foreign repo (or a fresh
195
+ scaffold) none of them resolve, so this returns ``()`` and a `**/*` lane
196
+ touches nothing — the correct admit. Against the DOS repo every entry
197
+ resolves and the guard fires on a whole-repo lease.
198
+
199
+ The single existence I/O of the self-modify guard. Called by
200
+ `admission.built_in_predicates(workspace=…)` at the boundary; the predicate
201
+ it feeds stays pure. ``workspace`` is a path-like (the `SubstrateConfig.workspace`);
202
+ a falsy/None workspace yields the full static set (cannot prove non-existence,
203
+ so stay conservative — the safe direction for a safety guard).
204
+ """
205
+ if not workspace:
206
+ return _DISPATCH_RUNTIME_FILES
207
+ from pathlib import Path
208
+ root = Path(workspace)
209
+ return tuple(f for f in _DISPATCH_RUNTIME_FILES if (root / f).exists())