opencode-codeops 2.2.1 → 2.3.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/CHANGELOG.md +38 -0
- package/README.md +53 -0
- package/_shared/discovery.md +92 -0
- package/_shared/zero-ambiguity-gate.md +4 -0
- package/bin/lib/discovery-rules.d.mts +228 -0
- package/bin/lib/discovery-rules.mjs +1213 -0
- package/bin/lib/discovery-server.d.mts +62 -0
- package/bin/lib/discovery-server.mjs +755 -0
- package/bin/lib/discovery-store.d.mts +224 -0
- package/bin/lib/discovery-store.mjs +1189 -0
- package/bin/lib/discovery-tools.d.mts +66 -0
- package/bin/lib/discovery-tools.mjs +540 -0
- package/discovery-ui/dist/assets/app.js +107 -0
- package/discovery-ui/dist/index.html +17 -0
- package/package.json +14 -2
- package/plugin/index.ts +45 -4
- package/schemas/codeops-config.schema.json +8 -0
- package/scripts/__pycache__/codeops_plan.cpython-314.pyc +0 -0
- package/scripts/__pycache__/install_agents.cpython-314.pyc +0 -0
- package/scripts/build-discovery-ui.mjs +59 -0
- package/skills/analyze-project/SKILL.md +19 -0
- package/skills/make-plan/SKILL.md +7 -1
- package/skills/preflight/SKILL.md +4 -0
- package/skills/preflight/report-format.md +4 -0
- package/scripts/__pycache__/codeops_plan.cpython-312.pyc +0 -0
- package/scripts/__pycache__/install_agents.cpython-312.pyc +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to CodeOps are recorded here.
|
|
4
4
|
|
|
5
|
+
## 2.3.0 — 2026-10-29
|
|
6
|
+
|
|
7
|
+
### Fixes
|
|
8
|
+
|
|
9
|
+
- discovery: close the browser acceptance findings
|
|
10
|
+
- discovery: guard the locked-group selection rule
|
|
11
|
+
|
|
12
|
+
### Documentation
|
|
13
|
+
|
|
14
|
+
- discovery: mark browser acceptance as the remaining gate
|
|
15
|
+
- discovery: document the redesigned plan workspace
|
|
16
|
+
- discovery-ui-redesign: add the preflighted implementation plan
|
|
17
|
+
- discovery: add the agent protocol and skill adoption
|
|
18
|
+
- discovery: record the host spike outcomes
|
|
19
|
+
- discovery: add the plan set with preflight fixes applied
|
|
20
|
+
|
|
21
|
+
### Features
|
|
22
|
+
|
|
23
|
+
- discovery: complete the Fluent UI workspace
|
|
24
|
+
- discovery: add guarded editing and the workspace shell
|
|
25
|
+
- discovery: add workspace destinations and theme preference
|
|
26
|
+
- discovery: finish the docs, live evidence, and UI fixes
|
|
27
|
+
- discovery: add the Fluent UI app and packaging
|
|
28
|
+
- discovery: wire the opt-in tools and waking
|
|
29
|
+
- discovery: add the listener and HTTP API
|
|
30
|
+
- discovery: add the store
|
|
31
|
+
- discovery: add the rules and model module
|
|
32
|
+
|
|
33
|
+
### Tests
|
|
34
|
+
|
|
35
|
+
- discovery: add navigation and theme specification tests
|
|
36
|
+
- discovery: isolate the fallback-port spec test
|
|
37
|
+
|
|
38
|
+
### Chores
|
|
39
|
+
|
|
40
|
+
- codeops: refresh the specialist check
|
|
41
|
+
- project: refresh managed guidance
|
|
42
|
+
|
|
5
43
|
## 2.2.1 — 2026-10-10
|
|
6
44
|
|
|
7
45
|
### Fixes
|
package/README.md
CHANGED
|
@@ -162,6 +162,59 @@ when no run is active, when the host lacks the plugin APIs, or when every call f
|
|
|
162
162
|
Markdown execution plan remains the single progress source of truth; the live view never gates
|
|
163
163
|
execution.
|
|
164
164
|
|
|
165
|
+
## Discovery
|
|
166
|
+
|
|
167
|
+
Discovery is an opt-in local web page where the agent publishes a CodeOps question round
|
|
168
|
+
instead of asking every question in chat. Questions, options, recommendations, and dependency
|
|
169
|
+
rules render in the browser; answers are saved there, and Submit wakes the OpenCode session that
|
|
170
|
+
published them so it can continue.
|
|
171
|
+
|
|
172
|
+
### Plan workspace
|
|
173
|
+
|
|
174
|
+
The plan workspace uses a persistent group navigation column and shows one selected group at a
|
|
175
|
+
time. Groups carry ready, pending, and completed cues, and a selected group stays selected while
|
|
176
|
+
you inspect a locked or completed group. The plan URL, group links
|
|
177
|
+
(`#/p/<project>/<plan>/groups/<key>`), and the summary link
|
|
178
|
+
(`#/p/<project>/<plan>/summary`) are stable and work with browser back and forward.
|
|
179
|
+
|
|
180
|
+
The action bar always shows **Submit group** for the selected group. The **Decision summary**
|
|
181
|
+
lists current answers, their statuses, and links back to each group; **Submit all ready groups**
|
|
182
|
+
sends every ready group from one place. Readiness and completion are labelled separately.
|
|
183
|
+
|
|
184
|
+
Save state is server-backed. **Saved** appears only after the server acknowledges the write;
|
|
185
|
+
**Unsaved changes** and **Saving…** show pending work, and a failed save keeps the draft with
|
|
186
|
+
**Retry**. Edits survive group changes and live refreshes. A draft that can no longer be saved —
|
|
187
|
+
because the question became read-only, locked, retired, hidden, or its group was removed — stays
|
|
188
|
+
visible under **Unsent drafts** and can be explicitly discarded or left after confirmation.
|
|
189
|
+
|
|
190
|
+
The shared header carries a real breadcrumb and a **System / Light / Dark** theme control. System
|
|
191
|
+
follows the operating-system setting; an explicit choice is remembered in the browser. If storage
|
|
192
|
+
is unavailable, the choice still applies for the current tab.
|
|
193
|
+
|
|
194
|
+
Enable it for a project with `analyze-project --with-discovery`, or add the setting to
|
|
195
|
+
`codeops/codeops.json`:
|
|
196
|
+
|
|
197
|
+
```json
|
|
198
|
+
{
|
|
199
|
+
"discovery": { "enabled": true, "chatMaxQuestions": 3 }
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Rounds with at most `chatMaxQuestions` (default 3) questions stay in chat. The page is served
|
|
204
|
+
from the plugin at `http://localhost:4747/`; set `CODEOPS_DISCOVERY_PORT` to listen on a
|
|
205
|
+
different port. Discovery data lives in `$CODEOPS_DATA_DIR/discovery`, which defaults to
|
|
206
|
+
`~/.local/share/opencode/codeops/discovery`.
|
|
207
|
+
|
|
208
|
+
The listener binds to `127.0.0.1` only and has no login. It assumes a single-user machine: any
|
|
209
|
+
local user can read the page and the stored answers. Browsers limit concurrent
|
|
210
|
+
connections per host, so keep only a few plan tabs open at once. The agent continues only while
|
|
211
|
+
OpenCode is running in that project; submissions that arrive while it is not are delivered when
|
|
212
|
+
it starts again.
|
|
213
|
+
|
|
214
|
+
Pass `--no-discovery` to a skill to skip the page for one run and use chat. Building from a
|
|
215
|
+
checkout with `npm run build:discovery` refreshes the page assets without restarting OpenCode.
|
|
216
|
+
Remote and SSH sessions are not supported.
|
|
217
|
+
|
|
165
218
|
## Agent model configuration
|
|
166
219
|
|
|
167
220
|
All CodeOps subagents inherit the model of the primary agent that invoked them. No provider-specific configuration is required out of the box.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Discovery (optional answer surface)
|
|
2
|
+
|
|
3
|
+
> **CodeOps Artifact Schema**: 1
|
|
4
|
+
|
|
5
|
+
Discovery moves large question rounds out of the chat scroll and into a local
|
|
6
|
+
browser page. It is opt-in per project and never replaces the Ambiguity
|
|
7
|
+
Register. Use it only when this file's conditions hold; otherwise ask in chat
|
|
8
|
+
exactly as before.
|
|
9
|
+
|
|
10
|
+
## When it applies
|
|
11
|
+
|
|
12
|
+
All three must hold:
|
|
13
|
+
|
|
14
|
+
1. `codeops/codeops.json` sets `discovery.enabled: true`.
|
|
15
|
+
2. The `codeops_discovery_publish`, `codeops_discovery_answers`, and
|
|
16
|
+
`codeops_discovery_resolve` tools are available.
|
|
17
|
+
3. The run does not carry the `--no-discovery` token.
|
|
18
|
+
|
|
19
|
+
If any condition fails, ask in chat. Never mention a page the user cannot use.
|
|
20
|
+
|
|
21
|
+
## Round size
|
|
22
|
+
|
|
23
|
+
A round with at most `discovery.chatMaxQuestions` questions (default 3) stays
|
|
24
|
+
in chat. Larger rounds are published. The user can always ask to answer in
|
|
25
|
+
chat instead; honor that for the rest of the run.
|
|
26
|
+
|
|
27
|
+
## What never goes to the page
|
|
28
|
+
|
|
29
|
+
The **Complexity Escalation Gate** always stays in chat: its visible STOP
|
|
30
|
+
packet, the challenger verdict, and the approval decision. The page has no
|
|
31
|
+
"accept all" control, so a question that would waive a gate must not be
|
|
32
|
+
published.
|
|
33
|
+
|
|
34
|
+
## Publishing
|
|
35
|
+
|
|
36
|
+
- One Discovery plan per artifact: the `kind` (`plan`, `requirements`, or
|
|
37
|
+
`preflight`) plus the repository-relative `artifact` path. Republishing the
|
|
38
|
+
same pair creates the next revision.
|
|
39
|
+
- Groups follow dependency order; a foundational group is listed in `dependsOn`
|
|
40
|
+
of the groups it unlocks.
|
|
41
|
+
- Every question carries `why`, `evidence` (`file:line` where possible),
|
|
42
|
+
options with their trade-offs, and a `recommendation`. The recommendation is
|
|
43
|
+
shown and never pre-selected.
|
|
44
|
+
- Use `showIf` and `implies` when one answer settles another question in the
|
|
45
|
+
same plan. No chains: an implied answer never implies a third.
|
|
46
|
+
- Keys are stable: `DQ-<n>` for discovery, `PF-NNN` for preflight findings.
|
|
47
|
+
Never reuse a key for a different decision.
|
|
48
|
+
- A question whose answer is already recorded may be republished unchanged.
|
|
49
|
+
Omitting an **open** question retires it; recorded, submitted, and
|
|
50
|
+
already-retired questions are kept (with a warning when the content changed).
|
|
51
|
+
Retire a submitted question through `codeops_discovery_resolve`. A follow-up
|
|
52
|
+
publish therefore resends every earlier question plus the new ones.
|
|
53
|
+
|
|
54
|
+
## After publishing
|
|
55
|
+
|
|
56
|
+
Send one chat line with the link (and the fallback-port notice when present).
|
|
57
|
+
Then **end the turn** and **do not poll**. The user answers in the browser; the
|
|
58
|
+
wake message continues the work. Under the no-waker fallback, call
|
|
59
|
+
`codeops_discovery_answers` with `wait: true` after publishing.
|
|
60
|
+
|
|
61
|
+
## Ownership
|
|
62
|
+
|
|
63
|
+
On an `owned` result, ask the user in chat whether to take over. Publish with
|
|
64
|
+
`takeover: true` only after an explicit yes. Never take over silently.
|
|
65
|
+
|
|
66
|
+
## Reading and recording
|
|
67
|
+
|
|
68
|
+
On the wake message, or when the user says they answered, call
|
|
69
|
+
`codeops_discovery_answers`. Then:
|
|
70
|
+
|
|
71
|
+
- Record each answer as exact text with its question key and its source.
|
|
72
|
+
- Record an implied answer as "**implied by** <key>, shown and submitted".
|
|
73
|
+
Note when the user overrode an implication.
|
|
74
|
+
- Record a Defer as a named deferral: decision · owner · revisit.
|
|
75
|
+
- Leave "needs discussion" questions open; they are not decisions.
|
|
76
|
+
- Call `codeops_discovery_resolve` with the register ids for the recorded
|
|
77
|
+
answers. The **Ambiguity Register stays the authoritative record**; the page
|
|
78
|
+
is only the collection surface.
|
|
79
|
+
- Answers the user gives in chat for published questions are recorded and
|
|
80
|
+
resolved exactly the same way.
|
|
81
|
+
|
|
82
|
+
## Follow-ups
|
|
83
|
+
|
|
84
|
+
Republish the same artifact with **every earlier question plus the new ones**.
|
|
85
|
+
A publish is the plan's full question set: omitting an open question retires
|
|
86
|
+
it, while submitted and recorded questions are kept until they are recorded. Use `proposal` for
|
|
87
|
+
settlements the agent infers; the user confirms or reopens them on the page.
|
|
88
|
+
|
|
89
|
+
## Unavailable
|
|
90
|
+
|
|
91
|
+
On `disabled` or `unavailable`, say so in one line and ask in chat. **Never
|
|
92
|
+
skip or weaken a gate** because the page is unavailable.
|
|
@@ -221,6 +221,10 @@ named larger machinery.
|
|
|
221
221
|
save the register with header `✅ GATE PASSED — 0 ambiguities identified (systematic review
|
|
222
222
|
completed)`. This proves the gate was executed.
|
|
223
223
|
|
|
224
|
+
## Discovery (optional answer surface)
|
|
225
|
+
|
|
226
|
+
When a workflow runs with Discovery enabled, the Ambiguity Register stays the **authoritative record** ([discovery.md](discovery.md)). Answers transcribed from the page are exact text with their question key; an implied answer counts as a user decision only when the page showed it and the user submitted it. Deferrals still need decision, owner, and revisit. Complexity escalations stay in **chat**: the STOP packet, the challenger verdict, and the approval are never published to the page.
|
|
227
|
+
|
|
224
228
|
## Deferral rules (named deferral only)
|
|
225
229
|
|
|
226
230
|
A decision may be **explicitly deferred** — the status the whole pipeline shares (grill-me
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for the CodeOps Discovery rules and model helper.
|
|
3
|
+
*
|
|
4
|
+
* The helper is plain JavaScript so the browser bundle and `node --test` can
|
|
5
|
+
* import the same file. These declarations give the TypeScript plugin entry
|
|
6
|
+
* point and the UI build typed access to it.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** Schema version written into every Discovery data file. */
|
|
10
|
+
export declare const DISCOVERY_SCHEMA_VERSION: 1
|
|
11
|
+
|
|
12
|
+
/** A question type. */
|
|
13
|
+
export type QuestionType = "single" | "multiple" | "text" | "single-other"
|
|
14
|
+
|
|
15
|
+
/** Every supported question type. */
|
|
16
|
+
export declare const QUESTION_TYPES: readonly QuestionType[]
|
|
17
|
+
|
|
18
|
+
/** Question types that carry options. */
|
|
19
|
+
export declare const CHOICE_TYPES: readonly QuestionType[]
|
|
20
|
+
|
|
21
|
+
/** A plan kind. */
|
|
22
|
+
export type PlanKind = "plan" | "requirements" | "preflight"
|
|
23
|
+
|
|
24
|
+
/** Every supported plan kind. */
|
|
25
|
+
export declare const PLAN_KINDS: readonly PlanKind[]
|
|
26
|
+
|
|
27
|
+
/** Pattern for question keys, group keys, and option ids. */
|
|
28
|
+
export declare const KEY_PATTERN: RegExp
|
|
29
|
+
|
|
30
|
+
/** Pattern for URL and file-name slugs. */
|
|
31
|
+
export declare const SLUG_PATTERN: RegExp
|
|
32
|
+
|
|
33
|
+
/** Maximum length of a title, group title, or option label. */
|
|
34
|
+
export declare const TITLE_MAX: 200
|
|
35
|
+
|
|
36
|
+
/** Maximum length of every other free-text value. */
|
|
37
|
+
export declare const TEXT_MAX: 4000
|
|
38
|
+
|
|
39
|
+
/** Maximum length of an artifact path. */
|
|
40
|
+
export declare const ARTIFACT_MAX: 300
|
|
41
|
+
|
|
42
|
+
/** Structural caps for one publish. */
|
|
43
|
+
export declare const LIMITS: {
|
|
44
|
+
readonly groups: 50
|
|
45
|
+
readonly questions: 200
|
|
46
|
+
readonly optionsMin: 2
|
|
47
|
+
readonly optionsMax: 20
|
|
48
|
+
readonly evidence: 20
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Question fields covered by the fingerprint. */
|
|
52
|
+
export declare const FINGERPRINT_FIELDS: readonly string[]
|
|
53
|
+
|
|
54
|
+
/** A stored answer; exactly one field is set. */
|
|
55
|
+
export interface Answer {
|
|
56
|
+
choice?: string
|
|
57
|
+
choices?: string[]
|
|
58
|
+
text?: string
|
|
59
|
+
other?: string
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** A deferral recorded by the user. */
|
|
63
|
+
export interface Defer {
|
|
64
|
+
decision: string
|
|
65
|
+
owner: string
|
|
66
|
+
revisit: string
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** A compact answer: an option id, option ids, or text. */
|
|
70
|
+
export type CompactAnswer = string | string[]
|
|
71
|
+
|
|
72
|
+
/** An implication attached to one option. */
|
|
73
|
+
export interface Implication {
|
|
74
|
+
question: string
|
|
75
|
+
answer: CompactAnswer
|
|
76
|
+
reason: string
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** One question option. */
|
|
80
|
+
export interface QuestionOption {
|
|
81
|
+
id: string
|
|
82
|
+
label: string
|
|
83
|
+
description?: string
|
|
84
|
+
implies?: Implication[]
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** One visibility condition. */
|
|
88
|
+
export interface ShowIfCondition {
|
|
89
|
+
question: string
|
|
90
|
+
anyOf: string[]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** An agent-proposed settlement. */
|
|
94
|
+
export interface Proposal {
|
|
95
|
+
answer: CompactAnswer
|
|
96
|
+
reason: string
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** One normalized published question. */
|
|
100
|
+
export interface Question {
|
|
101
|
+
key: string
|
|
102
|
+
group: string
|
|
103
|
+
type: QuestionType
|
|
104
|
+
title: string
|
|
105
|
+
why?: string
|
|
106
|
+
evidence?: string[]
|
|
107
|
+
options?: QuestionOption[]
|
|
108
|
+
recommendation?: string | string[]
|
|
109
|
+
showIf?: ShowIfCondition[]
|
|
110
|
+
proposal?: Proposal
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** One normalized published group. */
|
|
114
|
+
export interface Group {
|
|
115
|
+
key: string
|
|
116
|
+
title: string
|
|
117
|
+
description?: string
|
|
118
|
+
dependsOn: string[]
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** A normalized published plan. */
|
|
122
|
+
export interface PublishedPlan {
|
|
123
|
+
artifact: string
|
|
124
|
+
kind: PlanKind
|
|
125
|
+
title: string
|
|
126
|
+
groups: Group[]
|
|
127
|
+
questions: Question[]
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** The stored lifecycle of one question. */
|
|
131
|
+
export type QuestionStatus = "open" | "submitted" | "recorded" | "retired"
|
|
132
|
+
|
|
133
|
+
/** The mutable state of one question. */
|
|
134
|
+
export interface QuestionState {
|
|
135
|
+
answer: Answer | null
|
|
136
|
+
notes: string
|
|
137
|
+
defer: Defer | null
|
|
138
|
+
needsDiscussion: boolean
|
|
139
|
+
proposalDismissed: boolean
|
|
140
|
+
status: QuestionStatus
|
|
141
|
+
changed: boolean
|
|
142
|
+
revisedAt: number
|
|
143
|
+
recordedAs: string | null
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** The mutable state of one plan. */
|
|
147
|
+
export interface PlanState {
|
|
148
|
+
questions: Record<string, QuestionState>
|
|
149
|
+
submittedGroups: string[]
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** One evaluated question. */
|
|
153
|
+
export interface EvaluationEntry {
|
|
154
|
+
visible: boolean
|
|
155
|
+
effective: Answer | null
|
|
156
|
+
source: "user" | "implied" | null
|
|
157
|
+
impliedBy: string[]
|
|
158
|
+
impliedReasons: string[]
|
|
159
|
+
overridesImplied: boolean
|
|
160
|
+
conflict: { from: string; answer: Answer }[]
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** The result of evaluating a plan. */
|
|
164
|
+
export interface Evaluation {
|
|
165
|
+
questions: Map<string, EvaluationEntry>
|
|
166
|
+
lockedGroups: Set<string>
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** One reason a group is not ready. */
|
|
170
|
+
export interface Missing {
|
|
171
|
+
key: string
|
|
172
|
+
reason: "unanswered" | "changed" | "conflict" | "proposal" | "nothing-to-submit"
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** Per-bucket question counts for list views. */
|
|
176
|
+
export interface PlanCounts {
|
|
177
|
+
open: number
|
|
178
|
+
answered: number
|
|
179
|
+
deferred: number
|
|
180
|
+
discussion: number
|
|
181
|
+
submitted: number
|
|
182
|
+
recorded: number
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** The JSON Schema of the publish tool input. */
|
|
186
|
+
export declare const DiscoveryPublishSchema: Record<string, unknown>
|
|
187
|
+
|
|
188
|
+
/** Validate and normalize a publish payload; never throws. */
|
|
189
|
+
export declare function validatePublish(
|
|
190
|
+
input: unknown
|
|
191
|
+
):
|
|
192
|
+
| { ok: true; plan: PublishedPlan }
|
|
193
|
+
| { ok: false; errors: string[] }
|
|
194
|
+
|
|
195
|
+
/** Validate one stored answer for its question type. */
|
|
196
|
+
export declare function validateAnswer(
|
|
197
|
+
question: Question,
|
|
198
|
+
answer: unknown
|
|
199
|
+
): { ok: true } | { ok: false; error: string }
|
|
200
|
+
|
|
201
|
+
/** Validate a deferral. */
|
|
202
|
+
export declare function validateDefer(
|
|
203
|
+
defer: unknown
|
|
204
|
+
): { ok: true } | { ok: false; error: string }
|
|
205
|
+
|
|
206
|
+
/** Convert a compact answer into the stored shape. */
|
|
207
|
+
export declare function toAnswer(question: Question, compact: CompactAnswer): Answer
|
|
208
|
+
|
|
209
|
+
/** Canonical content string used to detect a changed question. */
|
|
210
|
+
export declare function questionFingerprint(question: Question): string
|
|
211
|
+
|
|
212
|
+
/** Evaluate visibility, implications, conflicts, and group locks. */
|
|
213
|
+
export declare function evaluatePlan(plan: PublishedPlan, state: PlanState): Evaluation
|
|
214
|
+
|
|
215
|
+
/** Whether a group can be submitted, and what is missing when it cannot. */
|
|
216
|
+
export declare function groupReadiness(
|
|
217
|
+
plan: PublishedPlan,
|
|
218
|
+
state: PlanState,
|
|
219
|
+
evaluation: Evaluation,
|
|
220
|
+
groupKey: string
|
|
221
|
+
): { ready: boolean; locked: boolean; missing: Missing[] }
|
|
222
|
+
|
|
223
|
+
/** Counts used by list views. */
|
|
224
|
+
export declare function planCounts(
|
|
225
|
+
plan: PublishedPlan,
|
|
226
|
+
state: PlanState,
|
|
227
|
+
evaluation: Evaluation
|
|
228
|
+
): PlanCounts
|