cclaw-cli 0.51.30 → 1.0.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 +24 -18
- package/dist/artifact-linter/brainstorm.d.ts +2 -0
- package/dist/artifact-linter/brainstorm.js +289 -0
- package/dist/artifact-linter/design.d.ts +2 -0
- package/dist/artifact-linter/design.js +354 -0
- package/dist/artifact-linter/plan.d.ts +2 -0
- package/dist/artifact-linter/plan.js +183 -0
- package/dist/artifact-linter/review-army.d.ts +24 -0
- package/dist/artifact-linter/review-army.js +365 -0
- package/dist/artifact-linter/review.d.ts +2 -0
- package/dist/artifact-linter/review.js +99 -0
- package/dist/artifact-linter/scope.d.ts +2 -0
- package/dist/artifact-linter/scope.js +125 -0
- package/dist/artifact-linter/shared.d.ts +247 -0
- package/dist/artifact-linter/shared.js +1517 -0
- package/dist/artifact-linter/ship.d.ts +2 -0
- package/dist/artifact-linter/ship.js +82 -0
- package/dist/artifact-linter/spec.d.ts +2 -0
- package/dist/artifact-linter/spec.js +130 -0
- package/dist/artifact-linter/tdd.d.ts +2 -0
- package/dist/artifact-linter/tdd.js +198 -0
- package/dist/artifact-linter.d.ts +4 -76
- package/dist/artifact-linter.js +56 -2949
- package/dist/cli.d.ts +1 -6
- package/dist/cli.js +4 -159
- package/dist/codex-feature-flag.d.ts +1 -1
- package/dist/codex-feature-flag.js +1 -1
- package/dist/config.d.ts +3 -2
- package/dist/config.js +67 -3
- package/dist/constants.d.ts +1 -7
- package/dist/constants.js +10 -15
- package/dist/content/cancel-command.js +2 -2
- package/dist/content/closeout-guidance.d.ts +1 -1
- package/dist/content/closeout-guidance.js +15 -13
- package/dist/content/core-agents.d.ts +46 -29
- package/dist/content/core-agents.js +216 -82
- package/dist/content/decision-protocol.d.ts +1 -1
- package/dist/content/decision-protocol.js +1 -1
- package/dist/content/diff-command.js +1 -1
- package/dist/content/examples.d.ts +0 -3
- package/dist/content/examples.js +197 -752
- package/dist/content/harness-doc.js +20 -2
- package/dist/content/hook-manifest.d.ts +2 -2
- package/dist/content/hook-manifest.js +2 -2
- package/dist/content/hooks.d.ts +1 -0
- package/dist/content/hooks.js +32 -137
- package/dist/content/idea.d.ts +60 -0
- package/dist/content/idea.js +404 -0
- package/dist/content/iron-laws.d.ts +0 -1
- package/dist/content/iron-laws.js +31 -16
- package/dist/content/learnings.d.ts +2 -4
- package/dist/content/learnings.js +11 -27
- package/dist/content/meta-skill.js +7 -7
- package/dist/content/node-hooks.d.ts +10 -0
- package/dist/content/node-hooks.js +163 -95
- package/dist/content/opencode-plugin.js +15 -29
- package/dist/content/reference-patterns.js +2 -2
- package/dist/content/runtime-shared-snippets.d.ts +8 -0
- package/dist/content/runtime-shared-snippets.js +80 -0
- package/dist/content/session-hooks.js +1 -1
- package/dist/content/skills.d.ts +1 -0
- package/dist/content/skills.js +69 -7
- package/dist/content/stage-schema.js +147 -61
- package/dist/content/stages/_lint-metadata/index.js +26 -2
- package/dist/content/stages/brainstorm.js +13 -7
- package/dist/content/stages/design.js +16 -11
- package/dist/content/stages/plan.js +7 -4
- package/dist/content/stages/review.js +12 -12
- package/dist/content/stages/schema-types.d.ts +2 -2
- package/dist/content/stages/scope.js +15 -12
- package/dist/content/stages/ship.js +3 -3
- package/dist/content/stages/spec.js +9 -3
- package/dist/content/stages/tdd.js +14 -4
- package/dist/content/start-command.js +11 -10
- package/dist/content/status-command.js +5 -5
- package/dist/content/subagent-context-skills.js +156 -1
- package/dist/content/subagents.d.ts +0 -5
- package/dist/content/subagents.js +65 -81
- package/dist/content/templates.d.ts +1 -1
- package/dist/content/templates.js +187 -154
- package/dist/content/tree-command.js +2 -2
- package/dist/content/utility-skills.d.ts +2 -2
- package/dist/content/utility-skills.js +28 -99
- package/dist/content/view-command.js +4 -2
- package/dist/delegation.d.ts +2 -0
- package/dist/delegation.js +2 -1
- package/dist/early-loop.d.ts +66 -0
- package/dist/early-loop.js +275 -0
- package/dist/flow-state.d.ts +5 -6
- package/dist/flow-state.js +4 -6
- package/dist/gate-evidence.d.ts +0 -23
- package/dist/gate-evidence.js +111 -153
- package/dist/harness-adapters.d.ts +2 -2
- package/dist/harness-adapters.js +48 -19
- package/dist/install.js +190 -32
- package/dist/internal/advance-stage/advance.d.ts +50 -0
- package/dist/internal/advance-stage/advance.js +479 -0
- package/dist/internal/advance-stage/cancel-run.d.ts +8 -0
- package/dist/internal/advance-stage/cancel-run.js +19 -0
- package/dist/internal/advance-stage/flow-state-coercion.d.ts +3 -0
- package/dist/internal/advance-stage/flow-state-coercion.js +81 -0
- package/dist/internal/advance-stage/helpers.d.ts +14 -0
- package/dist/internal/advance-stage/helpers.js +145 -0
- package/dist/internal/advance-stage/hook.d.ts +8 -0
- package/dist/internal/advance-stage/hook.js +40 -0
- package/dist/internal/advance-stage/parsers.d.ts +54 -0
- package/dist/internal/advance-stage/parsers.js +307 -0
- package/dist/internal/advance-stage/review-loop.d.ts +7 -0
- package/dist/internal/advance-stage/review-loop.js +161 -0
- package/dist/internal/advance-stage/rewind.d.ts +14 -0
- package/dist/internal/advance-stage/rewind.js +108 -0
- package/dist/internal/advance-stage/start-flow.d.ts +11 -0
- package/dist/internal/advance-stage/start-flow.js +136 -0
- package/dist/internal/advance-stage/verify.d.ts +29 -0
- package/dist/internal/advance-stage/verify.js +225 -0
- package/dist/internal/advance-stage.js +21 -1470
- package/dist/internal/compound-readiness.d.ts +1 -1
- package/dist/internal/compound-readiness.js +2 -2
- package/dist/internal/early-loop-status.d.ts +7 -0
- package/dist/internal/early-loop-status.js +90 -0
- package/dist/internal/runtime-integrity.d.ts +7 -0
- package/dist/internal/runtime-integrity.js +288 -0
- package/dist/internal/tdd-red-evidence.js +1 -1
- package/dist/knowledge-store.d.ts +5 -28
- package/dist/knowledge-store.js +57 -84
- package/dist/managed-resources.js +24 -2
- package/dist/policy.js +7 -9
- package/dist/retro-gate.js +8 -90
- package/dist/run-archive.d.ts +1 -1
- package/dist/run-archive.js +13 -16
- package/dist/run-persistence.js +20 -15
- package/dist/runtime/run-hook.entry.d.ts +3 -0
- package/dist/runtime/run-hook.entry.js +5 -0
- package/dist/runtime/run-hook.mjs +9477 -0
- package/dist/tdd-cycle.d.ts +3 -3
- package/dist/tdd-cycle.js +1 -1
- package/dist/types.d.ts +18 -10
- package/package.json +4 -2
- package/dist/content/hook-inline-snippets.d.ts +0 -83
- package/dist/content/hook-inline-snippets.js +0 -302
- package/dist/content/ideate-command.d.ts +0 -8
- package/dist/content/ideate-command.js +0 -315
- package/dist/content/ideate-frames.d.ts +0 -31
- package/dist/content/ideate-frames.js +0 -140
- package/dist/content/ideate-ranking.d.ts +0 -25
- package/dist/content/ideate-ranking.js +0 -65
- package/dist/content/next-command.d.ts +0 -20
- package/dist/content/next-command.js +0 -298
- package/dist/content/seed-shelf.d.ts +0 -36
- package/dist/content/seed-shelf.js +0 -301
- package/dist/content/stage-common-guidance.d.ts +0 -1
- package/dist/content/stage-common-guidance.js +0 -106
- package/dist/doctor-registry.d.ts +0 -10
- package/dist/doctor-registry.js +0 -186
- package/dist/doctor.d.ts +0 -17
- package/dist/doctor.js +0 -2201
- package/dist/internal/hook-manifest.d.ts +0 -16
- package/dist/internal/hook-manifest.js +0 -77
- package/dist/trace-matrix.d.ts +0 -27
- package/dist/trace-matrix.js +0 -226
|
@@ -5,128 +5,57 @@
|
|
|
5
5
|
export function languageTypescriptSkill() {
|
|
6
6
|
return `---
|
|
7
7
|
name: language-typescript
|
|
8
|
-
description: "TypeScript rule pack.
|
|
8
|
+
description: "TypeScript rule pack. Compact opt-in lens for tdd/review when diffs touch TS/JS files."
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# TypeScript Rule Pack
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
> 1. Activate during tdd or review whenever the diff touches \`.ts\`, \`.tsx\`, \`.mts\`, \`.cts\`, or \`.js\` files.
|
|
16
|
-
> 2. Walk the rule tiers in order. Tier-1 violations block merge. Tier-2 need a named follow-up.
|
|
17
|
-
> 3. Cite each finding as \`file:line — <rule id> — <one-line remediation>\`.
|
|
18
|
-
|
|
19
|
-
## HARD-GATE
|
|
20
|
-
|
|
21
|
-
Do not approve a TypeScript change that ships \`any\`, \`@ts-ignore\`, or
|
|
22
|
-
\`@ts-expect-error\` *without* (a) a comment explaining why, (b) a linked issue,
|
|
23
|
-
and (c) an assertion that the blast radius is bounded to the current file.
|
|
24
|
-
No exceptions in production code paths.
|
|
25
|
-
|
|
26
|
-
## Tier 1 — blocking rules
|
|
27
|
-
|
|
28
|
-
1. **No silent \`any\`.** Unknown inputs must be typed as \`unknown\` first, then narrowed.
|
|
29
|
-
2. **Runtime validate trust boundaries.** HTTP bodies, env vars, file contents, and
|
|
30
|
-
IPC payloads must be parsed through a schema validator (zod, valibot, io-ts) before
|
|
31
|
-
being treated as typed data.
|
|
32
|
-
3. **No \`as\` without a narrowing reason.** \`value as Foo\` is only acceptable when
|
|
33
|
-
preceded by a runtime check that proves the shape (e.g. \`if ("id" in value)\`).
|
|
34
|
-
4. **Exhaustive switches on discriminated unions.** Every \`switch\` on a tagged
|
|
35
|
-
union must end with a \`default\` branch that assigns to \`never\` to surface
|
|
36
|
-
missing cases at compile time.
|
|
37
|
-
5. **Promise hygiene.** No unawaited promises in \`async\` functions; no
|
|
38
|
-
\`void promise\` unless documented. Use \`@typescript-eslint/no-floating-promises\`.
|
|
39
|
-
6. **Null-safety at the boundary.** Optional chaining (\`?.\`) and nullish
|
|
40
|
-
coalescing (\`??\`) must only be used when the null path is handled, not as a
|
|
41
|
-
silent default.
|
|
13
|
+
Use this only when a diff includes \`.ts\`, \`.tsx\`, \`.mts\`, \`.cts\`, or \`.js\`.
|
|
42
14
|
|
|
43
|
-
##
|
|
15
|
+
## Blocking rules
|
|
44
16
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
11. Co-locate test fixtures with their types to keep drift visible.
|
|
17
|
+
1. **No silent \`any\` or blanket \`@ts-ignore\`.** Unknown input starts as \`unknown\` and gets narrowed.
|
|
18
|
+
2. **Validate trust boundaries at runtime.** HTTP/env/file/IPC payloads require schema parse before typed use.
|
|
19
|
+
3. **No floating promises.** Await promises or explicitly document fire-and-forget behavior.
|
|
20
|
+
4. **Exhaustive union handling.** Discriminated-union switches must fail loudly on missing branches.
|
|
50
21
|
|
|
51
|
-
##
|
|
22
|
+
## Follow-up rules
|
|
52
23
|
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
- Using enums where a string-literal union would do (enums carry runtime cost and erase at tree-shaking time only when \`const\`).
|
|
24
|
+
- Prefer immutable/readonly data by default.
|
|
25
|
+
- Keep types local and explicit at module boundaries.
|
|
26
|
+
- Add/adjust tests when changing inferred public behavior.
|
|
57
27
|
|
|
58
|
-
##
|
|
28
|
+
## Output format
|
|
59
29
|
|
|
60
|
-
|
|
61
|
-
- **Rule:** T1-2 (runtime validate trust boundaries)
|
|
62
|
-
- **File:line:** src/api/users.ts:42
|
|
63
|
-
- **Finding:** POST body cast directly to \`UserCreateInput\`; no schema parse.
|
|
64
|
-
- **Remediation:** Parse through \`userCreateSchema\` (zod) before passing to the service layer.
|
|
65
|
-
\`\`\`
|
|
30
|
+
\`file:line — rule id — concise remediation\`
|
|
66
31
|
`;
|
|
67
32
|
}
|
|
68
33
|
export function languagePythonSkill() {
|
|
69
34
|
return `---
|
|
70
35
|
name: language-python
|
|
71
|
-
description: "Python rule pack.
|
|
36
|
+
description: "Python rule pack. Compact opt-in lens for tdd/review when diffs touch Python files."
|
|
72
37
|
---
|
|
73
38
|
|
|
74
39
|
# Python Rule Pack
|
|
75
40
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
> 1. Activate during tdd or review whenever the diff touches \`.py\` / \`.pyi\` files.
|
|
79
|
-
> 2. Walk the rule tiers in order. Tier-1 violations block merge. Tier-2 need a named follow-up.
|
|
80
|
-
> 3. Cite each finding as \`file:line — <rule id> — <one-line remediation>\`.
|
|
81
|
-
|
|
82
|
-
## HARD-GATE
|
|
83
|
-
|
|
84
|
-
Do not approve a Python change that catches bare \`except:\` or \`except Exception:\`
|
|
85
|
-
in production code *without* (a) re-raising, (b) logging with \`logger.exception\`, or
|
|
86
|
-
(c) a comment explaining the intentional swallow. Silent broad catches are the
|
|
87
|
-
single biggest source of "works on my machine" bugs in Python services.
|
|
88
|
-
|
|
89
|
-
## Tier 1 — blocking rules
|
|
90
|
-
|
|
91
|
-
1. **Type hints on public APIs.** Every exported function, method, and dataclass
|
|
92
|
-
must have full type hints. Use \`from __future__ import annotations\` or PEP 604 union syntax.
|
|
93
|
-
2. **No mutable default arguments.** \`def f(x=[])\` is a bug. Use \`None\` + inline default.
|
|
94
|
-
3. **Exception specificity.** Catch the narrowest exception class you actually handle.
|
|
95
|
-
4. **Context managers for resources.** Files, sockets, DB sessions, locks — always \`with\`.
|
|
96
|
-
5. **No bare \`assert\` in production code.** \`assert\` is stripped under \`python -O\`.
|
|
97
|
-
For invariants, raise \`ValueError\`/\`RuntimeError\` explicitly.
|
|
98
|
-
6. **Deterministic imports.** No conditional imports at module top level except for
|
|
99
|
-
platform branches; no import-time side effects.
|
|
100
|
-
|
|
101
|
-
## Tier 2 — follow-up rules
|
|
102
|
-
|
|
103
|
-
7. Prefer \`@dataclass(slots=True, frozen=True)\` for value objects.
|
|
104
|
-
8. Prefer \`pathlib.Path\` over \`os.path\` for new code.
|
|
105
|
-
9. Use f-strings for interpolation; reserve \`%\` and \`.format\` for logger messages (lazy eval).
|
|
106
|
-
10. Use \`logging.getLogger(__name__)\` per module; never the root logger.
|
|
107
|
-
11. Pin dependency ranges in \`pyproject.toml\`; lock with \`uv lock\` / \`pip-compile\`.
|
|
41
|
+
Use this only when a diff includes \`.py\` / \`.pyi\`.
|
|
108
42
|
|
|
109
|
-
##
|
|
43
|
+
## Blocking rules
|
|
110
44
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
45
|
+
1. **No broad silent catches.** Avoid bare \`except\` / \`except Exception\` unless re-raised or justified.
|
|
46
|
+
2. **No mutable defaults.** Use \`None\` + local initialization.
|
|
47
|
+
3. **Type exported surfaces.** Public functions/classes include clear type hints.
|
|
48
|
+
4. **Resource safety by default.** File/DB/network handles use context managers.
|
|
114
49
|
|
|
115
|
-
##
|
|
50
|
+
## Follow-up rules
|
|
116
51
|
|
|
117
|
-
-
|
|
118
|
-
-
|
|
119
|
-
-
|
|
120
|
-
- Re-inventing \`itertools\`/\`functools\` instead of using stdlib.
|
|
52
|
+
- Prefer explicit, narrow exceptions.
|
|
53
|
+
- Keep async and sync I/O models separated.
|
|
54
|
+
- Add/adjust tests with behavior changes.
|
|
121
55
|
|
|
122
|
-
##
|
|
56
|
+
## Output format
|
|
123
57
|
|
|
124
|
-
|
|
125
|
-
- **Rule:** P1-3 (exception specificity)
|
|
126
|
-
- **File:line:** users/service.py:88
|
|
127
|
-
- **Finding:** \`except Exception\` around DB call silently drops integrity errors.
|
|
128
|
-
- **Remediation:** Catch \`IntegrityError\` explicitly; re-raise everything else.
|
|
129
|
-
\`\`\`
|
|
58
|
+
\`file:line — rule id — concise remediation\`
|
|
130
59
|
`;
|
|
131
60
|
}
|
|
132
61
|
export function languageGoSkill() {
|
|
@@ -211,7 +140,7 @@ export const LANGUAGE_RULE_PACK_FILES = {
|
|
|
211
140
|
};
|
|
212
141
|
/**
|
|
213
142
|
* Folder (relative to runtime root) that holds every enabled language rule
|
|
214
|
-
* pack. A single folder keeps discovery trivial for hooks and for `
|
|
143
|
+
* pack. A single folder keeps discovery trivial for hooks and for `sync`.
|
|
215
144
|
*/
|
|
216
145
|
export const LANGUAGE_RULE_PACK_DIR = ["rules", "lang"];
|
|
217
146
|
export const LANGUAGE_RULE_PACK_GENERATORS = {
|
|
@@ -221,7 +150,7 @@ export const LANGUAGE_RULE_PACK_GENERATORS = {
|
|
|
221
150
|
};
|
|
222
151
|
/**
|
|
223
152
|
* Legacy per-language folders under `.cclaw/skills/` used in v0.7.0. Listed
|
|
224
|
-
* here so `cclaw sync` and `
|
|
153
|
+
* here so `cclaw sync` and `sync` can surface drift and the installer can
|
|
225
154
|
* clean them up after the move to `.cclaw/rules/lang/`.
|
|
226
155
|
*/
|
|
227
156
|
export const LEGACY_LANGUAGE_RULE_PACK_FOLDERS = [
|
|
@@ -32,8 +32,10 @@ ${conversationLanguagePolicyMarkdown()}
|
|
|
32
32
|
- \`diff\` -> use the **Diff Subcommand** section in \`${RUNTIME_ROOT}/skills/${VIEW_SKILL_FOLDER}/SKILL.md\`
|
|
33
33
|
3. Unknown subcommand -> print supported values and stop.
|
|
34
34
|
|
|
35
|
-
## Headless mode
|
|
35
|
+
## Headless mode (CI/automation only)
|
|
36
36
|
|
|
37
|
+
Headless envelopes are a machine-mode exception for CI/automation orchestration.
|
|
38
|
+
In normal interactive runs, respond with concise read-only prose instead.
|
|
37
39
|
For machine orchestration, emit one JSON envelope:
|
|
38
40
|
|
|
39
41
|
\`\`\`json
|
|
@@ -43,7 +45,7 @@ For machine orchestration, emit one JSON envelope:
|
|
|
43
45
|
Use the parsed/defaulted subcommand in both \`payload.command\` and \`payload.subcommand\`; do not collapse \`tree\` or \`diff\` responses to \`status\`.
|
|
44
46
|
|
|
45
47
|
Validate envelopes with:
|
|
46
|
-
\`cclaw internal envelope-validate --stdin\`
|
|
48
|
+
\`npx cclaw-cli internal envelope-validate --stdin\`
|
|
47
49
|
|
|
48
50
|
## Primary skill
|
|
49
51
|
|
package/dist/delegation.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export interface DelegationTokenUsage {
|
|
|
34
34
|
output: number;
|
|
35
35
|
model: string;
|
|
36
36
|
}
|
|
37
|
+
export type DelegationWaiverAcceptedBy = "user-flag";
|
|
37
38
|
export type DelegationEntry = {
|
|
38
39
|
stage: string;
|
|
39
40
|
agent: string;
|
|
@@ -56,6 +57,7 @@ export type DelegationEntry = {
|
|
|
56
57
|
*/
|
|
57
58
|
taskId?: string;
|
|
58
59
|
waiverReason?: string;
|
|
60
|
+
acceptedBy?: DelegationWaiverAcceptedBy;
|
|
59
61
|
ts?: string;
|
|
60
62
|
/**
|
|
61
63
|
* Run id the entry belongs to. Older ledgers written before 0.5.17 may omit this;
|
package/dist/delegation.js
CHANGED
|
@@ -198,6 +198,7 @@ function isDelegationEntry(value) {
|
|
|
198
198
|
(o.endTs === undefined || typeof o.endTs === "string") &&
|
|
199
199
|
(o.taskId === undefined || typeof o.taskId === "string") &&
|
|
200
200
|
(o.waiverReason === undefined || typeof o.waiverReason === "string") &&
|
|
201
|
+
(o.acceptedBy === undefined || o.acceptedBy === "user-flag") &&
|
|
201
202
|
waiverOk &&
|
|
202
203
|
(o.runId === undefined || typeof o.runId === "string") &&
|
|
203
204
|
(o.fulfillmentMode === undefined ||
|
|
@@ -269,7 +270,7 @@ function parseLedger(raw, runId) {
|
|
|
269
270
|
// and entry, the entry has no fulfillmentMode, and there is no
|
|
270
271
|
// dispatch-surface or dispatch-id evidence on the row. We honor
|
|
271
272
|
// that by tagging fulfillmentMode = "legacy-inferred" so callers
|
|
272
|
-
// (stage-complete,
|
|
273
|
+
// (stage-complete, sync/runtime checks) can require an explicit `--rerecord`
|
|
273
274
|
// before the row counts as proof-era.
|
|
274
275
|
const ledgerHasNoVersion = ledgerSchemaVersion === undefined || ledgerSchemaVersion === 1;
|
|
275
276
|
const entryHasNoVersion = item.schemaVersion === undefined || item.schemaVersion === 1;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export declare const EARLY_LOOP_STAGES: readonly ["brainstorm", "scope", "design"];
|
|
2
|
+
export type EarlyLoopStage = (typeof EARLY_LOOP_STAGES)[number];
|
|
3
|
+
export type EarlyLoopConcernSeverity = "critical" | "important" | "suggestion";
|
|
4
|
+
export interface EarlyLoopConcern {
|
|
5
|
+
id: string;
|
|
6
|
+
severity: EarlyLoopConcernSeverity;
|
|
7
|
+
locator: string;
|
|
8
|
+
summary: string;
|
|
9
|
+
firstSeenIteration: number;
|
|
10
|
+
lastSeenIteration: number;
|
|
11
|
+
resolvedAtIteration?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface EarlyLoopStatus {
|
|
14
|
+
schemaVersion: 1;
|
|
15
|
+
stage: EarlyLoopStage;
|
|
16
|
+
runId: string;
|
|
17
|
+
iteration: number;
|
|
18
|
+
maxIterations: number;
|
|
19
|
+
openConcerns: EarlyLoopConcern[];
|
|
20
|
+
resolvedConcerns: EarlyLoopConcern[];
|
|
21
|
+
lastSeenConcernIds: string[];
|
|
22
|
+
convergenceTripped: boolean;
|
|
23
|
+
escalationReason?: string;
|
|
24
|
+
lastUpdatedAt: string;
|
|
25
|
+
}
|
|
26
|
+
export interface EarlyLoopLogConcern {
|
|
27
|
+
id: string;
|
|
28
|
+
severity: EarlyLoopConcernSeverity;
|
|
29
|
+
locator: string;
|
|
30
|
+
summary: string;
|
|
31
|
+
}
|
|
32
|
+
export interface EarlyLoopLogEntry {
|
|
33
|
+
ts: string;
|
|
34
|
+
runId: string;
|
|
35
|
+
stage: string;
|
|
36
|
+
iteration?: number;
|
|
37
|
+
concerns: EarlyLoopLogConcern[];
|
|
38
|
+
resolvedConcernIds: string[];
|
|
39
|
+
}
|
|
40
|
+
export interface EarlyLoopParseIssue {
|
|
41
|
+
lineNumber: number;
|
|
42
|
+
reason: string;
|
|
43
|
+
rawLine: string;
|
|
44
|
+
}
|
|
45
|
+
export interface ParseEarlyLoopLogOptions {
|
|
46
|
+
issues?: EarlyLoopParseIssue[];
|
|
47
|
+
strict?: boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface DeriveEarlyLoopStatusOptions {
|
|
50
|
+
stage: EarlyLoopStage;
|
|
51
|
+
runId: string;
|
|
52
|
+
maxIterations?: number;
|
|
53
|
+
now?: Date;
|
|
54
|
+
}
|
|
55
|
+
export interface ComputeEarlyLoopStatusOptions {
|
|
56
|
+
maxIterations?: number;
|
|
57
|
+
now?: Date;
|
|
58
|
+
parseIssues?: EarlyLoopParseIssue[];
|
|
59
|
+
strictParse?: boolean;
|
|
60
|
+
}
|
|
61
|
+
export declare function isEarlyLoopStage(value: unknown): value is EarlyLoopStage;
|
|
62
|
+
export declare function normalizeEarlyLoopMaxIterations(value: number | undefined): number;
|
|
63
|
+
export declare function parseEarlyLoopLog(text: string, options?: ParseEarlyLoopLogOptions): EarlyLoopLogEntry[];
|
|
64
|
+
export declare function deriveEarlyLoopStatus(entries: EarlyLoopLogEntry[], options: DeriveEarlyLoopStatusOptions): EarlyLoopStatus;
|
|
65
|
+
export declare function computeEarlyLoopStatus(stage: EarlyLoopStage, runId: string, concernsLogPath: string, options?: ComputeEarlyLoopStatusOptions): Promise<EarlyLoopStatus>;
|
|
66
|
+
export declare function formatEarlyLoopStatusLine(status: EarlyLoopStatus): string;
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { DEFAULT_EARLY_LOOP_MAX_ITERATIONS } from "./config.js";
|
|
3
|
+
export const EARLY_LOOP_STAGES = ["brainstorm", "scope", "design"];
|
|
4
|
+
const CONCERN_ID_PREFIX = "C-";
|
|
5
|
+
function severityWeight(severity) {
|
|
6
|
+
if (severity === "critical")
|
|
7
|
+
return 3;
|
|
8
|
+
if (severity === "important")
|
|
9
|
+
return 2;
|
|
10
|
+
return 1;
|
|
11
|
+
}
|
|
12
|
+
function normalizeSeverity(value) {
|
|
13
|
+
if (value === "critical" || value === "important" || value === "suggestion") {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
return "important";
|
|
17
|
+
}
|
|
18
|
+
function normalizeText(value, fallback) {
|
|
19
|
+
if (typeof value !== "string")
|
|
20
|
+
return fallback;
|
|
21
|
+
const trimmed = value.trim();
|
|
22
|
+
return trimmed.length > 0 ? trimmed : fallback;
|
|
23
|
+
}
|
|
24
|
+
function stableConcernFallbackId(locator, summary) {
|
|
25
|
+
const seed = `${locator}::${summary}`.trim().toLowerCase();
|
|
26
|
+
let hash = 0;
|
|
27
|
+
for (let index = 0; index < seed.length; index += 1) {
|
|
28
|
+
hash = (Math.imul(31, hash) + seed.charCodeAt(index)) >>> 0;
|
|
29
|
+
}
|
|
30
|
+
return `${CONCERN_ID_PREFIX}${hash.toString(16).padStart(8, "0")}`;
|
|
31
|
+
}
|
|
32
|
+
function normalizeConcernId(id, locator, summary) {
|
|
33
|
+
if (typeof id === "string") {
|
|
34
|
+
const trimmed = id.trim();
|
|
35
|
+
if (trimmed.length > 0) {
|
|
36
|
+
return trimmed;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return stableConcernFallbackId(locator, summary);
|
|
40
|
+
}
|
|
41
|
+
function normalizeConcerns(value) {
|
|
42
|
+
if (!Array.isArray(value))
|
|
43
|
+
return [];
|
|
44
|
+
const concerns = [];
|
|
45
|
+
for (const row of value) {
|
|
46
|
+
if (!row || typeof row !== "object" || Array.isArray(row))
|
|
47
|
+
continue;
|
|
48
|
+
const typed = row;
|
|
49
|
+
const locator = normalizeText(typed.locator, "unknown-location");
|
|
50
|
+
const summary = normalizeText(typed.summary, "missing-summary");
|
|
51
|
+
concerns.push({
|
|
52
|
+
id: normalizeConcernId(typed.id, locator, summary),
|
|
53
|
+
severity: normalizeSeverity(typed.severity),
|
|
54
|
+
locator,
|
|
55
|
+
summary
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return concerns;
|
|
59
|
+
}
|
|
60
|
+
function normalizeResolvedConcernIds(value) {
|
|
61
|
+
if (!Array.isArray(value))
|
|
62
|
+
return [];
|
|
63
|
+
return value
|
|
64
|
+
.filter((entry) => typeof entry === "string")
|
|
65
|
+
.map((entry) => entry.trim())
|
|
66
|
+
.filter((entry) => entry.length > 0);
|
|
67
|
+
}
|
|
68
|
+
export function isEarlyLoopStage(value) {
|
|
69
|
+
return typeof value === "string" && EARLY_LOOP_STAGES.includes(value);
|
|
70
|
+
}
|
|
71
|
+
export function normalizeEarlyLoopMaxIterations(value) {
|
|
72
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 1) {
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
return DEFAULT_EARLY_LOOP_MAX_ITERATIONS;
|
|
76
|
+
}
|
|
77
|
+
export function parseEarlyLoopLog(text, options = {}) {
|
|
78
|
+
const strict = options.strict === true;
|
|
79
|
+
const issues = options.issues;
|
|
80
|
+
const normalized = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
|
|
81
|
+
const lines = normalized.split(/\r?\n/u);
|
|
82
|
+
const entries = [];
|
|
83
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
84
|
+
const raw = lines[index] ?? "";
|
|
85
|
+
const line = raw.trim();
|
|
86
|
+
if (line.length === 0)
|
|
87
|
+
continue;
|
|
88
|
+
const lineNumber = index + 1;
|
|
89
|
+
let parsed;
|
|
90
|
+
try {
|
|
91
|
+
parsed = JSON.parse(line);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
issues?.push({
|
|
95
|
+
lineNumber,
|
|
96
|
+
reason: `json-parse-failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
97
|
+
rawLine: raw
|
|
98
|
+
});
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
const runId = normalizeText(parsed.runId, "");
|
|
102
|
+
const stage = normalizeText(parsed.stage, "");
|
|
103
|
+
const concerns = normalizeConcerns(parsed.concerns);
|
|
104
|
+
const resolvedConcernIds = normalizeResolvedConcernIds(parsed.resolvedConcernIds);
|
|
105
|
+
const iteration = typeof parsed.iteration === "number" &&
|
|
106
|
+
Number.isInteger(parsed.iteration) &&
|
|
107
|
+
parsed.iteration >= 1
|
|
108
|
+
? parsed.iteration
|
|
109
|
+
: undefined;
|
|
110
|
+
if (strict) {
|
|
111
|
+
const missing = [];
|
|
112
|
+
if (runId.length === 0)
|
|
113
|
+
missing.push("runId");
|
|
114
|
+
if (stage.length === 0)
|
|
115
|
+
missing.push("stage");
|
|
116
|
+
if (concerns.length === 0 && resolvedConcernIds.length === 0) {
|
|
117
|
+
missing.push("concerns/resolvedConcernIds");
|
|
118
|
+
}
|
|
119
|
+
if (missing.length > 0) {
|
|
120
|
+
issues?.push({
|
|
121
|
+
lineNumber,
|
|
122
|
+
reason: `missing-required-fields: ${missing.join(",")}`,
|
|
123
|
+
rawLine: raw
|
|
124
|
+
});
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
entries.push({
|
|
129
|
+
ts: normalizeText(parsed.ts, ""),
|
|
130
|
+
runId: runId.length > 0 ? runId : "active",
|
|
131
|
+
stage: stage.length > 0 ? stage : "brainstorm",
|
|
132
|
+
iteration,
|
|
133
|
+
concerns,
|
|
134
|
+
resolvedConcernIds
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return entries;
|
|
138
|
+
}
|
|
139
|
+
function sortConcerns(a, b) {
|
|
140
|
+
const severityDiff = severityWeight(b.severity) - severityWeight(a.severity);
|
|
141
|
+
if (severityDiff !== 0)
|
|
142
|
+
return severityDiff;
|
|
143
|
+
if (a.firstSeenIteration !== b.firstSeenIteration) {
|
|
144
|
+
return a.firstSeenIteration - b.firstSeenIteration;
|
|
145
|
+
}
|
|
146
|
+
if (a.lastSeenIteration !== b.lastSeenIteration) {
|
|
147
|
+
return a.lastSeenIteration - b.lastSeenIteration;
|
|
148
|
+
}
|
|
149
|
+
return a.id.localeCompare(b.id, "en");
|
|
150
|
+
}
|
|
151
|
+
export function deriveEarlyLoopStatus(entries, options) {
|
|
152
|
+
const maxIterations = normalizeEarlyLoopMaxIterations(options.maxIterations);
|
|
153
|
+
const concerns = new Map();
|
|
154
|
+
const filtered = entries.filter((entry) => entry.runId === options.runId && entry.stage === options.stage);
|
|
155
|
+
let previousConcernSnapshotKey = "";
|
|
156
|
+
let sameConcernStreak = 0;
|
|
157
|
+
let convergenceTripped = false;
|
|
158
|
+
let escalationReason;
|
|
159
|
+
let currentIteration = 0;
|
|
160
|
+
let lastSeenConcernIds = [];
|
|
161
|
+
for (const entry of filtered) {
|
|
162
|
+
currentIteration += 1;
|
|
163
|
+
const iteration = entry.iteration ?? currentIteration;
|
|
164
|
+
const seenThisIteration = new Set();
|
|
165
|
+
for (const concern of entry.concerns) {
|
|
166
|
+
seenThisIteration.add(concern.id);
|
|
167
|
+
const existing = concerns.get(concern.id);
|
|
168
|
+
if (!existing) {
|
|
169
|
+
concerns.set(concern.id, {
|
|
170
|
+
id: concern.id,
|
|
171
|
+
severity: concern.severity,
|
|
172
|
+
locator: concern.locator,
|
|
173
|
+
summary: concern.summary,
|
|
174
|
+
firstSeenIteration: iteration,
|
|
175
|
+
lastSeenIteration: iteration
|
|
176
|
+
});
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
existing.lastSeenIteration = iteration;
|
|
180
|
+
existing.locator = concern.locator;
|
|
181
|
+
existing.summary = concern.summary;
|
|
182
|
+
if (severityWeight(concern.severity) >= severityWeight(existing.severity)) {
|
|
183
|
+
existing.severity = concern.severity;
|
|
184
|
+
}
|
|
185
|
+
delete existing.resolvedAtIteration;
|
|
186
|
+
}
|
|
187
|
+
for (const concernId of entry.resolvedConcernIds) {
|
|
188
|
+
const existing = concerns.get(concernId);
|
|
189
|
+
if (!existing)
|
|
190
|
+
continue;
|
|
191
|
+
if (seenThisIteration.has(concernId))
|
|
192
|
+
continue;
|
|
193
|
+
if (existing.resolvedAtIteration === undefined) {
|
|
194
|
+
existing.resolvedAtIteration = iteration;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
for (const concern of concerns.values()) {
|
|
198
|
+
if (concern.resolvedAtIteration !== undefined)
|
|
199
|
+
continue;
|
|
200
|
+
if (seenThisIteration.has(concern.id))
|
|
201
|
+
continue;
|
|
202
|
+
concern.resolvedAtIteration = iteration;
|
|
203
|
+
}
|
|
204
|
+
const openConcernIds = Array.from(concerns.values())
|
|
205
|
+
.filter((concern) => concern.resolvedAtIteration === undefined)
|
|
206
|
+
.map((concern) => concern.id)
|
|
207
|
+
.sort((a, b) => a.localeCompare(b, "en"));
|
|
208
|
+
lastSeenConcernIds = openConcernIds;
|
|
209
|
+
const snapshotKey = openConcernIds.join("|");
|
|
210
|
+
if (snapshotKey.length > 0 && snapshotKey === previousConcernSnapshotKey) {
|
|
211
|
+
sameConcernStreak += 1;
|
|
212
|
+
if (!convergenceTripped && sameConcernStreak >= 2) {
|
|
213
|
+
convergenceTripped = true;
|
|
214
|
+
escalationReason = `same concerns ${sameConcernStreak} iterations in a row`;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
sameConcernStreak = snapshotKey.length > 0 ? 1 : 0;
|
|
219
|
+
}
|
|
220
|
+
previousConcernSnapshotKey = snapshotKey;
|
|
221
|
+
}
|
|
222
|
+
const openConcerns = Array.from(concerns.values())
|
|
223
|
+
.filter((concern) => concern.resolvedAtIteration === undefined)
|
|
224
|
+
.sort(sortConcerns);
|
|
225
|
+
const resolvedConcerns = Array.from(concerns.values())
|
|
226
|
+
.filter((concern) => concern.resolvedAtIteration !== undefined)
|
|
227
|
+
.sort((a, b) => {
|
|
228
|
+
if (a.resolvedAtIteration !== b.resolvedAtIteration) {
|
|
229
|
+
return a.resolvedAtIteration - b.resolvedAtIteration;
|
|
230
|
+
}
|
|
231
|
+
return sortConcerns(a, b);
|
|
232
|
+
});
|
|
233
|
+
if (!convergenceTripped && openConcerns.length > 0 && currentIteration >= maxIterations) {
|
|
234
|
+
convergenceTripped = true;
|
|
235
|
+
escalationReason = `max iterations ${maxIterations} reached with ${openConcerns.length} open concern(s)`;
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
schemaVersion: 1,
|
|
239
|
+
stage: options.stage,
|
|
240
|
+
runId: options.runId,
|
|
241
|
+
iteration: currentIteration,
|
|
242
|
+
maxIterations,
|
|
243
|
+
openConcerns,
|
|
244
|
+
resolvedConcerns,
|
|
245
|
+
lastSeenConcernIds,
|
|
246
|
+
convergenceTripped,
|
|
247
|
+
...(escalationReason ? { escalationReason } : {}),
|
|
248
|
+
lastUpdatedAt: (options.now ?? new Date()).toISOString()
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
export async function computeEarlyLoopStatus(stage, runId, concernsLogPath, options = {}) {
|
|
252
|
+
let raw = "";
|
|
253
|
+
try {
|
|
254
|
+
raw = await fs.readFile(concernsLogPath, "utf8");
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
if (error.code !== "ENOENT") {
|
|
258
|
+
throw error;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
const parsed = parseEarlyLoopLog(raw, {
|
|
262
|
+
issues: options.parseIssues,
|
|
263
|
+
strict: options.strictParse
|
|
264
|
+
});
|
|
265
|
+
return deriveEarlyLoopStatus(parsed, {
|
|
266
|
+
stage,
|
|
267
|
+
runId,
|
|
268
|
+
maxIterations: options.maxIterations,
|
|
269
|
+
now: options.now
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
export function formatEarlyLoopStatusLine(status) {
|
|
273
|
+
const convergence = status.convergenceTripped ? "tripped" : "clear";
|
|
274
|
+
return `Early Loop: stage=${status.stage}, iter=${status.iteration}/${status.maxIterations}, open=${status.openConcerns.length}, convergence=${convergence}`;
|
|
275
|
+
}
|
package/dist/flow-state.d.ts
CHANGED
|
@@ -32,14 +32,13 @@ export interface RetroState {
|
|
|
32
32
|
/**
|
|
33
33
|
* Ship closeout substate machine.
|
|
34
34
|
*
|
|
35
|
-
* After ship completes, cclaw auto-chains
|
|
35
|
+
* After ship completes, cclaw auto-chains post-ship review → archive.
|
|
36
36
|
* Each step is interruptible: `/cc` reads `shipSubstate` and resumes
|
|
37
37
|
* from the correct step even across sessions.
|
|
38
38
|
*
|
|
39
39
|
* - `idle` — ship not complete, or closeout not yet started.
|
|
40
|
-
* - `
|
|
41
|
-
*
|
|
42
|
-
* (or user skip).
|
|
40
|
+
* - `post_ship_review` — unified closeout leg: retro acceptance/edit/skip
|
|
41
|
+
* plus compound pass execution (or explicit skip).
|
|
43
42
|
* - `ready_to_archive` — retro + compound done; archive is the next
|
|
44
43
|
* automatic step.
|
|
45
44
|
* - `archived` — archive completed in this session (transient — archive
|
|
@@ -53,7 +52,7 @@ export interface RetroState {
|
|
|
53
52
|
* These are not duplicates: `done` lives in stage transitions; `archived` /
|
|
54
53
|
* `idle` live in closeout lifecycle state.
|
|
55
54
|
*/
|
|
56
|
-
export declare const SHIP_SUBSTATES: readonly ["idle", "
|
|
55
|
+
export declare const SHIP_SUBSTATES: readonly ["idle", "post_ship_review", "ready_to_archive", "archived"];
|
|
57
56
|
export type ShipSubstate = (typeof SHIP_SUBSTATES)[number];
|
|
58
57
|
export interface CloseoutState {
|
|
59
58
|
shipSubstate: ShipSubstate;
|
|
@@ -85,7 +84,7 @@ export interface FlowState {
|
|
|
85
84
|
rewinds: RewindRecord[];
|
|
86
85
|
/** Mandatory retrospective gate status before archive. */
|
|
87
86
|
retro: RetroState;
|
|
88
|
-
/** Ship →
|
|
87
|
+
/** Ship → post_ship_review → archive substate for resumable closeout. */
|
|
89
88
|
closeout: CloseoutState;
|
|
90
89
|
}
|
|
91
90
|
export interface InitialFlowStateOptions {
|
package/dist/flow-state.js
CHANGED
|
@@ -5,14 +5,13 @@ export const FLOW_STATE_SCHEMA_VERSION = 1;
|
|
|
5
5
|
/**
|
|
6
6
|
* Ship closeout substate machine.
|
|
7
7
|
*
|
|
8
|
-
* After ship completes, cclaw auto-chains
|
|
8
|
+
* After ship completes, cclaw auto-chains post-ship review → archive.
|
|
9
9
|
* Each step is interruptible: `/cc` reads `shipSubstate` and resumes
|
|
10
10
|
* from the correct step even across sessions.
|
|
11
11
|
*
|
|
12
12
|
* - `idle` — ship not complete, or closeout not yet started.
|
|
13
|
-
* - `
|
|
14
|
-
*
|
|
15
|
-
* (or user skip).
|
|
13
|
+
* - `post_ship_review` — unified closeout leg: retro acceptance/edit/skip
|
|
14
|
+
* plus compound pass execution (or explicit skip).
|
|
16
15
|
* - `ready_to_archive` — retro + compound done; archive is the next
|
|
17
16
|
* automatic step.
|
|
18
17
|
* - `archived` — archive completed in this session (transient — archive
|
|
@@ -28,8 +27,7 @@ export const FLOW_STATE_SCHEMA_VERSION = 1;
|
|
|
28
27
|
*/
|
|
29
28
|
export const SHIP_SUBSTATES = [
|
|
30
29
|
"idle",
|
|
31
|
-
"
|
|
32
|
-
"compound_review",
|
|
30
|
+
"post_ship_review",
|
|
33
31
|
"ready_to_archive",
|
|
34
32
|
"archived"
|
|
35
33
|
];
|
package/dist/gate-evidence.d.ts
CHANGED
|
@@ -29,29 +29,6 @@ export interface CompletedStagesClosureResult {
|
|
|
29
29
|
blocked: string[];
|
|
30
30
|
}>;
|
|
31
31
|
}
|
|
32
|
-
export declare const RECONCILIATION_NOTICES_REL_PATH = ".cclaw/state/reconciliation-notices.json";
|
|
33
|
-
export interface ReconciliationNotice {
|
|
34
|
-
id: string;
|
|
35
|
-
runId: string;
|
|
36
|
-
stage: FlowStage;
|
|
37
|
-
gateId: string;
|
|
38
|
-
reason: string;
|
|
39
|
-
demotedAt: string;
|
|
40
|
-
}
|
|
41
|
-
export interface ReconciliationNoticesPayload {
|
|
42
|
-
schemaVersion: number;
|
|
43
|
-
notices: ReconciliationNotice[];
|
|
44
|
-
parseOk: boolean;
|
|
45
|
-
schemaOk: boolean;
|
|
46
|
-
}
|
|
47
|
-
export interface ReconciliationNoticeBuckets {
|
|
48
|
-
activeBlocked: ReconciliationNotice[];
|
|
49
|
-
currentStageBlocked: ReconciliationNotice[];
|
|
50
|
-
unsynced: ReconciliationNotice[];
|
|
51
|
-
staleRun: ReconciliationNotice[];
|
|
52
|
-
}
|
|
53
|
-
export declare function readReconciliationNotices(projectRoot: string): Promise<ReconciliationNoticesPayload>;
|
|
54
|
-
export declare function classifyReconciliationNotices(flowState: FlowState, notices: ReconciliationNotice[]): ReconciliationNoticeBuckets;
|
|
55
32
|
export declare function verifyCurrentStageGateEvidence(projectRoot: string, flowState: FlowState): Promise<GateEvidenceCheckResult>;
|
|
56
33
|
export declare function verifyCompletedStagesGateClosure(flowState: FlowState): CompletedStagesClosureResult;
|
|
57
34
|
export interface GateReconciliationResult {
|