@letterstory/cli 0.73.0 → 0.73.1
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/lib/cli.mjs +4 -3
- package/lib/commands/admin.mjs +85 -40
- package/package.json +1 -1
package/lib/cli.mjs
CHANGED
|
@@ -56,7 +56,7 @@ import {
|
|
|
56
56
|
} from "./commands.mjs";
|
|
57
57
|
|
|
58
58
|
// Keep in sync with cli/package.json.
|
|
59
|
-
export const VERSION = "0.73.
|
|
59
|
+
export const VERSION = "0.73.1";
|
|
60
60
|
|
|
61
61
|
// Flags that never take a value. Listing them explicitly means `deploy get --json <id>`
|
|
62
62
|
// can't accidentally swallow the id as --json's value.
|
|
@@ -639,8 +639,9 @@ Admin (STAFF key — --staff-key / LETTERSTORY_STAFF_API_KEY; acts before an org
|
|
|
639
639
|
candidate, compare, comparisons, picks, pick,
|
|
640
640
|
ledger, reading-list, finalists,
|
|
641
641
|
finalists-queue, pair, side-by-side,
|
|
642
|
-
|
|
643
|
-
|
|
642
|
+
checklist, checklist-add, checklist-set,
|
|
643
|
+
checklist-labels, feedback, feedback-add,
|
|
644
|
+
feedback-file, feedback-reject, promote,
|
|
644
645
|
sessions, session-new, propose, trial-add,
|
|
645
646
|
trial-run, trial-save, the Write input bank
|
|
646
647
|
(inputs, input, inputs-harvest, input-save),
|
package/lib/commands/admin.mjs
CHANGED
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
// admin tuning-context [--q "<question>"] (tuning:experiment)
|
|
9
9
|
// admin tuning-import <brief-results.json> [--experiment <id>] (tuning:experiment)
|
|
10
10
|
// admin tuning kernels|kernel|candidate|compare|comparisons|comparison|picks|pick|criteria|promote … (tuning:experiment)
|
|
11
|
-
// admin tuning
|
|
12
|
-
// (
|
|
11
|
+
// admin tuning checklist|checklist-add|checklist-set|checklist-labels|feedback|feedback-add|feedback-file|feedback-reject … (tuning:experiment)
|
|
12
|
+
// (feedback-add --fix "…" files the reader's rewrite; the diff is classified and a disposition suggested.
|
|
13
|
+
// The old names — evals, eval-add, eval-set, proposals, propose-eval, capture-eval, reject-eval — still work.)
|
|
13
14
|
// admin evals scoreboard|cases|runs|run|triage|guard|retire … (fleet:read / tuning:experiment)
|
|
14
15
|
// admin crm import|capture|signals|record|signal|accounts|account|score|slack-channels|link-slack|white-glove|set-white-glove|churn|record-churn|undo-churn|onboard|onboarding|retry-onboarding|onboard-plan|payment-link … (crm:read / crm:write)
|
|
15
16
|
// admin replays [--org <id>] [--target <id>] [--status …] [--limit n] (fleet:read)
|
|
@@ -449,15 +450,19 @@ async function cmdAdminTuningContext(ctx) {
|
|
|
449
450
|
* admin tuning criteria the Eye Test's criteria: id, label and the question each asks of a pair
|
|
450
451
|
* admin tuning loop where the loop stands: what waits at each station, and where to go next
|
|
451
452
|
* admin tuning ledger <comparison-id> the defect ledger: per arm, what the diagnosis read named, the floor and the ceiling, the candidate's next arm; then the reader edits per arm by lever, and the schema gaps
|
|
452
|
-
* admin tuning
|
|
453
|
-
* admin tuning
|
|
454
|
-
* admin tuning
|
|
455
|
-
* admin tuning
|
|
456
|
-
* admin tuning
|
|
457
|
-
* admin tuning
|
|
458
|
-
* admin tuning
|
|
459
|
-
* admin tuning
|
|
460
|
-
* admin tuning
|
|
453
|
+
* admin tuning checklist the checklist: every defect category with its status, the picks' agreement (n, agreement, suggestion) and its labels (+/−) (alias: evals)
|
|
454
|
+
* admin tuning checklist-add --id … --label … --how … --layer … --lever … --locality local|structural --axis … [--example "span"] add a category (starts unvetted) (alias: eval-add)
|
|
455
|
+
* admin tuning checklist-set <id> [--status confirmed|rejected|unvetted] [--example "span" [--fix "…"]] confirm / reject (a person's call), or add an example (alias: eval-set)
|
|
456
|
+
* admin tuning checklist-labels <id> the labelled set of one category: positives (examples + feedback filed into it), negatives (rejected feedback suggested for it), grid overrules
|
|
457
|
+
* admin tuning feedback [--status proposed|captured|rejected] the feedback: spans readers marked in a pair's text with their words, each with its disposition (alias: proposals)
|
|
458
|
+
* admin tuning feedback-add --span "…" --comment "…" [--fix "…"] [--comparison <id> --cell <key> --side control|candidate] [--kind narrow|wide] add feedback (no --side from a blind read); --fix is the reader's rewrite, classified and a disposition suggested (alias: propose-eval)
|
|
459
|
+
* admin tuning feedback-file <id> --as checklist --id … --label … --how … --layer … --lever … --locality local|structural --axis … file it on the checklist, the span (and its fix) as the category's example (appends when the id exists) (alias: capture-eval; --as category)
|
|
460
|
+
* admin tuning feedback-file <id> --as rule file it as a rule; the rule itself is written in the kernels rule lab
|
|
461
|
+
* admin tuning feedback-file <id> --as config [--note "…"] file it as a lever this candidate should pull
|
|
462
|
+
* admin tuning feedback-file <id> --as implementation [--note "…"] file it as a schema gap no setting can express, for the kernels repo (--as lever still works)
|
|
463
|
+
* admin tuning feedback-file <id> --as material [--note "…"] file it as the material's: the input never carried it
|
|
464
|
+
* admin tuning feedback-file <id> --as note keep it; no action
|
|
465
|
+
* admin tuning feedback-reject <id> feedback that was not worth keeping after all (alias: reject-eval)
|
|
461
466
|
* admin tuning reading-list <comparison-id> [--n 10] your next blind pairs as links, to read off the screen
|
|
462
467
|
* admin tuning finalists [--limit 10] the finalists feed: done, gates clean, no craft loss, not yet decided by the picks; ranked by the objective's order
|
|
463
468
|
* admin tuning finalists-queue [--n 10] your next blind pairs round-robin across the finalists (a key reads the queue of the person who owns it; recording a pick still needs a person)
|
|
@@ -507,8 +512,8 @@ async function cmdAdminTuningContext(ctx) {
|
|
|
507
512
|
* admin tuning screens | screen <id> list / one screen: per candidate, each measure against the Base, with its noise
|
|
508
513
|
* admin tuning screen-advance <id> queue what the caps allow now (the cron does it every minute)
|
|
509
514
|
*
|
|
510
|
-
* The bake-off: a screen's versions graded item by item against the checklist
|
|
511
|
-
*
|
|
515
|
+
* The bake-off: a screen's versions graded item by item against the checklist's confirmed and
|
|
516
|
+
* unvetted categories and the craft axes commitment / economy / movement, the winner
|
|
512
517
|
* per item tallied across the inputs, and a merge that composes the best of each into one candidate.
|
|
513
518
|
* Deterministic; a person overrides a grade and confirms the merge, which is a hypothesis until the
|
|
514
519
|
* eye test confirms it.
|
|
@@ -793,6 +798,7 @@ async function cmdAdminTuning(ctx) {
|
|
|
793
798
|
})
|
|
794
799
|
);
|
|
795
800
|
}
|
|
801
|
+
case "checklist":
|
|
796
802
|
case "evals": {
|
|
797
803
|
const r = await staffRequest(ctx, "GET", `${T}/evals`);
|
|
798
804
|
if (json) return printResult(io, flags, r);
|
|
@@ -800,16 +806,29 @@ async function cmdAdminTuning(ctx) {
|
|
|
800
806
|
if (!rows.length) return io.log("(no categories)");
|
|
801
807
|
const cell = (v, w) => String(v ?? "—").padEnd(w);
|
|
802
808
|
io.log(
|
|
803
|
-
`${cell("id", 24)} ${cell("status", 10)} ${cell("layer", 10)} ${cell("lever", 30)} ${"n".padStart(4)} ${"agree".padStart(6)} ${cell("suggest", 10)} ${"
|
|
809
|
+
`${cell("id", 24)} ${cell("status", 10)} ${cell("layer", 10)} ${cell("lever", 30)} ${"n".padStart(4)} ${"agree".padStart(6)} ${cell("suggest", 10)} ${"+".padStart(3)} ${"−".padStart(3)}`
|
|
804
810
|
);
|
|
805
811
|
for (const c of rows) {
|
|
806
812
|
const v = c.vetting ?? {};
|
|
813
|
+
const l = c.labels ?? { positives: (c.examples ?? []).length, negatives: 0 };
|
|
807
814
|
io.log(
|
|
808
|
-
`${cell(c.id, 24)} ${cell(c.status, 10)} ${cell(c.layer, 10)} ${cell(c.lever || "—", 30)} ${String(v.n ?? 0).padStart(4)} ${String(v.agreement ?? "—").padStart(6)} ${cell(v.suggestion, 10)} ${String(
|
|
815
|
+
`${cell(c.id, 24)} ${cell(c.status, 10)} ${cell(c.layer, 10)} ${cell(c.lever || "—", 30)} ${String(v.n ?? 0).padStart(4)} ${String(v.agreement ?? "—").padStart(6)} ${cell(v.suggestion, 10)} ${String(l.positives).padStart(3)} ${String(l.negatives).padStart(3)}`
|
|
809
816
|
);
|
|
810
817
|
}
|
|
811
818
|
return;
|
|
812
819
|
}
|
|
820
|
+
case "checklist-labels": {
|
|
821
|
+
if (!id) throw new CliError("Usage: admin tuning checklist-labels <category-id>");
|
|
822
|
+
const r = await staffRequest(ctx, "GET", `${T}/evals/${encodeURIComponent(id)}/labels`);
|
|
823
|
+
if (json) return printResult(io, flags, r);
|
|
824
|
+
io.log(
|
|
825
|
+
`${r.category_id}: ${r.counts.positives} positive, ${r.counts.negatives} negative (${r.grid_overrules} grid overrule${r.grid_overrules === 1 ? "" : "s"})`
|
|
826
|
+
);
|
|
827
|
+
for (const x of r.positives ?? []) io.log(` + [${x.source}] "${x.span}"${x.fix ? ` → "${x.fix}"` : ""}`);
|
|
828
|
+
for (const x of r.negatives ?? []) io.log(` − [${x.source}] "${x.span}"${x.fix ? ` → "${x.fix}"` : ""}`);
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
case "checklist-add":
|
|
813
832
|
case "eval-add": {
|
|
814
833
|
const body = {
|
|
815
834
|
id: flagStr(flags.id),
|
|
@@ -830,25 +849,26 @@ async function cmdAdminTuning(ctx) {
|
|
|
830
849
|
!["local", "structural"].includes(body.locality ?? "")
|
|
831
850
|
)
|
|
832
851
|
throw new CliError(
|
|
833
|
-
'Usage: admin tuning
|
|
852
|
+
'Usage: admin tuning checklist-add --id … --label … --how … --layer … --lever … --locality local|structural --axis … [--example "span"]'
|
|
834
853
|
);
|
|
835
854
|
if (example) body.examples = [{ span: example, ...(flags.fix ? { fix: flagStr(flags.fix) } : {}) }];
|
|
836
855
|
const r = await staffRequest(ctx, "POST", `${T}/evals`, body);
|
|
837
856
|
return json ? printResult(io, flags, r) : io.log(`added ${r.category.id} (${r.category.status})`);
|
|
838
857
|
}
|
|
858
|
+
case "checklist-set":
|
|
839
859
|
case "eval-set": {
|
|
840
860
|
const status = flagStr(flags.status);
|
|
841
861
|
const example = flagStr(flags.example);
|
|
842
862
|
if (!id || (!status && !example) || (status && !["confirmed", "rejected", "unvetted"].includes(status)))
|
|
843
863
|
throw new CliError(
|
|
844
|
-
'Usage: admin tuning
|
|
864
|
+
'Usage: admin tuning checklist-set <id> [--status confirmed|rejected|unvetted] [--example "span" [--fix "…"]]'
|
|
845
865
|
);
|
|
846
866
|
const patch = {};
|
|
847
867
|
if (status) patch.status = status;
|
|
848
868
|
if (example) {
|
|
849
869
|
// The PATCH replaces the whole list: read what is stored and append.
|
|
850
870
|
const current = (await staffRequest(ctx, "GET", `${T}/evals`)).categories?.find((c) => c.id === id);
|
|
851
|
-
if (!current) throw new CliError(`No category ${id}
|
|
871
|
+
if (!current) throw new CliError(`No category ${id} on the checklist`);
|
|
852
872
|
const fix = flagStr(flags.fix);
|
|
853
873
|
patch.examples = [...(current.examples ?? []), { span: example, ...(fix ? { fix } : {}) }];
|
|
854
874
|
}
|
|
@@ -857,27 +877,32 @@ async function cmdAdminTuning(ctx) {
|
|
|
857
877
|
? printResult(io, flags, r)
|
|
858
878
|
: io.log(`${r.category.id}: ${r.category.status}, ${(r.category.examples ?? []).length} examples`);
|
|
859
879
|
}
|
|
880
|
+
case "feedback":
|
|
860
881
|
case "proposals": {
|
|
861
882
|
const status = flagStr(flags.status);
|
|
862
883
|
if (status && !["proposed", "captured", "rejected"].includes(status))
|
|
863
|
-
throw new CliError("Usage: admin tuning
|
|
884
|
+
throw new CliError("Usage: admin tuning feedback [--status proposed|captured|rejected]");
|
|
864
885
|
const qs = status ? `?${new URLSearchParams({ status })}` : "";
|
|
865
886
|
const r = await staffRequest(ctx, "GET", `${T}/proposals${qs}`);
|
|
866
887
|
if (json) return printResult(io, flags, r);
|
|
867
888
|
const rows = r.proposals ?? [];
|
|
868
|
-
if (!rows.length) return io.log("(no
|
|
889
|
+
if (!rows.length) return io.log("(no feedback)");
|
|
869
890
|
for (const p of rows) {
|
|
870
891
|
const where = [p.cell_key, p.side].filter(Boolean).join(" / ") || "—";
|
|
892
|
+
const suggested = p.suggested_disposition
|
|
893
|
+
? `; suggested ${p.suggested_disposition}${p.suggested_category ? ` (${p.suggested_category})` : ""}`
|
|
894
|
+
: "";
|
|
871
895
|
const edit =
|
|
872
896
|
p.fix != null
|
|
873
|
-
? `\n → "${p.fix}" [${p.edit_class ?? "?"} → ${p.lever ?? "no lever: schema gap"}]`
|
|
897
|
+
? `\n → "${p.fix}" [${p.edit_class ?? "?"} → ${p.lever ?? "no lever: schema gap"}${suggested}]`
|
|
874
898
|
: "";
|
|
875
899
|
io.log(
|
|
876
|
-
`${p.id} ${p.status.padEnd(9)} ${p.kind.padEnd(
|
|
900
|
+
`${p.id} ${p.status.padEnd(9)} ${(p.disposition ?? p.kind).padEnd(14)} ${where}${p.category_id ? ` → ${p.category_id}` : ""}\n "${p.span}"${edit}${p.comment ? `\n ${p.comment}` : ""}`
|
|
877
901
|
);
|
|
878
902
|
}
|
|
879
903
|
return;
|
|
880
904
|
}
|
|
905
|
+
case "feedback-add":
|
|
881
906
|
case "propose-eval": {
|
|
882
907
|
const span = flagStr(flags.span);
|
|
883
908
|
const side = flagStr(flags.side);
|
|
@@ -888,7 +913,7 @@ async function cmdAdminTuning(ctx) {
|
|
|
888
913
|
(kind && !["narrow", "wide"].includes(kind))
|
|
889
914
|
)
|
|
890
915
|
throw new CliError(
|
|
891
|
-
'Usage: admin tuning
|
|
916
|
+
'Usage: admin tuning feedback-add --span "…" --comment "…" [--fix "…"] [--comparison <id> --cell <key> --side control|candidate] [--kind narrow|wide]'
|
|
892
917
|
);
|
|
893
918
|
const r = await staffRequest(
|
|
894
919
|
ctx,
|
|
@@ -906,17 +931,27 @@ async function cmdAdminTuning(ctx) {
|
|
|
906
931
|
);
|
|
907
932
|
if (json) return printResult(io, flags, r);
|
|
908
933
|
const p = r.proposal;
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
934
|
+
const suggested = p.suggested_disposition
|
|
935
|
+
? `; suggested ${p.suggested_disposition}${p.suggested_category ? ` (${p.suggested_category})` : ""}`
|
|
936
|
+
: "";
|
|
937
|
+
const edit =
|
|
938
|
+
p.fix != null ? ` — ${p.edit_class}: ${p.lever ?? "no lever maps: a schema gap"}${suggested}` : "";
|
|
939
|
+
return io.log(`added feedback ${p.id} (${p.disposition ?? p.kind})${edit}`);
|
|
940
|
+
}
|
|
941
|
+
case "feedback-file":
|
|
912
942
|
case "capture-eval": {
|
|
913
|
-
|
|
943
|
+
// The first names of checklist and implementation, still accepted.
|
|
944
|
+
const AS_ALIAS = { category: "checklist", lever: "implementation" };
|
|
945
|
+
const asGiven = flagStr(flags.as);
|
|
946
|
+
const as = AS_ALIAS[asGiven] ?? asGiven;
|
|
914
947
|
const usage =
|
|
915
|
-
'Usage: admin tuning
|
|
916
|
-
if (!id || !["
|
|
948
|
+
'Usage: admin tuning feedback-file <id> --as checklist --id … --label … --how … --layer … --lever … --locality local|structural --axis … | --as rule | --as config|implementation|material [--note "…"] | --as note';
|
|
949
|
+
if (!id || !["checklist", "rule", "config", "implementation", "material", "note"].includes(as ?? ""))
|
|
950
|
+
throw new CliError(usage);
|
|
917
951
|
let body = { as };
|
|
918
|
-
if (as === "
|
|
919
|
-
|
|
952
|
+
if (as === "config" || as === "implementation" || as === "material")
|
|
953
|
+
body = compact({ as, note: flagStr(flags.note) });
|
|
954
|
+
if (as === "checklist") {
|
|
920
955
|
const category = {
|
|
921
956
|
id: flagStr(flags.id),
|
|
922
957
|
label: flagStr(flags.label),
|
|
@@ -938,14 +973,22 @@ async function cmdAdminTuning(ctx) {
|
|
|
938
973
|
body = { as, category };
|
|
939
974
|
}
|
|
940
975
|
const r = await staffRequest(ctx, "POST", `${T}/proposals/${id}/capture`, body);
|
|
941
|
-
return
|
|
942
|
-
|
|
943
|
-
:
|
|
944
|
-
|
|
945
|
-
|
|
976
|
+
if (json) return printResult(io, flags, r);
|
|
977
|
+
const FILED = {
|
|
978
|
+
rule: "; write the rule in the kernels rule lab",
|
|
979
|
+
implementation: "; a schema gap, filed for the kernels repo",
|
|
980
|
+
config: "; a lever for this candidate",
|
|
981
|
+
material: "; the input never carried it",
|
|
982
|
+
note: "; kept, no action",
|
|
983
|
+
};
|
|
984
|
+
const d = r.proposal.disposition ?? as;
|
|
985
|
+
return io.log(
|
|
986
|
+
`filed ${r.proposal.id} as ${d}${r.category ? ` → ${r.category.id} (${(r.category.examples ?? []).length} examples)` : (FILED[d] ?? "")}`
|
|
987
|
+
);
|
|
946
988
|
}
|
|
989
|
+
case "feedback-reject":
|
|
947
990
|
case "reject-eval": {
|
|
948
|
-
if (!id) throw new CliError("Usage: admin tuning reject
|
|
991
|
+
if (!id) throw new CliError("Usage: admin tuning feedback-reject <id>");
|
|
949
992
|
const r = await staffRequest(ctx, "POST", `${T}/proposals/${id}/reject`, {});
|
|
950
993
|
return json ? printResult(io, flags, r) : io.log(`rejected ${r.proposal.id}`);
|
|
951
994
|
}
|
|
@@ -958,11 +1001,13 @@ async function cmdAdminTuning(ctx) {
|
|
|
958
1001
|
case "loop": {
|
|
959
1002
|
const r = await staffRequest(ctx, "GET", `${T}/loop`);
|
|
960
1003
|
if (json) return printResult(io, flags, r);
|
|
961
|
-
|
|
1004
|
+
const week = r.propose.candidates_week ?? "?";
|
|
1005
|
+
const progs = r.propose.programmes_running ?? "?";
|
|
1006
|
+
io.log(`propose ${r.propose.running} running, ${week} candidates this week, ${progs} programmes running`);
|
|
962
1007
|
io.log(`read ${r.read.settled_unread} settled, unread`);
|
|
963
1008
|
io.log(`eye test ${r.eye_test.finalists} finalists, ${r.eye_test.pairs_to_read} pairs to read`);
|
|
964
1009
|
io.log(
|
|
965
|
-
`
|
|
1010
|
+
`checklist ${r.evals.proposals_waiting} feedback waiting, ${r.evals.unvetted} unvetted, ${r.evals.suggested} with a suggestion${r.evals.reachable ? "" : " (the checklist could not be read)"}`
|
|
966
1011
|
);
|
|
967
1012
|
io.log(`promote ${r.promote.promotable} won their picks`);
|
|
968
1013
|
io.log(`\nnext: ${r.next}`);
|
|
@@ -2188,7 +2233,7 @@ async function cmdAdminTuning(ctx) {
|
|
|
2188
2233
|
}
|
|
2189
2234
|
default:
|
|
2190
2235
|
throw new CliError(
|
|
2191
|
-
"Usage: admin tuning kernels|kernel|candidate|compare|comparisons|comparison|picks|pick|loop|ledger|
|
|
2236
|
+
"Usage: admin tuning kernels|kernel|candidate|compare|comparisons|comparison|picks|pick|loop|ledger|checklist|checklist-add|checklist-set|checklist-labels|feedback|feedback-add|feedback-file|feedback-reject|reading-list|finalists|finalists-queue|pair|bank|bank-eval|bank-draft|bank-shape|bank-pool|bank-judge|bank-score|bank-try|people|invite|revoke|assignments|assign|unassign|tasks|side-by-side|candidates|base-kernels|base-versions|sessions|session|session-new|propose|trial-add|trial-run|trial-save|promote|arenas|arena|arena-candidate|arena-dials|comparison-extend|research-compare|research-inputs|research-split|research-standing|promote-arena|shred-compare|shred-inputs|shred-split|shred-standing|shredders|shredder|shredder-candidate|promote-shredder|enhance-compare|enhance-inputs|enhance-split|enhance-standing|enhancers|enhancer|enhancer-candidate|promote-enhancer (see --help)"
|
|
2192
2237
|
);
|
|
2193
2238
|
}
|
|
2194
2239
|
}
|