@remits/remits-cli 0.1.156 → 0.1.157
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/index.js +80 -7
- package/package.json +1 -1
- package/skills/remits-cli/SKILL.md +6 -6
- package/skills/remits-cli/references/ai-quality.md +118 -0
- package/skills/remits-cli/references/command-reference.md +17 -2
- package/skills/remits-cli/references/development-loop.md +4 -0
- package/skills/remits-cli/references/investigation.md +9 -1
package/index.js
CHANGED
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
- L5223 Component Summaries, Status, And Sync Gates
|
|
14
14
|
- L7547 Branches, Promotion, Commit, And Test Runs
|
|
15
15
|
- L9676 Tokens, Tools, Verification, And Config
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
16
|
+
- L11174 Service Dashboard And WebSocket Listener
|
|
17
|
+
- L13673 Agent And Ticket Workflows
|
|
18
|
+
- L17849 Help, Auto Update, And Command Dispatch
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
/*
|
|
@@ -112,7 +112,7 @@ let LOCAL_ACTOR_CONTEXT = null;
|
|
|
112
112
|
|
|
113
113
|
// Flags that are legitimately repeatable accumulate into an array instead of last-wins. Every other
|
|
114
114
|
// flag keeps last-wins so existing callers are unaffected.
|
|
115
|
-
const REPEATABLE_FLAGS = new Set(['expected-removed', 'expectedRemoved', 'names', 'tag', 'tags', 'key', 'keys', 'case-key']);
|
|
115
|
+
const REPEATABLE_FLAGS = new Set(['expected-removed', 'expectedRemoved', 'names', 'tag', 'tags', 'key', 'keys', 'case-key', 'score', 'scores', 'lower-is-better']);
|
|
116
116
|
|
|
117
117
|
// `-m "msg"` is how git spells a commit message, and agents type it from habit. Before this the parser took a
|
|
118
118
|
// single-dash token as the VALUE of whatever flag preceded it: `--json -m "x"` silently turned JSON mode
|
|
@@ -10343,6 +10343,19 @@ function corpusAiLabel(row = {}) {
|
|
|
10343
10343
|
return 'no AI';
|
|
10344
10344
|
}
|
|
10345
10345
|
|
|
10346
|
+
function corpusScoreSummary(scores = {}) {
|
|
10347
|
+
return Object.keys(scores).map((k) => k + ' ' + (scores[k] && scores[k].mean !== undefined ? scores[k].mean : scores[k]) +
|
|
10348
|
+
(scores[k] && scores[k].cases !== undefined ? '/' + scores[k].cases : '')).join(', ');
|
|
10349
|
+
}
|
|
10350
|
+
|
|
10351
|
+
function corpusScoreDeltas(scores = {}) {
|
|
10352
|
+
return Object.keys(scores).map((k) => {
|
|
10353
|
+
const s = scores[k] || {};
|
|
10354
|
+
const delta = s.delta !== undefined && s.delta !== null ? ' (' + (Number(s.delta) >= 0 ? '+' : '') + s.delta + ')' : '';
|
|
10355
|
+
return k + ' ' + (s.base !== undefined ? s.base : '?') + ' -> ' + (s.head !== undefined ? s.head : '?') + delta + (s.regressed ? ' REGRESSED' : '');
|
|
10356
|
+
}).join(', ');
|
|
10357
|
+
}
|
|
10358
|
+
|
|
10346
10359
|
function corpusLines(sub, data = {}) {
|
|
10347
10360
|
const lines = ['Corpus: ' + (data.corpus || '?') + ' (owner account ' + (data.ownerAccountId || '?') + ')'];
|
|
10348
10361
|
if (sub === 'cases') {
|
|
@@ -10358,6 +10371,12 @@ function corpusLines(sub, data = {}) {
|
|
|
10358
10371
|
(data.runs || []).forEach((r) => lines.push('- ' + r.taskId + ' ' + r.cases + ' case(s) ' + Object.keys(r.outcomes || {}).map((k) => k + ' ' + r.outcomes[k]).join(', ') +
|
|
10359
10372
|
' ' + corpusAiLabel(r) + ' ' + (r.dataMode || '?') + ' ' + (r.branchName || '?') + (r.workspace ? ' [ws:' + r.workspace + ']' : '') +
|
|
10360
10373
|
(r.laneContentHash ? ' lane ' + String(r.laneContentHash).slice(0, 12) : '') + ' ' + formatTime(r.recordedAt)));
|
|
10374
|
+
(data.runs || []).forEach((r) => {
|
|
10375
|
+
const extra = [];
|
|
10376
|
+
if (r.scores && Object.keys(r.scores).length) extra.push('scores ' + corpusScoreSummary(r.scores));
|
|
10377
|
+
if (r.review) extra.push('graded ' + r.review.graded + ', spot-checked ' + r.review.reviewed + (r.review.agreement !== undefined ? ' (agreement ' + r.review.agreement + ')' : ''));
|
|
10378
|
+
if (extra.length) lines.push(' ' + r.taskId + ': ' + extra.join(' '));
|
|
10379
|
+
});
|
|
10361
10380
|
if ((data.runs || []).some((r) => r.aiMode === 'mocked')) {
|
|
10362
10381
|
lines.push('Runs marked AI MOCKED replayed every AI turn from aiMock - they measure the harness, not the model.');
|
|
10363
10382
|
}
|
|
@@ -10366,10 +10385,12 @@ function corpusLines(sub, data = {}) {
|
|
|
10366
10385
|
lines.push('Base ' + cmp.baseTaskId + ' -> head ' + cmp.headTaskId + ': ' + Object.keys(cmp.counts || {}).map((k) => k + ' ' + cmp.counts[k]).join(', '));
|
|
10367
10386
|
lines.push('AI mode: base ' + (cmp.baseAiMode || '?') + ', head ' + (cmp.headAiMode || '?'));
|
|
10368
10387
|
if (cmp.aiModeChanged) lines.push('WARNING: ' + cmp.aiModeChanged);
|
|
10369
|
-
(cmp.
|
|
10388
|
+
if (cmp.scores && Object.keys(cmp.scores).length) lines.push('Scores (cases both runs measured): ' + corpusScoreDeltas(cmp.scores));
|
|
10389
|
+
(cmp.cases || []).filter((c) => c.direction !== 'same' || c.changedScores).forEach((c) => {
|
|
10370
10390
|
lines.push('- ' + c.caseKey + ' ' + c.direction + (c.base ? ' ' + c.base.outcome : '') + (c.head ? ' -> ' + c.head.outcome : '') +
|
|
10371
10391
|
(c.expectedChanged ? ' (EXPECTED ANSWER CHANGED between runs)' : ''));
|
|
10372
10392
|
(c.changedMetrics || []).slice(0, 8).forEach((m) => lines.push(' ' + m.metric + ': ' + JSON.stringify(m.base) + ' -> ' + JSON.stringify(m.head)));
|
|
10393
|
+
Object.keys(c.changedScores || {}).slice(0, 8).forEach((k) => lines.push(' score ' + k + ': ' + c.changedScores[k].base + ' -> ' + c.changedScores[k].head));
|
|
10373
10394
|
if (c.head && c.head.outcomeReason && c.head.outcome !== 'passed') lines.push(' reason: ' + String(c.head.outcomeReason).slice(0, 300));
|
|
10374
10395
|
});
|
|
10375
10396
|
} else if (sub === 'retire') {
|
|
@@ -10380,6 +10401,28 @@ function corpusLines(sub, data = {}) {
|
|
|
10380
10401
|
if ((data.cases || []).some((c) => c.status === 'retired' || c.status === 'restored')) {
|
|
10381
10402
|
lines.push('Retired cases stay readable in results and compare; they are just no longer handed to runs. `corpus cases --include-retired` lists them, `corpus retire --restore` puts them back.');
|
|
10382
10403
|
}
|
|
10404
|
+
} else if (sub === 'gate') {
|
|
10405
|
+
const gate = data.gate || {};
|
|
10406
|
+
lines.push('Gate ' + (gate.baseTaskId || '?') + ' -> ' + (gate.headTaskId || '?') + ': ' + (gate.passed ? 'PASSED' : 'DID NOT PASS') +
|
|
10407
|
+
(gate.tolerance !== undefined ? ' (tolerance ' + gate.tolerance + ')' : ''));
|
|
10408
|
+
if (gate.scores && Object.keys(gate.scores).length) lines.push('Scores: ' + corpusScoreDeltas(gate.scores));
|
|
10409
|
+
(gate.reasons || []).forEach((r) => lines.push('- ' + r));
|
|
10410
|
+
if (gate.expectedChanged && gate.expectedChanged.length) lines.push('Left out (expected answer changed between runs): ' + gate.expectedChanged.join(', '));
|
|
10411
|
+
(gate.changedCases || []).slice(0, 15).forEach((c) => lines.push(' ' + c.caseKey + ' ' + c.direction +
|
|
10412
|
+
Object.keys(c.changedScores || {}).map((k) => ' ' + k + ' ' + c.changedScores[k].base + '->' + c.changedScores[k].head).join('')));
|
|
10413
|
+
} else if (sub === 'review') {
|
|
10414
|
+
if (data.review) {
|
|
10415
|
+
lines.push('Recorded: ' + data.review.caseKey + ' in run ' + data.review.taskId + ': ' + data.review.verdict + (data.review.score !== undefined && data.review.score !== null ? ' (score ' + data.review.score + ')' : ''));
|
|
10416
|
+
} else {
|
|
10417
|
+
const sample = data.sample || [];
|
|
10418
|
+
lines.push('Spot-check sample: ' + sample.length + ' graded case(s), unreviewed first');
|
|
10419
|
+
sample.forEach((c) => {
|
|
10420
|
+
const g = c.grade || {};
|
|
10421
|
+
lines.push('- ' + c.caseKey + ' grade ' + (g.verdict || '?') + ' ' + (g.score !== undefined ? g.score : '') + (c.review ? ' reviewed: ' + c.review.verdict : ' NOT REVIEWED'));
|
|
10422
|
+
if (g.reasons) lines.push(' ' + String(g.reasons).slice(0, 300));
|
|
10423
|
+
});
|
|
10424
|
+
if (sample.length) lines.push('Record a check: remits-cli corpus review --corpus ' + (data.corpus || '<name>') + ' --task-id <taskId> --case <caseKey> --verdict agree|disagree [--score n] [--note "..."]');
|
|
10425
|
+
}
|
|
10383
10426
|
} else if (sub === 'consistency') {
|
|
10384
10427
|
const con = data.consistency || {};
|
|
10385
10428
|
lines.push('Case ' + con.caseKey + ': ' + con.runs + ' run(s), ' + (con.stable ? 'STABLE (identical metrics every run)' : (con.variants || []).length + ' distinct metric results') +
|
|
@@ -10390,7 +10433,7 @@ function corpusLines(sub, data = {}) {
|
|
|
10390
10433
|
return lines;
|
|
10391
10434
|
}
|
|
10392
10435
|
|
|
10393
|
-
// `corpus import|cases|results|runs|compare|consistency`: test corpora through the platform's own corpus(name).
|
|
10436
|
+
// `corpus import|cases|results|runs|compare|consistency|retire|gate|review`: test corpora through the platform's own corpus(name).
|
|
10394
10437
|
async function corpusCommand(flags, subcommand) {
|
|
10395
10438
|
const cwd = process.cwd();
|
|
10396
10439
|
ensureLocalState(cwd);
|
|
@@ -10510,8 +10553,36 @@ async function corpusCommand(flags, subcommand) {
|
|
|
10510
10553
|
if (!retireKeys.length) throw new Error('corpus retire requires --case <caseKey> (repeatable) or --keys a,b,c');
|
|
10511
10554
|
data = await post({ command: 'retire', corpus: corpusName, keys: retireKeys, restore: flagEnabled(flags.restore) });
|
|
10512
10555
|
if (data && data.success === false) process.exitCode = 1;
|
|
10556
|
+
} else if (sub === 'gate') {
|
|
10557
|
+
const scores = parseCorpusFilterValues(flags, ['score', 'scores']);
|
|
10558
|
+
const lowerIsBetter = parseCorpusFilterValues(flags, ['lower-is-better']);
|
|
10559
|
+
data = await post({ command: 'gate', corpus: corpusName, base: flags.base, head: flags.head, tolerance: flags.tolerance,
|
|
10560
|
+
scores: scores.length ? scores : undefined,
|
|
10561
|
+
lowerIsBetter: lowerIsBetter.length ? lowerIsBetter : undefined,
|
|
10562
|
+
allowModeChange: flagEnabled(flags['allow-mode-change']) });
|
|
10563
|
+
const gate = (data && data.gate) || {};
|
|
10564
|
+
if (!gate.passed) process.exitCode = 1;
|
|
10565
|
+
await appendVerificationPacket(api, cwd, session, accountId, flags, {
|
|
10566
|
+
type: 'corpus_gate',
|
|
10567
|
+
success: !!gate.passed,
|
|
10568
|
+
claim: 'Corpus ' + corpusName + ' gate ' + (gate.baseTaskId || '?') + ' -> ' + (gate.headTaskId || '?') + ': ' + (gate.passed ? 'passed' : 'did not pass'),
|
|
10569
|
+
world: Object.assign({}, (data && data.world) || {}, { host: normalizeBaseUrl(baseUrl), branchName, workspace, dataMode }),
|
|
10570
|
+
revision: collectVerificationSource(cwd, flags),
|
|
10571
|
+
evidenceCategories: ['corpus_gate'],
|
|
10572
|
+
corpusGate: { corpus: corpusName, baseTaskId: gate.baseTaskId, headTaskId: gate.headTaskId, passed: !!gate.passed, scores: gate.scores, reasons: gate.reasons }
|
|
10573
|
+
}, { quiet: jsonOutput });
|
|
10574
|
+
} else if (sub === 'review') {
|
|
10575
|
+
const taskId = flags['task-id'];
|
|
10576
|
+
if (!taskId) throw new Error('corpus review requires --task-id <taskId> (and --case <caseKey> --verdict agree|disagree to record a check)');
|
|
10577
|
+
if (flags.verdict) {
|
|
10578
|
+
const caseKey = flags.case || flags['case-key'];
|
|
10579
|
+
if (!caseKey) throw new Error('corpus review --verdict requires --case <caseKey>');
|
|
10580
|
+
data = await post({ command: 'review', corpus: corpusName, taskId, caseKey, verdict: flags.verdict, score: flags.score, note: flags.note });
|
|
10581
|
+
} else {
|
|
10582
|
+
data = await post({ command: 'review', corpus: corpusName, taskId, size: flags.sample || flags.size });
|
|
10583
|
+
}
|
|
10513
10584
|
} else {
|
|
10514
|
-
throw new Error('Unknown corpus subcommand: ' + sub + ' (use import, cases, results, runs, compare, consistency or
|
|
10585
|
+
throw new Error('Unknown corpus subcommand: ' + sub + ' (use import, cases, results, runs, compare, consistency, retire, gate or review)');
|
|
10515
10586
|
}
|
|
10516
10587
|
if (jsonOutput) {
|
|
10517
10588
|
console.log(JSON.stringify(data, null, 2));
|
|
@@ -18144,6 +18215,8 @@ function printCorpusHelp() {
|
|
|
18144
18215
|
console.log(' remits-cli corpus compare --corpus <name> --base <taskId> --head <taskId> [--json]');
|
|
18145
18216
|
console.log(' remits-cli corpus consistency --corpus <name> --case <caseKey> [--limit 20] [--json]');
|
|
18146
18217
|
console.log(' remits-cli corpus retire --corpus <name> --case <caseKey> [--case ...] [--restore] [--json]');
|
|
18218
|
+
console.log(' remits-cli corpus gate --corpus <name> [--base <taskId>] [--head <taskId>] [--tolerance 0.02] [--score answer] [--lower-is-better <name>] [--allow-mode-change] [--json]');
|
|
18219
|
+
console.log(' remits-cli corpus review --corpus <name> --task-id <taskId> [--sample 5] | --case <caseKey> --verdict agree|disagree [--score n] [--note "..."]');
|
|
18147
18220
|
console.log('');
|
|
18148
18221
|
console.log('A corpus is a named, durable set of cases an evaluation-style Test runs again and again (front stage:');
|
|
18149
18222
|
console.log('corpus(name)). Cases live in a platform table owned by one account and visible to the accounts beneath');
|
package/package.json
CHANGED
|
@@ -46,6 +46,7 @@ reference below, load it before you act, not after something surprises you.
|
|
|
46
46
|
| work from a non-trunk checkout, or promote a branch | `branch-variants.md` | what a variant is, which world your checkout resolves, `--as-account` vs `--variant-branch`, tombstones, the promotion loop and its phases, subscribe/retire |
|
|
47
47
|
| touch a support ticket | `support-tickets.md` | the ticket record and its verbs, routed vs claimed vs owned, autonomous workers, the repository edit lease, ask/answer/resume, delivery |
|
|
48
48
|
| register this session as an agent, or ask why a routed ticket never started | `agent-sessions.md` | `serve` vs `register`, what registration does, worker spawning per agent kind, routing order, the control center, multi-session auth |
|
|
49
|
+
| build or evaluate cited Q&A, review learnings, or investigate answer quality | `ai-quality.md` | front-stage guide routing, interaction/session pivots, baseline vs proposal measurements, human checks and explicit corpus gates |
|
|
49
50
|
| investigate live behavior | `investigation.md` | which tool reads which record, correlation keys, reading a record's `content`, HTTP audits, AI activity, node/`localMode`, the production support flows |
|
|
50
51
|
| call any `mcp_*` tool | `tool-reference.md` | every tool's parameters, semantics, and traps — read the tool's entry before building its input |
|
|
51
52
|
| run, describe, poll, or interrupt an Action from the CLI | `tool-reference.md` → `mcp_run_action` | Action execution through `remits-cli tool --name mcp_run_action`; there is no separate top-level `remits-cli action` / `run action` wrapper |
|
|
@@ -158,12 +159,11 @@ reference named after it.
|
|
|
158
159
|
(`component-resolution.md`, `command-reference.md`)
|
|
159
160
|
- **Host and data mode are two independent decisions.** `--base-url` picks the Remits host,
|
|
160
161
|
`--data-mode` picks the data segment on it. Neither implies the other. (`command-reference.md`)
|
|
161
|
-
- **
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
server, while `test run` still defaults to `--data-mode test`. (`command-reference.md`)
|
|
162
|
+
- **Use a reachable host that has the implementation you are verifying.** For component work against
|
|
163
|
+
existing capabilities, localhost being down need not block verification: pass the intended deployed
|
|
164
|
+
host with `--base-url`, preserving account, branch, workspace and data-mode facts. For unreleased
|
|
165
|
+
platform capabilities, a deployed old binary is not a substitute; use the updated local/test runtime
|
|
166
|
+
and follow that repository's startup/restart instructions. (`command-reference.md`, `ai-quality.md`)
|
|
167
167
|
- **`test run` ignores the stored session data lane.** It defaults to `test` even when `whoami` shows
|
|
168
168
|
the session parked on prod; production Test runs require explicit prod provenance (`--data-mode prod`)
|
|
169
169
|
or Test source declaring `dataMode 'prod'` / `[dataMode:'prod']`. Check returned `dataModeSource`
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Answer Quality And Reviewed Learning
|
|
2
|
+
|
|
3
|
+
Load this when building cited Q&A, investigating a wrong answer, changing retrieval or prompts,
|
|
4
|
+
or measuring a proposed learning before review. Component APIs belong in the account's front-stage
|
|
5
|
+
guides; this reference owns the CLI investigation and measurement workflow.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Find the right guide](#find-the-right-guide)
|
|
10
|
+
- [Investigate one answer](#investigate-one-answer)
|
|
11
|
+
- [Measure a change](#measure-a-change)
|
|
12
|
+
- [Review and gate](#review-and-gate)
|
|
13
|
+
- [Keep the regression](#keep-the-regression)
|
|
14
|
+
|
|
15
|
+
## Find the right guide
|
|
16
|
+
|
|
17
|
+
| Building or changing | Read in the account repository |
|
|
18
|
+
|---|---|
|
|
19
|
+
| Search Tool → citations → feedback/review page | `guides/features/ai-interactions.md` → *Wiring a question-to-review workflow* |
|
|
20
|
+
| Learning proposals, review, retirement, Agent memory | `guides/features/agent-memory.md` → *Reviewing learnings* |
|
|
21
|
+
| Rewrite, learned wording, rerank, answer spans, retrieval misses | `guides/features/schema-vertex-ai-extension.md` → *Retrieval quality* |
|
|
22
|
+
| Named retrieval/answer scores, rubric grader, baseline/candidate design | `guides/test-components.md` → *Measuring a proposed learning* and *Human Review And Release Gates* |
|
|
23
|
+
| Per-question guidance and timeout recovery | `guides/agent-components.md` → *ai.turnContext* and *Wiring tools to an agent* |
|
|
24
|
+
|
|
25
|
+
Use the built-in interaction and learning stores. `interactions()` and `memories()` are component DSL
|
|
26
|
+
builders, not top-level CLI commands. Call them from an application's Action/review UI or a Test;
|
|
27
|
+
do not bypass their scope and review behavior with SQL. The CLI has `corpus review` and `corpus gate`.
|
|
28
|
+
|
|
29
|
+
## Investigate one answer
|
|
30
|
+
|
|
31
|
+
Start from the answer's `interactionId`, its sources and feedback. `interactions().trace(id)` gives
|
|
32
|
+
its provider rounds and tool calls; `sessionId` and `traceId` pivot into `mcp_ai_session_search`
|
|
33
|
+
(`investigation.md`, then that tool's entry in `tool-reference.md`). Inspect what the model actually
|
|
34
|
+
received before changing a prompt. For normalization, `unstructured()` also returns `aiSessionId`
|
|
35
|
+
when a model was called, plus `traceId`.
|
|
36
|
+
|
|
37
|
+
Decide what failed before choosing a remedy:
|
|
38
|
+
|
|
39
|
+
- Expected source missing: inspect the actual retrieval and `index_explain` diagnostics in the
|
|
40
|
+
authorized data lane. Test documents are not indexed. Diagnose eligibility/indexing before tuning
|
|
41
|
+
wording; `not_reached` describes a bounded search window, not proof that a document is irrelevant.
|
|
42
|
+
- Source retrieved but answer wrong: inspect the offered passages, per-turn instructions and answer
|
|
43
|
+
trace. A citation is not proof that the source supports the claim.
|
|
44
|
+
- Coverage absent: include a should-decline corpus case; a grounded decline can be the correct result.
|
|
45
|
+
- Terminology mismatch: propose `kind: 'phrasing'` with indexed wording in `details` and a source
|
|
46
|
+
document id in `subjectRef`, then measure it. Feedback alone does not alter retrieval.
|
|
47
|
+
|
|
48
|
+
`quality.rerank.applied: false` is fallback, not successful reranking. `indexSearchMock` bypasses
|
|
49
|
+
rewriting, learned retrieval and ranking; it proves how the caller uses supplied search results.
|
|
50
|
+
Use live-provider evidence for claims about those steps. Ranking charges are outside chat-token budgets.
|
|
51
|
+
|
|
52
|
+
## Measure a change
|
|
53
|
+
|
|
54
|
+
Use the existing account Test/corpus workflow. Keep durable regression components in the account
|
|
55
|
+
repository, and stage the workset in a dedicated workspace. Establish host, execution account, source
|
|
56
|
+
layer and data lane before running (`components status`). Compare the same cases, expected answers,
|
|
57
|
+
source inputs and model settings. Retrieve and score through the actual application path; a parallel
|
|
58
|
+
search written just for the evaluator may measure something the answering Agent never saw.
|
|
59
|
+
|
|
60
|
+
1. Capture a baseline with approved memories only. Keep each question in a fresh Agent session.
|
|
61
|
+
2. Make the source/prompt change, or enable `includeProposed` in the Test's Agent/retrieval path.
|
|
62
|
+
Preview includes all relevant proposals in scope, not one selected id. Keep the proposal set
|
|
63
|
+
stable. Learning records are shared across data lanes; a workspace does not isolate them.
|
|
64
|
+
3. Re-stage and run the candidate under an explicit `withAiBudget` in the Test. Retain both task ids,
|
|
65
|
+
per-case `scores: [retrieval: ..., answer: ...]`, grader results and retrieval diagnostics.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
remits-cli components stage --workset --account-id 1 --base-url http://localhost:8080 --data-mode test
|
|
69
|
+
remits-cli test run --test "Support Answer Evaluation" --account-id 1 --base-url http://localhost:8080 --data-mode test --json
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The suite name and account above are examples: create/use your application's suite and resolved account.
|
|
73
|
+
Repeat after the candidate edit and stage. `--test` selects a suite; `--names` selects exact case names
|
|
74
|
+
(or a `|`-separated list), not substrings or a regex. Check `total` and outcomes; a zero-case run proves
|
|
75
|
+
nothing. `--wait false` only launches; finish with `test status --task-id <id>`.
|
|
76
|
+
|
|
77
|
+
Use deterministic search/AI mocks for protocol regressions and live models against pinned corpus
|
|
78
|
+
artifacts for answer quality. Real index measurements need an explicitly authorized production data
|
|
79
|
+
lane and a read-only application path; changing the lane of a workflow with writes is not a shortcut.
|
|
80
|
+
The intended host must also have the implementation being verified: a deployed old binary cannot
|
|
81
|
+
verify unreleased platform changes, even with the same account and test lane.
|
|
82
|
+
|
|
83
|
+
## Review and gate
|
|
84
|
+
|
|
85
|
+
With the same explicit host/account/data-mode options as the runs:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
remits-cli corpus compare --corpus support-golden --base BASE_TASK_ID --head CANDIDATE_TASK_ID
|
|
89
|
+
remits-cli corpus review --corpus support-golden --task-id CANDIDATE_TASK_ID --sample 5
|
|
90
|
+
remits-cli corpus review --corpus support-golden --task-id CANDIDATE_TASK_ID --case CASE_KEY --verdict agree --note "Checked against the source"
|
|
91
|
+
remits-cli corpus gate --corpus support-golden --base BASE_TASK_ID --head CANDIDATE_TASK_ID --score retrieval --score answer
|
|
92
|
+
remits-cli evidence
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Replace uppercase ids with the returned task/case ids. Each repeated `--score` is a required metric;
|
|
96
|
+
`--scores retrieval,answer` is equivalent. Pin baseline and candidate instead of relying on latest runs.
|
|
97
|
+
The gate exits nonzero for regressions, lost/absent measurements or an unaccepted mocked/live mode
|
|
98
|
+
change. It records `corpus_gate` evidence. Inspect measured case counts and excluded changed expected
|
|
99
|
+
answers; also assess the application's minimum accuracy and the human spot checks. A non-regression
|
|
100
|
+
gate alone does not establish an absolute quality target or statistical significance.
|
|
101
|
+
|
|
102
|
+
Stop a release workflow on a failing gate. Approval and component sync do not automatically invoke it.
|
|
103
|
+
Human review preserves the model grade; it does not replace it. Approve/reject through the application's
|
|
104
|
+
`memories()` review path after the decision, and keep the relevant task ids in the review note. Editing
|
|
105
|
+
an approved memory affects later answers immediately; use a proposal when a replacement needs measurement.
|
|
106
|
+
|
|
107
|
+
## Keep the regression
|
|
108
|
+
|
|
109
|
+
Use `auxiliary: false` for durable Tests and their helper components. Set `regression: true` for suites
|
|
110
|
+
the account expects in its normal regression, including explicitly budgeted provider suites when
|
|
111
|
+
their cost/dependency is intentional. Otherwise leave costly/exploratory measurements opt-in.
|
|
112
|
+
Describe the proof boundary in the sidecar and README: mocked protocol, live provider smoke,
|
|
113
|
+
measured corpus quality, or UI journey. Retire/reject synthetic memories in cleanup.
|
|
114
|
+
|
|
115
|
+
Read `component-integrity.md` before landing. Source files alone do not update the platform's durable
|
|
116
|
+
regression selection: use normal account commit/push/sync when authorized, or stage for local runs.
|
|
117
|
+
Read `evidence` before reporting results; distinguish checked-in/staged source, measured behavior,
|
|
118
|
+
human approval and deployment.
|
|
@@ -65,8 +65,10 @@ Do not assume `--data-mode prod` implies the deployed prod host, or that `--data
|
|
|
65
65
|
localhost. Likewise, do not assume localhost is required because the current checkout is a back-stage
|
|
66
66
|
repo or because a previous command used localhost. If host matters, read `~/.remits-cli/sessions.json`,
|
|
67
67
|
`remits-cli whoami`, or the recent `remits-cli evidence` world blocks, then pass `--base-url` explicitly.
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
For existing capabilities, try the intended reachable host before declaring a connection blocker.
|
|
69
|
+
For unreleased platform capabilities, also verify that the host has the required implementation.
|
|
70
|
+
Staged component source cannot add missing platform APIs to an older deployed binary; use the updated
|
|
71
|
+
local/test runtime and follow that platform repository's startup/restart instructions.
|
|
70
72
|
|
|
71
73
|
### Tool Execution Lifecycle
|
|
72
74
|
|
|
@@ -266,6 +268,9 @@ remits-cli corpus cases --corpus <name> [--split S] [--tag T|--tags T,U] [--key
|
|
|
266
268
|
remits-cli corpus runs --corpus <name> [--limit 20] [--json] # runs that measured the corpus: outcomes, AI mode (live|MOCKED|none), cost, world
|
|
267
269
|
remits-cli corpus results --corpus <name> [--case <caseKey>] [--task-id <taskId>] [--json]
|
|
268
270
|
remits-cli corpus compare --corpus <name> --base <taskId> --head <taskId> [--json] # per-case direction, metric deltas, expectedChanged
|
|
271
|
+
remits-cli corpus gate --corpus <name> --base <taskId> --head <taskId> [--score retrieval --score answer] [--lower-is-better <name>] [--tolerance 0.02] [--allow-mode-change] [--json]
|
|
272
|
+
remits-cli corpus review --corpus <name> --task-id <taskId> [--sample 5] [--json]
|
|
273
|
+
remits-cli corpus review --corpus <name> --task-id <taskId> --case <key> --verdict agree|disagree [--score n] [--note "..."]
|
|
269
274
|
remits-cli corpus consistency --corpus <name> --case <caseKey> [--json] # did the case produce the same metrics every run?
|
|
270
275
|
remits-cli corpus retire --corpus <name> --case <caseKey> [--case ...] [--restore] [--json] # drop a case from future runs; past measurements stay readable
|
|
271
276
|
```
|
|
@@ -318,6 +323,16 @@ of the same **shape** - same data lane, branch, workspace and case count - and s
|
|
|
318
323
|
skipped; it refuses rather than comparing across worlds. `test status --data-mode prod` on a run recorded in
|
|
319
324
|
the test lane returns the run's real lane and now says that is what happened.
|
|
320
325
|
|
|
326
|
+
For the baseline → proposed learning → human review workflow, see `ai-quality.md` and
|
|
327
|
+
`guides/test-components.md` → *Measuring a proposed learning*. `--score` is repeatable;
|
|
328
|
+
`--score retrieval --score answer` requires both metrics (`--scores retrieval,answer` is equivalent).
|
|
329
|
+
`interactions()` and `memories()` are component DSL APIs, not top-level CLI subcommands.
|
|
330
|
+
|
|
331
|
+
`corpus gate` exits nonzero for a score regression, missing measurements, or an unaccepted AI-mode
|
|
332
|
+
change, and records `corpus_gate` evidence. Run it explicitly before sync or approving a learning;
|
|
333
|
+
neither operation invokes it automatically. Prefer explicit run ids and check their world.
|
|
334
|
+
`corpus review` samples graded cases or records a human spot check; it preserves the model grade.
|
|
335
|
+
|
|
321
336
|
A run labelled **`AI MOCKED`** replayed every AI turn from `aiMock`: it produces the same outcomes, scores and
|
|
322
337
|
metrics a measured run would, so read that label before treating green as evidence. `compare` warns when base and
|
|
323
338
|
head used different AI modes; `consistency` warns before calling a case unstable when its runs mixed modes.
|
|
@@ -68,6 +68,10 @@ The right model is:
|
|
|
68
68
|
|
|
69
69
|
Never treat "it looks right in prod data inspection" as sufficient proof that a code change is verified.
|
|
70
70
|
|
|
71
|
+
For cited answers, retrieval changes or reviewed learnings, load `ai-quality.md`. It connects the
|
|
72
|
+
component workflow to measured baseline/candidate runs, human checks and `corpus gate`; a passing
|
|
73
|
+
mocked Test alone cannot establish improved answer quality.
|
|
74
|
+
|
|
71
75
|
## Verification Envelopes
|
|
72
76
|
|
|
73
77
|
For a concrete user workflow, browser-facing change, branch-variant release, support ticket, or any loop
|
|
@@ -57,6 +57,8 @@ different lanes (see `platform-overview.md` → *Test and Production Data Lanes*
|
|
|
57
57
|
- **`sessionKey`** — salted user-activity session key. Pivot `mcp_user_activity` `sessions` → `story`;
|
|
58
58
|
each beat then carries a `traceId` / `threadGroupingId` for trace and log investigation.
|
|
59
59
|
- **`sessionId`** — pivots into persisted AI activity via `mcp_ai_session_search`.
|
|
60
|
+
- **`interactionId`** — one Agent answer, including feedback and citations. In component code,
|
|
61
|
+
`interactions().get(id)` / `.trace(id)` open it; use its `sessionId` / `traceId` for deeper forensics.
|
|
60
62
|
|
|
61
63
|
### Reading a record's `content` — persisted context, not a memory dump
|
|
62
64
|
|
|
@@ -109,7 +111,13 @@ embeddable patterns: `features/http-audits.md` (`mcp_get_guide`).
|
|
|
109
111
|
|
|
110
112
|
### AI activity
|
|
111
113
|
|
|
112
|
-
|
|
114
|
+
For a rated or corrected Agent answer, start with `interactions().trace(interactionId)` from an
|
|
115
|
+
Action or Test using the component DSL. `correlation: request_ids` identifies its recorded rounds;
|
|
116
|
+
legacy `time_window` matches may overlap adjacent answers. Empty rounds can mean logging was off or
|
|
117
|
+
telemetry expired. There is no top-level CLI `interactions` command: use your review Action, or pivot
|
|
118
|
+
its session/trace into the tools below. `ai-quality.md` routes the full correction/evaluation workflow.
|
|
119
|
+
|
|
120
|
+
AI request logging provides two deeper ways in:
|
|
113
121
|
|
|
114
122
|
- **`mcp_ai_session_search`** — search groupings and open their detail. A grouping spans every session
|
|
115
123
|
sharing one grouping id: the agent turns plus its guardrail and internal `ai()` calls. Use the
|