wicked-crew 0.7.30 → 0.7.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. package/dist/api/chat-scope.d.ts +15 -2
  2. package/dist/api/chat-scope.d.ts.map +1 -1
  3. package/dist/api/chat-scope.js +7 -2
  4. package/dist/api/chat-scope.js.map +1 -1
  5. package/dist/api/eval-sample.d.ts +12 -12
  6. package/dist/api/routes.d.ts +10 -0
  7. package/dist/api/routes.d.ts.map +1 -1
  8. package/dist/api/routes.js +231 -37
  9. package/dist/api/routes.js.map +1 -1
  10. package/dist/api/run-files.d.ts +26 -0
  11. package/dist/api/run-files.d.ts.map +1 -1
  12. package/dist/api/run-files.js +78 -0
  13. package/dist/api/run-files.js.map +1 -1
  14. package/dist/api/seat-health.d.ts +33 -0
  15. package/dist/api/seat-health.d.ts.map +1 -1
  16. package/dist/api/seat-health.js +86 -2
  17. package/dist/api/seat-health.js.map +1 -1
  18. package/dist/api/seat-signin.d.ts +8 -0
  19. package/dist/api/seat-signin.d.ts.map +1 -1
  20. package/dist/api/seat-signin.js +48 -3
  21. package/dist/api/seat-signin.js.map +1 -1
  22. package/dist/api/seat-standing.d.ts +14 -2
  23. package/dist/api/seat-standing.d.ts.map +1 -1
  24. package/dist/api/seat-standing.js +14 -4
  25. package/dist/api/seat-standing.js.map +1 -1
  26. package/dist/api/server.d.ts.map +1 -1
  27. package/dist/api/server.js +41 -4
  28. package/dist/api/server.js.map +1 -1
  29. package/dist/api/skills.d.ts +2 -2
  30. package/dist/api/testing.d.ts +28 -1
  31. package/dist/api/testing.d.ts.map +1 -1
  32. package/dist/api/testing.js +190 -0
  33. package/dist/api/testing.js.map +1 -1
  34. package/dist/campaigns/rollup.d.ts +4 -0
  35. package/dist/campaigns/rollup.d.ts.map +1 -1
  36. package/dist/campaigns/rollup.js.map +1 -1
  37. package/dist/campaigns/routes.d.ts +3 -1
  38. package/dist/campaigns/routes.d.ts.map +1 -1
  39. package/dist/campaigns/routes.js +3 -0
  40. package/dist/campaigns/routes.js.map +1 -1
  41. package/dist/core/adapter.d.ts.map +1 -1
  42. package/dist/core/adapter.js +23 -2
  43. package/dist/core/adapter.js.map +1 -1
  44. package/dist/core/deliver-text.d.ts +7 -0
  45. package/dist/core/deliver-text.d.ts.map +1 -1
  46. package/dist/core/deliver-text.js +41 -2
  47. package/dist/core/deliver-text.js.map +1 -1
  48. package/dist/core/engine-roster.d.ts +68 -0
  49. package/dist/core/engine-roster.d.ts.map +1 -0
  50. package/dist/core/engine-roster.js +156 -0
  51. package/dist/core/engine-roster.js.map +1 -0
  52. package/dist/interactive/chat-events.d.ts.map +1 -1
  53. package/dist/interactive/chat-events.js +26 -4
  54. package/dist/interactive/chat-events.js.map +1 -1
  55. package/dist/interactive/council-outcome.d.ts +28 -1
  56. package/dist/interactive/council-outcome.d.ts.map +1 -1
  57. package/dist/interactive/council-outcome.js +51 -1
  58. package/dist/interactive/council-outcome.js.map +1 -1
  59. package/dist/interactive/demo-events.d.ts.map +1 -1
  60. package/dist/interactive/demo-events.js +26 -4
  61. package/dist/interactive/demo-events.js.map +1 -1
  62. package/dist/interactive/doc-run-index.d.ts +56 -0
  63. package/dist/interactive/doc-run-index.d.ts.map +1 -0
  64. package/dist/interactive/doc-run-index.js +100 -0
  65. package/dist/interactive/doc-run-index.js.map +1 -0
  66. package/dist/interactive/docs-index.d.ts +68 -0
  67. package/dist/interactive/docs-index.d.ts.map +1 -0
  68. package/dist/interactive/docs-index.js +114 -0
  69. package/dist/interactive/docs-index.js.map +1 -0
  70. package/dist/interactive/draft-events.d.ts +18 -0
  71. package/dist/interactive/draft-events.d.ts.map +1 -1
  72. package/dist/interactive/draft-events.js +36 -3
  73. package/dist/interactive/draft-events.js.map +1 -1
  74. package/dist/interactive/edit-events.d.ts.map +1 -1
  75. package/dist/interactive/edit-events.js +26 -4
  76. package/dist/interactive/edit-events.js.map +1 -1
  77. package/dist/interactive/ledger.d.ts +2 -0
  78. package/dist/interactive/ledger.d.ts.map +1 -1
  79. package/dist/interactive/ledger.js +4 -0
  80. package/dist/interactive/ledger.js.map +1 -1
  81. package/dist/qe/acceptance.d.ts +72 -3
  82. package/dist/qe/acceptance.d.ts.map +1 -1
  83. package/dist/qe/acceptance.js +137 -17
  84. package/dist/qe/acceptance.js.map +1 -1
  85. package/dist/qe/author-workflow.d.ts +165 -0
  86. package/dist/qe/author-workflow.d.ts.map +1 -0
  87. package/dist/qe/author-workflow.js +377 -0
  88. package/dist/qe/author-workflow.js.map +1 -0
  89. package/dist/qe/ledger.d.ts +128 -28
  90. package/dist/qe/ledger.d.ts.map +1 -1
  91. package/dist/qe/ledger.js +272 -42
  92. package/dist/qe/ledger.js.map +1 -1
  93. package/dist/qe/test-sets.d.ts +112 -0
  94. package/dist/qe/test-sets.d.ts.map +1 -0
  95. package/dist/qe/test-sets.js +170 -0
  96. package/dist/qe/test-sets.js.map +1 -0
  97. package/dist/skills/refs.d.ts +37 -0
  98. package/dist/skills/refs.d.ts.map +1 -1
  99. package/dist/skills/refs.js +22 -0
  100. package/dist/skills/refs.js.map +1 -1
  101. package/dist/skills/runtime.d.ts +13 -1
  102. package/dist/skills/runtime.d.ts.map +1 -1
  103. package/dist/skills/runtime.js +88 -1
  104. package/dist/skills/runtime.js.map +1 -1
  105. package/dist/skills/store.d.ts +82 -5
  106. package/dist/skills/store.d.ts.map +1 -1
  107. package/dist/skills/store.js +109 -21
  108. package/dist/skills/store.js.map +1 -1
  109. package/dist/studio/assets/{index-_ydd-jKd.css → index-C0UZ2BxT.css} +1 -1
  110. package/dist/studio/assets/index-Clm0MXDF.js +544 -0
  111. package/dist/studio/index.html +2 -2
  112. package/dist/studio/testid-inventory.json +251 -5
  113. package/endpoint-manifest.json +24 -1
  114. package/package.json +3 -3
  115. package/dist/studio/assets/index-CWUS9Ump.js +0 -542
@@ -0,0 +1,377 @@
1
+ /**
2
+ * `qe-author-tests` — the governed test-authoring workflow (wave 6: F-7R2-003/004/005/012/014/015,
3
+ * acceptance verdict R4-r2), workflows-as-data.
4
+ *
5
+ * ## Why a workflow, not a free-text run
6
+ *
7
+ * The studio's "New test" used to `POST /testing/recon` a plain free-text governed run: the planner
8
+ * split the brief at sentence boundaries into seven alternating agent units, two of them chat
9
+ * replies ("Gate protocol acknowledged … What is the next task?"), every gate a vacuous
10
+ * default-allow, no `skill_ref` anywhere (the 40-specialist QE domain never routed), no deliver
11
+ * phase (the LAST worker opened the PR from its own shell — invisible to the ledger), no campaign
12
+ * registered (the Test landing stayed empty), and the produced Playwright e2e was never executed by
13
+ * the run — it failed at its first check on the first independent run (R4-r2: 0 of 12 checks).
14
+ *
15
+ * This def is the fix, as data the engine validates AS AUTHORED (wicked-core#414 — a code-writing
16
+ * agent phase carries the evidence-floor pin, the verified-evidence phase carries it too):
17
+ *
18
+ * recon (agent, neutral, `wicked-garden-qe` plan) — read the repo + its tests, detect the
19
+ * harness, map intent → behaviours → gaps
20
+ * author (agent, CREATOR, `wicked-garden-qe` author, — write behaviour tests against the repo's
21
+ * executes_code, evidence-floor pinned) own harness + `tests/PLAN-<slug>.md`
22
+ * verify (TOOL, verified_evidence, pinned) — {@link qeVerifyScript}: run the repo's own
23
+ * checks INCLUDING every produced test;
24
+ * FAIL if any fails or was never executed
25
+ * review (agent, EVALUATOR, `wicked-garden-qe` review,— independent fitness verdict; escalates a
26
+ * pinned, human_confirm_if verdict_not_pass) not-pass to a human, never auto-denies
27
+ * deliver — NOT here. Appended per run by the ENGINE-side composition (`deliver: "pr"`, the
28
+ * crew#393 default for a repo-scoped code-work def): the engine lifts, re-verifies,
29
+ * pushes and opens the PR so the ledger records the delivery. A worker seat never
30
+ * pushes (the wave-6 engine's remote-write fence refuses `git push`/`gh pr create`).
31
+ *
32
+ * The METHOD lives in the garden skill (`wicked-garden-qe`, actions plan/author/review) — each
33
+ * phase's inline `instructions` is a one-line orientation only, because a governed worker's prompt
34
+ * rides a single PTY line capped at 1022 bytes (wicked-core execute_wrapped.rs) and the planner
35
+ * folds the instructions onto that line beside the run intent.
36
+ *
37
+ * Crew-authored drop-in (like `capture-learnings`): NOT core-seeded, so `launchRun`'s
38
+ * `_writeBuiltinOverlay` write is the only way the engine resolves the id. Not `is_system`: it IS an
39
+ * operator-selectable work mode (the catalog finally carries something labelled test/QE — F-075).
40
+ */
41
+ import { EVIDENCE_FLOOR_PIN } from '../core/deliver.js';
42
+ /** The workflow id — `GET /workflows` lists it; `POST /testing/author` launches it. */
43
+ export const QE_AUTHOR_TESTS_WORKFLOW = 'qe-author-tests';
44
+ /** The garden skill every agent phase routes through (`skills/qe/SKILL.md`, actions by name). */
45
+ export const QE_SKILL = 'wicked-garden-qe';
46
+ /** The deterministic verification phase's id (its unit is `<run>:verify` on the wire). */
47
+ export const QE_VERIFY_PHASE_ID = 'verify';
48
+ /** Per-produced-file marker the verify phase prints: `QE-VERIFY: file=… harness=… exit=… status=…`. */
49
+ export const QE_VERIFY_MARKER = 'QE-VERIFY:';
50
+ /** Repo-own-checks marker: `QE-VERIFY-CHECK: harness=… cmd="…" exit=…`. */
51
+ export const QE_VERIFY_CHECK_MARKER = 'QE-VERIFY-CHECK:';
52
+ /** The one summary line: `QE-VERIFY-SUMMARY: produced=N executed=N passed=N failed=N …`. */
53
+ export const QE_VERIFY_SUMMARY_MARKER = 'QE-VERIFY-SUMMARY:';
54
+ /** Where the author phase must record its plan (glob, repo-relative). */
55
+ export const QE_PLAN_GLOB = 'tests/PLAN-*.md';
56
+ /** Ceiling for each phase's inline orientation — the PTY line also carries the intent. */
57
+ export const QE_MAX_INLINE_INSTRUCTION_BYTES = 600;
58
+ const RECON_INSTRUCTIONS = 'Phase 1/4 PLAN (qe skill, plan action): read the repository and its existing tests, detect the ' +
59
+ 'test harness (vitest/jest/pytest/Playwright and any loopback e2e fixture), map the operator ' +
60
+ 'intent to behaviours, and list the coverage gaps — cite file:line for anything you claim is ' +
61
+ 'already covered. Analysis only: write no files.';
62
+ const AUTHOR_INSTRUCTIONS = 'Phase 2/4 AUTHOR (qe skill, author action): write BEHAVIOUR tests for the planned gaps against ' +
63
+ "the repository's own harness, plus tests/PLAN-<slug>.md recording each test, the exact command " +
64
+ 'that runs it and its observed result. RUN every test you write before claiming it. Never push, ' +
65
+ "never open or edit a pull request — the run's deliver phase does that.";
66
+ const REVIEW_INSTRUCTIONS = 'Phase 4/4 REVIEW (qe skill, review action): judge the produced tests independently — behaviour ' +
67
+ 'vs implementation coupling, every claim of existing coverage verified at file:line, no sleeps ' +
68
+ "or flaky waits, the PLAN matches what shipped, and the verify phase's QE-VERIFY report shows " +
69
+ 'every produced test executed. Verdict PASS or FAIL with reasons. Read-only: change nothing.';
70
+ /**
71
+ * The verify phase's script — the R4-r2 floor: a produced test that fails, or was never run, FAILS
72
+ * the unit. Runs in the run worktree as `bash -lc`:
73
+ *
74
+ * 1. BASE — the run branch's fork point off origin's default branch (the engine's own derivation
75
+ * order: origin/HEAD, origin/main, origin/master; then a local main/master; else HEAD).
76
+ * 2. PRODUCED — every path changed/added since BASE plus every untracked file: the run's own work,
77
+ * committed or not. Test files are the conventional shapes (`*.test.*`, `*.spec.*`, `*_test.py`,
78
+ * `test_*.py`, `__tests__/`); fixtures, factories and the PLAN are produced but not "tests".
79
+ * 3. HARNESS per file — the REPOSITORY's own, resolved PER FILE by walking up from the file's
80
+ * directory to the nearest marker (monorepo-aware — review H-1/M-1 of #536): Playwright
81
+ * (`playwright.config.*` / `@playwright/test` in the nearest package.json) for `*.spec.*`, a
82
+ * Python Playwright rig (imports `playwright`), vitest/jest from the nearest package.json that
83
+ * declares them, pytest for every other test-shaped `.py` (nearest `pytest.ini` / `conftest.py`
84
+ * / `tox.ini` / `pyproject.toml [tool.pytest]` / `setup.cfg [tool:pytest]` dir, else the root).
85
+ * The RUNNER is the nearest `node_modules/.bin/<tool>` (or `.venv/bin/pytest` / an importable
86
+ * pytest) from that package dir up to the worktree root — NEVER `npx` (a registry fetch on the
87
+ * verify path) and NEVER a plain interpreter for a test-shaped file (a pytest module run as
88
+ * `python3 file.py` defines its functions and exits 0 with zero assertions — a vacuous PASS).
89
+ * A missing/unrunnable runner is NOT EXECUTED = fail, with "harness not available: <tool>" and
90
+ * the remedy (install the dependencies in the worktree in the author phase). The runner runs
91
+ * FROM its package dir with a package-relative path.
92
+ * 4. COLLECTED — every executed file must show ≥ 1 test in the RUNNER's own summary (pytest
93
+ * `N passed` / `collected N items`, vitest `Tests N passed`, jest `N total`, Playwright
94
+ * `N passed`, a Python rig's check/pass lines); exit 0 with 0 tests reported = NOT EXECUTED.
95
+ * 5. REPO CHECKS — the full unit suite of every (harness, package dir) a produced test used
96
+ * (vitest/jest/pytest) runs once too, so the produced tests are verified INSIDE the repository's
97
+ * own check, not beside it. The Playwright suite is not re-run whole (its produced specs ran
98
+ * individually).
99
+ * 6. VERDICT — `QE-VERIFY-SUMMARY:` then exit 0 only when: ≥ 1 produced test, every one executed
100
+ * with ≥ 1 test collected, none failed, the PLAN exists, and the repo checks passed. Every
101
+ * refusal names its reason.
102
+ *
103
+ * `set -u -o pipefail` but NOT `-e`: each command's exit is captured deliberately. `exec 2>&1`
104
+ * keeps the transcript chronological (the engine appends stderr after stdout otherwise).
105
+ */
106
+ export function qeVerifyScript() {
107
+ return [
108
+ 'set -uo pipefail',
109
+ 'exec 2>&1',
110
+ 'echo "QE-VERIFY-BEGIN worktree=$PWD"',
111
+ // 1. base
112
+ 'D=$(git symbolic-ref -q --short refs/remotes/origin/HEAD || true)',
113
+ 'if [ -z "$D" ] || ! git rev-parse --verify -q "$D^{commit}" >/dev/null; then',
114
+ ' if git rev-parse --verify -q origin/main^{commit} >/dev/null; then D=origin/main;',
115
+ ' elif git rev-parse --verify -q origin/master^{commit} >/dev/null; then D=origin/master;',
116
+ ' elif git rev-parse --verify -q main^{commit} >/dev/null; then D=main;',
117
+ ' elif git rev-parse --verify -q master^{commit} >/dev/null; then D=master;',
118
+ ' else D=""; fi',
119
+ 'fi',
120
+ 'if [ -n "$D" ]; then BASE=$(git merge-base "$D" HEAD 2>/dev/null || git rev-parse HEAD); else BASE=$(git rev-parse HEAD); fi',
121
+ 'echo "QE-VERIFY-BASE ref=${D:-HEAD} commit=$BASE"',
122
+ // 2. produced
123
+ 'PRODUCED=$( { git diff --name-only --diff-filter=AMR "$BASE" -- . ; git ls-files --others --exclude-standard ; } | sort -u )',
124
+ 'is_test_path() {',
125
+ ' case "$1" in *.md|*.json|*.snap|*.txt|*.yml|*.yaml|*.toml|*.lock|*.html|*.css|*.png|*.svg) return 1;; esac',
126
+ ' case "$1" in *.test.*|*.spec.*|*_test.py|test_*.py|*/test_*.py|__tests__/*|*/__tests__/*) return 0;; esac',
127
+ ' return 1',
128
+ '}',
129
+ // 3. harness detection — PER FILE, nearest marker dir walking UP from the file (monorepo-aware)
130
+ 'pkg_has_dep() { [ -f "$1/package.json" ] && grep -Eq "\\"$2\\"[[:space:]]*:" "$1/package.json"; }',
131
+ 'is_pw_dir() { ls "$1"/playwright.config.* >/dev/null 2>&1 || pkg_has_dep "$1" "@playwright/test"; }',
132
+ 'is_vitest_dir() { pkg_has_dep "$1" vitest; }',
133
+ 'is_jest_dir() { pkg_has_dep "$1" jest; }',
134
+ 'is_py_dir() { [ -f "$1/pytest.ini" ] || [ -f "$1/conftest.py" ] || [ -f "$1/tox.ini" ] || { [ -f "$1/pyproject.toml" ] && grep -q "tool.pytest" "$1/pyproject.toml"; } || { [ -f "$1/setup.cfg" ] && grep -q "tool:pytest" "$1/setup.cfg"; }; }',
135
+ 'nearest() { local d; d=$(dirname "$1"); while :; do if "$2" "$d"; then echo "$d"; return 0; fi; [ "$d" = "." ] && break; d=$(dirname "$d"); done; echo ""; return 1; }',
136
+ // the runner: the nearest node_modules/.bin/<tool> from the package dir up to the worktree root,
137
+ // spelled RELATIVE TO the package dir (every command runs from there: `./…` or `../…`) — NEVER npx
138
+ 'find_bin() { local d="$1" up=""; while :; do if [ -x "$d/node_modules/.bin/$2" ]; then echo "${up:-./}node_modules/.bin/$2"; return 0; fi; [ "$d" = "." ] && break; d=$(dirname "$d"); up="../$up"; done; echo ""; return 1; }',
139
+ 'find_pytest() { local d="$1" up=""; while :; do if [ -x "$d/.venv/bin/pytest" ]; then echo "${up:-./}.venv/bin/pytest"; return 0; fi; [ "$d" = "." ] && break; d=$(dirname "$d"); up="../$up"; done; if python3 -c "import pytest" >/dev/null 2>&1; then echo "python3 -m pytest"; return 0; fi; echo ""; return 1; }',
140
+ 'resolve() {',
141
+ ' local d',
142
+ ' case "$1" in',
143
+ ' *.py)',
144
+ ' if grep -Eq "^[[:space:]]*(from|import)[[:space:]]+playwright" "$1" 2>/dev/null; then echo "playwright-python $(dirname "$1")"; return; fi',
145
+ ' d=$(nearest "$1" is_py_dir); [ -n "$d" ] || d="."',
146
+ ' echo "pytest $d"; return;;',
147
+ ' esac',
148
+ ' case "$1" in *.spec.*) d=$(nearest "$1" is_pw_dir); if [ -n "$d" ]; then echo "playwright $d"; return; fi;; esac',
149
+ ' d=$(nearest "$1" is_vitest_dir); if [ -n "$d" ]; then echo "vitest $d"; return; fi',
150
+ ' d=$(nearest "$1" is_jest_dir); if [ -n "$d" ]; then echo "jest $d"; return; fi',
151
+ ' d=$(nearest "$1" is_pw_dir); if [ -n "$d" ]; then echo "playwright $d"; return; fi',
152
+ ' echo "unknown ."',
153
+ '}',
154
+ // how many tests the RUNNER says it executed (its own summary line) — 0 = nothing ran
155
+ 'sum_counts() { grep -E "$2" "$1" | tail -1 | awk \'{s=0; for(i=1;i<=NF;i++) if($i ~ /^[0-9]+$/ && $(i+1) ~ /^(passed|failed|flaky|skipped|total)/) s+=$i; print s}\'; }',
156
+ 'collected() {',
157
+ ' local n=""',
158
+ ' case "$1" in',
159
+ ' pytest) n=$(sum_counts "$2" "[0-9]+ (passed|failed)"); [ "${n:-0}" -gt 0 ] || n=$(grep -Eo "collected [0-9]+ item" "$2" | tail -1 | grep -Eo "[0-9]+");;',
160
+ ' vitest) n=$(sum_counts "$2" "^[[:space:]]*Tests[[:space:]]");;',
161
+ ' jest) n=$(grep -Eo "[0-9]+ total" "$2" | tail -1 | grep -Eo "[0-9]+");;',
162
+ ' playwright) n=$(sum_counts "$2" "[0-9]+ (passed|failed|flaky)");;',
163
+ ' playwright-python) n=$(grep -Eci "passed|\\bpass\\b|✓|\\"checks\\"|\\bok\\b" "$2");;',
164
+ ' esac',
165
+ ' echo "${n:-0}"',
166
+ '}',
167
+ 'P=0; E=0; PASS=0; FAIL=0; NX=0; PLAN=""; HARNESSES=""',
168
+ 'not_executed() { NX=$((NX+1)); echo "QE-VERIFY: file=$1 harness=$2 pkg=$3 cmd=\\"\\" exit=- tests=0 status=not-executed reason=\\"$4\\""; echo "qe-verify: $1 — $4"; }',
169
+ 'while IFS= read -r f; do',
170
+ ' [ -n "$f" ] || continue',
171
+ ' case "$f" in tests/PLAN-*.md|*/tests/PLAN-*.md) PLAN="$f";; esac',
172
+ ' is_test_path "$f" || continue',
173
+ ' P=$((P+1))',
174
+ ' R=$(resolve "$f"); H="${R%% *}"; PKG="${R#* }"',
175
+ ' rel="$f"; [ "$PKG" = "." ] || rel="${f#$PKG/}"',
176
+ ' case "$H" in',
177
+ ' playwright) BIN=$(find_bin "$PKG" playwright) || { not_executed "$f" "$H" "$PKG" "harness not available: playwright (@playwright/test) — no node_modules/.bin/playwright from $PKG up to the worktree root; install the repository dependencies in the worktree (npm ci) in the author phase. The verify path never runs npx"; continue; }; CMD=("$BIN" test "$rel");;',
178
+ ' vitest) BIN=$(find_bin "$PKG" vitest) || { not_executed "$f" "$H" "$PKG" "harness not available: vitest — no node_modules/.bin/vitest from $PKG up to the worktree root; install the repository dependencies in the worktree (npm ci) in the author phase. The verify path never runs npx"; continue; }; CMD=("$BIN" run "$rel");;',
179
+ ' jest) BIN=$(find_bin "$PKG" jest) || { not_executed "$f" "$H" "$PKG" "harness not available: jest — no node_modules/.bin/jest from $PKG up to the worktree root; install the repository dependencies in the worktree (npm ci) in the author phase. The verify path never runs npx"; continue; }; CMD=("$BIN" "$rel");;',
180
+ ' pytest) PT=$(find_pytest "$PKG") || { not_executed "$f" "$H" "$PKG" "harness not available: pytest — no .venv/bin/pytest from $PKG up to the worktree root and python3 cannot import pytest; a test-shaped .py file is NEVER run as a plain script (it would pass with zero assertions). Install pytest in the worktree in the author phase"; continue; }; if [ "$PT" = "python3 -m pytest" ]; then CMD=(python3 -m pytest -q "$rel"); else CMD=("$PT" -q "$rel"); fi;;',
181
+ ' playwright-python) python3 -c "import playwright" >/dev/null 2>&1 || { not_executed "$f" "$H" "$PKG" "harness not available: playwright (python) — python3 cannot import playwright; pip install playwright && playwright install chromium in the author phase"; continue; }; CMD=(python3 "$rel");;',
182
+ ' *) not_executed "$f" unknown "$PKG" "no harness recognised — declare one the repository owns (playwright.config.*, vitest/jest in the nearest package.json, a pytest marker)"; continue;;',
183
+ ' esac',
184
+ ' case " $HARNESSES " in *" $H|$PKG "*) ;; *) HARNESSES="$HARNESSES $H|$PKG";; esac',
185
+ ' echo "QE-VERIFY-RUN file=$f harness=$H pkg=$PKG cmd=\\"${CMD[*]}\\""',
186
+ ' OUT=$(mktemp)',
187
+ ' ( cd "$PKG" && "${CMD[@]}" ) 2>&1 | tee "$OUT"; RC=${PIPESTATUS[0]}',
188
+ ' N=$(collected "$H" "$OUT"); rm -f "$OUT"',
189
+ ' E=$((E+1))',
190
+ ' if [ "$RC" -ne 0 ]; then FAIL=$((FAIL+1)); ST=failed;',
191
+ ' elif [ "${N:-0}" -lt 1 ]; then NX=$((NX+1)); E=$((E-1)); ST=not-executed; echo "qe-verify: $f — the $H runner exited 0 but reported 0 tests (nothing was collected or executed)";',
192
+ ' else PASS=$((PASS+1)); ST=passed; fi',
193
+ ' echo "QE-VERIFY: file=$f harness=$H pkg=$PKG cmd=\\"${CMD[*]}\\" exit=$RC tests=${N:-0} status=$ST"',
194
+ "done < <(printf '%s\\n' \"$PRODUCED\")",
195
+ // 4. repo checks — the full unit suite once per (harness, package dir) the produced tests used
196
+ 'C=0; CF=0',
197
+ 'for HP in $HARNESSES; do',
198
+ ' H="${HP%%|*}"; PKG="${HP#*|}"',
199
+ ' case "$H" in',
200
+ ' vitest) BIN=$(find_bin "$PKG" vitest); CMD=("$BIN" run);;',
201
+ ' jest) BIN=$(find_bin "$PKG" jest); CMD=("$BIN");;',
202
+ ' pytest) PT=$(find_pytest "$PKG"); if [ "$PT" = "python3 -m pytest" ]; then CMD=(python3 -m pytest -q); else CMD=("$PT" -q); fi;;',
203
+ ' *) echo "QE-VERIFY-CHECK: harness=$H pkg=$PKG skipped=full-suite (produced files ran individually)"; continue;;',
204
+ ' esac',
205
+ ' C=$((C+1))',
206
+ ' echo "QE-VERIFY-CHECK-RUN harness=$H pkg=$PKG cmd=\\"${CMD[*]}\\""',
207
+ ' ( cd "$PKG" && "${CMD[@]}" ); RC=$?',
208
+ ' [ "$RC" -eq 0 ] || CF=$((CF+1))',
209
+ ' echo "QE-VERIFY-CHECK: harness=$H pkg=$PKG cmd=\\"${CMD[*]}\\" exit=$RC"',
210
+ 'done',
211
+ // 5. verdict
212
+ 'echo "QE-VERIFY-SUMMARY: produced=$P executed=$E passed=$PASS failed=$FAIL not_executed=$NX plan=${PLAN:-missing} checks=$C checks_failed=$CF"',
213
+ '[ "$P" -gt 0 ] || { echo "qe-verify: FAIL — the author phase produced no test files; nothing was verified, so done cannot be asserted"; exit 1; }',
214
+ '[ -n "$PLAN" ] || { echo "qe-verify: FAIL — no tests/PLAN-*.md was produced (the plan records every test, the command that ran it and its result)"; exit 1; }',
215
+ '[ "$NX" -eq 0 ] || { echo "qe-verify: FAIL — $NX produced test file(s) were never executed (no available harness, or the runner reported 0 tests); a test that never ran proves nothing"; exit 1; }',
216
+ '[ "$FAIL" -eq 0 ] || { echo "qe-verify: FAIL — $FAIL of $E produced test file(s) failed under the repository harness"; exit 1; }',
217
+ '[ "$CF" -eq 0 ] || { echo "qe-verify: FAIL — $CF repository check(s) failed with the produced tests in the tree"; exit 1; }',
218
+ 'echo "qe-verify: PASS — $E produced test file(s) executed and passed under the repository harness; plan $PLAN"',
219
+ ].join('\n');
220
+ }
221
+ function agentPhase(id, kind, instructions, over) {
222
+ return {
223
+ id,
224
+ kind,
225
+ instructions,
226
+ gate_type: 'execution',
227
+ gate: 'auto',
228
+ executes_code: false,
229
+ verified_evidence: false,
230
+ required_deliverables: [],
231
+ depends_on: [],
232
+ role: 'neutral',
233
+ skill_ref: QE_SKILL,
234
+ allowed_skills: [],
235
+ validator_pin: null,
236
+ ...over,
237
+ };
238
+ }
239
+ /** The verify TOOL phase — deterministic, pinned to the evidence floor (the run left a change),
240
+ * `executes_code: false` like the deliver phase: engine-owned tooling, not governed agent work
241
+ * (a Tool unit is never worktree-guarded, so the harness's caches cannot deny it). */
242
+ export function qeVerifyPhase(dependsOn = ['author']) {
243
+ return {
244
+ id: QE_VERIFY_PHASE_ID,
245
+ kind: 'test',
246
+ executor: { type: 'tool', cmd: ['bash', '-lc', qeVerifyScript()] },
247
+ gate_type: 'execution',
248
+ gate: 'auto',
249
+ executes_code: false,
250
+ verified_evidence: true,
251
+ required_deliverables: [],
252
+ depends_on: dependsOn,
253
+ role: 'neutral',
254
+ skill_ref: null,
255
+ allowed_skills: [],
256
+ validator_pin: EVIDENCE_FLOOR_PIN,
257
+ };
258
+ }
259
+ /** The shipped definition (see the module doc). Exported as data — tests assert it, the adapter
260
+ * serves it from `BUILTIN_WORKFLOWS`, and the engine validates it as authored at the overlay write. */
261
+ export const QE_AUTHOR_TESTS_WORKFLOW_DEF = {
262
+ id: QE_AUTHOR_TESTS_WORKFLOW,
263
+ phases: [
264
+ agentPhase('recon', 'recon', RECON_INSTRUCTIONS, { gate_type: 'strategy', role: 'neutral' }),
265
+ agentPhase('author', 'build', AUTHOR_INSTRUCTIONS, {
266
+ depends_on: ['recon'],
267
+ role: 'creator',
268
+ executes_code: true,
269
+ validator_pin: EVIDENCE_FLOOR_PIN,
270
+ }),
271
+ qeVerifyPhase(['author']),
272
+ agentPhase('review', 'review', REVIEW_INSTRUCTIONS, {
273
+ depends_on: [QE_VERIFY_PHASE_ID],
274
+ role: 'evaluator',
275
+ gate: { human_confirm_if: 'verdict_not_pass' },
276
+ validator_pin: EVIDENCE_FLOOR_PIN,
277
+ }),
278
+ ],
279
+ };
280
+ /** `key=value` and `key="quoted value"` pairs of one marker line. */
281
+ function fields(rest) {
282
+ const out = {};
283
+ const re = /([a-z_]+)=("([^"]*)"|\S*)/g;
284
+ for (const m of rest.matchAll(re))
285
+ out[m[1]] = m[3] !== undefined ? m[3] : (m[2] ?? '');
286
+ return out;
287
+ }
288
+ function int(v) {
289
+ const n = Number(v);
290
+ return Number.isInteger(n) && n >= 0 ? n : 0;
291
+ }
292
+ /**
293
+ * Parse the verify unit's transcript into a {@link QeVerifyReport}. `null` when the transcript
294
+ * carries no `QE-VERIFY-SUMMARY:` line (the phase never reached its verdict — the unit's status
295
+ * and `denial_reason` say why).
296
+ */
297
+ export function parseQeVerifyOutput(text) {
298
+ const files = [];
299
+ const checks = [];
300
+ let summary = null;
301
+ for (const raw of text.split('\n')) {
302
+ const line = raw.trim();
303
+ if (line.startsWith(QE_VERIFY_SUMMARY_MARKER)) {
304
+ summary = fields(line.slice(QE_VERIFY_SUMMARY_MARKER.length));
305
+ }
306
+ else if (line.startsWith(QE_VERIFY_CHECK_MARKER)) {
307
+ const f = fields(line.slice(QE_VERIFY_CHECK_MARKER.length));
308
+ if (f['skipped'] !== undefined || f['harness'] === undefined || f['exit'] === undefined)
309
+ continue;
310
+ checks.push({ harness: f['harness'], cmd: f['cmd'] ?? '', exit: int(f['exit']) });
311
+ }
312
+ else if (line.startsWith(QE_VERIFY_MARKER)) {
313
+ const f = fields(line.slice(QE_VERIFY_MARKER.length));
314
+ if (f['file'] === undefined)
315
+ continue;
316
+ const status = f['status'] === 'passed' || f['status'] === 'failed' ? f['status'] : 'not-executed';
317
+ // A file the runner ran but reported 0 tests for carries its exit code; a file never handed
318
+ // to a runner has `exit=-` (→ null).
319
+ const exit = f['exit'] === undefined || f['exit'] === '-' ? null : int(f['exit']);
320
+ files.push({
321
+ path: f['file'],
322
+ harness: f['harness'] ?? 'unknown',
323
+ cmd: f['cmd'] ?? '',
324
+ exit,
325
+ tests: int(f['tests']),
326
+ pkg: f['pkg'] === undefined || f['pkg'] === '' ? '.' : f['pkg'],
327
+ status,
328
+ ...(f['reason'] !== undefined && f['reason'] !== '' ? { reason: f['reason'] } : {}),
329
+ });
330
+ }
331
+ }
332
+ if (summary === null)
333
+ return null;
334
+ return {
335
+ files,
336
+ checks,
337
+ produced: int(summary['produced']),
338
+ executed: int(summary['executed']),
339
+ passed: int(summary['passed']),
340
+ failed: int(summary['failed']),
341
+ notExecuted: int(summary['not_executed']),
342
+ plan: summary['plan'] === undefined || summary['plan'] === 'missing' ? null : summary['plan'],
343
+ checksFailed: int(summary['checks_failed']),
344
+ };
345
+ }
346
+ function gateLabel(gate) {
347
+ if (typeof gate === 'object' && gate !== null && 'human_confirm' in gate)
348
+ return 'human';
349
+ if (typeof gate === 'object' && gate !== null && 'human_confirm_if' in gate)
350
+ return 'human_if_not_pass';
351
+ return 'auto';
352
+ }
353
+ export function qeAuthorPlan(def, seats, deliver) {
354
+ const phases = def.phases.map((p) => ({
355
+ id: p.id,
356
+ kind: p.kind,
357
+ role: p.role,
358
+ executor: p.executor?.type === 'tool' ? 'tool' : 'agent',
359
+ skillRef: p.skill_ref,
360
+ gate: gateLabel(p.gate),
361
+ executesCode: p.executes_code,
362
+ }));
363
+ if (deliver) {
364
+ phases.push({
365
+ id: 'deliver',
366
+ kind: 'build',
367
+ role: 'neutral',
368
+ executor: 'tool',
369
+ skillRef: null,
370
+ gate: 'auto',
371
+ executesCode: false,
372
+ engine: true,
373
+ });
374
+ }
375
+ return { workflow: def.id, phases, seats };
376
+ }
377
+ //# sourceMappingURL=author-workflow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"author-workflow.js","sourceRoot":"","sources":["../../src/qe/author-workflow.ts"],"names":[],"mappings":"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"}
@@ -6,15 +6,31 @@
6
6
  * this daemon owns: a DomainStore rooted at `<repo>/.wicked-qe/` (legacy
7
7
  * `<repo>/.wicked-testing/` — see qeLedgerRoot) holding `runs` and
8
8
  * `verdicts`, plus the public evidence manifest at
9
- * `<root>/evidence/<run-id>/manifest.json`. This module reads that state
10
- * through the wicked-ledger package API (public entry, no deep imports) and
11
- * never writes a domain record — the QE pipeline is the only writer.
9
+ * `<root>/evidence/<run-id>/manifest.json`.
12
10
  *
13
- * ONE deliberate mutation is allowed: `rebuildIndex()`, which regenerates the
14
- * store's DERIVED SQLite index from the canonical JSON files when the two
15
- * disagree (see `readAcceptanceState`). Canonical JSON is authoritative by the
16
- * ledger's own schema contract; the index is a cache, and rebuilding a cache
17
- * is not a write to the ledger's record of what happened.
11
+ * READ-ONLY means read-only (F-E2E-013). This module reads the ledger's
12
+ * CANONICAL JSON files directly and never opens a `DomainStore`: opening one
13
+ * is a write — `new DomainStore(root)` mkdirs the root, reaps `.tmp.*` files,
14
+ * creates `wicked-qe.db` (+ WAL/SHM) inside the checkout, runs migrations and
15
+ * a stale-run UPDATE sweep — and the index-healing `rebuildIndex()` this
16
+ * module used to call on top of that bulk-inserted every canonical row into
17
+ * the fresh index, which is where a legacy ledger's scenarios without
18
+ * `format_version` failed ("SQLite write failed … NOT NULL constraint"). A
19
+ * GET must leave the repository byte-identical. Canonical JSON is
20
+ * authoritative by the ledger's own schema contract (the SQLite index is a
21
+ * derived cache), so reading it directly loses nothing — and a record that
22
+ * cannot be parsed is surfaced as a read failure (`error`), never skipped
23
+ * into a cleaner answer.
24
+ *
25
+ * ATTRIBUTION (F-E2E-013): the gate answers about ONE crew run, so a verdict
26
+ * is served only when it can be tied to that run — see {@link ReadSubject}.
27
+ * "The newest PASS in the store" is not evidence about a run that recorded
28
+ * none (the regression: an onboarding run that failed at plan time was shown a
29
+ * two-month-old PASS from the repo's committed legacy ledger). Among the QE
30
+ * runs that ARE this run's, deny-dominates holds ACROSS them: each QE run's
31
+ * newest verdict is its current judgment, and one non-PASS among those denies
32
+ * (review of #539, F2 — a later PASS on scenario Y must not mask a FAIL on
33
+ * scenario X inside the same crew run).
18
34
  */
19
35
  import type { EvidenceManifest, RunRecord, VerdictRecord } from 'wicked-ledger';
20
36
  /**
@@ -28,6 +44,15 @@ import type { EvidenceManifest, RunRecord, VerdictRecord } from 'wicked-ledger';
28
44
  */
29
45
  export declare const DEFAULT_QE_LEDGER_DIRNAME = ".wicked-qe";
30
46
  export declare const LEGACY_QE_LEDGER_DIRNAME = ".wicked-testing";
47
+ /**
48
+ * The field a QE writer running INSIDE a governed run stamps on the ledger run
49
+ * (and/or verdict) it records: the crew run id the engine hands every worker as
50
+ * `WICKED_RUN_ID`. Canonical JSON preserves any field a writer supplies, so this
51
+ * needs no ledger schema change; a record carrying it is attributed to that run
52
+ * regardless of timing. (garden's QE runner does not stamp it yet — a wave-7
53
+ * follow-up; until then lifetime linkage below is the inferred fallback.)
54
+ */
55
+ export declare const CREW_RUN_ID_FIELD = "crew_run_id";
31
56
  /** The configured ledger dir name: `WICKED_QE_LEDGER_DIR` env override, else the default. */
32
57
  export declare function qeLedgerDirName(): string;
33
58
  /**
@@ -41,48 +66,123 @@ export declare function qeLedgerDirName(): string;
41
66
  */
42
67
  export declare function qeLedgerRoot(repoRoot: string): string;
43
68
  /**
44
- * The acceptance-relevant slice of a repo's ledger.
69
+ * The crew run a ledger read is FOR — what a verdict has to be tied to before
70
+ * it is served as that run's acceptance evidence.
71
+ */
72
+ export interface RunLinkage {
73
+ /** The crew run id (what a QE writer inside the run sees as `WICKED_RUN_ID`). */
74
+ runId: string;
75
+ /**
76
+ * Epoch millis the crew run started — its `sessionStarted` capture time from
77
+ * the durable event log. `null` when the log records no start (an unknown
78
+ * window links nothing: evidence that cannot be placed inside the run is not
79
+ * the run's evidence).
80
+ */
81
+ startedAt: number | null;
82
+ /**
83
+ * Epoch millis the run reached a terminal state — the FIRST terminal frame in
84
+ * its log (`sessionCompleted` / `sessionFailed` / `runCancelled`); `null`
85
+ * while it is live (window open).
86
+ */
87
+ finishedAt: number | null;
88
+ /**
89
+ * Set when the run's event log could not be read at all (the read binding
90
+ * threw) — a different fact from "the log records no start", with a
91
+ * different remedy, so the denial names it (review of #539, F5).
92
+ */
93
+ logUnreadable?: string;
94
+ }
95
+ /**
96
+ * What a read is scoped to: an explicit QE run (`?qeRun=<id>` — the caller
97
+ * asserts the linkage), or the crew run whose evidence is wanted.
98
+ */
99
+ export type ReadSubject = {
100
+ qeRunId: string;
101
+ } | {
102
+ run: RunLinkage;
103
+ };
104
+ /**
105
+ * How the served verdict was tied to the crew run — or why none was. Always
106
+ * populated on a found ledger; the route serves it so the answer explains itself.
107
+ */
108
+ export type QeAttribution =
109
+ /** The caller pinned the QE run (`?qeRun=`); its newest verdict is served. */
110
+ {
111
+ kind: 'pinned';
112
+ qeRunId: string;
113
+ }
114
+ /** The ledger run (or one of its verdicts) carries `crew_run_id` naming this crew run. */
115
+ | {
116
+ kind: 'stamped';
117
+ qeRunId: string;
118
+ }
119
+ /** INFERRED: the QE run started inside this crew run's recorded lifetime (not stamped by the writer). */
120
+ | {
121
+ kind: 'run-window';
122
+ qeRunId: string;
123
+ qeRunStartedAt: string;
124
+ }
125
+ /** Nothing in the ledger is this run's evidence; `reason` says what the ledger does hold. */
126
+ | {
127
+ kind: 'none';
128
+ reason: string;
129
+ };
130
+ /**
131
+ * One line a human can read on `gate.reason`: HOW the served verdict was tied to
132
+ * the run, with the inferred kind labelled as such — an operator must be able to
133
+ * tell "the writer said so" from "it happened while the run was live".
134
+ */
135
+ export declare function describeAttribution(a: QeAttribution): string;
136
+ /**
137
+ * The acceptance-relevant slice of a repo's ledger, scoped to one subject.
45
138
  *
46
139
  * `found: false` means the repo has no ledger at all (the dir is absent) —
47
140
  * a distinct fact from "a ledger with no verdicts", and the gate treats both
48
141
  * as a deny with different reasons. `error` carries a read-layer failure
49
- * (store threw); it is never set on an ordinary empty ledger.
142
+ * (unreadable dir, a record that is not valid JSON); it is never set on an
143
+ * ordinary empty ledger.
50
144
  */
51
145
  export interface QeAcceptanceState {
52
146
  /** Absolute ledger root that was read (or probed). */
53
147
  root: string;
54
148
  /** Whether the ledger root exists on disk. */
55
149
  found: boolean;
56
- /** The QE run the verdict belongs to, when resolvable. */
150
+ /** The QE run the served verdict belongs to, when resolvable. */
57
151
  run: RunRecord | null;
58
- /** The governing verdict row — newest first, the ledger's system of record. */
152
+ /**
153
+ * The governing verdict for the subject: among the attributed QE runs' newest
154
+ * verdicts, the newest non-PASS if there is one (deny-dominates), else the
155
+ * newest PASS.
156
+ */
59
157
  verdict: VerdictRecord | null;
60
158
  /** The public evidence manifest for that run, when present and parseable. */
61
159
  manifest: EvidenceManifest | null;
62
160
  /** Absolute path the manifest was read from (null when absent). */
63
161
  manifestPath: string | null;
64
- /** Read-layer failure detail (store open/read threw). */
162
+ /** How `verdict` was tied to the subject, or why nothing was. */
163
+ attribution: QeAttribution;
164
+ /** How many live verdicts the ledger holds in total — the repo-level picture the scoped read was carved from. */
165
+ ledgerVerdicts: number;
166
+ /**
167
+ * How many QE runs are attributed to the subject (each contributes its newest
168
+ * verdict to the deny-dominates set `verdict` was resolved over). 1 for a pin.
169
+ */
170
+ attributedVerdicts: number;
171
+ /** Read-layer failure detail (a record could not be read or parsed). */
65
172
  error?: string;
66
173
  }
67
174
  /**
68
- * Read the ledger's current acceptance state for a repo.
175
+ * Read the ledger's acceptance state for a repo, scoped to `subject`.
69
176
  *
70
- * The newest verdict row governs (`opts.qeRunId` narrows to one QE run's
71
- * newest verdict instead). The verdicts TABLE is the system of record — the
72
- * manifest is the public artifact built from it at run finalization, returned
73
- * here as display evidence, never as a gate input: a run can legitimately be
74
- * re-reviewed after its manifest was built, and the newer verdict row wins.
177
+ * The verdicts TABLE is the system of record — the manifest is the public
178
+ * artifact built from it at run finalization, returned here as display
179
+ * evidence, never as a gate input: a run can legitimately be re-reviewed
180
+ * after its manifest was built, and the newer verdict row wins.
75
181
  *
76
- * Index-drift healing: the store is dual-write (canonical JSON + a derived
77
- * SQLite index) and a writer that degraded to JSON-only leaves the index
78
- * behind. When the index answers "no verdicts" but canonical verdict files
79
- * exist, the index is provably stale, so it is rebuilt from JSON once and
80
- * re-read — otherwise a ledger full of evidence would read as "missing
81
- * evidence ⇒ deny", a wrong answer in the honest direction but still wrong.
182
+ * Reads canonical JSON only (see the module header): nothing under the ledger
183
+ * root is created, modified or removed by this call.
82
184
  */
83
- export declare function readAcceptanceState(repoRoot: string, opts?: {
84
- qeRunId?: string;
85
- }): Promise<QeAcceptanceState>;
185
+ export declare function readAcceptanceState(repoRoot: string, subject: ReadSubject): Promise<QeAcceptanceState>;
86
186
  /** The summary of a manifest the acceptance route serves (never the full artifact list). */
87
187
  export interface QeManifestSummary {
88
188
  manifestVersion: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/qe/ledger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEhF;;;;;;;;GAQG;AACH,eAAO,MAAM,yBAAyB,eAAe,CAAC;AACtD,eAAO,MAAM,wBAAwB,oBAAoB,CAAC;AAE1D,6FAA6F;AAC7F,wBAAgB,eAAe,IAAI,MAAM,CAGxC;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQrD;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,KAAK,EAAE,OAAO,CAAC;IACf,0DAA0D;IAC1D,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;IACtB,+EAA+E;IAC/E,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9B,6EAA6E;IAC7E,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAClC,mEAAmE;IACnE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAaD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1B,OAAO,CAAC,iBAAiB,CAAC,CAqC5B;AAgBD,4FAA4F;AAC5F,MAAM,WAAW,iBAAiB;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CACzE;AAED,sDAAsD;AACtD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,iBAAiB,CAa/E"}
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/qe/ledger.ts"],"names":[],"mappings":"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"}