@open-tomato/rafa 0.45.0 → 0.46.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/README.md +255 -23
- package/dist/PROMPT.md +18 -1
- package/dist/adapters/logger/settings.d.ts +132 -0
- package/dist/bugs/closest.d.ts +19 -0
- package/dist/bugs/fenced.d.ts +8 -0
- package/dist/bugs/places.d.ts +20 -0
- package/dist/bugs/record.d.ts +145 -0
- package/dist/bugs/title.d.ts +83 -0
- package/dist/bundled/agents/doc-updater.md +17 -3
- package/dist/bundled/agents/loop-implementer.md +14 -2
- package/dist/bundled/operators/skills/rafa-stretch-pit-stop/SKILL.md +10 -3
- package/dist/bundled/operators/skills/rafa-stretch-sweep/SKILL.md +5 -0
- package/dist/bundled/skills/dev-planner/SKILL.md +101 -18
- package/dist/cli.js +74726 -62960
- package/dist/config/entry.d.ts +119 -0
- package/dist/config/index.d.ts +36 -0
- package/dist/config/index.js +51 -0
- package/dist/config/shape.d.ts +232 -0
- package/dist/config-registry.d.ts +127 -0
- package/dist/config-schema-board-project.d.ts +157 -0
- package/dist/config-schema-bugs.d.ts +71 -0
- package/dist/config-schema-errors.d.ts +54 -0
- package/dist/config-schema-features.d.ts +64 -0
- package/dist/config-schema-hub.d.ts +150 -0
- package/dist/config-schema-issues.d.ts +95 -0
- package/dist/config-schema-logger.d.ts +91 -0
- package/dist/config-schema-loop-continue.d.ts +122 -0
- package/dist/config-schema-loop-retries.d.ts +94 -0
- package/dist/config-schema-release.d.ts +307 -0
- package/dist/config-schema-tests.d.ts +170 -0
- package/dist/config-schema-triage.d.ts +142 -0
- package/dist/config-schema-wrap-up.d.ts +96 -0
- package/dist/core/command/command.d.ts +228 -0
- package/dist/core/command/confirm.d.ts +67 -0
- package/dist/core/command/index.d.ts +112 -0
- package/dist/core/command/lazy.d.ts +54 -0
- package/dist/core/command/registry-types.d.ts +96 -0
- package/dist/core/command/running.d.ts +96 -0
- package/dist/core/command/spends.d.ts +39 -0
- package/dist/core/command/types.d.ts +92 -0
- package/dist/core/command/version.d.ts +4 -0
- package/dist/core/config/branch-template.d.ts +20 -0
- package/dist/core/config/config-items.d.ts +86 -0
- package/dist/core/config/config-load.d.ts +47 -0
- package/dist/core/config/config-origins.d.ts +57 -0
- package/dist/core/config/config-readers.d.ts +167 -0
- package/dist/core/config/config-schema.d.ts +207 -0
- package/dist/core/config/config-sections.d.ts +493 -0
- package/dist/core/config/config.d.ts +320 -0
- package/dist/core/config/core-section.d.ts +39 -0
- package/dist/core/config/dangerous.d.ts +58 -0
- package/dist/core/config/default-routing.d.ts +16 -0
- package/dist/core/config/index.d.ts +141 -0
- package/dist/core/config/scope.d.ts +42 -0
- package/dist/core/config/section.d.ts +206 -0
- package/dist/core/config/sections.d.ts +45 -0
- package/dist/core/config/standard-schema.d.ts +27 -0
- package/dist/core/git/commit.d.ts +232 -0
- package/dist/core/git/gh-runner.d.ts +24 -0
- package/dist/core/git/git.d.ts +22 -0
- package/dist/core/git/index.d.ts +85 -0
- package/dist/core/loop/claude.d.ts +388 -0
- package/dist/core/loop/hop-record.d.ts +52 -0
- package/dist/core/loop/index.d.ts +179 -0
- package/dist/core/loop/session-env.d.ts +20 -0
- package/dist/core/loop/session-record-parse.d.ts +63 -0
- package/dist/core/loop/sessions.d.ts +408 -0
- package/dist/core/loop/suite-failure.d.ts +25 -0
- package/dist/core/output/active.d.ts +100 -0
- package/dist/core/output/stream.d.ts +20 -0
- package/dist/core/output/text.d.ts +55 -0
- package/dist/core/plan/block-reader.d.ts +46 -0
- package/dist/core/plan/blocks.d.ts +139 -0
- package/dist/core/plan/declaration.d.ts +366 -0
- package/dist/core/plan/plan-stamp.d.ts +75 -0
- package/dist/core/runtime/identity.d.ts +21 -0
- package/dist/core/runtime/index.d.ts +46 -0
- package/dist/effort/store/bring-forward.d.ts +131 -0
- package/dist/effort/store/check-rows.d.ts +31 -0
- package/dist/effort/store/commit-stats.d.ts +42 -0
- package/dist/effort/store/copy.d.ts +35 -0
- package/dist/effort/store/development-build.d.ts +105 -0
- package/dist/effort/store/effort-levels.d.ts +13 -0
- package/dist/effort/store/findings.d.ts +214 -0
- package/dist/effort/store/fix-schema.d.ts +184 -0
- package/dist/effort/store/index.js +10 -7
- package/dist/effort/store/location.d.ts +44 -0
- package/dist/effort/store/merge-commit-gaps.d.ts +68 -0
- package/dist/effort/store/merge-conflicts.d.ts +91 -0
- package/dist/effort/store/merge-rules.d.ts +82 -0
- package/dist/effort/store/merge-store.d.ts +211 -0
- package/dist/effort/store/merge-union.d.ts +92 -0
- package/dist/effort/store/migrate.d.ts +146 -0
- package/dist/effort/store/migrations.d.ts +92 -0
- package/dist/effort/store/ndjson-files.d.ts +114 -0
- package/dist/effort/store/origins.d.ts +116 -0
- package/dist/effort/store/plan-stubs.d.ts +79 -0
- package/dist/effort/store/rebuild-aside.d.ts +172 -0
- package/dist/effort/store/schema-plan.d.ts +102 -0
- package/dist/effort/store/session-row-types.d.ts +64 -0
- package/dist/effort/store/settings.d.ts +13 -0
- package/dist/effort/store/sqlite.d.ts +340 -0
- package/dist/effort/store/store-generation.d.ts +17 -0
- package/dist/effort/store/store-identity.d.ts +220 -0
- package/dist/effort/store/store-meta.d.ts +305 -0
- package/dist/effort/store/store-replaced.d.ts +72 -0
- package/dist/effort/store/types.d.ts +223 -0
- package/dist/errors/codes.d.ts +56 -0
- package/dist/errors/rafa-codes.d.ts +36 -0
- package/dist/index-2a7nb3an.js +125 -0
- package/dist/{index-r28fzsvc.js → index-39kbcw68.js} +350 -165
- package/dist/{index-nv34ptht.js → index-77ajfffn.js} +14 -9
- package/dist/index-cy98y87p.js +2597 -0
- package/dist/index-gqz4jn6j.js +13447 -0
- package/dist/index-gvek4r8m.js +710 -0
- package/dist/index-j4k2vdz3.js +1125 -0
- package/dist/index-jh1r4kcn.js +1067 -0
- package/dist/{index-mgh2s9bj.js → index-ykggr043.js} +58 -455
- package/dist/index-z1w83f81.js +4 -0
- package/dist/index.js +14715 -21516
- package/dist/learning/index.js +5 -6
- package/dist/plan/index.js +12 -11
- package/dist/plan-prompt.md +3 -1
- package/dist/ports/index.d.ts +732 -0
- package/dist/ports/learning.d.ts +101 -0
- package/dist/ports/log-level.d.ts +13 -0
- package/dist/ports/spec-review.d.ts +66 -0
- package/dist/pr/checks.d.ts +133 -0
- package/dist/pr/types.d.ts +422 -0
- package/dist/ready-4f8edezt.js +2016 -0
- package/dist/release-templates/release-bot-fragment.yml +153 -0
- package/dist/release-templates/release-publish.yml +110 -0
- package/dist/release-templates/release-settle.yml +122 -0
- package/dist/release-templates/release-tag.yml +102 -0
- package/dist/report/parse.d.ts +415 -0
- package/dist/schema/project-id.d.ts +79 -0
- package/dist/schema/tiers.d.ts +107 -0
- package/package.json +41 -3
- package/dist/index-hf2k497d.js +0 -16
- package/dist/index-nsr78ct2.js +0 -8219
|
@@ -0,0 +1,732 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The package's ports: the seven interfaces core defines for an adapter
|
|
3
|
+
* to implement, declared as types with no implementation behind any of
|
|
4
|
+
* them.
|
|
5
|
+
*
|
|
6
|
+
* The spec's `exports` map names this module's build as the `./ports`
|
|
7
|
+
* subpath, declared in phase 0 and filled in phase 1. Phase 1 puts one
|
|
8
|
+
* adapter per port under `src/adapters/` and lets an add-on register
|
|
9
|
+
* more by name; core imports an add-on only from a `path` source the
|
|
10
|
+
* config's `allowList:` names (`src/modules/load.ts`). What those adapters
|
|
11
|
+
* implement is declared here first, so a service (the orchestrator's
|
|
12
|
+
* worker pool, a Drizzle-backed store) can type an implementation
|
|
13
|
+
* against the subpath without importing the CLI.
|
|
14
|
+
*
|
|
15
|
+
* ## Types, and nothing else
|
|
16
|
+
*
|
|
17
|
+
* The entry exports no runtime value, so importing it runs nothing and
|
|
18
|
+
* adds nothing to a service's bundle. Its imports are `import type`
|
|
19
|
+
* lines, which the bundler erases, so naming {@link GeneratedPlan}'s
|
|
20
|
+
* review type pulls `./spec-review.ts` into no bundle. That
|
|
21
|
+
* keeps three things a port could otherwise carry out of this module:
|
|
22
|
+
*
|
|
23
|
+
* - The store's key projection, `EFFORT_KEY_PROJECTIONS`, which is a
|
|
24
|
+
* value. It stays on `./store`, beside the backends that read it.
|
|
25
|
+
* - The Tracker source's `as const` tuples. Each union they derived
|
|
26
|
+
* is spelled out as a literal union instead, keeping the members and
|
|
27
|
+
* dropping the tuple, so nothing here enumerates them at runtime.
|
|
28
|
+
* - The number core compares a port version against. The modules spec
|
|
29
|
+
* gives every port a version and has core refuse a module whose
|
|
30
|
+
* version it no longer serves. Each version is declared here as a
|
|
31
|
+
* number literal type, gathered in {@link PortVersions} under the
|
|
32
|
+
* port types the adapter registry keys by. The number core compares
|
|
33
|
+
* is `PORT_VERSIONS` in `packages/rafa/src/core/adapters/registry.ts`, held equal to
|
|
34
|
+
* these literals there.
|
|
35
|
+
*
|
|
36
|
+
* ## Where each port comes from
|
|
37
|
+
*
|
|
38
|
+
* - {@link Tracker}: copied from open-tomato's
|
|
39
|
+
* `packages/shared/issue-tracker/src/port.ts` at commit
|
|
40
|
+
* `45aaab563e5b4f4e6258e19ebf752b7cfeb67bf0` (2026-08-05). The phase
|
|
41
|
+
* 1 spec asks for a copy rather than a dependency, with its source
|
|
42
|
+
* commit recorded, so the monorepo can later be pointed at this
|
|
43
|
+
* export and the port does not live twice. Member types and their
|
|
44
|
+
* TSDoc are carried over as written, so they speak of open-tomato's
|
|
45
|
+
* OPT numbers, CLI and ledger, with three exceptions: {@link TrackerKind}
|
|
46
|
+
* is opened, {@link IssueType} gains `epic` and `spec`, and the note on
|
|
47
|
+
* `IssueRef.externalId` names the `local` adapter's issue number
|
|
48
|
+
* where the source named a file path. Four members are rafa's own
|
|
49
|
+
* and absent from the source, all optional: the `openIssues`
|
|
50
|
+
* reading, with the {@link OpenIssue} it answers, the `filedIssues`
|
|
51
|
+
* reading, with the {@link FiledIssue} it answers, and the `editable`
|
|
52
|
+
* and `edit` pair `rafa issue edit` writes through, with the
|
|
53
|
+
* {@link EditableIssue} and {@link IssueEdit} they take and answer. Left
|
|
54
|
+
* out: `BOARD_COLUMNS`, `CLOSED_STATES` and `GITHUB_ISSUE_TYPES`,
|
|
55
|
+
* which are values and the GitHub adapter's projections, and
|
|
56
|
+
* `LedgerEntry`, the local ledger's line. rafa ports no OPT ledger,
|
|
57
|
+
* so the degradation chain (`src/adapters/tracker/resolve.ts`)
|
|
58
|
+
* arrived without it, and the `local` adapter records the reason the
|
|
59
|
+
* chain hands it in each issue file instead.
|
|
60
|
+
* - {@link Store}: the effort store port phase 0 already built, in
|
|
61
|
+
* `packages/rafa/src/effort/store/types.ts`, re-exported under the port's name
|
|
62
|
+
* rather than declared again. `Store` is `EffortStore`, and the rows
|
|
63
|
+
* and results it is typed against come with it.
|
|
64
|
+
* - {@link Learning}: `distributed-learning-library.md`, "rafa's
|
|
65
|
+
* Learning port", with the records it passes taken from that spec's
|
|
66
|
+
* package shape. {@link InstinctRecord}, {@link SyncPayload},
|
|
67
|
+
* {@link BlessedBundle}, {@link MergeRule}, {@link MergeDecision} and
|
|
68
|
+
* {@link MergeResult} are declared in `./learning.ts` and re-exported
|
|
69
|
+
* here under the same names. The learning library declares its own
|
|
70
|
+
* six in `src/learning/types.ts`, since neither side may import the
|
|
71
|
+
* other, and `src/tests/learning-contract.test.ts` holds each pair
|
|
72
|
+
* mutually assignable.
|
|
73
|
+
* - {@link Output}: open-tomato's `packages/shared/cli-core`, the
|
|
74
|
+
* events from `src/events.ts` at
|
|
75
|
+
* `2b00895eaec9edcdce02839831429da97441c0f5` and the interface from
|
|
76
|
+
* `src/output.ts` at `18f94c843fff1bce65ed2245f8712ed9b64a2d51`
|
|
77
|
+
* (both 2026-06-25), where it is named `CliOutput`. The source's
|
|
78
|
+
* `text` and `json` factories are the two adapters phase 1 ports.
|
|
79
|
+
* - {@link Planner}: no spec gives it a signature. The phase 1 table
|
|
80
|
+
* names its core adapter as today's `plan.ts`, so the request and the
|
|
81
|
+
* answer are read off what `rafa plan` takes and leaves behind.
|
|
82
|
+
* - {@link Sync}: issue #323, "choose how a project's effort store
|
|
83
|
+
* travels". The spec gives its two directions, push (send the rows
|
|
84
|
+
* this store wrote) and pull (merge the rows another device wrote),
|
|
85
|
+
* and the five strategies `effort.sync` selects between. Every
|
|
86
|
+
* adapter's pull runs `mergeStore` (`packages/rafa/src/effort/store/merge-store.ts`),
|
|
87
|
+
* so {@link SyncPullResult} carries that merge's own result type,
|
|
88
|
+
* imported as `StoreMergeResult` because the Learning port's
|
|
89
|
+
* `MergeResult` already holds the name here. The request types are
|
|
90
|
+
* read off what the `file` strategy needs: the directory
|
|
91
|
+
* `rafa effort copy` writes into, and the `effort.sqlite` and
|
|
92
|
+
* `--dry-run` that `rafa effort import` hands the merge.
|
|
93
|
+
*
|
|
94
|
+
* ## Property signatures
|
|
95
|
+
*
|
|
96
|
+
* Every function a port declares is a function-typed property, never a
|
|
97
|
+
* method, as the store port's already are. TypeScript compares a
|
|
98
|
+
* method's parameters bivariantly and a property's strictly, so an
|
|
99
|
+
* adapter accepting less than the port hands it, such as a `transition`
|
|
100
|
+
* taking only `done`, compiles against a method and is refused against a
|
|
101
|
+
* property. The Tracker source and the Learning spec both spell methods.
|
|
102
|
+
* This is the one change the Learning port makes to a member's type; the
|
|
103
|
+
* Tracker copy makes one more, opening {@link TrackerKind}.
|
|
104
|
+
*
|
|
105
|
+
* ## Left open for phase 1
|
|
106
|
+
*
|
|
107
|
+
* - {@link MergeResult}: the spec names `discarded` and describes the
|
|
108
|
+
* rest without naming it. `decisions`, {@link MergeDecision} and the
|
|
109
|
+
* {@link MergeRule} names are the learning library's, one rule per
|
|
110
|
+
* row of the spec's merge table plus the record no existing trigger
|
|
111
|
+
* shares.
|
|
112
|
+
* - {@link PlanRequest} and {@link GeneratedPlan} carry what `rafa
|
|
113
|
+
* plan` has today. The webhook adapter phase 6 adds lands a plan on a
|
|
114
|
+
* branch, which may widen the answer.
|
|
115
|
+
*/
|
|
116
|
+
import type { BlessedBundle, MergeResult, SyncPayload } from './learning.js';
|
|
117
|
+
import type { LogLevel } from './log-level.js';
|
|
118
|
+
import type { SpecReviewReading } from './spec-review.js';
|
|
119
|
+
import type { MergeResult as StoreMergeResult } from '../effort/store/merge-store.js';
|
|
120
|
+
export type { LogLevel } from './log-level.js';
|
|
121
|
+
export type { BlessedBundle, InstinctRecord, MergeDecision, MergeResult, MergeRule, SyncPayload, } from './learning.js';
|
|
122
|
+
export type { AppendResult, CommitEffortRow, EffortKeyProjections, EffortRow, EffortRowByKind, EffortRowKind, EffortStore as Store, SessionEffortRow, SessionMode, } from '../effort/store/types.js';
|
|
123
|
+
/** The version of the {@link Tracker} port this entry declares. */
|
|
124
|
+
export type TrackerPortVersion = 1;
|
|
125
|
+
/** The version of the {@link Store} port this entry declares. */
|
|
126
|
+
export type StorePortVersion = 1;
|
|
127
|
+
/** The version of the {@link Learning} port this entry declares. */
|
|
128
|
+
export type LearningPortVersion = 1;
|
|
129
|
+
/** The version of the {@link Output} port this entry declares. */
|
|
130
|
+
export type OutputPortVersion = 1;
|
|
131
|
+
/** The version of the {@link Planner} port this entry declares. */
|
|
132
|
+
export type PlannerPortVersion = 1;
|
|
133
|
+
/** The version of the {@link Sync} port this entry declares. */
|
|
134
|
+
export type SyncPortVersion = 1;
|
|
135
|
+
/**
|
|
136
|
+
* Each port's version, under the port type the adapter registry keys its
|
|
137
|
+
* adapters by. An adapter states the version of its port it implements,
|
|
138
|
+
* and the registry refuses one core does not serve.
|
|
139
|
+
*/
|
|
140
|
+
export interface PortVersions {
|
|
141
|
+
tracker: TrackerPortVersion;
|
|
142
|
+
store: StorePortVersion;
|
|
143
|
+
learning: LearningPortVersion;
|
|
144
|
+
output: OutputPortVersion;
|
|
145
|
+
planner: PlannerPortVersion;
|
|
146
|
+
sync: SyncPortVersion;
|
|
147
|
+
logger: LoggerPortVersion;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* The seven port types: `tracker`, `store`, `learning`, `output`,
|
|
151
|
+
* `planner`, `sync` and `logger`.
|
|
152
|
+
*/
|
|
153
|
+
export type PortType = keyof PortVersions;
|
|
154
|
+
/**
|
|
155
|
+
* The platform a tracker adapter projects issues onto, named as the
|
|
156
|
+
* adapter registry keys it.
|
|
157
|
+
*
|
|
158
|
+
* The source closes this over `github`, `linear` and `local`. The copy
|
|
159
|
+
* keeps those three and admits any other name, because an add-on
|
|
160
|
+
* registers a tracker under a kind core has never heard of (`obsidian`
|
|
161
|
+
* in the phase 1 table), and the config accepts any kind name for
|
|
162
|
+
* `tracker.default` and `tracker.fallback`. Whether a kind has an
|
|
163
|
+
* adapter is the registry's answer, not this type's.
|
|
164
|
+
*
|
|
165
|
+
* `string & {}` is a string the checker does not merge with the three
|
|
166
|
+
* named members. Under `| string` the union reduces to `string`, and the
|
|
167
|
+
* three names are gone from the type.
|
|
168
|
+
*/
|
|
169
|
+
export type TrackerKind = 'github' | 'linear' | 'local' | (string & {});
|
|
170
|
+
/**
|
|
171
|
+
* What an issue is for. `epic` and `spec` are rafa's own members, absent
|
|
172
|
+
* from the source: an issue labelled `type:epic` groups the issues
|
|
173
|
+
* carrying its `epic:<slug>` label, and one labelled `type:spec` holds a
|
|
174
|
+
* spec on the board.
|
|
175
|
+
*/
|
|
176
|
+
export type IssueType = 'code' | 'bug' | 'spike' | 'adr' | 'chore' | 'package-api' | 'epic' | 'spec';
|
|
177
|
+
/** How urgent an issue is, once triage has set it. */
|
|
178
|
+
export type IssuePriority = 'urgent' | 'high' | 'medium' | 'low';
|
|
179
|
+
/**
|
|
180
|
+
* One state per board column, plus `cancelled`, spelled in lifecycle
|
|
181
|
+
* order.
|
|
182
|
+
*/
|
|
183
|
+
export type IssueState = 'backlog' | 'todo' | 'in-progress' | 'in-review' | 'done' | 'released' | 'cancelled';
|
|
184
|
+
/** A resolved pointer to an issue on some tracker. */
|
|
185
|
+
export interface IssueRef {
|
|
186
|
+
/** Internal OPT number — stable across trackers. */
|
|
187
|
+
opt: number;
|
|
188
|
+
kind: TrackerKind;
|
|
189
|
+
/**
|
|
190
|
+
* GitHub issue number as a string, Linear uuid, or the number the `local`
|
|
191
|
+
* adapter gave the issue under `.rafa/issues/`, as a string.
|
|
192
|
+
*/
|
|
193
|
+
externalId: string;
|
|
194
|
+
url: string | null;
|
|
195
|
+
/**
|
|
196
|
+
* Module the issue belongs to, when the caller already knows it (e.g. from
|
|
197
|
+
* a ledger entry). `transition()` on the GitHub adapter needs this to
|
|
198
|
+
* resolve which project board an issue lives on; when it's absent, the
|
|
199
|
+
* adapter falls back to reading the `module:` label via `get()` — an extra
|
|
200
|
+
* `gh issue view` round trip that a caller who already has this for free
|
|
201
|
+
* (the CLI, reading the ledger) can skip by setting it here.
|
|
202
|
+
*/
|
|
203
|
+
module?: string;
|
|
204
|
+
/**
|
|
205
|
+
* `owner/name` for github refs. Absent means the configured default repo
|
|
206
|
+
* (`github.repo`) — set only when the issue's module routes to a repo of
|
|
207
|
+
* its own (`modules.*.repo`), so the common case stays untagged.
|
|
208
|
+
*/
|
|
209
|
+
repo?: string;
|
|
210
|
+
/**
|
|
211
|
+
* Set when the issue was filed but a follow-up step failed — today, Projects
|
|
212
|
+
* v2 board placement. The issue exists and MUST still be recorded in the
|
|
213
|
+
* ledger; the caller is responsible for surfacing this to the user.
|
|
214
|
+
*/
|
|
215
|
+
warning?: string;
|
|
216
|
+
}
|
|
217
|
+
/** Everything needed to create an issue, before any platform is chosen. */
|
|
218
|
+
export interface IssueDraft {
|
|
219
|
+
opt: number;
|
|
220
|
+
title: string;
|
|
221
|
+
body: string;
|
|
222
|
+
type: IssueType;
|
|
223
|
+
module: string;
|
|
224
|
+
/** null means triage has not set one yet — the adapter applies needs-triage. */
|
|
225
|
+
priority: IssuePriority | null;
|
|
226
|
+
/** Project/board name, or null when the tracker has no project concept. */
|
|
227
|
+
project: string | null;
|
|
228
|
+
/** OPT numbers this issue is blocked by. */
|
|
229
|
+
blockedBy: readonly number[];
|
|
230
|
+
/**
|
|
231
|
+
* rafa's own field, absent from the source: true when the issue waits
|
|
232
|
+
* on other issues of the board, which the `github` adapter files as
|
|
233
|
+
* the board's `spec:blocked` label (`SPEC_BLOCKED_LABEL` in
|
|
234
|
+
* `src/board/blocked.ts`) and the `local` adapter records in the issue
|
|
235
|
+
* file. Absent or false files no such mark. Not {@link blockedBy},
|
|
236
|
+
* which names OPT ledger numbers.
|
|
237
|
+
*/
|
|
238
|
+
specBlocked?: boolean;
|
|
239
|
+
/**
|
|
240
|
+
* rafa's own field, absent from the source: the cause code a bug was
|
|
241
|
+
* filed with (`context/error-codes.md`). The `github` adapter files it
|
|
242
|
+
* as the label `cause:<code>` and the `local` adapter records it in the
|
|
243
|
+
* issue file. Absent files no such mark.
|
|
244
|
+
*/
|
|
245
|
+
cause?: string;
|
|
246
|
+
}
|
|
247
|
+
/** An issue as a tracker holds it: the draft, where it lives, its state. */
|
|
248
|
+
export interface Issue extends IssueDraft {
|
|
249
|
+
ref: IssueRef;
|
|
250
|
+
state: IssueState;
|
|
251
|
+
}
|
|
252
|
+
/** What `find` narrows by; an absent field narrows by nothing. */
|
|
253
|
+
export interface IssueQuery {
|
|
254
|
+
module?: string;
|
|
255
|
+
text?: string;
|
|
256
|
+
type?: IssueType;
|
|
257
|
+
state?: IssueState;
|
|
258
|
+
limit?: number;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* One open issue as `openIssues` answers it: where it lives, the title
|
|
262
|
+
* and body it holds, and when it was created, so a caller comparing
|
|
263
|
+
* issue text, or asking which issues were filed after a time, reads
|
|
264
|
+
* every open issue in one call rather than a `get` for each.
|
|
265
|
+
*/
|
|
266
|
+
export interface OpenIssue {
|
|
267
|
+
ref: IssueRef;
|
|
268
|
+
title: string;
|
|
269
|
+
body: string;
|
|
270
|
+
/**
|
|
271
|
+
* When the issue was created, as an ISO 8601 date and time with its
|
|
272
|
+
* offset, or null when the tracker does not say: it holds no creation
|
|
273
|
+
* time for the issue, or one that does not read as a time. {@link Issue}
|
|
274
|
+
* carries no such time.
|
|
275
|
+
*/
|
|
276
|
+
createdAt: string | null;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* One issue as `filedIssues` answers it: where it lives, the title and
|
|
280
|
+
* body it holds, and when it was filed. rafa's own type, absent from the
|
|
281
|
+
* source.
|
|
282
|
+
*
|
|
283
|
+
* Unlike {@link OpenIssue}, its creation time is never null: a listing
|
|
284
|
+
* by filing time cannot place an undated issue against its cut-off, so
|
|
285
|
+
* an adapter never answers one.
|
|
286
|
+
*/
|
|
287
|
+
export interface FiledIssue {
|
|
288
|
+
readonly ref: IssueRef;
|
|
289
|
+
readonly title: string;
|
|
290
|
+
/** The body as the tracker holds it, unnormalised. */
|
|
291
|
+
readonly body: string;
|
|
292
|
+
/**
|
|
293
|
+
* When the issue was created, as an ISO 8601 date and time with its
|
|
294
|
+
* offset.
|
|
295
|
+
*/
|
|
296
|
+
readonly createdAt: string;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* One issue as `editable` answers it: what `rafa issue edit` weighs
|
|
300
|
+
* before it writes, read in one call. rafa's own type, absent from the
|
|
301
|
+
* source.
|
|
302
|
+
*
|
|
303
|
+
* {@link Issue} carries neither labels nor an author, and the edit's
|
|
304
|
+
* gates read both: the author for whose issue it is, the labels for
|
|
305
|
+
* whether a plan or a loop is building from it.
|
|
306
|
+
*/
|
|
307
|
+
export interface EditableIssue {
|
|
308
|
+
readonly ref: IssueRef;
|
|
309
|
+
readonly title: string;
|
|
310
|
+
/** The body as the tracker holds it, unnormalised. */
|
|
311
|
+
readonly body: string;
|
|
312
|
+
/**
|
|
313
|
+
* False when the tracker holds the issue closed: `done`, `released`
|
|
314
|
+
* or `cancelled`, as `openIssues` reads them.
|
|
315
|
+
*/
|
|
316
|
+
readonly open: boolean;
|
|
317
|
+
/**
|
|
318
|
+
* The issue's labels, by name, in the order the tracker answers them.
|
|
319
|
+
* Empty on a tracker that holds no labels.
|
|
320
|
+
*/
|
|
321
|
+
readonly labels: readonly string[];
|
|
322
|
+
/**
|
|
323
|
+
* The login of the account that opened the issue, or the empty string
|
|
324
|
+
* on a tracker that holds no accounts, such as `local`.
|
|
325
|
+
*/
|
|
326
|
+
readonly author: string;
|
|
327
|
+
/**
|
|
328
|
+
* The priority the tracker holds for the issue, or null when triage
|
|
329
|
+
* has set none, as `get` answers {@link IssueDraft.priority}. Absent on
|
|
330
|
+
* a tracker whose `edit` writes no priority: a caller reads it here
|
|
331
|
+
* before it hands {@link IssueEdit} a priority, and hands none to a
|
|
332
|
+
* tracker that answers none.
|
|
333
|
+
*/
|
|
334
|
+
readonly priority?: IssuePriority | null;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* What one `edit` writes: a new body, a new title, a new priority, or
|
|
338
|
+
* any of the three together. Whatever it leaves out stays as the
|
|
339
|
+
* tracker holds it. A body is written whole: an append is the caller's,
|
|
340
|
+
* which hands the old body with its update below it. A priority is
|
|
341
|
+
* written in place of the one the issue held, and takes the issue out
|
|
342
|
+
* of triage: after it the tracker holds that priority and no other, and
|
|
343
|
+
* no mark that triage has yet to set one. An edit never returns an
|
|
344
|
+
* issue to triage, so the member holds no null. The union holds an edit
|
|
345
|
+
* to at least one of the three.
|
|
346
|
+
*/
|
|
347
|
+
export type IssueEdit = {
|
|
348
|
+
readonly body: string;
|
|
349
|
+
readonly title?: string;
|
|
350
|
+
readonly priority?: IssuePriority;
|
|
351
|
+
} | {
|
|
352
|
+
readonly body?: string;
|
|
353
|
+
readonly title: string;
|
|
354
|
+
readonly priority?: IssuePriority;
|
|
355
|
+
} | {
|
|
356
|
+
readonly body?: string;
|
|
357
|
+
readonly title?: string;
|
|
358
|
+
readonly priority: IssuePriority;
|
|
359
|
+
};
|
|
360
|
+
/** What a tracker's platform supports beyond issues themselves. */
|
|
361
|
+
export interface TrackerCapabilities {
|
|
362
|
+
/** Supports project/board grouping (GitHub Projects v2, Linear projects). */
|
|
363
|
+
projects: boolean;
|
|
364
|
+
/** Supports custom single-select fields (priority as a board field). */
|
|
365
|
+
customFields: boolean;
|
|
366
|
+
/** Supports native issue types (GitHub org issue types). */
|
|
367
|
+
issueTypes: boolean;
|
|
368
|
+
}
|
|
369
|
+
/** A preflight's answer: reachable, or the reason it is not. */
|
|
370
|
+
export type PreflightResult = {
|
|
371
|
+
ok: true;
|
|
372
|
+
} | {
|
|
373
|
+
ok: false;
|
|
374
|
+
reason: string;
|
|
375
|
+
};
|
|
376
|
+
/**
|
|
377
|
+
* Outcome of a `transition()` call. Mirrors `IssueRef.warning`'s convention
|
|
378
|
+
* rather than inventing a new shape: the transition's primary write (the
|
|
379
|
+
* tracker's own open/closed or workflow state) succeeded — otherwise
|
|
380
|
+
* `transition()` throws — but a secondary write (the GitHub adapter's board
|
|
381
|
+
* column) may not have landed. `warning` set means exactly that: the caller
|
|
382
|
+
* MUST NOT treat this as a full success, and — for callers that persist a
|
|
383
|
+
* state claim of their own (the ledger) — must not persist that claim when a
|
|
384
|
+
* board-shaped warning says the board never received it.
|
|
385
|
+
*/
|
|
386
|
+
export interface TransitionResult {
|
|
387
|
+
warning?: string;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* An issue tracker: where `loop start` files what a task report's
|
|
391
|
+
* out-of-scope bugs name, through its triage (`start/triage.ts`, over
|
|
392
|
+
* `triage/triage.ts`). A report's blockers are written onto its task's
|
|
393
|
+
* tracker line instead.
|
|
394
|
+
*/
|
|
395
|
+
export interface Tracker {
|
|
396
|
+
readonly kind: TrackerKind;
|
|
397
|
+
capabilities: () => TrackerCapabilities;
|
|
398
|
+
/** Cheap reachability/auth probe. Must not throw — returns a reason instead. */
|
|
399
|
+
preflight: () => Promise<PreflightResult>;
|
|
400
|
+
find: (query: IssueQuery) => Promise<IssueRef[]>;
|
|
401
|
+
get: (ref: IssueRef) => Promise<Issue>;
|
|
402
|
+
create: (draft: IssueDraft) => Promise<IssueRef>;
|
|
403
|
+
comment: (ref: IssueRef, body: string) => Promise<void>;
|
|
404
|
+
transition: (ref: IssueRef, state: IssueState) => Promise<TransitionResult>;
|
|
405
|
+
/**
|
|
406
|
+
* The open issues of `type`, each with its title, its body and the
|
|
407
|
+
* time it was created, in the order the adapter lists them. Open is
|
|
408
|
+
* every state the tracker does not hold closed: `done`, `released`
|
|
409
|
+
* and `cancelled` are closed, the rest open. rafa's own member, absent
|
|
410
|
+
* from the source, and optional: a tracker without it reads no open
|
|
411
|
+
* issues, and a caller that needs them goes without.
|
|
412
|
+
*/
|
|
413
|
+
openIssues?: (type: IssueType) => Promise<OpenIssue[]>;
|
|
414
|
+
/**
|
|
415
|
+
* The issues of `type` filed after `since`, in any state: one closed
|
|
416
|
+
* since it was filed is listed beside the open ones, so a caller asking
|
|
417
|
+
* what was filed after a time does not lose an issue closed before it
|
|
418
|
+
* asks. Each carries its title, its body and the time it was created,
|
|
419
|
+
* in the order the adapter lists them. An issue whose creation time the
|
|
420
|
+
* tracker does not hold, or holds unreadable, is never answered: a
|
|
421
|
+
* listing by filing time cannot place it against `since`. rafa's own
|
|
422
|
+
* member, absent from the source, and optional: a tracker without it
|
|
423
|
+
* reads no filed issues, and a caller that needs them goes without.
|
|
424
|
+
*/
|
|
425
|
+
filedIssues?: (type: IssueType, since: Date) => Promise<FiledIssue[]>;
|
|
426
|
+
/**
|
|
427
|
+
* The issue's title, body, open state, labels and author, in one
|
|
428
|
+
* read, with its priority on a tracker whose `edit` writes one.
|
|
429
|
+
* Rejects for a ref the tracker does not hold. rafa's own member,
|
|
430
|
+
* absent from the source, and optional with {@link edit}: a tracker
|
|
431
|
+
* without the two cannot have a body edited, and `rafa issue edit`
|
|
432
|
+
* refuses with a line naming the tracker's kind.
|
|
433
|
+
*/
|
|
434
|
+
editable?: (ref: IssueRef) => Promise<EditableIssue>;
|
|
435
|
+
/**
|
|
436
|
+
* Writes the body, the title and the priority that `change` names,
|
|
437
|
+
* leaving the rest of the issue as it was, so that `editable` and
|
|
438
|
+
* `get` read the written text and priority back unchanged. Rejects
|
|
439
|
+
* when the tracker refuses the write or does not hold the ref.
|
|
440
|
+
* Optional with {@link editable}.
|
|
441
|
+
*/
|
|
442
|
+
edit?: (ref: IssueRef, change: IssueEdit) => Promise<void>;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* The learning tunnel: findings go in as instincts, blessed instincts
|
|
446
|
+
* come out for dispatch to inject. The modules spec closes it to third
|
|
447
|
+
* parties; there is core's `local` adapter and a first-party `remote`
|
|
448
|
+
* one, never a second source of blessed instincts.
|
|
449
|
+
*/
|
|
450
|
+
export interface Learning {
|
|
451
|
+
/** Merges one source's instincts into what the adapter holds. */
|
|
452
|
+
push: (payload: SyncPayload) => Promise<MergeResult>;
|
|
453
|
+
/** The instincts currently blessed. */
|
|
454
|
+
pullBlessed: () => Promise<BlessedBundle>;
|
|
455
|
+
/** Flags one instinct by id, keeping it out of every later bundle. */
|
|
456
|
+
flag: (id: string, reason: string) => Promise<void>;
|
|
457
|
+
}
|
|
458
|
+
/** A command began. `ts` is an ISO 8601 timestamp on every event. */
|
|
459
|
+
export interface CliEventStart {
|
|
460
|
+
type: 'start';
|
|
461
|
+
command: string;
|
|
462
|
+
ts: string;
|
|
463
|
+
}
|
|
464
|
+
/** A named step of the command began. */
|
|
465
|
+
export interface CliEventStep {
|
|
466
|
+
type: 'step';
|
|
467
|
+
name: string;
|
|
468
|
+
ts: string;
|
|
469
|
+
}
|
|
470
|
+
/** One message at one level. */
|
|
471
|
+
export interface CliEventLog {
|
|
472
|
+
type: 'log';
|
|
473
|
+
level: 'debug' | 'info' | 'warn' | 'error';
|
|
474
|
+
message: string;
|
|
475
|
+
/** What a logger knew beyond the message: module, action, code, hint and the like. */
|
|
476
|
+
fields?: Readonly<Record<string, unknown>>;
|
|
477
|
+
ts: string;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* The command's outcome: its data when it succeeded, its error when not.
|
|
481
|
+
*
|
|
482
|
+
* `error.code` is the dispatcher's own code (`command_exit`,
|
|
483
|
+
* `no_project`, …) and `error.message` the failure's words. The four
|
|
484
|
+
* optional members say what the failure was beside them (#952): `cause`
|
|
485
|
+
* is a `<family>:<leaf>` cause code on every failure the dispatcher
|
|
486
|
+
* writes; `hint` is a coded error's next step, when it has one; `module`
|
|
487
|
+
* and `action` name where a coded error was thrown, or the command that
|
|
488
|
+
* ran for an uncoded failure of one.
|
|
489
|
+
*/
|
|
490
|
+
export interface CliEventResult {
|
|
491
|
+
type: 'result';
|
|
492
|
+
ok: boolean;
|
|
493
|
+
data?: unknown;
|
|
494
|
+
error?: {
|
|
495
|
+
code: string;
|
|
496
|
+
message: string;
|
|
497
|
+
cause?: string;
|
|
498
|
+
hint?: string;
|
|
499
|
+
module?: string;
|
|
500
|
+
action?: string;
|
|
501
|
+
};
|
|
502
|
+
ts: string;
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* One named event of a long-running command, such as a loop's task
|
|
506
|
+
* ending. `summary` is the one line the events output prints; `data`
|
|
507
|
+
* holds the same facts as fields for a reader of json.
|
|
508
|
+
*/
|
|
509
|
+
export interface CliEventNamed {
|
|
510
|
+
type: 'event';
|
|
511
|
+
name: string;
|
|
512
|
+
summary: string;
|
|
513
|
+
data: Readonly<Record<string, unknown>>;
|
|
514
|
+
ts: string;
|
|
515
|
+
}
|
|
516
|
+
/** Every event an output renders, closed over its five kinds. */
|
|
517
|
+
export type CliEvent = CliEventStart | CliEventStep | CliEventLog | CliEventResult | CliEventNamed;
|
|
518
|
+
/**
|
|
519
|
+
* Where a command's output goes: a terminal's text, or NDJSON events a
|
|
520
|
+
* service or the TUI reads. Phase 1 routes the sibling's `console.log`
|
|
521
|
+
* calls through it.
|
|
522
|
+
*/
|
|
523
|
+
export interface Output {
|
|
524
|
+
/** A message at `info`. */
|
|
525
|
+
info: (message: string) => void;
|
|
526
|
+
/** A message at `warn`. */
|
|
527
|
+
warn: (message: string) => void;
|
|
528
|
+
/** A message at `error`. */
|
|
529
|
+
error: (message: string) => void;
|
|
530
|
+
/** A message at `debug`. */
|
|
531
|
+
debug: (message: string) => void;
|
|
532
|
+
/** One event, rendered as the adapter renders its kind. */
|
|
533
|
+
emit: (event: CliEvent) => void;
|
|
534
|
+
/** The command's answer. */
|
|
535
|
+
result: (payload: unknown) => void;
|
|
536
|
+
}
|
|
537
|
+
/** One request to a service and what came back, as metadata only: never a body. */
|
|
538
|
+
export interface ApiExchange {
|
|
539
|
+
/** The service called, such as `hub`. */
|
|
540
|
+
service: string;
|
|
541
|
+
/** The request method, such as `GET`. */
|
|
542
|
+
method: string;
|
|
543
|
+
/** The request URL; an adapter filters it before writing it. */
|
|
544
|
+
url: string;
|
|
545
|
+
/** The response status; absent when no response arrived. */
|
|
546
|
+
status?: number;
|
|
547
|
+
/** How long the exchange took, in milliseconds. */
|
|
548
|
+
durationMs?: number;
|
|
549
|
+
/** The size of the request body, in bytes. */
|
|
550
|
+
requestBytes?: number;
|
|
551
|
+
/** The size of the response body, in bytes. */
|
|
552
|
+
responseBytes?: number;
|
|
553
|
+
/** The id the service gave the request, when it gives one. */
|
|
554
|
+
requestId?: string;
|
|
555
|
+
/** Response headers; an adapter writes only the ones its allow-list names. */
|
|
556
|
+
headers?: Readonly<Record<string, string>>;
|
|
557
|
+
}
|
|
558
|
+
/** One diagnostic line, with what is known about where it comes from. */
|
|
559
|
+
export interface LogEntry {
|
|
560
|
+
/** How loud the entry is, or `api`. */
|
|
561
|
+
level: LogLevel;
|
|
562
|
+
/** What happened, as one sentence. */
|
|
563
|
+
message: string;
|
|
564
|
+
/** The module writing it, as a child logger binds it. */
|
|
565
|
+
module?: string;
|
|
566
|
+
/** The action or step running. */
|
|
567
|
+
action?: string;
|
|
568
|
+
/** A cause code, `<family>:<leaf>` (`packages/rafa/src/errors/codes.ts`). */
|
|
569
|
+
code?: string;
|
|
570
|
+
/** The recommended next action. */
|
|
571
|
+
hint?: string;
|
|
572
|
+
/** Anything else worth keeping, for an adapter that stores fields. */
|
|
573
|
+
data?: Readonly<Record<string, unknown>>;
|
|
574
|
+
/** The error behind the entry, kept whole so its own stack survives. */
|
|
575
|
+
error?: unknown;
|
|
576
|
+
/** The exchange an `api` entry is about. */
|
|
577
|
+
api?: ApiExchange;
|
|
578
|
+
}
|
|
579
|
+
/** What a child logger adds to every entry it writes. */
|
|
580
|
+
export interface LogBindings {
|
|
581
|
+
/** The module every entry of the child comes from. */
|
|
582
|
+
module?: string;
|
|
583
|
+
/** The action every entry of the child belongs to. */
|
|
584
|
+
action?: string;
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Where diagnostics go: errors, warnings, debug lines and `api`
|
|
588
|
+
* exchanges, each with its module, cause code and hint when known. A
|
|
589
|
+
* command's own text and its result stay on {@link Output}.
|
|
590
|
+
*/
|
|
591
|
+
export interface Logger {
|
|
592
|
+
/** Writes `entry`, unless its level is off for its module. */
|
|
593
|
+
log: (entry: LogEntry) => void;
|
|
594
|
+
/** A logger adding `bindings` to every entry; an entry's own fields win. */
|
|
595
|
+
child: (bindings: LogBindings) => Logger;
|
|
596
|
+
/** Whether an entry at `level` from `module` would be written. */
|
|
597
|
+
enabled: (level: LogLevel, module?: string) => boolean;
|
|
598
|
+
}
|
|
599
|
+
/** The version of the {@link Logger} port this entry declares. */
|
|
600
|
+
export type LoggerPortVersion = 1;
|
|
601
|
+
/**
|
|
602
|
+
* One plan to generate, from one spec. The adapter is bound to a
|
|
603
|
+
* repository when it is made, as a store is opened on a root, so the
|
|
604
|
+
* paths here are that repository's.
|
|
605
|
+
*/
|
|
606
|
+
export interface PlanRequest {
|
|
607
|
+
/** The spec, as `--spec=` names it: repository-relative or absolute. */
|
|
608
|
+
specPath: string;
|
|
609
|
+
/** The plan's stub. `rafa plan` defaults it to the spec's basename. */
|
|
610
|
+
stub: string;
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Where a planner left the plan it generated: repository-relative, or
|
|
614
|
+
* absolute when the directory it writes plans into is.
|
|
615
|
+
*/
|
|
616
|
+
export interface GeneratedPlan {
|
|
617
|
+
/** The plan: `PLAN-<stub>.md` in `plan.dir` under today's planner. */
|
|
618
|
+
planPath: string;
|
|
619
|
+
/**
|
|
620
|
+
* The setup steps the planner found no task could automate, or null
|
|
621
|
+
* when it wrote none: `PREREQUISITES-<stub>.md` in `plan.dir` today.
|
|
622
|
+
*/
|
|
623
|
+
prerequisitesPath: string | null;
|
|
624
|
+
/**
|
|
625
|
+
* What the planner's own session said about the spec BEFORE it
|
|
626
|
+
* planned, read out of that session's output: the `rafa:spec-review`
|
|
627
|
+
* block of the readiness gate's check 3
|
|
628
|
+
* (`src/board/spec-review.ts`).
|
|
629
|
+
*
|
|
630
|
+
* Optional, because a planner that reads no session output has judged
|
|
631
|
+
* nothing and must still satisfy this port — the fixture planners the
|
|
632
|
+
* command's tests resolve, and the `webhook` adapter phase 6 adds.
|
|
633
|
+
* The `claude` adapter carries one on every plan it answers, `absent`
|
|
634
|
+
* when its session returned no block. Nothing here acts on it: the
|
|
635
|
+
* gate is `rafa plan`'s, which is what lets `--skip-review` weigh a
|
|
636
|
+
* verdict the planner still read.
|
|
637
|
+
*/
|
|
638
|
+
review?: SpecReviewReading;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Generates a plan from a spec: core's `claude` adapter runs a Claude
|
|
642
|
+
* Code session, as `rafa plan` does, and phase 6's `webhook` adapter
|
|
643
|
+
* hands the spec to hyperloop's plan-to-branch.
|
|
644
|
+
*/
|
|
645
|
+
export interface Planner {
|
|
646
|
+
/**
|
|
647
|
+
* Generates the plan. Rejects when none was generated: the session
|
|
648
|
+
* failed, or it finished without writing the plan.
|
|
649
|
+
*/
|
|
650
|
+
create: (request: PlanRequest) => Promise<GeneratedPlan>;
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Where a push leaves this store's rows.
|
|
654
|
+
*
|
|
655
|
+
* `to` is the directory the `file` strategy copies the store into, as
|
|
656
|
+
* `rafa effort copy --to=<dir>` names it, and null for a strategy that
|
|
657
|
+
* sends the rows on by itself or sends none.
|
|
658
|
+
*/
|
|
659
|
+
export interface SyncPushRequest {
|
|
660
|
+
readonly to: string | null;
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Where a pull reads another device's rows from, and whether it swaps
|
|
664
|
+
* the merge in.
|
|
665
|
+
*/
|
|
666
|
+
export interface SyncPullRequest {
|
|
667
|
+
/**
|
|
668
|
+
* The other device's store file the `file` strategy merges, the
|
|
669
|
+
* `effort.sqlite` a copy carried, and null for a strategy that fetches
|
|
670
|
+
* the rows by itself or fetches none.
|
|
671
|
+
*/
|
|
672
|
+
readonly from: string | null;
|
|
673
|
+
/**
|
|
674
|
+
* Build and check the merged store, then delete it rather than swap it
|
|
675
|
+
* in, as `mergeStore`'s own `dryRun` does.
|
|
676
|
+
*/
|
|
677
|
+
readonly dryRun: boolean;
|
|
678
|
+
/**
|
|
679
|
+
* The session id of the loop run pulling, which a strategy passes on as
|
|
680
|
+
* `mergeStore`'s `sessionId` so the live-loop guard passes the run's own
|
|
681
|
+
* record; absent or null for a pull no run makes.
|
|
682
|
+
*/
|
|
683
|
+
readonly sessionId?: string | null;
|
|
684
|
+
}
|
|
685
|
+
/** What a push did. */
|
|
686
|
+
export type SyncPushResult =
|
|
687
|
+
/** The strategy moves no rows: `local`. */
|
|
688
|
+
{
|
|
689
|
+
readonly status: 'nothing-to-sync';
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* The rows were sent. `path` is the file the person carries to the
|
|
693
|
+
* other device, the copy's `effort.sqlite` under `file`, and null for
|
|
694
|
+
* a strategy that delivered the rows itself.
|
|
695
|
+
*/
|
|
696
|
+
| {
|
|
697
|
+
readonly status: 'pushed';
|
|
698
|
+
readonly path: string | null;
|
|
699
|
+
};
|
|
700
|
+
/** What a pull did. */
|
|
701
|
+
export type SyncPullResult =
|
|
702
|
+
/** The strategy moves no rows: `local`. */
|
|
703
|
+
{
|
|
704
|
+
readonly status: 'nothing-to-sync';
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* The rows were merged by `mergeStore`, whose result says whether the
|
|
708
|
+
* merge was swapped in or, under `dryRun`, built and deleted.
|
|
709
|
+
*/
|
|
710
|
+
| {
|
|
711
|
+
readonly status: 'pulled';
|
|
712
|
+
readonly merge: StoreMergeResult;
|
|
713
|
+
};
|
|
714
|
+
/**
|
|
715
|
+
* How a project's effort store travels between devices: the strategy
|
|
716
|
+
* `effort.sync` selects. `local` and `file` are core's strategies and
|
|
717
|
+
* `git`, `service` and `p2p` modules'; the adapter registry holds core's
|
|
718
|
+
* as `sync/local` (`src/effort/sync/select.ts`) and `sync/file`
|
|
719
|
+
* (`src/effort/sync/file.ts`). Push and pull are directions,
|
|
720
|
+
* not strategies: in every strategy a device pushes the rows it wrote
|
|
721
|
+
* and pulls the rows others wrote.
|
|
722
|
+
*/
|
|
723
|
+
export interface Sync {
|
|
724
|
+
readonly kind: 'local' | 'file' | 'git' | 'service' | 'p2p';
|
|
725
|
+
/** Sends the rows this store wrote. */
|
|
726
|
+
push: (request: SyncPushRequest) => Promise<SyncPushResult>;
|
|
727
|
+
/**
|
|
728
|
+
* Merges the rows another device wrote into this store. Rejects with
|
|
729
|
+
* the merge's own refusal when `mergeStore` refuses.
|
|
730
|
+
*/
|
|
731
|
+
pull: (request: SyncPullRequest) => Promise<SyncPullResult>;
|
|
732
|
+
}
|