@tiphys/kernel 0.0.0 → 0.2.0
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.
- package/AGENTS.md +663 -0
- package/LICENSE +202 -0
- package/assurance-modes.yaml +299 -0
- package/checklists/clean-room.yaml +325 -0
- package/checklists/env-failure-diagnosis.yaml +68 -0
- package/checklists/flake-playbook.yaml +68 -0
- package/checklists/hazard-review.yaml +144 -0
- package/checklists/plan-review.yaml +103 -0
- package/dist/bin/tiphys.d.ts +2 -0
- package/dist/bin/tiphys.js +92 -0
- package/dist/src/adapters/load.d.ts +202 -0
- package/dist/src/adapters/load.js +440 -0
- package/dist/src/brief.d.ts +32 -0
- package/dist/src/brief.js +66 -0
- package/dist/src/checklists.d.ts +169 -0
- package/dist/src/checklists.js +310 -0
- package/dist/src/checks.d.ts +1539 -0
- package/dist/src/checks.js +5025 -0
- package/dist/src/cli.d.ts +9 -0
- package/dist/src/cli.js +68 -0
- package/dist/src/commands/brief.d.ts +92 -0
- package/dist/src/commands/brief.js +343 -0
- package/dist/src/commands/checklist.d.ts +42 -0
- package/dist/src/commands/checklist.js +168 -0
- package/dist/src/commands/cutover.d.ts +35 -0
- package/dist/src/commands/cutover.js +448 -0
- package/dist/src/commands/doctor.d.ts +264 -0
- package/dist/src/commands/doctor.js +1546 -0
- package/dist/src/commands/gates.d.ts +9 -0
- package/dist/src/commands/gates.js +360 -0
- package/dist/src/commands/init.d.ts +30 -0
- package/dist/src/commands/init.js +177 -0
- package/dist/src/commands/lock.d.ts +34 -0
- package/dist/src/commands/lock.js +340 -0
- package/dist/src/commands/mode.d.ts +40 -0
- package/dist/src/commands/mode.js +134 -0
- package/dist/src/commands/next.d.ts +130 -0
- package/dist/src/commands/next.js +597 -0
- package/dist/src/commands/plan.d.ts +20 -0
- package/dist/src/commands/plan.js +105 -0
- package/dist/src/commands/pool.d.ts +1 -0
- package/dist/src/commands/pool.js +139 -0
- package/dist/src/commands/resume.d.ts +1 -0
- package/dist/src/commands/resume.js +88 -0
- package/dist/src/commands/spawn.d.ts +1 -0
- package/dist/src/commands/spawn.js +195 -0
- package/dist/src/commands/status.d.ts +21 -0
- package/dist/src/commands/status.js +122 -0
- package/dist/src/commands/sync.d.ts +47 -0
- package/dist/src/commands/sync.js +341 -0
- package/dist/src/commands/teardown.d.ts +1 -0
- package/dist/src/commands/teardown.js +87 -0
- package/dist/src/commands/tuition.d.ts +21 -0
- package/dist/src/commands/tuition.js +218 -0
- package/dist/src/commands/validate.d.ts +78 -0
- package/dist/src/commands/validate.js +430 -0
- package/dist/src/commands/watch.d.ts +1 -0
- package/dist/src/commands/watch.js +172 -0
- package/dist/src/cutover.d.ts +584 -0
- package/dist/src/cutover.js +1444 -0
- package/dist/src/exclusion.d.ts +389 -0
- package/dist/src/exclusion.js +843 -0
- package/dist/src/exec/env.d.ts +278 -0
- package/dist/src/exec/env.js +334 -0
- package/dist/src/fleet.d.ts +223 -0
- package/dist/src/fleet.js +298 -0
- package/dist/src/gates/adapters/http-json.d.ts +5 -0
- package/dist/src/gates/adapters/http-json.js +283 -0
- package/dist/src/gates/adapters/migrations-command.d.ts +1 -0
- package/dist/src/gates/adapters/migrations-command.js +373 -0
- package/dist/src/gates/citations.d.ts +408 -0
- package/dist/src/gates/citations.js +1169 -0
- package/dist/src/gates/coverage.d.ts +375 -0
- package/dist/src/gates/coverage.js +836 -0
- package/dist/src/gates/credentials.d.ts +233 -0
- package/dist/src/gates/credentials.js +752 -0
- package/dist/src/gates/deploy.d.ts +1 -0
- package/dist/src/gates/deploy.js +33 -0
- package/dist/src/gates/gate-classes.d.ts +56 -0
- package/dist/src/gates/gate-classes.js +633 -0
- package/dist/src/gates/manifest.d.ts +99 -0
- package/dist/src/gates/manifest.js +208 -0
- package/dist/src/gates/merge-preconditions.d.ts +319 -0
- package/dist/src/gates/merge-preconditions.js +932 -0
- package/dist/src/gates/migrations.d.ts +1 -0
- package/dist/src/gates/migrations.js +36 -0
- package/dist/src/gates/pin.d.ts +114 -0
- package/dist/src/gates/pin.js +154 -0
- package/dist/src/gates/red-witness.d.ts +22 -0
- package/dist/src/gates/red-witness.js +482 -0
- package/dist/src/gates/release.d.ts +283 -0
- package/dist/src/gates/release.js +820 -0
- package/dist/src/gates/result.d.ts +116 -0
- package/dist/src/gates/result.js +91 -0
- package/dist/src/gates/run.d.ts +614 -0
- package/dist/src/gates/run.js +1614 -0
- package/dist/src/gates/schemas/citation-config.schema.json +59 -0
- package/dist/src/gates/schemas/coverage-config.schema.json +77 -0
- package/dist/src/gates/schemas/gate-manifest.schema.json +125 -0
- package/dist/src/gates/schemas/gate-result.schema.json +160 -0
- package/dist/src/gates/schemas/phase-declaration.schema.json +87 -0
- package/dist/src/gates/schemas/release-record.schema.json +119 -0
- package/dist/src/gates/schemas/verifier-config.schema.json +101 -0
- package/dist/src/gates/schemas/witness-spec.schema.json +110 -0
- package/dist/src/gates/scope.d.ts +131 -0
- package/dist/src/gates/scope.js +1018 -0
- package/dist/src/gates/suite.d.ts +217 -0
- package/dist/src/gates/suite.js +968 -0
- package/dist/src/gates/validate.d.ts +121 -0
- package/dist/src/gates/validate.js +414 -0
- package/dist/src/hooks.d.ts +84 -0
- package/dist/src/hooks.js +125 -0
- package/dist/src/index.d.ts +31 -0
- package/dist/src/index.js +30 -0
- package/dist/src/liveness.d.ts +321 -0
- package/dist/src/liveness.js +396 -0
- package/dist/src/lock.d.ts +256 -0
- package/dist/src/lock.js +792 -0
- package/dist/src/model-resolution.d.ts +159 -0
- package/dist/src/model-resolution.js +307 -0
- package/dist/src/modes.d.ts +149 -0
- package/dist/src/modes.js +258 -0
- package/dist/src/path-identity.d.ts +34 -0
- package/dist/src/path-identity.js +48 -0
- package/dist/src/plan.d.ts +73 -0
- package/dist/src/plan.js +153 -0
- package/dist/src/pool.d.ts +326 -0
- package/dist/src/pool.js +988 -0
- package/dist/src/roles.d.ts +461 -0
- package/dist/src/roles.js +776 -0
- package/dist/src/spawn.d.ts +482 -0
- package/dist/src/spawn.js +1003 -0
- package/dist/src/status.d.ts +116 -0
- package/dist/src/status.js +148 -0
- package/dist/src/task.d.ts +504 -0
- package/dist/src/task.js +307 -0
- package/dist/src/teardown.d.ts +39 -0
- package/dist/src/teardown.js +422 -0
- package/dist/src/tuition.d.ts +159 -0
- package/dist/src/tuition.js +311 -0
- package/dist/src/validate.d.ts +263 -0
- package/dist/src/validate.js +742 -0
- package/dist/src/version.d.ts +3 -0
- package/dist/src/version.js +38 -0
- package/dist/src/watcher.d.ts +275 -0
- package/dist/src/watcher.js +849 -0
- package/dist/src/witness/run.d.ts +299 -0
- package/dist/src/witness/run.js +1373 -0
- package/dist/src/witness/spec.d.ts +270 -0
- package/dist/src/witness/spec.js +475 -0
- package/dist/tsconfig.src.tsbuildinfo +1 -0
- package/gate-registry.yaml +526 -0
- package/gates.manifest.json +257 -0
- package/package.json +72 -3
- package/role-model-config.yaml +88 -0
- package/roles/README.md +128 -0
- package/roles/_shared-dispatch-contract.md +87 -0
- package/roles/adversarial-plan-reviewer.md +80 -0
- package/roles/clean-room-reviewer.md +140 -0
- package/roles/implementer.md +463 -0
- package/roles/investigator.md +138 -0
- package/roles/plan-writer.md +95 -0
- package/schemas/README.md +82 -0
- package/schemas/assurance-modes.schema.json +264 -0
- package/schemas/charter.schema.json +185 -0
- package/schemas/checklist.schema.json +114 -0
- package/schemas/cutover-state.schema.json +64 -0
- package/schemas/decision-record.schema.json +88 -0
- package/schemas/executor-record.schema.json +36 -0
- package/schemas/final-report.schema.json +90 -0
- package/schemas/finding.schema.json +106 -0
- package/schemas/gate-registry.schema.json +260 -0
- package/schemas/mechanism-index.schema.json +94 -0
- package/schemas/model-resolution.schema.json +362 -0
- package/schemas/plan.schema.json +300 -0
- package/schemas/report.schema.json +579 -0
- package/schemas/role-brief.schema.json +105 -0
- package/schemas/role-model-config.schema.json +90 -0
- package/schemas/status-line.schema.json +40 -0
- package/schemas/tuition.schema.json +191 -0
- package/schemas/verdict.schema.json +295 -0
- package/schemas/work-history.schema.json +183 -0
- package/schemas/write-bypass.schema.json +69 -0
- package/templates/charter.example.yaml +54 -0
- package/templates/decision-record.example.yaml +27 -0
- package/templates/final-report.example.yaml +80 -0
- package/templates/plan.example.yaml +87 -0
- package/templates/report.example.yaml +236 -0
- package/templates/warnings.md +74 -0
- package/templates/work-history.example.yaml +185 -0
- package/tuition/README.md +76 -0
- package/tuition/T-001.yaml +48 -0
- package/tuition/T-002.yaml +51 -0
- package/tuition/T-003.yaml +100 -0
- package/tuition/T-004.yaml +52 -0
- package/tuition/T-005.yaml +72 -0
- package/tuition/T-006.yaml +81 -0
- package/tuition/T-007.yaml +56 -0
- package/tuition/T-008.yaml +111 -0
- package/tuition/T-009.yaml +50 -0
- package/tuition/T-015.yaml +36 -0
- package/tuition/T-016.yaml +36 -0
- package/tuition/T-017.yaml +46 -0
- package/tuition/T-018.yaml +84 -0
- package/tuition/T-021.yaml +40 -0
- package/tuition/T-022.yaml +36 -0
- package/tuition/mechanism-index.yaml +256 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import type { Fleet } from "./fleet.ts";
|
|
2
|
+
import { classifyPathEntry as classifyEntry, readRegularPathIfPresent as readRegularFileIfPresent, refuseOpenPathForWrite as refuseOpenForWrite } from "./fleet.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Task state (kernel plan v1, M1-P4 step 1). A task's durable state is
|
|
5
|
+
* <fleet>/tasks/<id>/, holding plain JSON per plan decision D-3:
|
|
6
|
+
*
|
|
7
|
+
* meta.json the task record written by spawn and closed by teardown
|
|
8
|
+
* brief.md the assembled brief (src/brief.ts)
|
|
9
|
+
* turn-end the payload's completion record (src/hooks.ts)
|
|
10
|
+
* executor.json the launch record written by the executor adapter
|
|
11
|
+
* report.md a scout's report, the teardown gate for shape scout
|
|
12
|
+
*
|
|
13
|
+
* PLAN CONSTRAINT C-1 (FM-052, FM-049), binding on every consumer: the
|
|
14
|
+
* ONE current-state authority for a task is meta.json's status plus the
|
|
15
|
+
* turn-end file's recorded exit code. Currency is never derived from the
|
|
16
|
+
* tail of an event or status log, and the turn-end file is a completion
|
|
17
|
+
* NOTIFICATION, not the task's state: a missing turn-end never means
|
|
18
|
+
* success (tuition T-002).
|
|
19
|
+
*
|
|
20
|
+
* The task directory deliberately sits OUTSIDE the worktree (FM-059), so
|
|
21
|
+
* the pool's dirty check never needs an exemption list for the kernel's
|
|
22
|
+
* own injected files. That invariant is absolute: nothing this phase
|
|
23
|
+
* writes may ever land inside <fleet>/worktrees/<id>.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* OPEN NOTHING WHOSE TYPE HAS NOT BEEN ESTABLISHED (fix round 4, CR-520).
|
|
27
|
+
*
|
|
28
|
+
* THE MECHANISM AND ITS RULE. Opening a path is not a total operation.
|
|
29
|
+
* open(2) on a named pipe with no peer BLOCKS IN THE KERNEL, for reading
|
|
30
|
+
* and for writing, until a peer appears. A block is not an exception, so
|
|
31
|
+
* no try/catch sees it, no "this function never raises" reasoning touches
|
|
32
|
+
* it, and no error classification reaches it. Every process that reaches
|
|
33
|
+
* such an open stops forever with no output at all.
|
|
34
|
+
*
|
|
35
|
+
* This project has now paid for that mechanism three times in one phase:
|
|
36
|
+
* once at tasks/<id>/meta.json (delta review NEW-2), then on six further
|
|
37
|
+
* paths after the first fix was applied at ONE CALL SITE instead of at the
|
|
38
|
+
* read (CR-520), then on four more this round found by deriving the
|
|
39
|
+
* inventory again rather than inheriting it. The lesson recorded in
|
|
40
|
+
* tuition T-005 is that a rule fixed at a call site does not travel; the
|
|
41
|
+
* rule has to be a property of the operation.
|
|
42
|
+
*
|
|
43
|
+
* So the probe lives HERE, in the readers and in one classifier, and every
|
|
44
|
+
* caller is protected by construction rather than by remembering:
|
|
45
|
+
*
|
|
46
|
+
* - lstat the path (the link itself), then stat (what it resolves to),
|
|
47
|
+
* and open ONLY when that is a regular file;
|
|
48
|
+
* - a directory, FIFO, socket, device node, or a symlink resolving to
|
|
49
|
+
* any of those is classified WITHOUT being opened;
|
|
50
|
+
* - nothing at the path is not an error, because absence is the normal
|
|
51
|
+
* transient shape of most of this kernel's state files.
|
|
52
|
+
*
|
|
53
|
+
* WHY THIS LIVES IN src/task.ts. It is a general filesystem rule and not a
|
|
54
|
+
* task rule, and a dedicated module would be its right home. This module
|
|
55
|
+
* is the lowest one in the import graph that the fix round authorized to
|
|
56
|
+
* touch (src/liveness.ts imports it, src/watcher.ts imports it, and it
|
|
57
|
+
* imports neither), and it already carries one cross-cutting helper for
|
|
58
|
+
* the same reason (runStep, below). Moving both to their own module is
|
|
59
|
+
* recorded as an M2 item rather than done here without authorization.
|
|
60
|
+
*
|
|
61
|
+
* RESIDUAL, stated rather than papered over: the probe and the open are
|
|
62
|
+
* two syscalls, so a path that changes type between them can still be
|
|
63
|
+
* opened as something other than a regular file. Closing that needs
|
|
64
|
+
* open(O_NONBLOCK) followed by fstat, which Node's synchronous fs API
|
|
65
|
+
* does not expose for reads. Nothing in this kernel writes that state, and
|
|
66
|
+
* the window is now the only way to reach the block rather than the
|
|
67
|
+
* default path to it.
|
|
68
|
+
*/
|
|
69
|
+
/**
|
|
70
|
+
* ONE IMPLEMENTATION, RE-EXPORTED, AND THAT IS THE DR-0047 SWEEP ROUND 2
|
|
71
|
+
* CONSOLIDATION.
|
|
72
|
+
*
|
|
73
|
+
* The paragraphs above explain WHY the probe exists and they still hold. What
|
|
74
|
+
* changed is where it lives. Until this round there were TWO byte-equivalent
|
|
75
|
+
* copies of it: `classifyEntry`/`readRegularFileIfPresent`/`refuseOpenForWrite`
|
|
76
|
+
* here, and `classifyPathEntry`/`readRegularPathIfPresent`/
|
|
77
|
+
* `refuseOpenPathForWrite` in src/fleet.ts, with identical types, identical
|
|
78
|
+
* bodies and identical sentences. Round 1 created the second deliberately and
|
|
79
|
+
* both implementers flagged it: src/lock.ts and src/exclusion.ts sit BELOW this
|
|
80
|
+
* module in the import graph, so importing from here would have made a cycle.
|
|
81
|
+
*
|
|
82
|
+
* THE DIRECTION THAT REMOVES THE COPY WITHOUT MAKING A CYCLE IS DOWNWARD, AND
|
|
83
|
+
* IT IS THE ONE THE COMMENT ABOVE ALREADY ASKED FOR. src/fleet.ts imports NO
|
|
84
|
+
* local module at all, so it is the leaf; this module already imports
|
|
85
|
+
* `Fleet` from it. The chain is task -> fleet, and lock -> fleet, and nothing
|
|
86
|
+
* points back. The alternative, making lock and exclusion call into here, is
|
|
87
|
+
* the cycle task -> lock -> task and is what round 1 correctly refused.
|
|
88
|
+
*
|
|
89
|
+
* THE NAMES HERE ARE KEPT AS ALIASES rather than the 88 call sites in 31 files
|
|
90
|
+
* being rewritten. The names are the whole reason the rewrite would be
|
|
91
|
+
* expensive and none of the reason the duplication was a defect: what mattered
|
|
92
|
+
* was two bodies that could drift, and there is now one body.
|
|
93
|
+
*/
|
|
94
|
+
export type { PathEntryClass as EntryClass, RegularPathRead as RegularRead } from "./fleet.ts";
|
|
95
|
+
export { classifyEntry, readRegularFileIfPresent, refuseOpenForWrite };
|
|
96
|
+
export type TaskShape = "ship" | "scout";
|
|
97
|
+
export type TaskStatus = "open" | "closed";
|
|
98
|
+
/**
|
|
99
|
+
* WHOSE AUTHORITY A PAYLOAD RUNS UNDER (M4-P8 step 2).
|
|
100
|
+
*
|
|
101
|
+
* `orchestrator` is the process that plans, reviews and merges, and is the
|
|
102
|
+
* only payload class the declared credential escape hatch was ever written
|
|
103
|
+
* for (SpawnOptions.allowPrCredentials, M2-P8 criterion 1: "for the
|
|
104
|
+
* orchestrator's own spawns, never for an implementer payload"). `project`
|
|
105
|
+
* is every payload that works inside a project clone.
|
|
106
|
+
*
|
|
107
|
+
* It is DECLARED, never derived. The kernel cannot infer it from a command,
|
|
108
|
+
* a role or a branch without holding a vocabulary for one of those, and a
|
|
109
|
+
* derived answer would be wrong in the one direction that matters.
|
|
110
|
+
*/
|
|
111
|
+
export type PayloadClass = "orchestrator" | "project";
|
|
112
|
+
/**
|
|
113
|
+
* One granted allowlist extension, as it is recorded in meta.json.
|
|
114
|
+
*
|
|
115
|
+
* BUILT THROUGH THE ACCESSORS, NEVER BY READING `entry.name` DIRECTLY
|
|
116
|
+
* (CR-B-002, the half of it that is about the record). A bare-string entry
|
|
117
|
+
* has no `name` property, so `{name: entry.name, reason: entry.reason}`
|
|
118
|
+
* produced the literal record `{}`: an operator opening the task directory
|
|
119
|
+
* could see THAT a widening happened and not WHICH name was widened. The
|
|
120
|
+
* record is now built with `extensionName` / `extensionReason`
|
|
121
|
+
* (src/exec/env.ts), which read both forms.
|
|
122
|
+
*
|
|
123
|
+
* `reason` is OPTIONAL HERE AND REQUIRED ON THE AUDITED ROUTE, and the
|
|
124
|
+
* difference is the point rather than an inconsistency. This type describes
|
|
125
|
+
* what was actually recorded; the audited route refuses an entry with no
|
|
126
|
+
* usable reason before this record is built, so an entry reaching meta.json
|
|
127
|
+
* without one came through a seam that does not demand one. Recording the
|
|
128
|
+
* absence AS an absence (the key is simply not present in the JSON) is what
|
|
129
|
+
* keeps it distinguishable from a blank reason. Defaulting it to `""` would
|
|
130
|
+
* substitute a valid-looking value for a missing one, which is the same
|
|
131
|
+
* mechanism as CR-B-002 wearing a record's clothes.
|
|
132
|
+
*/
|
|
133
|
+
export interface CredentialExtensionRecord {
|
|
134
|
+
name: string;
|
|
135
|
+
reason?: string;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* THE HANDOVER COMPARISON (M4-P8 criterion 6): what the kernel handed the
|
|
139
|
+
* adapter against what actually reached the child.
|
|
140
|
+
*
|
|
141
|
+
* TWO PROPERTIES, AND THE STATUS WORD NAMES WHICH OF THEM WERE CHECKED.
|
|
142
|
+
* That is finding CR-B-001 (clean-room-retro-B-criteria, HIGH) and it is
|
|
143
|
+
* worth stating as a mechanism rather than as one word: `status: "compared"`
|
|
144
|
+
* used to be written after comparing NAME SETS ONLY, while an operator reads
|
|
145
|
+
* "compared" as "the handover was verified". An adapter that keeps the name
|
|
146
|
+
* set byte-identical and merely restores `HOME` and `XDG_CONFIG_HOME` to
|
|
147
|
+
* their real paths defeats the whole M2R-004 defense, because that defense
|
|
148
|
+
* works by REDIRECTING those names rather than by dropping them, and the
|
|
149
|
+
* record then POSITIVELY ASSERTED a clean handover while the child read the
|
|
150
|
+
* real gh credential store. The general shape is a record whose status word
|
|
151
|
+
* is stronger than the check behind it.
|
|
152
|
+
*
|
|
153
|
+
* Both halves of the repair are here. The word is weakened so it can only
|
|
154
|
+
* say what was checked, and the check is strengthened so the pointer VALUES
|
|
155
|
+
* are compared too.
|
|
156
|
+
*
|
|
157
|
+
* compared names AND the five CREDENTIAL_STORE_REDIRECTIONS
|
|
158
|
+
* pointers were both in hand and both compared.
|
|
159
|
+
* names-compared the name sets were compared and NO pointer evidence
|
|
160
|
+
* was available. This is the value the old `compared`
|
|
161
|
+
* should have carried: it is a true statement about one
|
|
162
|
+
* property of two, and it never reads as a clean bill.
|
|
163
|
+
* pointers-compared the pointers were observed in the child and compared,
|
|
164
|
+
* and the adapter reported no name set. Reachable with
|
|
165
|
+
* any adapter written before M4-P8, which reports
|
|
166
|
+
* nothing, running under the current turn-end hook.
|
|
167
|
+
* unreported neither property could be checked: no reported name
|
|
168
|
+
* set and no pointer evidence.
|
|
169
|
+
* not-applicable the spawn ran under the declared escape hatch, so the
|
|
170
|
+
* kernel handed over no environment at all and there is
|
|
171
|
+
* nothing a difference could be measured against.
|
|
172
|
+
*
|
|
173
|
+
* NO VALUE OF ANY VARIABLE IS RECORDED HERE, and the original design's
|
|
174
|
+
* reason for that is untouched and right: a value comparison that WROTE the
|
|
175
|
+
* values would put credential material into a record an operator reads.
|
|
176
|
+
* `changedRedirections` carries NAMES only. The five redirection targets are
|
|
177
|
+
* harness-owned paths inside the task directory and carry no credential
|
|
178
|
+
* material, but recording only the names that differ is strictly less and is
|
|
179
|
+
* enough to act on.
|
|
180
|
+
*/
|
|
181
|
+
export interface CredentialHandoverRecord {
|
|
182
|
+
status: "compared" | "names-compared" | "pointers-compared" | "unreported" | "not-applicable";
|
|
183
|
+
/** Names the adapter reported that the kernel did not hand over. */
|
|
184
|
+
added: string[];
|
|
185
|
+
/** Names the kernel handed over that the adapter did not report. */
|
|
186
|
+
removed: string[];
|
|
187
|
+
/**
|
|
188
|
+
* Credential-store pointers whose value where the payload ran is not the
|
|
189
|
+
* harness-owned path the kernel handed over. Names only, never values.
|
|
190
|
+
* Empty when the pointers were compared and agreed, and also empty when
|
|
191
|
+
* there was no pointer evidence, which is why `status` and not this array
|
|
192
|
+
* is what says whether the comparison happened.
|
|
193
|
+
*/
|
|
194
|
+
changedRedirections: string[];
|
|
195
|
+
/**
|
|
196
|
+
* WHICH ARTIFACT THE POINTER EVIDENCE WAS READ FROM, absent when there was
|
|
197
|
+
* none. It is a statement about a FILE, not a provenance the kernel
|
|
198
|
+
* verified, and the value used to say otherwise.
|
|
199
|
+
*
|
|
200
|
+
* turn-end-record read from tasks/<id>/turn-end. The kernel generates the
|
|
201
|
+
* hook that normally writes it, and for an adapter that
|
|
202
|
+
* honours the contract those values come from inside the
|
|
203
|
+
* child environment. THE FILE IS ALSO ADAPTER-REACHABLE:
|
|
204
|
+
* the adapter is handed `hookPath` and the record sits
|
|
205
|
+
* beside it, so an adapter that skips the hook and writes
|
|
206
|
+
* the record itself produces a record the kernel cannot
|
|
207
|
+
* tell from the hook's.
|
|
208
|
+
* adapter read from the adapter's launch outcome. The adapter's
|
|
209
|
+
* word about its own behaviour, and always was.
|
|
210
|
+
*
|
|
211
|
+
* THE VALUE WAS `child` UNTIL THE DR-0047 SWEEP FIX ROUND (CR-F-CRED-001,
|
|
212
|
+
* MEDIUM). Measured: an adapter that reverted HOME and XDG_CONFIG_HOME for
|
|
213
|
+
* its payload and wrote the turn-end record itself produced
|
|
214
|
+
* `{"status":"compared","changedRedirections":[],"redirectionSource":"child"}`
|
|
215
|
+
* while the payload could reach a real gh credential store. The word `child`
|
|
216
|
+
* asserted an observation no child had made. Neither value is evidence that
|
|
217
|
+
* the adapter was honest, and the record now says only what it can support.
|
|
218
|
+
* This is CR-B-001's own mechanism, a record whose status word is stronger
|
|
219
|
+
* than the check behind it, recurring one level up inside the same record;
|
|
220
|
+
* see src/hooks.ts for why a nonce does not close it and a rename does.
|
|
221
|
+
*/
|
|
222
|
+
redirectionSource?: "turn-end-record" | "adapter";
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* THE CREDENTIAL DECISION FOR ONE SPAWN (M4-P8 step 6), recorded in the
|
|
226
|
+
* file the kernel already owns rather than in a new one across a new seam.
|
|
227
|
+
*
|
|
228
|
+
* The hazard this phase names is "a credential reaches a project payload
|
|
229
|
+
* and no artifact says so". Every arm of the decision is therefore written
|
|
230
|
+
* down even when it is the boring one: the payload class, whether the
|
|
231
|
+
* scrub ran, every widening with the reason it was granted, the handover
|
|
232
|
+
* comparison, and the refusal if one fired after this record existed.
|
|
233
|
+
*/
|
|
234
|
+
export interface TaskCredentialRecord {
|
|
235
|
+
payloadClass: PayloadClass;
|
|
236
|
+
/**
|
|
237
|
+
* `scrubbed` when buildChildEnv constructed the environment, `inherited`
|
|
238
|
+
* when allowPrCredentials handed the parent's environment over unchanged.
|
|
239
|
+
*
|
|
240
|
+
* THIS FIELD RECORDS THE KERNEL'S DECISION AND ITS OWN CONSTRUCTION, NOT
|
|
241
|
+
* WHAT THE CHILD RECEIVED, and it is spelled out because it is the second
|
|
242
|
+
* member of CR-B-001's class found in this record (the derivation is in
|
|
243
|
+
* delivery/work-history/credential-route-fixes.md). "scrubbed" is a strong
|
|
244
|
+
* word: it says buildChildEnv ran, staged the five empty redirect targets
|
|
245
|
+
* and returned an environment, because a failure there is a rollback. It
|
|
246
|
+
* does NOT say the payload ran with that environment, because between this
|
|
247
|
+
* field being written and the payload starting there is an adapter. The
|
|
248
|
+
* field that speaks to what reached the child is `handover`, and it says
|
|
249
|
+
* which of its two properties it checked.
|
|
250
|
+
*/
|
|
251
|
+
scrubMode: "scrubbed" | "inherited";
|
|
252
|
+
/** Every granted extension, in the order the caller declared them. */
|
|
253
|
+
extensions: CredentialExtensionRecord[];
|
|
254
|
+
/**
|
|
255
|
+
* ABSENT UNTIL THE LAUNCH HAS REPORTED, and the absence is a fact rather
|
|
256
|
+
* than an omission: this record is written BEFORE the payload starts,
|
|
257
|
+
* which is what makes a failure to write it safe to roll back, and at
|
|
258
|
+
* that moment no adapter has launched anything to compare. It is the same
|
|
259
|
+
* reason `ExecutorRecord` carries no resolved model.
|
|
260
|
+
*/
|
|
261
|
+
handover?: CredentialHandoverRecord;
|
|
262
|
+
/**
|
|
263
|
+
* The refusal that fired AFTER this record was written, if one did.
|
|
264
|
+
*
|
|
265
|
+
* The step-5 refusal (the escape hatch asked for on a project payload) is
|
|
266
|
+
* deliberately absent from this field and cannot appear in it: that one
|
|
267
|
+
* refuses before the task directory exists, so there is no meta.json to
|
|
268
|
+
* record it in, and creating one would undo the property that a refused
|
|
269
|
+
* spawn creates nothing.
|
|
270
|
+
*/
|
|
271
|
+
refusal?: string;
|
|
272
|
+
}
|
|
273
|
+
export declare const TASK_SHAPES: readonly TaskShape[];
|
|
274
|
+
/**
|
|
275
|
+
* Task meta (the plan's field set, M1-P4 step 1).
|
|
276
|
+
*
|
|
277
|
+
* - id: the task id, a safe path segment (pool's TASK_ID_PATTERN).
|
|
278
|
+
* - project: absolute path of the project clone the worktree came from.
|
|
279
|
+
* - shape: ship or scout; it selects teardown's refusal rules.
|
|
280
|
+
* - branch: the TASK branch (task/<id>), the branch teardown judges for
|
|
281
|
+
* landedness. Note the deliberate naming difference from the pool
|
|
282
|
+
* record, whose "branch" is the project's default branch and whose
|
|
283
|
+
* "branchName" is this one; meta records the branch the task works on.
|
|
284
|
+
* - worktree: absolute path of the task worktree.
|
|
285
|
+
* - baseSha: the fetched base SHA pool create emitted (EXT-F-03).
|
|
286
|
+
* - baseOffline: provenance, COPIED from the pool record's offline field
|
|
287
|
+
* and never recomputed (PR-212). It is true only when the fetch failed
|
|
288
|
+
* and --offline authorized falling back to the last fetched
|
|
289
|
+
* remote-tracking SHA. A spawn that recomputed it from its own flags
|
|
290
|
+
* would report a fetched base as offline whenever a contended fetch
|
|
291
|
+
* succeeded on retry, which is exactly the provenance inversion V-2
|
|
292
|
+
* produced in M1-P3.
|
|
293
|
+
* - status: open at spawn, closed by a successful teardown.
|
|
294
|
+
* - createdAt: ISO-8601 timestamp of the spawn.
|
|
295
|
+
* - credentials: the M4-P8 credential decision for this spawn. OPTIONAL in
|
|
296
|
+
* the type, and the optionality is about READING rather than writing:
|
|
297
|
+
* every spawn from M4-P8 on writes it, and `readTaskMeta` deliberately
|
|
298
|
+
* does not require it, so a record written by an earlier kernel still
|
|
299
|
+
* reads instead of becoming an unreadable task whose worktree nobody can
|
|
300
|
+
* tear down. Consumers that need it check for it.
|
|
301
|
+
*/
|
|
302
|
+
export interface TaskMeta {
|
|
303
|
+
id: string;
|
|
304
|
+
project: string;
|
|
305
|
+
shape: TaskShape;
|
|
306
|
+
branch: string;
|
|
307
|
+
worktree: string;
|
|
308
|
+
baseSha: string;
|
|
309
|
+
baseOffline: boolean;
|
|
310
|
+
status: TaskStatus;
|
|
311
|
+
createdAt: string;
|
|
312
|
+
credentials?: TaskCredentialRecord;
|
|
313
|
+
}
|
|
314
|
+
export declare function taskDir(fleet: Fleet, taskId: string): string;
|
|
315
|
+
export declare function metaPath(fleet: Fleet, taskId: string): string;
|
|
316
|
+
export declare function briefPath(fleet: Fleet, taskId: string): string;
|
|
317
|
+
export declare function turnEndPath(fleet: Fleet, taskId: string): string;
|
|
318
|
+
export declare function executorRecordPath(fleet: Fleet, taskId: string): string;
|
|
319
|
+
export declare function reportPath(fleet: Fleet, taskId: string): string;
|
|
320
|
+
/** Serialize meta the way every kernel JSON state file is written. */
|
|
321
|
+
export declare function renderTaskMeta(meta: TaskMeta): string;
|
|
322
|
+
export declare function writeTaskMeta(fleet: Fleet, meta: TaskMeta): void;
|
|
323
|
+
/**
|
|
324
|
+
* THE FOUR WAYS A TASK RECORD FAILS TO READ, KEPT APART (CR-F02, MEDIUM;
|
|
325
|
+
* T-036's mechanism).
|
|
326
|
+
*
|
|
327
|
+
* `absent` is a category that is empty BY OBSERVATION: the kernel looked and
|
|
328
|
+
* there is no record. Every other member is a category that is empty BY
|
|
329
|
+
* CONSTRUCTION: there IS something there and this read could not turn it into
|
|
330
|
+
* a record. Collapsing them makes "nothing here" and "something here I could
|
|
331
|
+
* not read" the same answer, and a task killed mid-write is the ordinary
|
|
332
|
+
* failure this repository keeps meeting, so the two are not the same answer at
|
|
333
|
+
* any caller that decides whether work is in flight.
|
|
334
|
+
*/
|
|
335
|
+
export type TaskMetaRead =
|
|
336
|
+
/** A record that read and passed the field check. */
|
|
337
|
+
{
|
|
338
|
+
kind: "read";
|
|
339
|
+
meta: TaskMeta;
|
|
340
|
+
}
|
|
341
|
+
/** Nothing at this path. Empty by observation. */
|
|
342
|
+
| {
|
|
343
|
+
kind: "absent";
|
|
344
|
+
}
|
|
345
|
+
/** Present and not openable as a regular file, with the probe's reason. */
|
|
346
|
+
| {
|
|
347
|
+
kind: "unreadable";
|
|
348
|
+
reason: string;
|
|
349
|
+
}
|
|
350
|
+
/** Present, opened, and not JSON. The truncated-mid-write shape. */
|
|
351
|
+
| {
|
|
352
|
+
kind: "unparsable";
|
|
353
|
+
reason: string;
|
|
354
|
+
}
|
|
355
|
+
/** Present, parsed, and not a task record. Names the first bad field. */
|
|
356
|
+
| {
|
|
357
|
+
kind: "malformed";
|
|
358
|
+
reason: string;
|
|
359
|
+
};
|
|
360
|
+
/**
|
|
361
|
+
* THE ONE READ of a task record, and the one that says WHICH of the four
|
|
362
|
+
* outcomes happened.
|
|
363
|
+
*
|
|
364
|
+
* The type probe is INSIDE this function and not in front of one of its
|
|
365
|
+
* callers (CR-520, CR-521). There is exactly one implementation of "read a
|
|
366
|
+
* task record", every caller of it is protected, and adding a caller
|
|
367
|
+
* cannot reopen the hole: src/teardown.ts reaches this directly, without
|
|
368
|
+
* going through the liveness classifier, and a named pipe here used to
|
|
369
|
+
* hang it forever.
|
|
370
|
+
*
|
|
371
|
+
* WHY THIS IS A SEPARATE FUNCTION FROM `readTaskMeta` RATHER THAN ITS NEW
|
|
372
|
+
* SIGNATURE, declared rather than left to be inferred. Widening the return
|
|
373
|
+
* type of `readTaskMeta` is the shape this repair wants, and it is a
|
|
374
|
+
* compile-time break in four modules that this fix round's file list does not
|
|
375
|
+
* own (src/pool.ts, src/liveness.ts, src/teardown.ts, src/commands/next.ts).
|
|
376
|
+
* The distinction is therefore made AVAILABLE upstream of all of them here,
|
|
377
|
+
* `readTaskMeta` is documented as the deliberate NARROWING of it, and the
|
|
378
|
+
* sites that still collapse are named in the fix round's work history rather
|
|
379
|
+
* than quietly left.
|
|
380
|
+
*/
|
|
381
|
+
export declare function classifyTaskMeta(fleet: Fleet, taskId: string): TaskMetaRead;
|
|
382
|
+
/**
|
|
383
|
+
* Read meta.json, or undefined when it is absent, is not a regular file,
|
|
384
|
+
* does not parse, or parses and is not a task record.
|
|
385
|
+
*
|
|
386
|
+
* THIS IS A DELIBERATE NARROWING OF `classifyTaskMeta` AND THE COLLAPSE IS THE
|
|
387
|
+
* WHOLE OF CR-F02. `undefined` answers "is there a readable record" and it
|
|
388
|
+
* cannot answer "is there a task here", because it is returned both when the
|
|
389
|
+
* kernel looked and found nothing and when it found something it could not
|
|
390
|
+
* read. A caller that reports an absence, skips an entry, or decides nothing
|
|
391
|
+
* is in flight must call `classifyTaskMeta` instead; a caller that REFUSES on
|
|
392
|
+
* every one of the four (teardown does) loses nothing by using this.
|
|
393
|
+
*/
|
|
394
|
+
export declare function readTaskMeta(fleet: Fleet, taskId: string): TaskMeta | undefined;
|
|
395
|
+
/** Set meta.json status (teardown's last step; C-1's state authority). */
|
|
396
|
+
export declare function setTaskStatus(fleet: Fleet, meta: TaskMeta, status: TaskStatus): void;
|
|
397
|
+
export type GuardResult = {
|
|
398
|
+
ok: true;
|
|
399
|
+
} | {
|
|
400
|
+
ok: false;
|
|
401
|
+
reason: string;
|
|
402
|
+
};
|
|
403
|
+
/**
|
|
404
|
+
* Holdership guard for the task-mutating commands (PR-203), shared by
|
|
405
|
+
* spawn and teardown so there is exactly one implementation of the rule.
|
|
406
|
+
* It lives here rather than in a guard module of its own because this
|
|
407
|
+
* phase's files-to-touch list has no shared-guard file and the rule is a
|
|
408
|
+
* precondition of every task mutation.
|
|
409
|
+
*
|
|
410
|
+
* The transport is M1-P3's, unchanged and not re-invented: lock acquire
|
|
411
|
+
* PRINTS the opaque holderId, the operator carries it, and here it
|
|
412
|
+
* arrives as the TIPHYS_HOLDER_ID environment variable. No second
|
|
413
|
+
* identity mechanism exists, and nothing about the holder is derived
|
|
414
|
+
* from the running program (plan constraint C-2): this reads the lease
|
|
415
|
+
* FILE and nothing else.
|
|
416
|
+
*
|
|
417
|
+
* With no lease file present the command proceeds (the plan's M1 test
|
|
418
|
+
* contexts). A lease that exists but is unreadable, expired, or held by
|
|
419
|
+
* a different holder refuses: fail closed, because every one of those
|
|
420
|
+
* states means this caller cannot prove it is the one orchestrator.
|
|
421
|
+
*/
|
|
422
|
+
export declare function checkHoldership(fleet: Fleet): GuardResult;
|
|
423
|
+
/** True when the task directory already exists (spawn's rollback scope). */
|
|
424
|
+
export declare function taskDirExists(fleet: Fleet, taskId: string): boolean;
|
|
425
|
+
/**
|
|
426
|
+
* True when tasks/<id>/ already holds a previous incarnation of the task
|
|
427
|
+
* id, which spawn refuses (CR-301).
|
|
428
|
+
*
|
|
429
|
+
* The task directory is the DURABLE record: teardown removes the worktree
|
|
430
|
+
* and the pool record but deliberately leaves tasks/<id>/ behind, so the
|
|
431
|
+
* id is free from the pool's point of view and occupied from the task
|
|
432
|
+
* state's. Spawning into it would overwrite the closed task's records,
|
|
433
|
+
* hand the launch-failure rollback files it did not create, and leave the
|
|
434
|
+
* previous incarnation's turn-end file readable while the new
|
|
435
|
+
* incarnation's meta says open, which is a completion that did not happen
|
|
436
|
+
* sitting under the C-1 state authority.
|
|
437
|
+
*
|
|
438
|
+
* A path that exists but is not a directory counts as occupied too: it is
|
|
439
|
+
* not a state this kernel may write into, and refusing costs the operator
|
|
440
|
+
* one rename while guessing could cost the record.
|
|
441
|
+
*/
|
|
442
|
+
export declare function taskDirOccupied(fleet: Fleet, taskId: string): boolean;
|
|
443
|
+
/**
|
|
444
|
+
* Collapse captured git or error output to ONE line (CR-303). Plan step 5 ends
|
|
445
|
+
* "every refusal is exit nonzero plus a single reason line", and git's
|
|
446
|
+
* own stderr is routinely five lines, so any interpolation of it must be
|
|
447
|
+
* flattened rather than trusted to be short. The M1-P6 harness reads
|
|
448
|
+
* these reason lines as evidence.
|
|
449
|
+
*/
|
|
450
|
+
export declare function singleLine(text: string): string;
|
|
451
|
+
export type StepResult<T> = {
|
|
452
|
+
ok: true;
|
|
453
|
+
value: T;
|
|
454
|
+
} | {
|
|
455
|
+
ok: false;
|
|
456
|
+
reason: string;
|
|
457
|
+
};
|
|
458
|
+
/**
|
|
459
|
+
* Run one step that may signal failure by THROWING, and fold a raised
|
|
460
|
+
* error into the same ok/reason shape every other step in spawn and
|
|
461
|
+
* teardown returns (F-1, F-2).
|
|
462
|
+
*
|
|
463
|
+
* This exists because the modules were written as a result type end to
|
|
464
|
+
* end while the Node fs calls underneath them are not: writeFileSync and
|
|
465
|
+
* mkdirSync raise. Every returned failure was handled correctly and every
|
|
466
|
+
* THROWN one walked straight past the handler, out of the command, and
|
|
467
|
+
* onto stderr as a stack trace, taking spawn's rollback and teardown's
|
|
468
|
+
* state update with it. Wrapping is therefore not defensive decoration
|
|
469
|
+
* for a state M1 never reaches: it is the difference between a rollback
|
|
470
|
+
* that runs and an orphaned worktree, and between a task marked closed
|
|
471
|
+
* and a meta.json that lies about a worktree that is already gone.
|
|
472
|
+
*
|
|
473
|
+
* It never swallows: the caller still gets a reason naming the step, and
|
|
474
|
+
* still decides whether to roll back, report a partial failure, or refuse.
|
|
475
|
+
*/
|
|
476
|
+
export declare function runStep<T>(what: string, step: () => T): StepResult<T>;
|
|
477
|
+
/**
|
|
478
|
+
* runStep's shape for a step that returns a PROMISE (M4-P2 step 2).
|
|
479
|
+
*
|
|
480
|
+
* This is a PROMOTION, not a new function. It lived module-private in
|
|
481
|
+
* src/watcher.ts, where its own comment said "src/task.ts covers the sync
|
|
482
|
+
* one"; spawn's launch call site needs the same shape now that
|
|
483
|
+
* `ExecutorAdapter.launch` returns a promise, and T-005's one-mechanism
|
|
484
|
+
* rule makes copying it a third time the wrong answer. The promotion was
|
|
485
|
+
* measured before it was made: the two functions were run side by side over
|
|
486
|
+
* six `what`/thrown-error pairs covering an ordinary Error, an Error with an
|
|
487
|
+
* empty message, a thrown string, a thrown object with a custom toString, an
|
|
488
|
+
* Error subclass and a multi-line pair, and their reason strings were
|
|
489
|
+
* compared as buffers. All six were byte-identical, so no watcher message
|
|
490
|
+
* changes on any of those six. The capture is in the M4-P2 work history
|
|
491
|
+
* under `delivery/work-history/m4-p2.md`.
|
|
492
|
+
*
|
|
493
|
+
* WHY A SEPARATE FUNCTION RATHER THAN WIDENING runStep. `runStep` returns
|
|
494
|
+
* `{ok: true, value}` the instant its callback returns, and a callback that
|
|
495
|
+
* returns a promise returns one IMMEDIATELY. So `runStep` over an async
|
|
496
|
+
* step is not merely imprecise: its `ok: true` is a claim that the step
|
|
497
|
+
* SUCCEEDED when nothing has run yet, and a later rejection escapes the
|
|
498
|
+
* result type entirely as an unhandled rejection. That is the exact defect
|
|
499
|
+
* this phase's criterion 4 witness reddens against.
|
|
500
|
+
*
|
|
501
|
+
* It never swallows, on the same terms as the sync one: the caller gets a
|
|
502
|
+
* reason naming the step and still decides what to do about it.
|
|
503
|
+
*/
|
|
504
|
+
export declare function runStepAsync<T>(what: string, step: () => Promise<T>): Promise<StepResult<T>>;
|