bitfab-cli 0.2.421 → 0.2.423
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/dist/index.js +79 -75
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28787,12 +28787,12 @@ var labelEvidenceShape = external_exports.preprocess(parseJsonString, external_e
|
|
|
28787
28787
|
var saveAgentLabels = {
|
|
28788
28788
|
name: "save_agent_labels",
|
|
28789
28789
|
title: "Save Agent Labels",
|
|
28790
|
-
description: "Set, skip, or archive the agent's pass/fail verdict on one or more
|
|
28790
|
+
description: "Set, skip, or archive the agent's pass/fail verdict on one or more of a trace's approved assertions (`labelSource=\"agent\"`). Every verdict and skip scores ONE assertion and must carry its `assertionId` (the `[ID: ...]` get_trace_assertions prints). Labels are only ever written on assertions, never directly on a trace: a trace with no approved assertions gets no verdict, so add assertions first (save_trace_assertions) or report your judgment in the conversation. Use this AFTER you have read the traces with get_traces and decided yourself whether each one looks like a pass, a fail, or genuinely cannot be judged. To set a verdict, pass `label` (true=PASS, false=FAIL) and `annotation` (your reasoning, shown to the human reviewer in the labeling UI). Add `evidence` as `{ spanId, text }` entries to ground that reasoning in spans on the evaluated trace. Optionally pass `confidence` (`VeryLow|Low|Medium|High|VeryHigh`) to record how confident you are - surfaced in the labeling UI so reviewers can prioritize low-confidence verdicts. To explicitly skip an assertion you cannot decide on (instead of leaving it unlabeled), pass `skip: true` and omit label. That records an explicit skip so coverage checks know you intentionally did not verdict it, and `annotation` still applies: pass it to say why the verdict was withheld (the check never ran, the tape was punctured, the run never terminated), which is the only place a reader learns that. A skip with no annotation clears the note the previous verdict carried, so a stale reason never sits under a withheld verdict. To clear a previously-set agent verdict (e.g., you changed your mind or labeled the wrong trace), pass `archive: true` and omit label/annotation; an archive may omit `assertionId` to clear an old whole-trace agent verdict. A verdict counts as soon as it is written; there is no approval step, and a person can override it with their own label. Archiving is non-destructive - the row is hidden from all reads but kept for audit, and you can immediately re-label the trace from scratch. For replay results, key each label by the replay item's `originalTraceId` (the original trace it was replayed from; `sourceTraceId` is accepted as a deprecated alias) plus the top-level `experimentId` instead of a `traceId`: the server resolves it to the replay trace via lineage, so you never need a server-generated replay trace id. When the experiment ran each trace more than once, also pass the item's `attempt` so each attempt gets its own verdict. Returns an agent-readable summary with one parseable effective label line per entry you sent (so a trace scored on several assertions gets one line per assertion), keyed by the id you supplied (`originalTraceId` for replay verdicts, otherwise `traceId`), so command callers can verify persistence. Before judging a replay, call get_trace_assertions on the original trace ids: an assertion says what the user asked this specific case to do, so the verdict is measured against that rather than a guess. When an assertion's target cannot be found on the trace you are judging, the check never ran, so pass `skip: true` for that assertion rather than recording a FAIL. Send one entry per assertion, all in the same call. get_trace_labels reads these back one line per assertion, keyed by the same assertionId, so a per-assertion write is verifiable per assertion.",
|
|
28791
28791
|
inputSchema: {
|
|
28792
28792
|
experimentId: external_exports.uuid().optional().describe("The experiment ID. Required when any label targets a trace by originalTraceId (replay verdicts); ignored otherwise."),
|
|
28793
28793
|
labels: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.object({
|
|
28794
28794
|
traceId: external_exports.uuid().optional().describe("The trace ID to update. Provide this OR originalTraceId, not both."),
|
|
28795
|
-
assertionId: external_exports.uuid().optional().describe("The assertion this verdict scores, taken from the `[ID: ...]` get_trace_assertions prints.
|
|
28795
|
+
assertionId: external_exports.uuid().optional().describe("The assertion this verdict scores, taken from the `[ID: ...]` get_trace_assertions prints. Required unless `archive` is true: labels are never written directly on a trace. A replay may name an assertion it inherited from the original trace it was replayed from."),
|
|
28796
28796
|
originalTraceId: external_exports.uuid().optional().describe("For replay verdicts: the original trace ID the replay item was replayed from (the replay item's originalTraceId). The server resolves it to the replay trace via lineage, so you never need a server replay trace id. Requires the top-level experimentId. Provide this OR traceId, not both."),
|
|
28797
28797
|
sourceTraceId: external_exports.uuid().optional().meta({ deprecated: true }).describe("Deprecated alias for originalTraceId. Prefer originalTraceId; accepted for back-compat."),
|
|
28798
28798
|
attempt: external_exports.number().int().min(0).optional().describe("For replay verdicts on an experiment that ran each trace more than once: which attempt of originalTraceId this verdict is for (0-based, the replay item's attempt). Defaults to 0, so single-attempt callers never need it."),
|
|
@@ -28802,22 +28802,22 @@ var saveAgentLabels = {
|
|
|
28802
28802
|
annotation: external_exports.string().min(1).optional().describe("Your reasoning, shown to the human reviewer in the labeling UI. Required when archive and skip are both false/omitted. Optional alongside `skip: true`, where it says why the verdict was withheld."),
|
|
28803
28803
|
confidence: external_exports.enum(["VeryLow", "Low", "Medium", "High", "VeryHigh"]).optional().describe("How confident you are in this verdict: VeryLow / Low / Medium / High / VeryHigh. Surfaced as a chip in the labeling UI; low-confidence labels are prioritized for human review. Optional; only meaningful when paired with a label."),
|
|
28804
28804
|
evidence: labelEvidenceShape.nullable().optional().describe("Span-by-span evidence for this verdict. Omit it to preserve existing evidence. Pass null to clear existing evidence.")
|
|
28805
|
-
})).min(1).max(200)).describe("One entry per assertion
|
|
28805
|
+
})).min(1).max(200)).describe("One entry per approved assertion, each carrying its assertionId; an archive entry may omit it (1-200)")
|
|
28806
28806
|
}
|
|
28807
28807
|
};
|
|
28808
28808
|
var saveHumanLabels = {
|
|
28809
28809
|
name: "save_human_labels",
|
|
28810
28810
|
title: "Save Human Labels",
|
|
28811
|
-
description: "Record one or more human-authored pass/fail verdicts that are VALIDATED immediately (`labelSource=\"human\"
|
|
28811
|
+
description: "Record one or more human-authored pass/fail verdicts that are VALIDATED immediately (`labelSource=\"human\"`) on one or more of a trace's approved assertions. Unlike save_agent_labels (the agent's own verdicts), labels set here join the validated dataset the instant they are written and satisfy `search_traces` with `validated: true`. Pass `label` (true=PASS, false=FAIL), `annotation` (the reasoning behind the verdict), and optionally `evidence` as `{ spanId, text }` entries grounded in the evaluated trace. Optionally pass `confidence` (`VeryLow|Low|Medium|High|VeryHigh`). USE ONLY when a human has explicitly decided the verdict, for example saving a known production bug as a test case. This records a verdict on a run that already happened; to record what a trace should do on its next replay, use save_trace_assertions. Do NOT use this for the agent's own first-pass guesses; use save_agent_labels for those. Every entry must carry the `assertionId` of the ONE assertion the human judged (the `[ID: ...]` get_trace_assertions prints), one entry per assertion: labels are never written directly on a trace, so a trace with no approved assertions needs assertions first (save_trace_assertions). get_trace_labels reads these back one line per assertion, keyed by the same assertionId, so a per-assertion write is verifiable per assertion.",
|
|
28812
28812
|
inputSchema: {
|
|
28813
28813
|
labels: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.object({
|
|
28814
28814
|
traceId: external_exports.uuid().describe("The trace ID to label"),
|
|
28815
|
-
assertionId: external_exports.uuid().
|
|
28815
|
+
assertionId: external_exports.uuid().describe("The assertion this verdict scores, taken from the `[ID: ...]` get_trace_assertions prints. Required: labels are never written directly on a trace."),
|
|
28816
28816
|
label: external_exports.boolean().describe("true for PASS, false for FAIL. Required."),
|
|
28817
28817
|
annotation: external_exports.string().min(1).describe("The reasoning behind the verdict. Required."),
|
|
28818
28818
|
confidence: external_exports.enum(["VeryLow", "Low", "Medium", "High", "VeryHigh"]).optional().describe("How confident the verdict is: VeryLow / Low / Medium / High / VeryHigh. Optional."),
|
|
28819
28819
|
evidence: labelEvidenceShape.nullable().optional().describe("Span-by-span evidence for this verdict. Omit it to preserve existing evidence. Pass null to clear existing evidence.")
|
|
28820
|
-
})).min(1).max(200)).describe("One entry per assertion
|
|
28820
|
+
})).min(1).max(200)).describe("One entry per approved assertion, each carrying its assertionId (1-200)")
|
|
28821
28821
|
}
|
|
28822
28822
|
};
|
|
28823
28823
|
var assertionTargetShape = external_exports.discriminatedUnion("kind", [
|
|
@@ -28886,7 +28886,7 @@ var generateTraceAssertions = {
|
|
|
28886
28886
|
var saveTraceAssertions = {
|
|
28887
28887
|
name: "save_trace_assertions",
|
|
28888
28888
|
title: "Save Trace Assertions",
|
|
28889
|
-
description: `Record what SHOULD happen when this trace is replayed. Bitfab stores two kinds of label on a trace. A VERDICT says how a run that already happened turned out, and that is save_agent_labels / save_human_labels. An ASSERTION, which is what this tool writes, says what a correct run looks like for THIS specific input, carries no pass/fail of its own, and is checked against a later replay once a reviewer approves it. A trace holds
|
|
28889
|
+
description: `Record what SHOULD happen when this trace is replayed. Bitfab stores two kinds of label on a trace. A VERDICT says how a run that already happened turned out, and that is save_agent_labels / save_human_labels. An ASSERTION, which is what this tool writes, says what a correct run looks like for THIS specific input, carries no pass/fail of its own, and is checked against a later replay once a reviewer approves it. A trace holds any number of assertions, and each verdict is recorded on one of them. Call it when the user describes the right answer for a particular case, for example "this booking should have picked the 6am flight, not the 9am". This is not a verdict on a run that already happened, which is save_agent_labels, and it is not a check that applies to every trace of a function, which is save_grader. Pass \`assertion\` plus optional \`passCriteria\` / \`failCriteria\`, the same trio save_grader takes, so an assertion that proves out across many traces can later be promoted into a grader with no rewriting. \`humanNote\` stores people-only context on the assertion; preserve and return it, but never use it to judge whether a replay passed. \`assigneeEmail\` names the member who should review it, which routes the assertion to that person without deciding anything, so it never changes the approval state. Pass an entry's \`id\` to edit an existing assertion, or omit it to add a new one, so two callers adding different assertions to one trace never overwrite each other. What this tool writes is saved awaiting review, so nothing checks it yet; a reviewer approves it, in Bitfab or with \`approvalState\` on a later call here, and from then on every replay of the trace is judged against it. \`approvalState\` decides an assertion that already exists, so deciding one is \`{ id, approvalState }\` with nothing else: an omitted \`assertion\` keeps the current wording. It is refused on a new assertion, so the call that invents a claim is never the call that puts it into force. One decision covers the assertion and the category it is filed under, so set \`category_assertion_id\` and \`approvalState\` in the same entry when you are deciding both; recategorizing on a later call without deciding returns it to not reviewed, since the approval covered the old category. What it gates is whether replays check the assertion at all, so decide from the assertion and the spans it cites rather than from memory of having written it. Rewording an assertion, or changing its criteria, target, or category, returns it to not reviewed unless the same call says otherwise. Call get_trace_assertions first to find the assertion to edit and to avoid creating a duplicate, and read them back before judging a replay, where it lists the approved ones by default. Pass it \`approvalStates: ["pending"]\` to read back drafts awaiting review, including the ones you just wrote. Up to ${MAX_ASSERTIONS_PER_REQUEST} per call.`,
|
|
28890
28890
|
inputSchema: {
|
|
28891
28891
|
traceId: external_exports.uuid().describe("The ORIGINAL trace to attach assertions to. A replay trace id is refused, and the error names the original to retry with, because a replay reads its original's assertions automatically."),
|
|
28892
28892
|
assertions: external_exports.preprocess(parseJsonString, external_exports.array(external_exports.object({
|
|
@@ -29882,10 +29882,10 @@ var semver3 = __toESM(require_semver2(), 1);
|
|
|
29882
29882
|
|
|
29883
29883
|
// ../bitfab-plugin-lib/dist/bakedSdkVersions.js
|
|
29884
29884
|
var BAKED_SDK_VERSIONS = {
|
|
29885
|
-
typescript: "0.64.
|
|
29886
|
-
python: "0.64.
|
|
29887
|
-
ruby: "0.64.
|
|
29888
|
-
go: "0.64.
|
|
29885
|
+
typescript: "0.64.8",
|
|
29886
|
+
python: "0.64.8",
|
|
29887
|
+
ruby: "0.64.8",
|
|
29888
|
+
go: "0.64.7"
|
|
29889
29889
|
};
|
|
29890
29890
|
|
|
29891
29891
|
// ../bitfab-plugin-lib/dist/installedSdk.js
|
|
@@ -33847,15 +33847,19 @@ var FAN_OUT_JUDGING = `{{#claude}}**Scale the judging with fan-out when there ar
|
|
|
33847
33847
|
Make each subagent prompt fully self-contained: its batch's per-item payloads (each item carries its own artifacts, enumerated below), the fixed rubric, and any shared context you gathered once (so no subagent re-derives it or touches the repo). Tell it to return one verdict entry per item in the exact shape this step persists, and nothing else.
|
|
33848
33848
|
|
|
33849
33849
|
**Then collect and persist once.** Wait for every batch, concatenate their verdict arrays into the single full set covering all items, and make the one batched persist call this step already describes, unchanged. Fan-out changes only how you produce the verdicts, never how they are stored or routed: same call, same shape, same buckets, same downstream steps.{{/claude}}{{^claude}}**Judge serially.** This editor doesn't use subagents for judging, so judge every item yourself, inline in this agent, as described above, regardless of how many there are.{{/claude}}`;
|
|
33850
|
-
var PER_ASSERTION_VERDICTS = `**Read each trace's assertions first, then write one verdict per assertion.** An assertion says what a correct run looks like for that one case, and it is checked against the trace under evaluation. That is what the {{tool:getTraceAssertions}} call in this step is for, and one call covers up to 100 trace IDs, so make it once and before you judge anything. Each assertion comes back on its own line under its trace as \`[ID: <uuid>] checks <target>: <assertion>\`, with its pass and fail criteria in parentheses when it has them. It may also carry a Human note so an agent can preserve or edit context intended for people. **Never use a Human note as evidence for a verdict; assess only the assertion, its pass/fail criteria, and the evaluated trace.** Hold that \`[ID: <uuid>]\` value, it is the \`assertionId\` every verdict on that assertion carries. **Only assertions a person has approved come back, because only those are checked on a replay.** A trace whose assertions are all still awaiting review comes back as "no approved assertions", the same wording a trace with none gets
|
|
33850
|
+
var PER_ASSERTION_VERDICTS = `**Read each trace's assertions first, then write one verdict per assertion.** An assertion says what a correct run looks like for that one case, and it is checked against the trace under evaluation. That is what the {{tool:getTraceAssertions}} call in this step is for, and one call covers up to 100 trace IDs, so make it once and before you judge anything. Each assertion comes back on its own line under its trace as \`[ID: <uuid>] checks <target>: <assertion>\`, with its pass and fail criteria in parentheses when it has them. It may also carry a Human note so an agent can preserve or edit context intended for people. **Never use a Human note as evidence for a verdict; assess only the assertion, its pass/fail criteria, and the evaluated trace.** Hold that \`[ID: <uuid>]\` value, it is the \`assertionId\` every verdict on that assertion carries. **Only assertions a person has approved come back, because only those are checked on a replay.** A trace whose assertions are all still awaiting review comes back as "no approved assertions", the same wording a trace with none gets.
|
|
33851
33851
|
|
|
33852
|
-
- **A trace that has assertions:** send one {{tool:saveAgentLabels}} entry per assertion, each carrying that assertion's \`assertionId\`, its own \`label\` (\`true\` for passed, \`false\` for failed), and its own \`annotation\`, which is the reasoning for that one assertion and nothing else. Judge each assertion on its own evidence.
|
|
33853
|
-
- **A trace with no assertions:**
|
|
33852
|
+
- **A trace that has assertions:** send one {{tool:saveAgentLabels}} entry per assertion, each carrying that assertion's \`assertionId\`, its own \`label\` (\`true\` for passed, \`false\` for failed), and its own \`annotation\`, which is the reasoning for that one assertion and nothing else. Judge each assertion on its own evidence. The trace verdict is derived from the per-assertion labels.
|
|
33853
|
+
- **A trace with no approved assertions:** write **no** verdict for it. Labels are only ever written on assertions, never directly on a trace, and {{tool:saveAgentLabels}} refuses an entry without an \`assertionId\`. Tell the user which traces have no approved assertions yet, so they can add assertions (and approve them) before those traces can be labeled.
|
|
33854
33854
|
- **An assertion whose target cannot be resolved on the trace:** skip it, never fail it. Send \`{ traceId, assertionId, skip: true }\` and no \`label\`. A missing target means the check never ran, so recording passed or failed there is a lie the reviewer cannot see. Its sibling assertions on the same trace are still verdicted normally.
|
|
33855
33855
|
|
|
33856
33856
|
**Never flatten the assertions into one entry.** No \`N/M assertion(s) passed\` tally, no \`[PASS]\` or \`[FAIL]\` lines inside an annotation, no paragraph standing in for the rows. One boolean cannot say which assertion failed, and prose cannot be filtered, counted, or trended. Each annotation covers exactly the assertion its entry names.
|
|
33857
33857
|
|
|
33858
|
-
|
|
33858
|
+
Send every trace's per-assertion entries in the same {{tool:saveAgentLabels}} call, batched into as few calls as the tool's per-call limit allows.`;
|
|
33859
|
+
var FIX_SCENARIO_LABEL = `**Labels are only ever written on assertions, never directly on a trace**, so the validated label goes on an assertion of the **original** trace:
|
|
33860
|
+
- Call {{tool:getTraceAssertions}} with the original trace ID. If an approved assertion already states the correct behavior this bug violates, use its \`assertionId\`.
|
|
33861
|
+
- Otherwise create one from the failure annotation you wrote in Phase Fix \`resolve\`: call {{tool:saveTraceAssertions}} with one entry whose \`assertion\` states the correct behavior (with \`failCriteria\` naming what the original output got wrong), then call {{tool:saveTraceAssertions}} again with \`{ id, approvalState: "approved" }\` for the id it returned. A new assertion is saved awaiting review and a label can only score an approved one, and the user has just asked to keep this scenario as a test, so approve it here and tell the user in one line that you added and approved the assertion (they can edit or reject it in Bitfab).
|
|
33862
|
+
- Then call {{tool:saveHumanLabels}} once with \`[{ traceId, assertionId, label: false, annotation }]\` on the **original** trace. This records a **validated human verdict**, so the trace counts as a real regression test the instant it's written. Use \`label: false\` because the original output was the bug; the fix is what now turns it green on replay. (On the rare occasion the user is adding an already-*passing* trace to guard against future regressions, use \`label: true\`.)`;
|
|
33859
33863
|
var REPLAY_SAFETY_CHECK = `**Mandatory pre-run replay safety check.** Complete this before executing replay for the first time, and re-run it whenever the registry entry, replay root, span boundaries, dispatch model, or mock strategy changes. Do not discover unsafe coverage by running replay: a successful email, payment, queue publish, or database write has already caused the damage.
|
|
33860
33864
|
|
|
33861
33865
|
1. Read the replay registry entry and require an explicit recorded-output strategy: normally \`mock: "marked"\` / \`mock="marked"\`; \`all\` is allowed only when every matched recorded child is intentionally frozen. Never accept \`none\` for a path with unsafe external actions.
|
|
@@ -33924,6 +33928,7 @@ var assistantFlow = Flow.parse({
|
|
|
33924
33928
|
getTraceAssertions: { kind: "mcp", name: "get_trace_assertions" },
|
|
33925
33929
|
saveAgentLabels: { kind: "mcp", name: "save_agent_labels" },
|
|
33926
33930
|
saveHumanLabels: { kind: "mcp", name: "save_human_labels" },
|
|
33931
|
+
saveTraceAssertions: { kind: "mcp", name: "save_trace_assertions" },
|
|
33927
33932
|
listDatasets: { kind: "mcp", name: "list_datasets" },
|
|
33928
33933
|
saveDataset: { kind: "mcp", name: "save_dataset" },
|
|
33929
33934
|
addTracesToDataset: { kind: "mcp", name: "add_traces_to_dataset" },
|
|
@@ -33940,7 +33945,7 @@ var assistantFlow = Flow.parse({
|
|
|
33940
33945
|
},
|
|
33941
33946
|
intro: `Use the local plugin MCP tools ({{tool:listFunctions}}, {{tool:searchTraces}}, {{tool:getTraces}}, {{tool:getTraceLabels}}, {{tool:getTraceAssertions}}, {{tool:saveAgentLabels}}, {{tool:listDatasets}}, {{tool:saveDataset}}, {{tool:addTracesToDataset}}, {{tool:removeTracesFromDataset}}, {{tool:listExperiments}}, {{tool:listExperimentTraces}}, {{tool:getReplayStatus}}) to find what's failing in a traced function, build a dataset of labeled traces, and iterate on the code/prompts using replay until pass rates improve.
|
|
33942
33947
|
|
|
33943
|
-
**MCP tools:** This skill uses \`list_trace_functions\`, \`search_traces\`, \`get_traces\`, \`get_trace_labels\`, \`get_trace_assertions\`, \`save_agent_labels\`, \`save_human_labels\`, \`list_datasets\`, \`save_dataset\`, \`add_traces_to_dataset\`, \`remove_traces_from_dataset\`, \`list_experiments\`, \`list_experiment_traces\`, and \`get_replay_status\` from the **local plugin MCP server** (bundled with this plugin){{#claude}}. Do NOT use the remote Bitfab MCP tools (\`mcp__Simforge__*\` or \`mcp__Bitfab__*\`), use only the \`mcp__plugin_bitfab_Bitfab__*\` variants.{{/claude}}{{^claude}}, exposed under the \`mcp__Bitfab__*\` prefix.{{/claude}}
|
|
33948
|
+
**MCP tools:** This skill uses \`list_trace_functions\`, \`search_traces\`, \`get_traces\`, \`get_trace_labels\`, \`get_trace_assertions\`, \`save_trace_assertions\`, \`save_agent_labels\`, \`save_human_labels\`, \`list_datasets\`, \`save_dataset\`, \`add_traces_to_dataset\`, \`remove_traces_from_dataset\`, \`list_experiments\`, \`list_experiment_traces\`, and \`get_replay_status\` from the **local plugin MCP server** (bundled with this plugin){{#claude}}. Do NOT use the remote Bitfab MCP tools (\`mcp__Simforge__*\` or \`mcp__Bitfab__*\`), use only the \`mcp__plugin_bitfab_Bitfab__*\` variants.{{/claude}}{{^claude}}, exposed under the \`mcp__Bitfab__*\` prefix.{{/claude}}
|
|
33944
33949
|
|
|
33945
33950
|
{{#ask-user-question}}**Always use** \`AskUserQuestion\` **when asking questions, reporting results, or presenting choices** (two exceptions, each a report with no decision to hand the user: the \`benchmark\` scorecard is printed as Markdown tables directly in chat, not via \`AskUserQuestion\`, since tables don't render inside that UI, see Phase Benchmark; and \`share-results\` reports the experiment outcome and then routes silently by the already-approved plan, see Phase 5, so it is a plain chat report and never an \`AskUserQuestion\`, using one there would re-introduce the post-experiment prompt this flow deliberately removed). Never print a question as text and wait. Rules:{{/ask-user-question}}{{^ask-user-question}}**When the flow branches with options for the user, always present them clearly and wait for the user's answer before proceeding.** Number or letter the options so the user can pick by reference. **Two steps are reports, not option branches, and must NOT wait:** the \`benchmark\` scorecard prints its tables directly in chat, and \`share-results\` reports the experiment outcome then routes silently by the already-approved plan (see Phase 5) via auto-evaluated \`when\` conditions with no options to present, waiting there would re-introduce the post-experiment prompt this flow removes. Rules:{{/ask-user-question}}
|
|
33946
33951
|
|
|
@@ -34616,7 +34621,7 @@ Hold the trace ID, function key, code path, the failure annotation, and the trac
|
|
|
34616
34621
|
{
|
|
34617
34622
|
id: "add-trace",
|
|
34618
34623
|
title: "Phase Add: Attach a Trace to a Dataset",
|
|
34619
|
-
intro: "Reached only from `add-trace` mode. This is the lightweight path: attach one or more existing traces to a dataset (picking or creating one), then stop. No labeling, no diagnosis, no experiments, and no browser interaction. It does still run the `readTracesBatched` CLI command to resolve trace function keys. The traces attach **raw**; the user labels
|
|
34624
|
+
intro: "Reached only from `add-trace` mode. This is the lightweight path: attach one or more existing traces to a dataset (picking or creating one), then stop. No labeling, no diagnosis, no experiments, and no browser interaction. It does still run the `readTracesBatched` CLI command to resolve trace function keys. The traces attach **raw**; the user labels their assertions later wherever they review datasets.",
|
|
34620
34625
|
stepStyle: "list",
|
|
34621
34626
|
steps: [
|
|
34622
34627
|
{
|
|
@@ -34680,7 +34685,7 @@ Then tell the user in one line, identifying the dataset by its \`datasetId\` (al
|
|
|
34680
34685
|
{
|
|
34681
34686
|
id: "dataset",
|
|
34682
34687
|
title: "Phase 3: Pick a Dataset and Label Traces",
|
|
34683
|
-
intro: `A **dataset** is the named bucket of labeled traces an experiment replays against. This phase picks (or creates) one for the trace function, labels candidate traces, attaches them to the dataset, then hands off to the per-dataset review page where the user
|
|
34688
|
+
intro: `A **dataset** is the named bucket of labeled traces an experiment replays against. This phase picks (or creates) one for the trace function, labels candidate traces, attaches them to the dataset, then hands off to the per-dataset review page where the user records their own verdicts and can ask the agent to add or remove traces.
|
|
34684
34689
|
|
|
34685
34690
|
In \`dataset\` mode this phase is the entry point, Phase 1 (function picker) and Phase 2 (instrumentation/replay verification) are skipped, so the trace function key comes from the argument. Before calling any MCP tools, grep the codebase for the key (e.g. \`grep -r "<traceFunctionKey>" --include="*.ts" --include="*.tsx" --include="*.py" --include="*.rb" --include="*.go" --include="*.baml"\`) and note the file path, every later step ("Label them yourself", and Phase 4 "Read the code" in \`wizard\` mode) needs it.`,
|
|
34686
34691
|
stepStyle: "list",
|
|
@@ -34718,7 +34723,7 @@ In \`dataset\` mode this phase is the entry point, Phase 1 (function picker) and
|
|
|
34718
34723
|
branches: [
|
|
34719
34724
|
{
|
|
34720
34725
|
when: "the dataset already has traces (search returned results)",
|
|
34721
|
-
description: `The dataset is not empty. Tell the user the linked dataset page contains the existing traces, where they can review
|
|
34726
|
+
description: `The dataset is not empty. Tell the user the linked dataset page contains the existing traces, where they can review labels and record their own verdict on any assertion. Then go straight to waiting for their review. Do NOT ask how to source new candidates or offer to find more traces. The user should review what's already in the dataset first; they can request more traces in chat if needed.`,
|
|
34722
34727
|
next: "dataset/await-event"
|
|
34723
34728
|
},
|
|
34724
34729
|
{
|
|
@@ -34736,7 +34741,7 @@ In \`dataset\` mode this phase is the entry point, Phase 1 (function picker) and
|
|
|
34736
34741
|
body: `**Ask how to source candidate traces.** Before searching, decide *where* the candidate traces come from. Three real options:
|
|
34737
34742
|
|
|
34738
34743
|
1. **Define new criteria**: agent searches unlabeled traces shaped by what the user wants to surface. Best when the user has a hypothesis or a specific failure pattern in mind.
|
|
34739
|
-
2. **Reuse existing labels for this function**: pull traces that already have a validated human
|
|
34744
|
+
2. **Reuse existing labels for this function**: pull traces that already have a validated (human) label (from any prior dataset on this function) and seed the new dataset with them. Best when the user wants to hill-climb off prior labeling work, same labels, different cut, add more later.
|
|
34740
34745
|
3. **Open / you decide**: agent samples broadly with no hypothesis, ignoring prior labels for the search shape. Best for discovery passes.
|
|
34741
34746
|
|
|
34742
34747
|
**Probe for prior label volume first** so the recommendation is grounded. Call {{tool:searchTraces}} with \`traceFunctionKey: <key>\`, \`validated: true\`, \`limit: 50\` to see roughly how many validated labels already exist for this function. Note the count, you'll need it for the recommendation and for option 2.
|
|
@@ -34787,7 +34792,7 @@ Hold the chosen mode in working context, the next steps branch on it.
|
|
|
34787
34792
|
"ask"
|
|
34788
34793
|
],
|
|
34789
34794
|
title: "Seed dataset from existing validated labels",
|
|
34790
|
-
body: `**Seed dataset from existing validated labels.** Reachable only when the user picked Option B in \`ask-search-mode\`. Pull traces that already have a validated
|
|
34795
|
+
body: `**Seed dataset from existing validated labels.** Reachable only when the user picked Option B in \`ask-search-mode\`. Pull traces that already have a validated (human-authored) label for this function, attach them to the picked dataset, and route on whether the user also wants to add more.
|
|
34791
34796
|
|
|
34792
34797
|
1. Call {{tool:searchTraces}} with \`traceFunctionKey: <key>\`, \`validated: true\`, and a generous \`limit\` (50 is the cap). Both \`labelResult: true\` and \`labelResult: false\` matter, failures are the hill-climbing signal, but passes anchor the regression boundary. If 50 isn't enough to cover the function's labeled history, run a second call with \`labelResult: false\` only to bias toward fails first, then a third with \`labelResult: true\`. De-dupe trace IDs across calls.
|
|
34793
34798
|
2. Call {{tool:getTraceLabels}} on the resulting trace IDs so the labels, annotations, and saved evidence are in working context. Don't re-label them, these are already validated. {{tool:getTraceLabels}} returns only label + annotation + evidence + approved (no span content) and takes up to 100 IDs per call, so the deduped set from step 1 is usually a single call; if it deduped more than 100 IDs, split them into chunks of 100 and call {{tool:getTraceLabels}} once per chunk (sequential is fine, the responses are small).
|
|
@@ -34849,13 +34854,13 @@ Hold the user's answer (the chosen option **and** any free-text detail) in worki
|
|
|
34849
34854
|
|
|
34850
34855
|
{{branches}}
|
|
34851
34856
|
|
|
34852
|
-
Recommend Option A, an agent first pass
|
|
34857
|
+
Recommend Option A, an agent first pass means the user reviews verdicts that are already filled in: saving each one on the labeling page, whether they keep your pick or change it, records it as their own validated label. But respect the user's choice: if they pick B, do **not** call {{tool:saveAgentLabels}} for any of these candidates. They want to label from scratch in the labeling page, with no agent verdicts pre-filled. If no new candidate traces were found in the previous step, skip this question and continue.`,
|
|
34853
34858
|
branches: [
|
|
34854
34859
|
{
|
|
34855
34860
|
option: {
|
|
34856
34861
|
letter: "A",
|
|
34857
|
-
label: "Agent labels first, I
|
|
34858
|
-
description: "agent
|
|
34862
|
+
label: "Agent labels first, I override",
|
|
34863
|
+
description: "agent records a verdict on each assertion; you keep or change each one on the labeling page, and saving it makes it your validated label"
|
|
34859
34864
|
},
|
|
34860
34865
|
recommended: true,
|
|
34861
34866
|
next: "dataset/label-self"
|
|
@@ -34864,7 +34869,7 @@ Recommend Option A, an agent first pass turns the labeling page into a quick app
|
|
|
34864
34869
|
option: {
|
|
34865
34870
|
letter: "B",
|
|
34866
34871
|
label: "I'll label them manually",
|
|
34867
|
-
description: "no agent verdicts; you label
|
|
34872
|
+
description: "no agent verdicts; you label each assertion yourself on the labeling page"
|
|
34868
34873
|
},
|
|
34869
34874
|
next: "dataset/attach-to-dataset"
|
|
34870
34875
|
}
|
|
@@ -34883,19 +34888,19 @@ Recommend Option A, an agent first pass turns the labeling page into a quick app
|
|
|
34883
34888
|
commandCalls: ["readTracesBatched"],
|
|
34884
34889
|
title: "Agent first pass: label them yourself before opening the labeling page",
|
|
34885
34890
|
emit: "Labeling traces",
|
|
34886
|
-
body: `**Agent first pass: label them yourself before opening the labeling page**: Reachable only when the user picked Option A in the previous step. **You** label the approved candidate traces so the labeling page
|
|
34891
|
+
body: `**Agent first pass: label them yourself before opening the labeling page**: Reachable only when the user picked Option A in the previous step. **You** label the approved candidate traces' assertions so the labeling page opens with your verdicts already in place, and the user saves each one, keeping or changing your pick, which records it as their own validated label. Run \`{{command:readTracesBatched}} <trace-id...> --scope full\` **once** with all the approved trace IDs. It fans the reads out in parallel batches of 10 and writes the combined result to a temp file; the command prints \`{"status":"ok","outputFile":"..."}\` as JSON, so \`Read\` that \`outputFile\`. **Use this command here, not the \`get_traces\` MCP tool directly:** \`get_traces\` caps at 10 IDs, so calling it for the approved set would re-introduce the serial per-batch fan-out \`readTracesBatched\` exists to replace. Then call {{tool:getTraceAssertions}} with the same trace IDs, so you know what each trace was asked to do before you judge it. Read each trace's inputs / output / spans yourself, and decide whether it passed or failed, one decision per assertion on each trace that has approved assertions (the per-assertion block below fixes the shape). A trace with no approved assertions gets no verdict; tell the user it needs assertions before it can be labeled. **Ground your judgment in the codebase, not just the trace text.** Before you start labeling, read the instrumented function in the user's source (located in Phase 2 in \`wizard\` mode, or via the grep step in this phase's intro in \`dataset\` mode) and any nearby code that explains intent, comments, docstrings, README sections, related tests, BAML files, so you know what the function is *supposed* to do and what "good" looks like for it. Apply the same context to every trace: does this output achieve the function's goal as expressed in the code? Does it match the patterns in the already-validated traces? **{{#claude}}First decide how you'll produce the verdicts, judge serially yourself or fan the judging out per the fan-out block immediately below, based on how many candidates there are. Do not persist yet.{{/claude}}{{^claude}}Judge each candidate as the block immediately below describes. Do not persist yet.{{/claude}}** However you produce them, the verdicts then land in a single {{tool:saveAgentLabels}} call, one entry per assertion on the traces that have approved assertions, in the shape the per-assertion block below fixes. **Every entry carries its own \`label\` (true for passed, false for failed) and its own \`annotation\` (a one-or-two-sentence explanation written for the human reviewer, ideally referencing what the code is trying to do).** Commit to a verdict, if you genuinely cannot decide, you didn't read the trace or the code carefully enough. The labels you save here count as soon as they are written; there is no approval step. The user reviews them on the dataset page and records their own verdict on any assertion they disagree with, and only those human verdicts count as validated.
|
|
34887
34892
|
|
|
34888
34893
|
${PER_ASSERTION_VERDICTS}
|
|
34889
34894
|
|
|
34890
34895
|
${FAN_OUT_JUDGING}
|
|
34891
34896
|
|
|
34892
|
-
**Per-item inputs for this step (however you produce the verdicts):** each candidate trace's own artifacts, its input and output (from the \`{{command:readTracesBatched}} --scope full\` load above), its assertions with their ids (from the {{tool:getTraceAssertions}} call above), judged against the shared context, what the function is supposed to do and what "good" looks like for it (the instrumented function and the nearby intent you read above, plus the patterns from the already-validated traces). Each verdict is passed or failed plus its one-or-two-sentence annotation, in the \`{ traceId, assertionId, label, annotation }\` shape
|
|
34897
|
+
**Per-item inputs for this step (however you produce the verdicts):** each candidate trace's own artifacts, its input and output (from the \`{{command:readTracesBatched}} --scope full\` load above), its assertions with their ids (from the {{tool:getTraceAssertions}} call above), judged against the shared context, what the function is supposed to do and what "good" looks like for it (the instrumented function and the nearby intent you read above, plus the patterns from the already-validated traces). Each verdict is passed or failed plus its one-or-two-sentence annotation, in the \`{ traceId, assertionId, label, annotation }\` shape, one per assertion. A trace with no approved assertions produces no entry. {{#claude}}When you fan out, each subagent's prompt carries its batch's trace inputs/outputs and that batch's assertions plus that shared context, and returns its batch's entries in that same shape, one per assertion, which you concatenate across batches. {{/claude}}However the verdicts are produced, you make the single {{tool:saveAgentLabels}} call (one array, all traces) just as described above. The labels count as written no matter how they were produced, and the user can override any of them with their own verdict on the dataset page. Only verdicts the user saves count as validated, so tell the user to save the verdicts they agree with too, not just change the ones they don't: the dataset confirm step needs at least one failing verdict the user saved.
|
|
34893
34898
|
|
|
34894
34899
|
**The cross-trace failure-pattern synthesis stays separate.** Phase 4 (\`understand-failures\`) is a deliberate join: it reads all the labels at once so the holistic "these N traces fail the same way" view is never lost. Per-trace labeling here is mechanical and independent, {{#claude}}and when you fan out, each subagent sees only its own batch, so {{/claude}}do not fold cross-trace synthesis into the labeling, that recognition is Phase 4's job on the full set.
|
|
34895
34900
|
|
|
34896
|
-
> \u{1F6A8} **HARD RULE, DO NOT SKIP (agent-first mode only):** When the user picked Option A, you MUST call {{tool:saveAgentLabels}} with
|
|
34901
|
+
> \u{1F6A8} **HARD RULE, DO NOT SKIP (agent-first mode only):** When the user picked Option A, you MUST call {{tool:saveAgentLabels}} with a verdict for every assertion on every approved trace that has assertions BEFORE sharing the labeling page link, and tell the user which approved traces have no approved assertions yet and so cannot be labeled until assertions are added. Sending the user into an agent-first review without the verdicts you could have written is a process violation. If none of the approved traces has approved assertions, make no {{tool:saveAgentLabels}} call (an empty batch is refused), tell the user none of these traces can be labeled until they have approved assertions, and share the labeling page link as usual. (In manual mode this step is unreachable, and the rule does not apply.)
|
|
34897
34902
|
|
|
34898
|
-
> **Made a mistake?** If you realize a verdict was wrong (e.g., you mislabeled a trace or want to re-evaluate), call {{tool:saveAgentLabels}} again with \`{ traceId,
|
|
34903
|
+
> **Made a mistake?** If you realize a verdict was wrong (e.g., you mislabeled a trace or want to re-evaluate), call {{tool:saveAgentLabels}} again with \`{ traceId, assertionId, archive: true }\` to archive one assertion's verdict without touching its siblings. The previous label is hidden (kept for audit), and you can re-label that assertion with another \`save_agent_labels\` call.`,
|
|
34899
34904
|
next: "dataset/attach-to-dataset"
|
|
34900
34905
|
},
|
|
34901
34906
|
{
|
|
@@ -34990,7 +34995,7 @@ Then return to the await-event step. If the user wants more edits, they'll ask a
|
|
|
34990
34995
|
- **\`costRun\` is false (the common case):** call {{tool:getTraceLabels}} with all of them. It returns only the verdict fields (label + annotation + evidence + approved, no span content) and takes up to 100 IDs per call, so for almost every dataset this is a single call. Only if the dataset has more than 100 traces, split the IDs into chunks of 100 and call {{tool:getTraceLabels}} once per chunk; the responses are small, so plain sequential calls are fine.
|
|
34991
34996
|
- **\`costRun\` is set:** run \`{{command:readTracesBatched}} <trace-id...> --scope full\` **once** with all the trace IDs instead, because the Phase 5 cost-delta step reads each original trace's recorded token usage from this load and {{tool:getTraceLabels}} does not carry it. The command fans the \`scope: "full"\` reads out in parallel batches of 10 and writes the combined result to a temp file; it prints \`{"status":"ok","outputFile":"..."}\` as JSON, so \`Read\` that \`outputFile\`. **Use this command here, not the \`get_traces\` MCP tool directly:** \`get_traces\` caps at 10 IDs, so calling it for the dataset would re-introduce the serial per-batch fan-out \`readTracesBatched\` exists to replace.
|
|
34992
34997
|
|
|
34993
|
-
**Re-read them fresh here even if you read them earlier in this phase:** the Bitfab labeling
|
|
34998
|
+
**Re-read them fresh here even if you read them earlier in this phase:** the Bitfab labeling page persists the user's own verdicts and annotation edits to the DB, so cached context from the find / label steps can be stale. This is the working set for confirm + every Phase 5 experiment.`,
|
|
34994
34999
|
next: "dataset/confirm-dataset"
|
|
34995
35000
|
},
|
|
34996
35001
|
{
|
|
@@ -34998,12 +35003,17 @@ Then return to the await-event step. If the user wants more edits, they'll ask a
|
|
|
34998
35003
|
kind: "branch",
|
|
34999
35004
|
toolCalls: ["searchTraces", "ask"],
|
|
35000
35005
|
title: "Confirm the dataset",
|
|
35001
|
-
body: `**Confirm the dataset**: Present the dataset {{presentMode}}: each entry showing (trace ID, label, annotation summary). The dataset must contain at least one **validated failing label**: i.e. at least one trace where a
|
|
35006
|
+
body: `**Confirm the dataset**: Present the dataset {{presentMode}}: each entry showing (trace ID, label, annotation summary). The dataset must contain at least one **validated failing label**: i.e. at least one trace where a person recorded a \`false\` verdict on one of its assertions. To check, call {{tool:searchTraces}} restricted to the dataset trace IDs with \`validated: true\` and \`labelResult: false\`. Two outcomes:
|
|
35002
35007
|
|
|
35003
35008
|
{{whens}}
|
|
35004
35009
|
|
|
35005
|
-
|
|
35010
|
+
Agent labels do **not** satisfy this requirement by design, \`validated: true\` counts labels a person wrote or approved.`,
|
|
35006
35011
|
branches: [
|
|
35012
|
+
{
|
|
35013
|
+
when: "check fails, but the agent verdicts include failing ones the user has not saved yet",
|
|
35014
|
+
description: "ask the user to save (keep) the failing verdicts they agree with on the dataset page, then wait for their review and re-check",
|
|
35015
|
+
next: "dataset/await-event"
|
|
35016
|
+
},
|
|
35007
35017
|
{
|
|
35008
35018
|
when: "check fails (no validated failing label, search returns nothing)",
|
|
35009
35019
|
description: "tell the user and loop back to find or label more unlabeled traces",
|
|
@@ -35696,20 +35706,20 @@ Hold the verdicts in working context for the final report, the \`share-results\`
|
|
|
35696
35706
|
emit: "Evaluating results",
|
|
35697
35707
|
body: `**Evaluate against labels & annotations.** Score only items where \`item.error\` is unset. Items with \`item.error\` set are unreplayable (already classified) and go in their own bucket, never pass, fail, or regression.
|
|
35698
35708
|
|
|
35699
|
-
> \u{1F6A8} **MANDATORY, NOT A JUDGMENT CALL:** Reaching this step means verdicts can be persisted (by lineage \`originalTraceId\` or direct \`traceId\`, per \`check-verdict-persistence\`), so you MUST derive
|
|
35709
|
+
> \u{1F6A8} **MANDATORY, NOT A JUDGMENT CALL:** Reaching this step means verdicts can be persisted (by lineage \`originalTraceId\` or direct \`traceId\`, per \`check-verdict-persistence\`), so you MUST derive a verdict (or one of the three explicit non-verdicts below) for **every** completed (\`item.error\` unset) replay item, and persist one entry per assertion for every completed item whose original has approved assertions. **Agents never write whole-trace verdicts:** an item whose original has no approved assertions is judged in the chat report only, is not persisted, and is left out of the \`expected*\` list. If no completed item's original has approved assertions, there is nothing to persist: make no \`persistReplayLabels.js\` call and say so in the report. \`item.error\` unset means "score this item against its criteria", it does NOT mean "it passed". Persisting is not optional, you do not get to decide a run "isn't worth scoring", "looks like noise", or "would pollute the dataset" and jump to the report with an empty scorecard. There is no "this whole run isn't meaningful, so skip labeling" path: that path does not exist, and \`verify-replay-labels\` will route you back if the persisted set doesn't cover every completed item whose original has approved assertions.
|
|
35700
35710
|
|
|
35701
35711
|
**The verdict is decided by the output against its criteria, never by your opinion of how informative, useful, or production-like the run is.** Environmental doubts, a mock/stub model, no real gateway, flaky infra, an output that "looks like noise", a worry that the labels would pollute the dataset, are **caveats only**: write them into the \`annotation\` and lower the \`confidence\` (e.g. \`Low\` / \`VeryLow\`). They never suppress a verdict and never skip this step. A low-confidence verdict with a caveat is still a verdict and still gets persisted.
|
|
35702
35712
|
|
|
35703
35713
|
**Finalize the live evaluation loop; do not start from scratch.** If \`replay-against-dataset\` produced a non-empty run \`events.jsonl\`, read it before judging and reuse any prep, unreplayable buckets, verdicts, and persisted original trace IDs already produced during the running replay. Each progress event's \`item.originalTraceId\` is the original trace id: use it both to identify which dataset item settled and as the key each verdict is persisted by (item files carry it under the same \`originalTraceId\` field, plus a deprecated \`sourceTraceId\` alias). Enriched progress rows carry \`item.itemPath\`; the item file has the full input, replay output, original output, and metadata needed to judge during replay. Progress-only rows are only a trigger/prep signal. If the file is missing or empty, continue from the final complete event; older scripts remain valid and simply do not get incremental evaluation. The final verdict still comes from the completed replay item files / experiment trace data, and persistence is still keyed by the **original trace** id.
|
|
35704
35714
|
|
|
35705
|
-
When possible, evaluate and persist in small completed batches rather than waiting to judge every item at once: every time a progress event gives you a replay item ref with an original trace id, full item file, and its original label/annotation, read the item file, derive that item's verdict, and append it to the pending verdict set. Once a batch has complete coverage for its expected original trace ids, call \`persistReplayLabels.js\` for that batch. If the current SDK only exposes replay outputs at the end, use progress during the run for preparation and failed-item bucketing, then do the first persist immediately after this run's \`complete\` row appears. At the end of the replay, run the same coverage check over the complete row's item refs; any item not already persisted must be judged and persisted before continuing. This keeps Bitfab's experiment view filling in as early as the available data allows while preserving the same final correctness checks.
|
|
35715
|
+
When possible, evaluate and persist in small completed batches rather than waiting to judge every item at once: every time a progress event gives you a replay item ref with an original trace id, full item file, and its original label/annotation, read the item file, derive that item's verdict, and append it to the pending verdict set. Once a batch has complete coverage for its expected original trace ids (only items whose originals have approved assertions), call \`persistReplayLabels.js\` for that batch. If the current SDK only exposes replay outputs at the end, use progress during the run for preparation and failed-item bucketing, then do the first persist immediately after this run's \`complete\` row appears. At the end of the replay, run the same coverage check over the complete row's item refs; any item whose original has approved assertions and is not already persisted must be judged and persisted before continuing, and every other completed item is judged in the chat report. This keeps Bitfab's experiment view filling in as early as the available data allows while preserving the same final correctness checks.
|
|
35706
35716
|
|
|
35707
35717
|
For each completed (non-errored) replay item, derive a verdict by comparing the replay's new output against the original trace's label and annotation (from Phase 3 in \`wizard\`/\`dataset\` modes, loaded by \`pick-dataset\` in Phase 5 Setup in \`experiment\` and \`benchmark\` modes; in \`fix\` mode's single-trace pass, the failure annotation held from Phase Fix \`resolve\` is the criterion, and on a later full-dataset run the added trace plus any pre-existing dataset siblings loaded by \`fix-add-to-dataset\`):
|
|
35708
35718
|
|
|
35709
35719
|
- **fail**-labeled original: does the replay's new output address the annotation? If yes \u2192 \`label: true\` (PASS). If no \u2192 \`label: false\` (FAIL). Use the annotation as the acceptance criterion.
|
|
35710
35720
|
- **pass**-labeled original: preserved \u2192 \`label: true\` (PASS). regressed \u2192 \`label: false\` (FAIL).
|
|
35711
35721
|
|
|
35712
|
-
**The ONLY permitted non-verdicts for a completed item are these three enumerated skip cases.** Each is per-item and must be accounted for explicitly with a \`{skip: true}\` entry. Nothing else, and never "the whole run isn't worth scoring", justifies omitting a completed item:
|
|
35722
|
+
**The ONLY permitted non-verdicts for a completed item are these three enumerated skip cases.** Each is per-item and must be accounted for explicitly: with a \`{skip: true}\` entry per assertion when the original has approved assertions, or as a skipped row in the chat report when it has none. Nothing else, and never "the whole run isn't worth scoring", justifies omitting a completed item:
|
|
35713
35723
|
|
|
35714
35724
|
- **Unlabeled original** (no validated or agent label, possible in \`benchmark\` mode, where the dataset only needs \u22651 trace regardless of label mix): there is no acceptance criterion to score against, so do NOT pass/fail it. Mark it \`skip: true\` and note "unlabeled, no expected result", it counts toward \`S\` (skipped) and is excluded from \`scorable\`, never pass or fail.
|
|
35715
35725
|
- **Fail-labeled original with an unusable annotation** (the label exists and says FAIL, but the annotation is empty or contentless, e.g. "bad", "wrong", "fix this", so it states no criterion for what "fixed" looks like): the fail label tells you the direction but not what to check the new output against. This skip is narrow and applies ONLY when BOTH hold: (a) the original is **fail**-labeled (a pass-labeled original never needs the annotation, you score it by preserved-vs-regressed against the original output, so this case does not apply), and (b) you **cannot reconstruct the defect** from the code, the function's intent, or the original's own output. If you can recover what was wrong from any of those, you have a criterion, so verdict it (a thin-but-usable annotation like "returns null instead of the email" is NOT unusable). Only when the criterion is genuinely unrecoverable: \`skip: true\` and note "fail label, annotation states no checkable criterion". This is not an escape for annotations you simply find terse or inconvenient.
|
|
@@ -35728,7 +35738,7 @@ For each completed (non-errored) replay item, derive a verdict by comparing the
|
|
|
35728
35738
|
|
|
35729
35739
|
Keep re-replays to the minimum: only items that fail step 1, one replay each, never a full baseline arm across the dataset. If even step 2 errors or yields no usage, report that item's delta as uncomputable ("no baseline, original did not complete cleanly") instead of guessing.
|
|
35730
35740
|
|
|
35731
|
-
**The
|
|
35741
|
+
**The per-assertion verdicts you persist here land on the REPLAY trace (not the original).** Verdicts on items whose originals have no approved assertions stay in the chat report. You key each persisted verdict by the run's persist key (from \`check-verdict-persistence\`): in lineage keying, the original's \`originalTraceId\`, which the server resolves through its lineage to this run's replay trace; in direct keying, the server replay \`traceId\` directly. Either way the label lands on the replay trace, which is what makes "did this fix actually pass on replay?" queryable across iterations.
|
|
35732
35742
|
|
|
35733
35743
|
${FAN_OUT_JUDGING}
|
|
35734
35744
|
|
|
@@ -35748,9 +35758,9 @@ ${FAN_OUT_JUDGING}
|
|
|
35748
35758
|
"experimentId": "<experimentId>",
|
|
35749
35759
|
"expectedOriginalTraceIds": ["<originalTraceId1>", "<originalTraceId2>", "..."],
|
|
35750
35760
|
"verdicts": [
|
|
35751
|
-
{ "originalTraceId": "<originalTraceId1>", "label": true, "annotation": "Now returns the missing field
|
|
35752
|
-
{ "originalTraceId": "<originalTraceId2>", "label": false, "annotation": "Output still hallucinates a tool argument.", "confidence": "VeryHigh" },
|
|
35753
|
-
{ "originalTraceId": "<originalTraceId3>", "skip": true }
|
|
35761
|
+
{ "originalTraceId": "<originalTraceId1>", "assertionId": "<assertionId1>", "label": true, "annotation": "Now returns the missing field this assertion checks for.", "confidence": "High" },
|
|
35762
|
+
{ "originalTraceId": "<originalTraceId2>", "assertionId": "<assertionId2>", "label": false, "annotation": "Output still hallucinates a tool argument.", "confidence": "VeryHigh" },
|
|
35763
|
+
{ "originalTraceId": "<originalTraceId3>", "assertionId": "<assertionId3>", "skip": true, "annotation": "The span this assertion targets is missing on the replay." }
|
|
35754
35764
|
]
|
|
35755
35765
|
}
|
|
35756
35766
|
\`\`\`
|
|
@@ -35761,38 +35771,37 @@ ${FAN_OUT_JUDGING}
|
|
|
35761
35771
|
{
|
|
35762
35772
|
"expectedTraceIds": ["<serverReplayTraceId1>", "<serverReplayTraceId2>", "..."],
|
|
35763
35773
|
"verdicts": [
|
|
35764
|
-
{ "traceId": "<serverReplayTraceId1>", "label": true, "annotation": "Now returns the missing field.", "confidence": "High" },
|
|
35765
|
-
{ "traceId": "<serverReplayTraceId2>", "skip": true }
|
|
35774
|
+
{ "traceId": "<serverReplayTraceId1>", "assertionId": "<assertionId1>", "label": true, "annotation": "Now returns the missing field.", "confidence": "High" },
|
|
35775
|
+
{ "traceId": "<serverReplayTraceId2>", "assertionId": "<assertionId2>", "skip": true }
|
|
35766
35776
|
]
|
|
35767
35777
|
}
|
|
35768
35778
|
\`\`\`
|
|
35769
35779
|
|
|
35770
|
-
In lineage keying, \`experimentId\` is this replay run's experiment ID (from the progress rows or the final \`ReplayResult\`). The server uses it to resolve each original trace to its replay trace within this run. The \`expected*\` list MUST be the full set of ids covered by this call's batch (and across all batches, every completed \`item.error\`-unset replay item must be persisted exactly once, no fewer, per the mandatory-coverage rule above). For the final end-of-run call, use only the ids not already successfully persisted by an earlier batch. \`verdicts\` MUST cover every id in the \`expected*\` list, keyed by the same id field as that list.
|
|
35780
|
+
In lineage keying, \`experimentId\` is this replay run's experiment ID (from the progress rows or the final \`ReplayResult\`). The server uses it to resolve each original trace to its replay trace within this run. The \`expected*\` list MUST be the full set of ids covered by this call's batch (and across all batches, every completed \`item.error\`-unset replay item whose original has approved assertions must be persisted exactly once, no fewer, per the mandatory-coverage rule above; items with no approved assertions are never persisted). For the final end-of-run call, use only the ids not already successfully persisted by an earlier batch. \`verdicts\` MUST cover every id in the \`expected*\` list, keyed by the same id field as that list. The \`expected*\` list holds only items whose originals have approved assertions, and each of those gets one entry per assertion, every entry carrying its \`assertionId\`: either a \`{label, annotation, confidence?}\` verdict or a \`{skip: true, annotation?}\` explicit skip (skips allowed only for the three enumerated skip cases above or a missing assertion target, never for an environmental doubt). The script rejects an entry without an \`assertionId\` with \`status: "invalid-input"\`. \`confidence\` is optional but recommended (\`VeryLow|Low|Medium|High|VeryHigh\`). It surfaces in the labeling UI so reviewers can prioritize low-confidence verdicts. If any expected id gets no entry at all, the script returns \`status: "missing-coverage"\` and the verify step routes you back to fill the gaps.
|
|
35771
35781
|
|
|
35772
|
-
**A replay inherits the original trace's assertions, so score them one at a time.** Before you judge this batch, call {{tool:getTraceAssertions}} once with the batch's **original** trace ids (one call covers up to 100 ids, so make it once and before you judge anything). Each assertion comes back under its trace as \`[ID: <uuid>] checks <target>: <assertion>\`, and that \`[ID: <uuid>]\` value is the \`assertionId\` its verdict carries. Only assertions a person has approved are listed, since only those are checked on a replay. A trace that comes back "no approved assertions"
|
|
35782
|
+
**A replay inherits the original trace's assertions, so score them one at a time.** Before you judge this batch, call {{tool:getTraceAssertions}} once with the batch's **original** trace ids (one call covers up to 100 ids, so make it once and before you judge anything). Each assertion comes back under its trace as \`[ID: <uuid>] checks <target>: <assertion>\`, and that \`[ID: <uuid>]\` value is the \`assertionId\` its verdict carries. Only assertions a person has approved are listed, since only those are checked on a replay. A trace that comes back "no approved assertions" gets no persisted entry: judge it in the chat report only.
|
|
35773
35783
|
|
|
35774
35784
|
**Never use a Human note as evidence for a verdict; assess only the assertion, its pass/fail criteria, and the evaluated trace.** The note is returned only so you can preserve or edit context intended for people.
|
|
35775
35785
|
|
|
35776
|
-
- **The original had assertions:** write one entry per assertion, each carrying that assertion's \`assertionId\`, its own \`label\`, and its own \`annotation\` covering that one assertion and nothing else.
|
|
35777
|
-
- **The original had none:**
|
|
35786
|
+
- **The original had assertions:** write one entry per assertion, each carrying that assertion's \`assertionId\`, its own \`label\`, and its own \`annotation\` covering that one assertion and nothing else. The trace verdict is derived from the per-assertion rows.
|
|
35787
|
+
- **The original had none:** write no entry and leave it out of the \`expected*\` list. Agents do not write whole-trace verdicts, so its verdict is reported in the chat only.
|
|
35778
35788
|
- **An assertion whose target cannot be found on the trace you are judging:** \`{ "assertionId": "<uuid>", "skip": true }\` alongside the id key, never a FAIL. A missing target means the check never ran. Its sibling assertions on the same trace are still verdicted normally.
|
|
35779
35789
|
|
|
35780
|
-
|
|
35790
|
+
Lineage keying, one trace with three assertions (a second trace with no assertions is simply absent from the file):
|
|
35781
35791
|
|
|
35782
35792
|
\`\`\`json
|
|
35783
35793
|
{
|
|
35784
35794
|
"experimentId": "<experimentId>",
|
|
35785
|
-
"expectedOriginalTraceIds": ["<originalTraceId1>"
|
|
35795
|
+
"expectedOriginalTraceIds": ["<originalTraceId1>"],
|
|
35786
35796
|
"verdicts": [
|
|
35787
35797
|
{ "originalTraceId": "<originalTraceId1>", "assertionId": "<assertionId1>", "label": true, "annotation": "The rebooked leg now names the carrier this assertion asks for.", "confidence": "High" },
|
|
35788
35798
|
{ "originalTraceId": "<originalTraceId1>", "assertionId": "<assertionId2>", "label": false, "annotation": "The seat preference is still dropped." },
|
|
35789
|
-
{ "originalTraceId": "<originalTraceId1>", "assertionId": "<assertionId3>", "skip": true }
|
|
35790
|
-
{ "originalTraceId": "<originalTraceId2>", "label": true, "annotation": "No assertions on this trace, and the output addresses the original annotation." }
|
|
35799
|
+
{ "originalTraceId": "<originalTraceId1>", "assertionId": "<assertionId3>", "skip": true }
|
|
35791
35800
|
]
|
|
35792
35801
|
}
|
|
35793
35802
|
\`\`\`
|
|
35794
35803
|
|
|
35795
|
-
**The script's coverage check is per trace, not per assertion.** It cannot see
|
|
35804
|
+
**The script's coverage check is per trace, not per assertion.** It cannot see how many assertions each trace has, so it only reports a trace that got no entry at all. That makes the per-assertion count yours to hold. A trace with six assertions and one entry clears the script's check and still loses five verdicts. Count the assertions you read from {{tool:getTraceAssertions}} against the entries you wrote for that trace before you run the script.
|
|
35796
35805
|
|
|
35797
35806
|
3. Run the script:
|
|
35798
35807
|
|
|
@@ -35815,12 +35824,12 @@ Per-assertion and whole-trace entries travel in the same file and the same call,
|
|
|
35815
35824
|
title: "Verify replay labels persisted",
|
|
35816
35825
|
body: `**Verify replay labels persisted.** Route on the \`status\` field of the JSON the script printed in \`evaluate-results\`. The script is the deterministic check, if it didn't return \`ok\`, the agent's verdicts are NOT yet on the replay traces and the experiment delta will be wrong on the next iteration.
|
|
35817
35826
|
|
|
35818
|
-
**Nothing reaches the report without this: \`share-results\` and the benchmark scorecard are unreachable without a \`status: "ok"\` here.** The result carries \`idKind\` (\`originalTraceId\` for lineage keying, \`traceId\` for direct keying) and a \`persistedIds\` list under that key. \`status: "ok"\` only proves the script's \`expected*\` list was all verdicted; it does NOT prove you handed the script every completed item. Before you treat \`ok\` as a pass, **cross-check the coverage yourself**: \`persistedIds\` (plus the \`item.error\` unreplayable bucket) MUST account for every item in the replay output. If any completed (\`item.error\` unset) replay item is missing from the persisted set, you under-scoped the \`expected*\` list (most often by deciding the run "wasn't worth scoring") and the scorecard would be silently empty or partial: go back to \`evaluate-results\`, rebuild the verdicts file covering ALL completed items, and re-persist before routing onward.
|
|
35827
|
+
**Nothing reaches the report without this: \`share-results\` and the benchmark scorecard are unreachable without a \`status: "ok"\` here, unless no completed item's original has approved assertions, in which case nothing was persisted and there is nothing to verify.** The result carries \`idKind\` (\`originalTraceId\` for lineage keying, \`traceId\` for direct keying) and a \`persistedIds\` list under that key. \`status: "ok"\` only proves the script's \`expected*\` list was all verdicted; it does NOT prove you handed the script every completed item. Before you treat \`ok\` as a pass, **cross-check the coverage yourself**: \`persistedIds\` (plus the \`item.error\` unreplayable bucket and the items whose originals have no approved assertions, which are reported in chat only) MUST account for every item in the replay output. If any completed (\`item.error\` unset) replay item whose original has approved assertions is missing from the persisted set, you under-scoped the \`expected*\` list (most often by deciding the run "wasn't worth scoring") and the scorecard would be silently empty or partial: go back to \`evaluate-results\`, rebuild the verdicts file covering ALL completed items that have approved assertions, and re-persist before routing onward.
|
|
35819
35828
|
|
|
35820
35829
|
{{whens}}`,
|
|
35821
35830
|
branches: [
|
|
35822
35831
|
{
|
|
35823
|
-
when: '`status: "ok"` AND `persistedIds` (plus the unreplayable `item.error` bucket) account for every completed replay item',
|
|
35832
|
+
when: '`status: "ok"` AND `persistedIds` (plus the unreplayable `item.error` bucket and the items with no approved assertions) account for every completed replay item, or no completed item had approved assertions so nothing was persisted',
|
|
35824
35833
|
description: "labels are persisted on the replay traces and the verdicts file is gone. In `benchmark` mode continue to the benchmark scorecard (a terminal report, no iteration); in all other modes continue to share-results (experiments page was already opened before evaluation)",
|
|
35825
35834
|
next: {
|
|
35826
35835
|
byMode: {
|
|
@@ -35837,18 +35846,18 @@ Per-assertion and whole-trace entries travel in the same file and the same call,
|
|
|
35837
35846
|
}
|
|
35838
35847
|
},
|
|
35839
35848
|
{
|
|
35840
|
-
when: '`status: "ok"` but a completed (`item.error` unset) replay item is absent from `persistedIds` (your own coverage cross-check failed: the `expected*` list was under-scoped)',
|
|
35841
|
-
description: "the script passed only because you handed it a truncated `expected*` list; the report would be silently empty or partial. Go back to `evaluate-results`, rebuild the verdicts file so the `expected*` list is the full set of completed replay items, and re-persist. Do NOT proceed to the report",
|
|
35849
|
+
when: '`status: "ok"` but a completed (`item.error` unset) replay item whose original has approved assertions is absent from `persistedIds` (your own coverage cross-check failed: the `expected*` list was under-scoped)',
|
|
35850
|
+
description: "the script passed only because you handed it a truncated `expected*` list; the report would be silently empty or partial. Go back to `evaluate-results`, rebuild the verdicts file so the `expected*` list is the full set of completed replay items whose originals have approved assertions, and re-persist. Do NOT proceed to the report",
|
|
35842
35851
|
next: "iterate/evaluate-results"
|
|
35843
35852
|
},
|
|
35844
35853
|
{
|
|
35845
35854
|
when: '`status: "missing-coverage"` (script returned a non-empty `missingIds` array)',
|
|
35846
|
-
description: "you under-verdicted. Read the traces behind the missing ids, which are keyed by the run's `idKind` (in lineage mode each `missingIds` entry is the `originalTraceId` of the trace replayed from; in direct mode it is the replay trace's own server id): run `{{command:readTracesBatched}} <trace-id...> --scope full` and `Read` its `outputFile` if you didn't already, since you are persisting pass/fail verdicts on their span content. Decide each
|
|
35855
|
+
description: "you under-verdicted. Read the traces behind the missing ids, which are keyed by the run's `idKind` (in lineage mode each `missingIds` entry is the `originalTraceId` of the trace replayed from; in direct mode it is the replay trace's own server id): run `{{command:readTracesBatched}} <trace-id...> --scope full` and `Read` its `outputFile` if you didn't already, since you are persisting pass/fail verdicts on their span content. Decide each of their assertions (PASS / FAIL with annotation, or `skip: true` if genuinely ambiguous, every entry carrying its `assertionId`), write a NEW verdicts file at the same path covering ALL the originally expected ids (the script needs the full `expected*` list each call, not just the gaps), and re-run the script. Loop back here with the new result",
|
|
35847
35856
|
next: "iterate/verify-replay-labels"
|
|
35848
35857
|
},
|
|
35849
35858
|
{
|
|
35850
35859
|
when: '`status: "invalid-input"` (malformed verdicts JSON or missing fields)',
|
|
35851
|
-
description: "the verdicts file you wrote doesn't match either accepted shape. Read the script's `message` field, fix the JSON (most common: missing annotation on a non-skip entry; a lineage file missing `experimentId` or with an empty `expectedOriginalTraceIds`; a verdict keyed by the wrong id field for the file's mode), and re-run the script. Loop back here",
|
|
35860
|
+
description: "the verdicts file you wrote doesn't match either accepted shape. Read the script's `message` field, fix the JSON (most common: an entry missing its `assertionId`; missing annotation on a non-skip entry; a lineage file missing `experimentId` or with an empty `expectedOriginalTraceIds`; a verdict keyed by the wrong id field for the file's mode), and re-run the script. Loop back here",
|
|
35852
35861
|
next: "iterate/verify-replay-labels"
|
|
35853
35862
|
},
|
|
35854
35863
|
{
|
|
@@ -35888,12 +35897,12 @@ Route on the replay scope and the targeted trace's result. This split matters: i
|
|
|
35888
35897
|
next: "iterate/share-results"
|
|
35889
35898
|
},
|
|
35890
35899
|
{
|
|
35891
|
-
when: "the targeted single-trace replay verdict is PASS (
|
|
35900
|
+
when: "the targeted single-trace replay verdict is PASS (every approved assertion passes, or, when the trace has none, your in-chat judgment is a pass)",
|
|
35892
35901
|
description: "the target trace went green. Now add it to a dataset, then ask whether to re-run the full dataset, inspect the before/after, keep iterating, or stop",
|
|
35893
35902
|
next: "iterate/fix-add-to-dataset"
|
|
35894
35903
|
},
|
|
35895
35904
|
{
|
|
35896
|
-
when: "the targeted single-trace replay verdict is FAIL (
|
|
35905
|
+
when: "the targeted single-trace replay verdict is FAIL (any assertion fails, or the in-chat judgment fails), skipped, unreplayable, shape-incompatible, or could not be persisted because the item carried no persist key (neither the lineage `originalTraceId` nor a direct server `traceId`)",
|
|
35897
35906
|
description: "the fix did not prove the target trace green this pass. If `fixAddedToDataset` is not set, nothing is in a dataset yet; if it IS set (an earlier pass already added the trace to the dataset and a later iteration regressed it), the saved dataset trace is still there and this iteration just regressed it. Ask whether to inspect the before/after, keep iterating, or stop",
|
|
35898
35907
|
next: "iterate/fix-single-trace-failed"
|
|
35899
35908
|
}
|
|
@@ -35905,6 +35914,8 @@ Route on the replay scope and the targeted trace's result. This split matters: i
|
|
|
35905
35914
|
toolCalls: [
|
|
35906
35915
|
"read",
|
|
35907
35916
|
"getTraces",
|
|
35917
|
+
"getTraceAssertions",
|
|
35918
|
+
"saveTraceAssertions",
|
|
35908
35919
|
"saveHumanLabels",
|
|
35909
35920
|
"listDatasets",
|
|
35910
35921
|
"saveDataset",
|
|
@@ -35929,7 +35940,7 @@ So the prompt is **two** options when none exist yet (Create a new dataset \xB7
|
|
|
35929
35940
|
|
|
35930
35941
|
Once a dataset is chosen (existing or newly created), save the scenario, in order:
|
|
35931
35942
|
|
|
35932
|
-
1. **Label the original trace as a validated fail.**
|
|
35943
|
+
1. **Label the original trace as a validated fail.** ${FIX_SCENARIO_LABEL}
|
|
35933
35944
|
2. **Attach the trace.** Call {{tool:addTracesToDataset}} with the \`datasetId\` and \`[traceId]\` (idempotent, a safe no-op if already present). **Confirm the attach landed**: the trace is in the dataset when it was added now or was already present. If the tool reports it skipped the trace (e.g. a scope mismatch), do **not** claim it was added or set \`fixAddedToDataset\`, re-pick a dataset scoped to this function and attach again before continuing.
|
|
35934
35945
|
3. **Record whether the dataset has siblings, and load their labels if so.** Note whether the dataset already held other traces besides the one you just attached, and hold that as \`datasetHasSiblings\` (the next step uses it to decide whether re-running the whole dataset is meaningful). If there **are** siblings, the user may next choose to re-run the full dataset, so run \`{{command:readTracesBatched}} <trace-id...> --scope full\` over the dataset's trace IDs and \`Read\` the \`outputFile\` it prints, so every sibling's validated label + annotation is in context for that experiment. **Skip the load** when you just created the dataset or it held only this trace (no siblings).
|
|
35935
35946
|
|
|
@@ -36070,6 +36081,8 @@ When the user picks **Show in Bitfab**, open it with \`fixSingleTraceExperimentG
|
|
|
36070
36081
|
id: "fix-save-unfixed",
|
|
36071
36082
|
kind: "action",
|
|
36072
36083
|
toolCalls: [
|
|
36084
|
+
"getTraceAssertions",
|
|
36085
|
+
"saveTraceAssertions",
|
|
36073
36086
|
"saveHumanLabels",
|
|
36074
36087
|
"listDatasets",
|
|
36075
36088
|
"saveDataset",
|
|
@@ -36084,7 +36097,7 @@ When the user picks **Show in Bitfab**, open it with \`fixSingleTraceExperimentG
|
|
|
36084
36097
|
|
|
36085
36098
|
Otherwise add it now:
|
|
36086
36099
|
|
|
36087
|
-
1. **Label the original trace as a validated fail** using the failure annotation from Phase Fix \`resolve
|
|
36100
|
+
1. **Label the original trace as a validated fail** using the failure annotation from Phase Fix \`resolve\`. ${FIX_SCENARIO_LABEL}
|
|
36088
36101
|
2. **Pick or create a dataset:** if the user named a dataset ID, use it (first confirm it's scoped to this function via {{tool:listDatasets}}; if it belongs to a different function, {{tool:addTracesToDataset}} would silently skip every trace, so {{askVerb}} and offer a correctly-scoped dataset instead). Otherwise call {{tool:listDatasets}} with the function key and ask where to save, **keeping the prompt to at most four options, never one per dataset**: **none exist** \u2192 create silently; **exactly one** \u2192 offer "Add to \`<name>\`" (recommended) and "Create a new dataset"; **two or more** \u2192 offer, in this order, "Add to \`<recommended>\`" (the most recently used dataset with traces), "Choose a different dataset" (which then presents a second {{presentMode}} listing existing datasets, most recently used first, so the user can route to a specific one), then "Create a new dataset". Hold the \`datasetId\`.
|
|
36089
36102
|
3. **Attach the trace** with {{tool:addTracesToDataset}} (\`datasetId\`, \`[traceId]\`; idempotent). **Confirm the attach landed**: the trace is saved when it was added now or was already present. If the tool reports it skipped the trace (for example, the dataset is scoped to a different function or the trace is not in this org), do **not** claim save and do **not** set \`fixAddedToDataset\`; re-pick a dataset scoped to this function and attach again before stopping.
|
|
36090
36103
|
|
|
@@ -36239,7 +36252,7 @@ This is a single-trace, in-chat path: run the replay directly, no progress-bar w
|
|
|
36239
36252
|
|
|
36240
36253
|
There is no verdict to persist for an errored item. Offer a retry only after the diagnosed cause is addressed, or offer to stop.
|
|
36241
36254
|
|
|
36242
|
-
**If the replay completed**, call {{tool:getTraceAssertions}} with the ORIGINAL trace id first. An assertion says what the user asked this one case to do, and the replay inherits the original's assertions, so it is what the new output is measured against. Each one comes back as \`[ID: <uuid>] checks <target>: <assertion>\`, and that \`[ID: <uuid>]\` value is the \`assertionId\` its verdict carries. Only assertions a person has approved are listed, since only those are checked on a replay. "no approved assertions" means the
|
|
36255
|
+
**If the replay completed**, call {{tool:getTraceAssertions}} with the ORIGINAL trace id first. An assertion says what the user asked this one case to do, and the replay inherits the original's assertions, so it is what the new output is measured against. Each one comes back as \`[ID: <uuid>] checks <target>: <assertion>\`, and that \`[ID: <uuid>]\` value is the \`assertionId\` its verdict carries. Only assertions a person has approved are listed, since only those are checked on a replay. "no approved assertions" means the one-line verdict below stays in the chat and nothing is persisted.
|
|
36243
36256
|
|
|
36244
36257
|
**Never use a Human note as evidence for a verdict; assess only the assertion, its pass/fail criteria, and the evaluated trace.** The note is returned only so you can preserve or edit context intended for people.
|
|
36245
36258
|
|
|
@@ -36249,20 +36262,11 @@ Then compare the new output against the original trace's assertions, label, and
|
|
|
36249
36262
|
- Original was **pass**: preserved \u2192 "**Pass**: output unchanged in quality." regressed \u2192 "**Regressed**: was passing, now <what broke>."
|
|
36250
36263
|
- No label on the original: show a short before/after diff and summarize whether it looks better.
|
|
36251
36264
|
|
|
36252
|
-
**Then
|
|
36253
|
-
|
|
36254
|
-
- **If the completed item's trace id is non-null** (its value is already the SERVER replay trace id, per the \`run\` step): persist against it directly, no \`get_replay_status\` call. Write a one-entry verdicts file to an absolute path under \`<repoRoot>/.bitfab/tmp/\` (\`<repoRoot>\` = \`git rev-parse --show-toplevel\`; create the dir if missing) and run the persist script. **The verdicts file keys are the command's fixed camelCase contract (\`expectedTraceIds\`, \`traceId\`) regardless of the SDK language, its VALUE is the server trace id you held from the replay output (\`traceId\` in TS, \`trace_id\` in Python/Ruby):**
|
|
36265
|
+
**Then, when the original has approved assertions, persist one verdict per assertion onto the replay trace.** Save them so they survive the session and land on the replay trace, exactly like the full replay path. This is not optional when persistence is possible. **When the original has no approved assertions, persist nothing:** agents do not write whole-trace verdicts, so the one-line verdict stays in the chat.
|
|
36255
36266
|
|
|
36256
|
-
|
|
36257
|
-
{
|
|
36258
|
-
"expectedTraceIds": ["<server-trace-id>"],
|
|
36259
|
-
"verdicts": [
|
|
36260
|
-
{ "traceId": "<server-trace-id>", "label": true, "annotation": "<the same one-line reason you reported above>", "confidence": "High" }
|
|
36261
|
-
]
|
|
36262
|
-
}
|
|
36263
|
-
\`\`\`
|
|
36267
|
+
- **If the completed item's trace id is non-null** (its value is already the SERVER replay trace id, per the \`run\` step): persist against it directly, no \`get_replay_status\` call. Write a verdicts file to an absolute path under \`<repoRoot>/.bitfab/tmp/\` (\`<repoRoot>\` = \`git rev-parse --show-toplevel\`; create the dir if missing) and run the persist script. **The verdicts file keys are the command's fixed camelCase contract (\`expectedTraceIds\`, \`traceId\`) regardless of the SDK language, its VALUE is the server trace id you held from the replay output (\`traceId\` in TS, \`trace_id\` in Python/Ruby):**
|
|
36264
36268
|
|
|
36265
|
-
|
|
36269
|
+
The file carries one entry per assertion, each carrying its \`assertionId\`, its own \`label\`, and its own \`annotation\` for that assertion alone. The script rejects an entry without an \`assertionId\` with \`status: "invalid-input"\`. An assertion whose target cannot be found on the replay trace gets \`{ "traceId": "<server-trace-id>", "assertionId": "<uuid>", "skip": true }\`, never a FAIL, and its siblings are still verdicted:
|
|
36266
36270
|
|
|
36267
36271
|
\`\`\`json
|
|
36268
36272
|
{
|
|
@@ -36278,9 +36282,9 @@ Then compare the new output against the original trace's assertions, label, and
|
|
|
36278
36282
|
{{command:persistReplayLabels}} <repoRoot>/.bitfab/tmp/verdicts-<experiment-id>.json
|
|
36279
36283
|
\`\`\`
|
|
36280
36284
|
|
|
36281
|
-
\`label\` is \`true\`
|
|
36285
|
+
Each entry's \`label\` is \`true\` when that assertion passes on the replay and \`false\` when it fails. Read the script's single JSON status line: \`ok\` means the verdicts are now on the replay trace, add "\xB7 saved" to your one-line report.
|
|
36282
36286
|
- **If the completed item's trace id is \`null\`** (old server/SDK that returns no server-trace-id mapping, from the \`run\` step's note): persistence is impossible. Keep the verdict in-chat only and tell the user once: "This replay didn't return a server trace ID, so the verdict can't be saved. Upgrade the SDK/server." Don't block the flow on it.
|
|
36283
|
-
- **
|
|
36287
|
+
- **Original with no approved assertions:** there's nothing to persist, just report the one-line verdict (or, for a no-label original, the before/after diff). An unlabeled original that HAS assertions is not this case, the assertions are the criteria, so score them one per assertion and persist them.
|
|
36284
36288
|
|
|
36285
36289
|
{{branches}}`,
|
|
36286
36290
|
branches: [
|
package/package.json
CHANGED