torve 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.
Files changed (186) hide show
  1. torve/AGENTS.md +39 -0
  2. torve/__init__.py +10 -0
  3. torve/_migrations/AGENTS.md +12 -0
  4. torve/_migrations/substrate/FORZE_VERSION +1 -0
  5. torve/_migrations/substrate/postgres/0001_durable.sql +42 -0
  6. torve/_migrations/torve/postgres/0001_events.sql +40 -0
  7. torve/_migrations/torve/postgres/0002_decisions.sql +16 -0
  8. torve/_migrations/torve/postgres/README.md +4 -0
  9. torve/_skills/AGENTS.md +50 -0
  10. torve/_skills/corpus-bootstrap/AGENTS.md +3 -0
  11. torve/_skills/corpus-bootstrap/SKILL.md +128 -0
  12. torve/_skills/corpus-bootstrap/fixtures/S-0001/decisions.yaml +45 -0
  13. torve/_skills/corpus-bootstrap/fixtures/S-0001/document.yaml +56 -0
  14. torve/_skills/corpus-bootstrap/fixtures/survey-report.json +359 -0
  15. torve/_skills/corpus-bootstrap/references/doctrine.md +85 -0
  16. torve/_skills/corpus-bootstrap/references/draft-template.md +74 -0
  17. torve/_skills/corpus-bootstrap/references/workflow.md +93 -0
  18. torve/_skills/flag-dont-flip/SKILL.md +166 -0
  19. torve/_skills/ratchet-what-you-build/SKILL.md +66 -0
  20. torve/_skills/ratchet-what-you-build/references/the-ladder.md +29 -0
  21. torve/_skills/spec-writer/AGENTS.md +13 -0
  22. torve/_skills/spec-writer/SKILL.md +191 -0
  23. torve/_skills/spec-writer/references/authoring.md +59 -0
  24. torve/_skills/spec-writer/references/spec-template.md +161 -0
  25. torve/_skills/spec-writer/references/workflows.md +178 -0
  26. torve/_skills/working-rules/AGENTS.md +54 -0
  27. torve/_skills/working-rules/SKILL.md +137 -0
  28. torve/_web/AGENTS.md +12 -0
  29. torve/_web/assets/index-KQVC6uXc.css +1 -0
  30. torve/_web/assets/index-Nc9hy-rB.js +70 -0
  31. torve/_web/index.html +15 -0
  32. torve/adapters/AGENTS.md +21 -0
  33. torve/adapters/__init__.py +2 -0
  34. torve/adapters/agent/AGENTS.md +212 -0
  35. torve/adapters/agent/__init__.py +0 -0
  36. torve/adapters/agent/fake.py +117 -0
  37. torve/adapters/agent/harness.py +1971 -0
  38. torve/adapters/broker/AGENTS.md +13 -0
  39. torve/adapters/broker/__init__.py +36 -0
  40. torve/adapters/broker/local.py +956 -0
  41. torve/adapters/broker/none.py +57 -0
  42. torve/adapters/eventstore/__init__.py +0 -0
  43. torve/adapters/eventstore/document.py +73 -0
  44. torve/adapters/notify/AGENTS.md +3 -0
  45. torve/adapters/notify/__init__.py +12 -0
  46. torve/adapters/notify/inert.py +26 -0
  47. torve/adapters/notify/webhook.py +106 -0
  48. torve/adapters/runtime/AGENTS.md +42 -0
  49. torve/adapters/runtime/__init__.py +0 -0
  50. torve/adapters/runtime/docker.py +357 -0
  51. torve/adapters/runtime/opensandbox.py +439 -0
  52. torve/adapters/store/__init__.py +0 -0
  53. torve/adapters/store/durable.py +70 -0
  54. torve/adapters/vcs/AGENTS.md +62 -0
  55. torve/adapters/vcs/__init__.py +0 -0
  56. torve/adapters/vcs/git.py +1200 -0
  57. torve/adapters/workspace/AGENTS.md +27 -0
  58. torve/adapters/workspace/__init__.py +0 -0
  59. torve/adapters/workspace/git.py +377 -0
  60. torve/application/AGENTS.md +1648 -0
  61. torve/application/__init__.py +1 -0
  62. torve/application/channel.py +152 -0
  63. torve/application/colocation.py +475 -0
  64. torve/application/contextpack.py +844 -0
  65. torve/application/decisions.py +989 -0
  66. torve/application/dispatch.py +710 -0
  67. torve/application/divergence.py +571 -0
  68. torve/application/enginelock.py +74 -0
  69. torve/application/equipment.py +418 -0
  70. torve/application/evals.py +626 -0
  71. torve/application/eventlog.py +415 -0
  72. torve/application/executors.py +143 -0
  73. torve/application/feedback.py +126 -0
  74. torve/application/fleet.py +256 -0
  75. torve/application/forge.py +436 -0
  76. torve/application/intake.py +2057 -0
  77. torve/application/lane.py +1982 -0
  78. torve/application/ledger.py +683 -0
  79. torve/application/manager.py +767 -0
  80. torve/application/migrate.py +224 -0
  81. torve/application/notify.py +268 -0
  82. torve/application/planner.py +1135 -0
  83. torve/application/ports.py +660 -0
  84. torve/application/projections.py +2158 -0
  85. torve/application/reaper.py +514 -0
  86. torve/application/residency.py +952 -0
  87. torve/application/review.py +1283 -0
  88. torve/application/reviewleg.py +1250 -0
  89. torve/application/runner.py +1501 -0
  90. torve/application/runstate.py +226 -0
  91. torve/application/session.py +942 -0
  92. torve/application/shadow.py +184 -0
  93. torve/application/sizing.py +107 -0
  94. torve/application/skills.py +91 -0
  95. torve/application/specquality.py +928 -0
  96. torve/application/standing.py +665 -0
  97. torve/application/survey.py +230 -0
  98. torve/application/taskstore.py +167 -0
  99. torve/application/telemetry.py +1302 -0
  100. torve/application/threads.py +107 -0
  101. torve/application/worker.py +239 -0
  102. torve/base/AGENTS.md +28 -0
  103. torve/base/__init__.py +1 -0
  104. torve/base/clock.py +49 -0
  105. torve/base/model.py +14 -0
  106. torve/base/naming.py +151 -0
  107. torve/base/shell.py +149 -0
  108. torve/cli/AGENTS.md +568 -0
  109. torve/cli/__init__.py +9 -0
  110. torve/cli/__main__.py +7 -0
  111. torve/cli/assembly.py +231 -0
  112. torve/cli/brief.py +183 -0
  113. torve/cli/console.py +295 -0
  114. torve/cli/context.py +458 -0
  115. torve/cli/decisions.py +440 -0
  116. torve/cli/doctor.py +988 -0
  117. torve/cli/equip.py +184 -0
  118. torve/cli/evals.py +559 -0
  119. torve/cli/feedback.py +54 -0
  120. torve/cli/fleet.py +215 -0
  121. torve/cli/gates.py +288 -0
  122. torve/cli/init.py +291 -0
  123. torve/cli/intake.py +350 -0
  124. torve/cli/ledger.py +327 -0
  125. torve/cli/log.py +469 -0
  126. torve/cli/main.py +164 -0
  127. torve/cli/manager.py +1104 -0
  128. torve/cli/mcp.py +179 -0
  129. torve/cli/merge.py +438 -0
  130. torve/cli/migrate.py +87 -0
  131. torve/cli/night.py +244 -0
  132. torve/cli/options.py +285 -0
  133. torve/cli/plan.py +403 -0
  134. torve/cli/review.py +744 -0
  135. torve/cli/run.py +404 -0
  136. torve/cli/sandbox.py +311 -0
  137. torve/cli/serve.py +275 -0
  138. torve/cli/shadow.py +205 -0
  139. torve/cli/sources.py +245 -0
  140. torve/cli/spec.py +1664 -0
  141. torve/cli/status.py +248 -0
  142. torve/cli/survey.py +245 -0
  143. torve/cli/why.py +276 -0
  144. torve/config/AGENTS.md +638 -0
  145. torve/config/__init__.py +1 -0
  146. torve/config/agents.py +713 -0
  147. torve/config/equipment.py +165 -0
  148. torve/config/fleet.py +187 -0
  149. torve/config/layout.py +116 -0
  150. torve/config/manifest.py +341 -0
  151. torve/config/providers.py +374 -0
  152. torve/config/runconfig.py +2025 -0
  153. torve/config/sources.py +222 -0
  154. torve/config/spec.py +1474 -0
  155. torve/config/spec_emit.py +692 -0
  156. torve/domain/AGENTS.md +317 -0
  157. torve/domain/__init__.py +1 -0
  158. torve/domain/attempt.py +87 -0
  159. torve/domain/events.py +701 -0
  160. torve/domain/source.py +111 -0
  161. torve/domain/spec.py +769 -0
  162. torve/domain/states.py +148 -0
  163. torve/domain/task.py +232 -0
  164. torve/domain/vocabulary.py +82 -0
  165. torve/gates/AGENTS.md +269 -0
  166. torve/gates/__init__.py +37 -0
  167. torve/gates/acceptance.py +190 -0
  168. torve/gates/context.py +309 -0
  169. torve/gates/contract.py +41 -0
  170. torve/gates/decisions_reported.py +487 -0
  171. torve/gates/evidence.py +100 -0
  172. torve/gates/no_test_tampering.py +52 -0
  173. torve/gates/red_on_base.py +198 -0
  174. torve/gates/runner.py +318 -0
  175. torve/gates/sabotage.py +747 -0
  176. torve/gates/scope.py +89 -0
  177. torve/gates/secrets.py +111 -0
  178. torve/gates/self_audit.py +44 -0
  179. torve/gates/source_layout.py +58 -0
  180. torve/gates/user_facing_text.py +157 -0
  181. torve/py.typed +0 -0
  182. torve-0.1.0.dist-info/METADATA +257 -0
  183. torve-0.1.0.dist-info/RECORD +186 -0
  184. torve-0.1.0.dist-info/WHEEL +4 -0
  185. torve-0.1.0.dist-info/entry_points.txt +2 -0
  186. torve-0.1.0.dist-info/licenses/LICENSE +21 -0
torve/AGENTS.md ADDED
@@ -0,0 +1,39 @@
1
+ <!-- torve:managed src/torve — rendered from the corpus; do not edit by hand -->
2
+
3
+ ## Decisions governing `src/torve/`
4
+
5
+ ### S-0055/D-23 — `LOCKED` (Standing decisions)
6
+
7
+ Five layers — `base`, `domain`, `application`, `adapters`, `cli`, beside `gates` and `config` — with import directions enforced by import-linter over the whole package; the `layering` gate blocks
8
+
9
+ - Paths: `src/torve/**` `pyproject.toml`
10
+ - Consequence: The hexagon is visible in the tree and mechanically held
11
+ - Touching these paths owes a divergence entry: `torve log owed <task> --touched <files>` before you finish
12
+
13
+ ### S-0055/D-27 — `ASSUMED` (Standing decisions)
14
+
15
+ No module is named `models`, `utils`, `helpers`, `common` or `base` below the package root; modules are named for what they hold, and the `source-layout` gate reads it
16
+
17
+ - Paths: `src/torve/**`
18
+ - Consequence: Names that admit anything accumulate everything
19
+
20
+ ### S-0055/D-59 — `ASSUMED` (Standing decisions)
21
+
22
+ Strict typing is a floor over `src/`: `mypy --strict` and `basedpyright` strict block CI and the acceptance fallback; tests, scripts and skills carry no type floor
23
+
24
+ - Paths: `src/torve/**` `pyproject.toml`
25
+ - Consequence: A substrate surface change fails at the type check
26
+
27
+ ## Invariants holding over `src/torve/`
28
+
29
+ - **S-0055/I-1**: The five layer contracts hold over the whole package
30
+ - Paths: `src/torve/**` `pyproject.toml`
31
+ - Check: `uv run lint-imports --config pyproject.toml`
32
+ - **S-0055/I-4**: The typing floor over src holds
33
+ - Paths: `src/torve/**`
34
+ - Check: `uv run mypy src && uv run basedpyright src`
35
+ - **S-0055/I-5**: The suite is green
36
+ - Paths: `src/torve/**` `tests/**`
37
+ - Check: `uv run pytest`
38
+
39
+ <!-- /torve:managed -->
torve/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ """Torve — deterministic gates for agent and human pull requests.
2
+
3
+ Names resolve from their canonical modules: `torve.gates.runner.run_gates`,
4
+ `torve.domain.task.Task`, and so on. There is no re-export table — the
5
+ gates-only CI path never imports the runner, the store or an adapter because
6
+ nothing here imports them, not because a lazy front door defers them
7
+ (S-0015/the-front-door, S-0015/A-2).
8
+ """
9
+
10
+ __version__ = "0.1.0"
@@ -0,0 +1,12 @@
1
+ <!-- torve:managed migrations — rendered from the corpus; do not edit by hand -->
2
+
3
+ ## Decisions governing `migrations/`
4
+
5
+ ### S-0055/D-43 — `ASSUMED` (Standing decisions)
6
+
7
+ Migrations are owner-grouped, forward-only SQL under `migrations/`; the substrate is pinned by `FORZE_VERSION`, which `torve doctor` enforces and `config_hash` digests
8
+
9
+ - Paths: `migrations/**` `src/torve/application/migrate.py` `src/torve/cli/doctor.py`
10
+ - Consequence: A substrate surface change fails at the pin bump, not in production
11
+
12
+ <!-- /torve:managed -->
@@ -0,0 +1 @@
1
+ 0.10.0
@@ -0,0 +1,42 @@
1
+ -- Substrate: durable run store + step journal (forze, schema per the
2
+ -- adapter docstrings of PostgresDurableRunStore / the step adapter).
3
+ -- Owner: substrate — this file changes when forze is upgraded, never for
4
+ -- torve's own reasons (S-0012/D-1). Forward-only (S-0012/D-4).
5
+ --
6
+ -- Do not trim columns: every read projects cancel_requested_at /
7
+ -- cancel_refused_at, and a missing column makes lease renewal raise, which a
8
+ -- heartbeat reads as lease lost.
9
+
10
+ CREATE TABLE IF NOT EXISTS public.torve_durable_run (
11
+ run_id text NOT NULL,
12
+ name text NOT NULL,
13
+ status text NOT NULL,
14
+ idempotency_key text,
15
+ input jsonb,
16
+ output jsonb,
17
+ error text,
18
+ tenant_id uuid,
19
+ attempts integer NOT NULL DEFAULT 0,
20
+ leased_until timestamptz,
21
+ available_at timestamptz,
22
+ created_at timestamptz NOT NULL,
23
+ updated_at timestamptz NOT NULL,
24
+ cancel_requested_at timestamptz,
25
+ cancel_refused_at timestamptz,
26
+ PRIMARY KEY (run_id),
27
+ UNIQUE (idempotency_key)
28
+ );
29
+
30
+ CREATE INDEX IF NOT EXISTS torve_durable_run_status_created
31
+ ON public.torve_durable_run (status, created_at);
32
+ CREATE INDEX IF NOT EXISTS torve_durable_run_created_desc
33
+ ON public.torve_durable_run (created_at DESC, run_id DESC);
34
+
35
+ CREATE TABLE IF NOT EXISTS public.torve_durable_step (
36
+ run_id text NOT NULL,
37
+ step_id text NOT NULL,
38
+ result jsonb NOT NULL,
39
+ tenant_id uuid,
40
+ created_at timestamptz NOT NULL DEFAULT now(),
41
+ PRIMARY KEY (run_id, step_id)
42
+ );
@@ -0,0 +1,40 @@
1
+ -- Torve: the event log (S-0044/the-event-log). Owner: torve — this file changes for
2
+ -- torve's own reasons, never for a forze upgrade (S-0012/D-1). Forward-only
3
+ -- (S-0012/D-4).
4
+ --
5
+ -- The relation backs the `torve-events` document spec, so every column name
6
+ -- is a field name on the aggregate's models and the two must move together;
7
+ -- `id`, `rev`, `created_at` and `last_update_at` come from the document base
8
+ -- rather than from anything torve declared.
9
+ --
10
+ -- Append-only is a property of the spec above this table — it declares no
11
+ -- update command, so the adapter exposes no update port — and `rev` is here
12
+ -- because the document base carries it, not because a row is ever revised.
13
+ -- A correction is another event.
14
+
15
+ CREATE TABLE IF NOT EXISTS public.torve_event (
16
+ id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
17
+ rev integer NOT NULL DEFAULT 1,
18
+ created_at timestamptz NOT NULL DEFAULT now(),
19
+ last_update_at timestamptz NOT NULL DEFAULT now(),
20
+ schema_version integer NOT NULL DEFAULT 1,
21
+ kind text NOT NULL,
22
+ partition text NOT NULL,
23
+ subject_type text NOT NULL,
24
+ subject_id text NOT NULL,
25
+ actor_kind text NOT NULL,
26
+ actor_id text NOT NULL,
27
+ payload jsonb NOT NULL DEFAULT '{}'::jsonb,
28
+ correlation_id text,
29
+ causation_id text
30
+ );
31
+
32
+ -- The log's two reads (S-0044/ports-and-what-binds-first): one subject's history, and one
33
+ -- partition's tail. Both order by (created_at, id) — the timestamp is not a
34
+ -- total order under concurrency and the id is the stable tiebreak, so the
35
+ -- index carries both or the sort spills.
36
+ CREATE INDEX IF NOT EXISTS torve_event_subject
37
+ ON public.torve_event (subject_id, created_at, id);
38
+
39
+ CREATE INDEX IF NOT EXISTS torve_event_partition
40
+ ON public.torve_event (partition, created_at, id);
@@ -0,0 +1,16 @@
1
+ -- Torve: the decision graph's read (S-0047/the-read-that-does-not-fold-an-execution-log, S-0047/D-7). Owner: torve —
2
+ -- this file changes for torve's own reasons, never for a forze upgrade
3
+ -- (S-0012/D-1). Forward-only (S-0012/D-4).
4
+ --
5
+ -- Sources and decisions grow with the corpus; attempts, gates and burn grow
6
+ -- with execution. `torve_event_partition` serves the board, which wants a
7
+ -- partition's whole tail; this one serves the decision graph, which wants
8
+ -- the corpus-sized slice of it and would otherwise fold an execution log to
9
+ -- find a few hundred rows.
10
+ --
11
+ -- Ordered by (created_at, id) for the same reason 0001 is: the timestamp is
12
+ -- not a total order under concurrency and the id is the stable tiebreak, so
13
+ -- the index carries both or the sort spills.
14
+
15
+ CREATE INDEX IF NOT EXISTS torve_event_subject_type
16
+ ON public.torve_event (partition, subject_type, created_at, id);
@@ -0,0 +1,4 @@
1
+ # Torve document tables (tasks_ref, attempts, gate_results, findings,
2
+ # review_feedback) arrive with the attempts store; this history starts at the
3
+ # first table. Creating the directory early keeps `torve migrate torve` a
4
+ # stable no-op rather than an unknown target (S-0012/D-6).
@@ -0,0 +1,50 @@
1
+ <!-- torve:managed skills — rendered from the corpus; do not edit by hand -->
2
+
3
+ ## Decisions governing `skills/`
4
+
5
+ ### S-0055/D-36 — `ASSUMED` (Standing decisions)
6
+
7
+ Skills ship as package data under `skills/` and are materialised role-scoped into the sandbox; a vendored skill lives under `.torve/skills-vendor/`; a name in both refuses, a name in neither is a configuration error
8
+
9
+ - Paths: `skills/**` `.torve/skills-vendor/**` `src/torve/application/skills.py`
10
+ - Consequence: What an agent knows is versioned and named, never ambient
11
+
12
+ ### S-0056/D-8 — `ASSUMED` (Structure for everything) — implementation: none
13
+
14
+ `SKILL.md`, `AGENTS.md`, the colocated sections and the pack are unchanged: projections rendered from the model, read by harnesses and people
15
+
16
+ - Paths: `skills/**` `src/torve/application/colocation.py`
17
+ - Consequence: Nothing a harness reads changes shape
18
+
19
+ ### S-0057/D-4 — `LOCKED` (The specification is a directory)
20
+
21
+ `torve spec` absorbs every `torve rfc` verb and `cli/rfc.py` is deleted; the gate is `spec-valid`; the skill is `spec-writer`; `rfc_emit.py` becomes `spec_emit.py`; new prose says specification or document
22
+
23
+ - Paths: `src/torve/cli/spec.py` `src/torve/cli/spec.py` `src/torve/config/spec_emit.py` `skills/**` `.torve/gates.yaml`
24
+ - Consequence: One namespace for the corpus; old prose and test file names keep the old word
25
+ - Touching these paths owes a divergence entry: `torve log owed <task> --touched <files>` before you finish
26
+
27
+ ### S-0058/D-2 — `LOCKED` (One grammar and the anatomy)
28
+
29
+ Every existing identifier converts once through a mapping committed as `.torve/archive/identifiers.yaml`, applied to the corpus, the archive, `src/`, `pages/`, `skills/`, the README, the local task files and the projections, with "RFC NNNN §n" converted to the section key at that position; a parity script gates the commit and is not committed
30
+
31
+ - Paths: `.torve/specs/**` `.torve/archive/**` `src/**` `pages/**` `skills/**` `README.md`
32
+ - Consequence: No legacy identifier stands anywhere the check reads; history keeps its words and resolves through the mapping
33
+ - Touching these paths owes a divergence entry: `torve log owed <task> --touched <files>` before you finish
34
+
35
+ ### S-0058/D-10 — `ASSUMED` (One grammar and the anatomy)
36
+
37
+ The skill and its template, the schemas `torve init` writes, the projections beside the code and the operating page follow the grammar and the anatomy in the same phase that changes them
38
+
39
+ - Paths: `skills/**` `src/torve/application/colocation.py` `src/torve/cli/init.py` `pages/docs/operating.md`
40
+ - Consequence: Nothing a harness or a person reads names an identifier the check refuses
41
+
42
+ ### S-0062/D-6 — `LOCKED` (Equipment is declared, and the harness is told how to take it)
43
+
44
+ A skill a gate reads stays package data, versioned with the engine; `torve:` is the source that names one.
45
+
46
+ - Paths: `src/torve/application/skills.py` `skills/**`
47
+ - Consequence: the two skills this engine ships cannot drift against the gate that parses what they teach
48
+ - Touching these paths owes a divergence entry: `torve log owed <task> --touched <files>` before you finish
49
+
50
+ <!-- /torve:managed -->
@@ -0,0 +1,3 @@
1
+ <!-- torve:managed skills/corpus-bootstrap — rendered from the corpus; do not edit by hand -->
2
+
3
+ <!-- /torve:managed -->
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: corpus-bootstrap
3
+ description: Drafting a brownfield adopter's standing-decisions baseline from the survey report — mostly ASSUMED, LOCKED only on defended boundaries, paths on every row, no phasing, one draft document for a human to edit, commit and accept.
4
+ roles: [author]
5
+ gate: spec-valid
6
+ ---
7
+
8
+ > **Specialisation.** Derived from `agent-skills/corpus-bootstrap`, specialised for
9
+ > artefacts that Torve parses. Divergence from upstream is expected and
10
+ > intentional — **do not reconcile**. Improvements of general value flow
11
+ > upstream, not the reverse.
12
+
13
+ # Corpus Bootstrap — the baseline draft
14
+
15
+ This skill is the second half of first contact with a repository that has no
16
+ corpus. The engine's `torve survey` did the first half: it replayed the last
17
+ landings through the gate battery and produced a read-only report of what
18
+ would have fired and what stayed silent for want of a corpus. This skill —
19
+ run in a supervised session, **never by the engine** — reads that report and
20
+ the repository, and drafts the standing-decisions document that turns the
21
+ report's silence into governance.
22
+
23
+ The engine never reads this skill's output while it is a draft. It only ever
24
+ reads the committed, human-accepted document, through the standing
25
+ inheritance layer: from acceptance, every row whose declared paths intersect
26
+ a contract's scope rides into that contract at mint time, and nothing else
27
+ (S-0031/D-2). There is no engine surface for the baseline at all — which is the
28
+ point: a skill can be supervised, edited and refused in a diff; an engine
29
+ surface would drift.
30
+
31
+ ## Inputs and output
32
+
33
+ - **Input:** the survey report (the JSON `torve survey --format json`
34
+ writes) and the repository working tree. The report is the evidence base;
35
+ the tree is where the boundaries live and where their globs are read from.
36
+ - **Output:** one draft document in this corpus's format — frontmatter, an
37
+ H1, a Decisions section holding the decision table, no Phasing section —
38
+ written so a human can edit, commit and accept it in one review. The draft
39
+ is `status: draft`; acceptance is the human flipping it to `accepted` in
40
+ the same commit that signs it.
41
+
42
+ ## The extraction doctrine
43
+
44
+ Four rules, in order of how much damage breaking them does:
45
+
46
+ 1. **Mostly ASSUMED.** A grade is a human judgement about reversal cost, and
47
+ a stranger's repository has no corpus — nobody has made that judgement.
48
+ Default every candidate row to `ASSUMED` and let the human's acceptance
49
+ edits do the real grading. An extraction that comes back mostly `LOCKED`
50
+ poisons the entry: the acceptance review reads it as a takeover and
51
+ refuses the whole document.
52
+
53
+ 2. **LOCKED only on defended boundaries.** A `LOCKED` row claims reopening is
54
+ expensive. The only evidence an extraction can cite for that is the
55
+ repository's own history showing the boundary being **defended**: a
56
+ firing followed by a correction landing, or a consistent clean record
57
+ with the boundary visible in the tree (the layout is deliberate because
58
+ the history holds it). A single firing in a short window is
59
+ `ASSUMED`-grade evidence — the boundary exists as a check, but nobody has
60
+ been shown defending it.
61
+
62
+ 3. **Paths on every row.** A row without declared paths is never standing —
63
+ the inheritance layer skips it and the row governs nothing. Every row
64
+ names the globs it governs, read from the actual tree, not invented.
65
+
66
+ 4. **No phasing.** The baseline is a set of standing rows, not work to
67
+ sequence. A Phasing section would make the engine mint tasks from a
68
+ baseline document — exactly the engine surface S-0031/D-2 refuses. The
69
+ baseline answers "how is this repository governed"; sequencing is another
70
+ document's job, and the draft has no `## Phasing` section.
71
+
72
+ ## Reading the survey
73
+
74
+ The report is the evidence base; read it before the tree.
75
+
76
+ - **Fired** (`outcome` in `fail`/`error`/`bypassed`) — a boundary was
77
+ crossed in history. Name the landing, then go to the tree: was the
78
+ crossing corrected later? That correction is the defended-boundary
79
+ evidence rule 2 wants.
80
+ - **Clean** (`pass`/`flaky`) — measured nothing wrong. No row follows from a
81
+ clean gate alone; the boundary it measures is already being held.
82
+ - **Skipped with `no_corpus: true`** — the gate did not run because no task
83
+ contract existed: the corpus's absence made visible. These are the rows
84
+ the baseline exists to write: a standing row that gives the gate
85
+ something to measure once work is minted.
86
+ - **Skipped with `no_corpus: false`** — the runner's fail-fast "not run"
87
+ after an earlier blocking gate fired, or acceptance's structural no-commands
88
+ skip. Not a corpus gap; no row follows.
89
+ - **The summary's `corpus_adds`** — the gates that never measured a single
90
+ landing and whose silence is the no-task skip. The report already names
91
+ them; every one of them becomes a candidate `ASSUMED` row.
92
+
93
+ ## The shape this skill records (S-0031/D-6)
94
+
95
+ The baseline is **one document per adoption**, named
96
+ the `S-NNNN/` directory in the corpus path (`.torve/specs/` by default),
97
+ with `NNNN` the next free corpus number and the title "Standing decisions".
98
+ Rationale: a first adopter has no areas taxonomy to split by, a single
99
+ document keeps the acceptance ceremony to one commit, and the inheritance
100
+ layer already selects rows by path intersection — several-by-area buys
101
+ nothing until a corpus has enough rows to need it. The first real adoption
102
+ settles this shape; this paragraph is the skill recording the shape it
103
+ chose, and the recording is updated to what the adoption chose.
104
+
105
+ ## The hand-off
106
+
107
+ The extraction produces a **draft**. The human then:
108
+
109
+ 1. Edits — grading, wording, the paths, anything the history suggests.
110
+ 2. Commits — the draft becomes a committed document.
111
+ 3. Accepts — `status: accepted`, the commit that makes the rows stand.
112
+
113
+ Nothing the engine reads exists before that commit, and nothing the engine
114
+ reads after it is this skill's doing: standing inheritance copies grade and
115
+ paths at mint time and the battery convicts only work minted after — a
116
+ ratchet, never a purge (S-0031/D-3).
117
+
118
+ ## References
119
+
120
+ - `references/doctrine.md` — the extraction doctrine in detail: grading
121
+ evidence, path discipline, what the survey's four outcomes mean
122
+ - `references/workflow.md` — the supervised-session procedure, inputs to
123
+ accepted document
124
+ - `references/draft-template.md` — the corpus-format skeleton the draft is
125
+ written from
126
+ - `fixtures/survey-report.json` — a sample survey report (input)
127
+ - `fixtures/S-0001/` — the checkable output shape: the draft
128
+ the doctrine produces from the sample report
@@ -0,0 +1,45 @@
1
+ # yaml-language-server: $schema=../../schemas/decisions.json
2
+ decisions:
3
+ - id: D-1
4
+ grade: LOCKED
5
+ text: >-
6
+ Credentials never enter the source tree; secrets are injected from the deployment
7
+ environment
8
+ paths: [src/**, .env.example]
9
+ consequence: >-
10
+ The repository's history defended this boundary: the survey's secrets gate fired
11
+ when a key landed in `src/leak.py` and the next landing removed it; crossing
12
+ it again is expensive to undo
13
+ - id: D-2
14
+ grade: ASSUMED
15
+ text: >-
16
+ Product code lives under `src/**`; operational scripts live under `scripts/**`
17
+ paths: [src/**, scripts/**]
18
+ consequence: >-
19
+ The survey's scope gate fired once on a root-level deploy script and stayed
20
+ clean on the other four landings; whether the layout is deliberate is the acceptance
21
+ edit's call
22
+ - id: D-3
23
+ grade: ASSUMED
24
+ text: >-
25
+ Test files are edited only under a task's licence; adding a test is an addition,
26
+ editing one needs scope
27
+ paths: [tests/**]
28
+ consequence: >-
29
+ The no-test-tampering gate never measured a single landing — no task existed
30
+ — so this row gives the gate a spec once work is minted
31
+ - id: D-4
32
+ grade: ASSUMED
33
+ text: >-
34
+ Work touching governed areas records its decisions in the task log
35
+ paths: [.torve/tasks/**, logs/**]
36
+ consequence: >-
37
+ The decisions-reported gate never measured a landing; this row is the corpus
38
+ layer that turns its no-task skip into something that can fire
39
+ - id: D-5
40
+ grade: ASSUMED
41
+ text: Execution logs carry the declared drift count
42
+ paths: [.torve/tasks/**, logs/**]
43
+ consequence: >-
44
+ The self-audit gate never measured a landing; this row names the log discipline
45
+ the gate reads
@@ -0,0 +1,56 @@
1
+ # yaml-language-server: $schema=../../schemas/document.json
2
+ id: S-0001
3
+ title: Standing decisions
4
+ kind: design
5
+ status: draft
6
+ implementation: none
7
+ depends_on: []
8
+ informed_by: []
9
+ supersedes: []
10
+ superseded_by: null
11
+ owner: <adopter>
12
+ schema_version: 4
13
+ summary: |-
14
+ The survey found two crossed boundaries and three gates that never measured
15
+ a single landing. The secrets boundary was defended: a key landed in
16
+ `src/leak.py` and the next landing removed it, so that row stands `LOCKED`.
17
+ The source-layout boundary was crossed once and left uncorrected in the
18
+ window, so its row stands `ASSUMED`. The three gates that never measured
19
+ anything for want of a corpus — test licensing, decision recording, and the
20
+ self-audit log — each get an `ASSUMED` row that gives them something to
21
+ measure once work is minted.
22
+ motivation: ''
23
+ current_state: ''
24
+ goals: ''
25
+ non_goals: ''
26
+ design:
27
+ - key: the-survey-findings
28
+ md: |-
29
+ - **`secrets` fired** on landing `f901a7b` ("chore: wire stripe
30
+ credentials"): a new file `src/leak.py` carried an access key. Landing `d55fecb`
31
+ ("fix: rotate credentials out of the tree") deleted the file and added a
32
+ `.env.example` placeholder, and measured clean — the boundary was
33
+ defended in the window.
34
+ - **`scope` fired** on landing `8ef5b12` ("chore: deployment script at
35
+ repository root"): `scripts/deploy.sh` landed outside the manifest's
36
+ `src/**` and `.env.example` allow. No later landing corrected it in the
37
+ window, and the tree's `src/` home for product code holds across the
38
+ other four landings.
39
+ - **`no-test-tampering`, `decisions-reported`, `self-audit` skipped every
40
+ landing** with the no-task skip: no contract has ever existed in this
41
+ repository, so nothing gave them a spec to check against. The survey's
42
+ `corpus_adds` names exactly these three — the corpus's absence made
43
+ visible.
44
+ - key: decisions
45
+ md: |-
46
+ | # | Grade | Decision | Paths | Consequence |
47
+ | --- | --- | --- | --- | --- |
48
+ | S-0001/D-1 | `LOCKED` | Credentials never enter the source tree; secrets are injected from the deployment environment | `src/**` `.env.example` | The repository's history defended this boundary: the survey's secrets gate fired when a key landed in `src/leak.py` and the next landing removed it; crossing it again is expensive to undo |
49
+ | S-0001/D-2 | `ASSUMED` | Product code lives under `src/**`; operational scripts live under `scripts/**` | `src/**` `scripts/**` | The survey's scope gate fired once on a root-level deploy script and stayed clean on the other four landings; whether the layout is deliberate is the acceptance edit's call |
50
+ | S-0001/D-3 | `ASSUMED` | Test files are edited only under a task's licence; adding a test is an addition, editing one needs scope | `tests/**` | The no-test-tampering gate never measured a single landing — no task existed — so this row gives the gate a spec once work is minted |
51
+ | S-0001/D-4 | `ASSUMED` | Work touching governed areas records its decisions in the task log | `.torve/tasks/**` `logs/**` | The decisions-reported gate never measured a landing; this row is the corpus layer that turns its no-task skip into something that can fire |
52
+ | S-0001/D-5 | `ASSUMED` | Execution logs carry the declared drift count | `.torve/tasks/**` `logs/**` | The self-audit gate never measured a landing; this row names the log discipline the gate reads |
53
+ tests: ''
54
+ docs: ''
55
+ out_of_scope: ''
56
+ risks: ''