agent-qa 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -3
- package/capability-catalog/capability-catalog.json +51 -327
- package/dist/cli-main.cjs +5658 -11153
- package/dist/cli-main.cjs.map +1 -1
- package/dist/cli-main.js +4451 -10024
- package/dist/cli-main.js.map +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +332 -3875
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -348
- package/dist/index.d.ts +12 -348
- package/dist/index.js +282 -3963
- package/dist/index.js.map +1 -1
- package/package.json +31 -23
- package/skills/agent-qa-authoring/SKILL.md +5 -11
- package/skills/agent-qa-authoring/references/agent-qa-contracts.json +2 -4
- package/skills/agent-qa-debug-fix/SKILL.md +1 -4
- package/skills/agent-qa-knowledge/SKILL.md +2 -4
- package/skills/agent-qa-knowledge/references/knowledge-reads.md +19 -22
- package/skills/agent-qa-result-triage/SKILL.md +0 -2
- package/skills/agent-qa-result-triage/references/triage-categories.md +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,39 +1,13 @@
|
|
|
1
1
|
import { AgentQaConfig, resolveKnowledgeRoot } from '@vostride/agent-qa-core';
|
|
2
2
|
export * from '@vostride/agent-qa-core';
|
|
3
|
-
import { MaskFn, CompletedRunHandoff,
|
|
4
|
-
import { CuratorAuthor,
|
|
3
|
+
import { MaskFn, CompletedRunHandoff, CanaryOutcome, RecordId, Anchor, RunAnalysisArtifact, CurationSnapshot } from '@vostride/agent-qa-schema';
|
|
4
|
+
import { CuratorAuthor, CurationCandidateProjection, EnqueueCurationCandidateProjectionOutcome, OpenServingKnowledgeReaderConfig, LocalCurationCapabilities } from '@vostride/agent-qa-agent-local';
|
|
5
5
|
import { ModelProfile, ResolvedModelAuth, createModel, resolveCapabilities, createTokenAccounting } from '@vostride/agent-qa-model';
|
|
6
6
|
import { KnowledgeHintReader } from '@vostride/agent-qa-runner';
|
|
7
7
|
import { loadCompletedRunHandoff } from '@vostride/agent-qa-runner-local';
|
|
8
8
|
import { runSafetyScope } from '@vostride/agent-qa-safety-local';
|
|
9
9
|
import { z } from 'zod';
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
* A committed config the parser cannot read, named by WHERE and never by WHAT (395-14 · F-01).
|
|
13
|
-
*
|
|
14
|
-
* WHY A NAMED CLASS AND NOT A BARE `Error`. This throw leaves the loader through every config op,
|
|
15
|
-
* through `resolveConfig`, and — via `refusalMessage` — into a chat turn's answer and its session
|
|
16
|
-
* jsonl. A caller that wants to recognise the case (F-03's typed-refusal arm, later) needs
|
|
17
|
-
* something better than a substring match on a sentence; `name` and `configPath` are that. The
|
|
18
|
-
* message itself carries the path because the surfaces this reaches print a message and nothing
|
|
19
|
-
* else.
|
|
20
|
-
*
|
|
21
|
-
* NO MEMBER INTERPOLATES THE CAUGHT ERROR'S OWN TEXT. `yaml` pretty-prints its parse errors with a
|
|
22
|
-
* verbatim excerpt of the person's source, which was measured carrying a planted credential out of
|
|
23
|
-
* three ops.
|
|
24
|
-
*
|
|
25
|
-
* AND THE CAUSE IS SANITIZED TOO, BECAUSE THE RUNTIME READS THE CAUSE CHAIN (395-16 · CR-01).
|
|
26
|
-
* An earlier revision of this class attached the raw `YAMLParseError` as `cause` on the reasoning
|
|
27
|
-
* that no composed member reads it. That reasoning was false of the runtime, and shipped as a
|
|
28
|
-
* defect: a thrown value is read by far more than the code that composed it. Node's DEFAULT
|
|
29
|
-
* UNCAUGHT PRINTER renders an error with `util.inspect`, which walks `cause` and prints it under a
|
|
30
|
-
* `[cause]:` header — `agent-qa config show` awaited this loader bare and crash-printed the
|
|
31
|
-
* person's own file back at them. `console.error(err)` does the same thing, structured loggers
|
|
32
|
-
* (pino, winston) serialize the chain, and crash reporters upload it. So the cause carried here is
|
|
33
|
-
* a fresh `Error` holding THE SAME POSITION-ONLY SENTENCE as the message: a debugger still gets a
|
|
34
|
-
* chain to walk, and every one of those readers gets a position and nothing else. The caught value
|
|
35
|
-
* is not reachable from this error by any property.
|
|
36
|
-
*/
|
|
37
11
|
declare class ConfigFileNotYamlError extends Error {
|
|
38
12
|
readonly configPath: string;
|
|
39
13
|
constructor(configPath: string, cause: unknown);
|
|
@@ -41,24 +15,12 @@ declare class ConfigFileNotYamlError extends Error {
|
|
|
41
15
|
/**
|
|
42
16
|
* THE READ AND THE PARSE GET SEPARATE CATCHES, and that separation is load-bearing.
|
|
43
17
|
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
18
|
+
* A missing file reads as `{}` here; `loadRequiredConfigFile` throws `ConfigFileNotFoundError`,
|
|
19
|
+
* whose message is `Config file not found: <path>`. A non-ENOENT read fault (EISDIR, EACCES) is a
|
|
20
|
+
* fact about the filesystem and not about the file's content, and is rethrown untouched. Only the
|
|
21
|
+
* PARSE throw is reworded — one guard, aimed at one channel.
|
|
48
22
|
*/
|
|
49
23
|
declare function loadConfigFile(configPath: string): Promise<unknown>;
|
|
50
|
-
/**
|
|
51
|
-
* The absent-file case, RECOGNISABLE WITHOUT A SUBSTRING MATCH ON ITS OWN SENTENCE (395-27 · WR-02).
|
|
52
|
-
*
|
|
53
|
-
* The message is BYTE-IDENTICAL to the bare `Error` this replaces, because that exact string is
|
|
54
|
-
* matched by the live lane's third-arm detection (`scripts/395-live-from-zero.sh:514`), folded by
|
|
55
|
-
* `coveringGrantInForce`, and pinned by `(E1)`. Nothing about what any surface PRINTS changes.
|
|
56
|
-
*
|
|
57
|
-
* What changes is that `showAction` can now ask WHICH fault this is by `instanceof` instead of by
|
|
58
|
-
* `message.startsWith(...)` — the reasoning `ConfigFileNotYamlError` above already wrote down: a
|
|
59
|
-
* caller that wants to recognise the case needs something better than a substring match on a
|
|
60
|
-
* sentence, and a sentence-matching caller is one rewording away from silently taking the wrong arm.
|
|
61
|
-
*/
|
|
62
24
|
declare class ConfigFileNotFoundError extends Error {
|
|
63
25
|
readonly configPath: string;
|
|
64
26
|
constructor(configPath: string);
|
|
@@ -68,50 +30,9 @@ declare function loadEnvOverrides(): Record<string, unknown>;
|
|
|
68
30
|
declare function mergeConfigs(file: unknown, env: Record<string, unknown>, flags: Record<string, unknown>): unknown;
|
|
69
31
|
declare function deepMerge(base: unknown, override: unknown): unknown;
|
|
70
32
|
declare function mergeWithTestConfig(globalConfig: unknown, testConfig: unknown, flagOverrides: Record<string, unknown>): unknown;
|
|
71
|
-
/**
|
|
72
|
-
* The one shaping `resolveConfig` applies before validating (395-01: exported, not moved).
|
|
73
|
-
*
|
|
74
|
-
* EXPORTED so the config-mutation validate port can run the SAME three steps a real run runs —
|
|
75
|
-
* merge, normalize, parse. A port that skipped this would refuse a candidate whose only problem is
|
|
76
|
-
* a key `resolveConfig` deletes, which is a proposal blocked by a rule nothing enforces.
|
|
77
|
-
*/
|
|
78
33
|
declare function normalizeConfigForValidation(config: unknown): unknown;
|
|
79
34
|
declare function mergeUseBlocks(globalUse: Record<string, unknown> | undefined, suiteUse: Record<string, unknown> | undefined, testUse: Record<string, unknown> | undefined, cliFlags: Record<string, unknown>): Record<string, unknown>;
|
|
80
35
|
declare function formatConfigDebug(globalConfig: unknown, testConfig: unknown, flagOverrides: Record<string, unknown>, mask: MaskFn): string;
|
|
81
|
-
/**
|
|
82
|
-
* The privacy declaration alone, read out of the RAW document without validating it
|
|
83
|
-
* (Phase 398 gap round — finding F-1 / review CR-01).
|
|
84
|
-
*
|
|
85
|
-
* WHY THIS EXISTS. The composition roots that emit telemetry read their config through a tolerant
|
|
86
|
-
* `try { resolveConfig() } catch { undefined }`, because refusing a surface over its telemetry
|
|
87
|
-
* configuration would be refusing the capability over the telemetry (T-398-32). That posture is
|
|
88
|
-
* right and is unchanged. What was wrong is that the `catch` collapsed TWO different states into
|
|
89
|
-
* one `undefined`:
|
|
90
|
-
*
|
|
91
|
-
* 1. there is no config file — nothing was ever declared, and
|
|
92
|
-
* 2. there IS one, it declares `analytics.privacy: true`, and it failed schema validation for
|
|
93
|
-
* some unrelated reason (a missing `use.mobile.appState`, a legacy key, a typo three levels
|
|
94
|
-
* away).
|
|
95
|
-
*
|
|
96
|
-
* In state 2 a user set a shipped privacy control and was transmitted anyway, silently, with the
|
|
97
|
-
* surface working normally so nothing ever looked wrong. A product cannot ship a config field and
|
|
98
|
-
* then honour it only when the REST of the document happens to validate.
|
|
99
|
-
*
|
|
100
|
-
* READING IS NOT VALIDATING. Extracting one boolean from a parsed document does not require the
|
|
101
|
-
* document to be well-formed anywhere else, so honouring the declaration costs the availability
|
|
102
|
-
* posture nothing: the surface still opens, still scaffolds, still runs.
|
|
103
|
-
*
|
|
104
|
-
* IT READS THE SAME FILE THE VALIDATOR READ. The path expression and the loader are literally the
|
|
105
|
-
* ones `resolveConfig` uses, immediately below — not a second spelling of "where the config lives".
|
|
106
|
-
* A privacy read that consults a different file than the validator did would be a new defect
|
|
107
|
-
* wearing this fix's clothes.
|
|
108
|
-
*
|
|
109
|
-
* THE UNPARSEABLE ARM FAILS CLOSED. If the file exists but is not YAML at all, no reader can tell
|
|
110
|
-
* whether the declaration is in there. The cost of opting out is lost telemetry; the cost of the
|
|
111
|
-
* other choice is transmitting against a declaration that may be sitting in the file. For a
|
|
112
|
-
* privacy control the doubt resolves toward the user. A MISSING file is not doubt — nothing was
|
|
113
|
-
* declared — so it stays `undefined`.
|
|
114
|
-
*/
|
|
115
36
|
declare function readAnalyticsPrivacyDeclaration(configPath?: string): Promise<{
|
|
116
37
|
analytics: {
|
|
117
38
|
privacy: boolean;
|
|
@@ -125,19 +46,6 @@ declare function resolveConfig(options: {
|
|
|
125
46
|
declare function loadConfigPlugins(config: AgentQaConfig, configPath: string): Promise<void>;
|
|
126
47
|
/** Resolve the writer-gated knowledge root without making each CLI surface import core directly. */
|
|
127
48
|
declare function resolveConfiguredKnowledgeRoot(config: AgentQaConfig, configDir: string): string;
|
|
128
|
-
/**
|
|
129
|
-
* The same root, from a RAW config (407 D-18/D-19).
|
|
130
|
-
*
|
|
131
|
-
* `doctor` deliberately loads the config unvalidated so it can REPORT on a broken one, so it
|
|
132
|
-
* never has an `AgentQaConfig` to hand the resolver above. The schema's own `dir` default is what
|
|
133
|
-
* makes the root knowable from a config that never mentions `knowledge` at all — reproducing that
|
|
134
|
-
* default at a call site would be a second answer to "where does this project keep its knowledge".
|
|
135
|
-
*
|
|
136
|
-
* `null` means the question has no answer for this host: the config could not be read, or its
|
|
137
|
-
* `knowledge` block is malformed enough that the directory cannot be named. It does NOT mean the
|
|
138
|
-
* directory is missing — that is the caller's own existence check, because "configured but absent"
|
|
139
|
-
* and "not configured" read the same to a resolver and differently to a person.
|
|
140
|
-
*/
|
|
141
49
|
declare function resolveRawConfigKnowledgeRoot(config: Record<string, unknown> | null, configPath: string): string | null;
|
|
142
50
|
|
|
143
51
|
interface ResolvedTarget {
|
|
@@ -149,7 +57,6 @@ interface ResolvedTarget {
|
|
|
149
57
|
appPackage?: string;
|
|
150
58
|
appActivity?: string;
|
|
151
59
|
device?: string;
|
|
152
|
-
/** D-06 (phase 401), 432 D-18: the target's declared environment; `undefined` means undeclared. */
|
|
153
60
|
environment?: 'local' | 'shared' | 'production';
|
|
154
61
|
}
|
|
155
62
|
interface TargetSummary {
|
|
@@ -159,21 +66,14 @@ interface TargetSummary {
|
|
|
159
66
|
declare function resolveTarget(config: AgentQaConfig, targetName: string): ResolvedTarget;
|
|
160
67
|
declare function listTargets(config: AgentQaConfig): TargetSummary[];
|
|
161
68
|
|
|
162
|
-
/**
|
|
163
|
-
* Per-test targeting is not possible under the redesigned cache: keys are opaque,
|
|
164
|
-
* content-addressed over masked step text + a runtime screenIdentityHash, and carry no raw
|
|
165
|
-
* step text on disk (D-06). `--test` therefore clears the whole branch-namespaced cache so
|
|
166
|
-
* the requested test's stale plans are guaranteed gone.
|
|
167
|
-
*/
|
|
168
|
-
declare function purgeTest(_testPath: string, cacheDir: string): Promise<number>;
|
|
169
69
|
declare function purgeAll(cacheDir: string): Promise<number>;
|
|
170
70
|
|
|
171
|
-
/**
|
|
71
|
+
/** Malformed-output retry cap; deliberately not a user-facing config key. */
|
|
172
72
|
declare const CURATOR_RETRY_CAP = 2;
|
|
173
|
-
/**
|
|
73
|
+
/** Neighbor presentation cap; deliberately not a user-facing config key. */
|
|
174
74
|
declare const CURATOR_MAX_NEIGHBORS = 12;
|
|
175
75
|
interface BuildCuratorAuthorDependencies {
|
|
176
|
-
/** The run's already-resolved registry profile. It is reused verbatim
|
|
76
|
+
/** The run's already-resolved registry profile. It is reused verbatim. */
|
|
177
77
|
profile: ModelProfile;
|
|
178
78
|
auth: ResolvedModelAuth;
|
|
179
79
|
mask: MaskFn;
|
|
@@ -205,153 +105,16 @@ interface NudgeDaemonOptions {
|
|
|
205
105
|
/** Best-effort curation wake-up returning the durable session identity when acknowledged. */
|
|
206
106
|
declare function nudgeDaemon(agentQaRoot: string, options?: NudgeDaemonOptions): Promise<NudgeResponse | undefined>;
|
|
207
107
|
|
|
208
|
-
declare const completionSchema: z.ZodObject<{
|
|
209
|
-
schemaVersion: z.ZodLiteral<2>;
|
|
210
|
-
runId: z.ZodString;
|
|
211
|
-
candidateId: z.ZodString;
|
|
212
|
-
attributedObservationId: z.ZodString;
|
|
213
|
-
observedStateDigest: z.ZodString;
|
|
214
|
-
result: z.ZodUnion<readonly [z.ZodObject<{
|
|
215
|
-
kind: z.ZodLiteral<"ablated">;
|
|
216
|
-
crossCheck: z.ZodEnum<{
|
|
217
|
-
flipped: "flipped";
|
|
218
|
-
held: "held";
|
|
219
|
-
inconclusive: "inconclusive";
|
|
220
|
-
"not-run": "not-run";
|
|
221
|
-
}>;
|
|
222
|
-
corpusDigest: z.ZodString;
|
|
223
|
-
k: z.ZodNumber;
|
|
224
|
-
baseSeed: z.ZodNumber;
|
|
225
|
-
seeds: z.ZodArray<z.ZodNumber>;
|
|
226
|
-
seedHonored: z.ZodBoolean;
|
|
227
|
-
spend: z.ZodObject<{
|
|
228
|
-
modelCalls: z.ZodNumber;
|
|
229
|
-
screens: z.ZodNumber;
|
|
230
|
-
promptTokens: z.ZodNumber;
|
|
231
|
-
completionTokens: z.ZodNumber;
|
|
232
|
-
}, z.core.$strict>;
|
|
233
|
-
gate2Dir: z.ZodString;
|
|
234
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
235
|
-
kind: z.ZodLiteral<"error">;
|
|
236
|
-
message: z.ZodLiteral<"ablation-error">;
|
|
237
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
238
|
-
kind: z.ZodLiteral<"unavailable">;
|
|
239
|
-
reason: z.ZodLiteral<"ablation-unavailable">;
|
|
240
|
-
}, z.core.$strict>, z.ZodObject<{
|
|
241
|
-
kind: z.ZodLiteral<"no-eval-root">;
|
|
242
|
-
}, z.core.$strict>]>;
|
|
243
|
-
}, z.core.$strict>;
|
|
244
|
-
type CompletedCanaryAblation = z.infer<typeof completionSchema>;
|
|
245
|
-
|
|
246
|
-
interface CanaryAblationInput {
|
|
247
|
-
handoff: CompletedRunHandoff;
|
|
248
|
-
runDir: string;
|
|
249
|
-
candidateId: string;
|
|
250
|
-
runId: string;
|
|
251
|
-
testId: string;
|
|
252
|
-
testIndex: number;
|
|
253
|
-
stepIndex: number;
|
|
254
|
-
attributedObservationId: string;
|
|
255
|
-
}
|
|
256
|
-
type CanaryAblationResult = {
|
|
257
|
-
kind: 'ablated';
|
|
258
|
-
crossCheck: CrossCheck;
|
|
259
|
-
corpusDigest: string;
|
|
260
|
-
k: number;
|
|
261
|
-
baseSeed: number;
|
|
262
|
-
seeds: number[];
|
|
263
|
-
seedHonored: boolean;
|
|
264
|
-
spend: {
|
|
265
|
-
modelCalls: number;
|
|
266
|
-
screens: number;
|
|
267
|
-
promptTokens: number;
|
|
268
|
-
completionTokens: number;
|
|
269
|
-
};
|
|
270
|
-
gate2Dir: string;
|
|
271
|
-
} | {
|
|
272
|
-
kind: 'no-eval-root';
|
|
273
|
-
} | {
|
|
274
|
-
kind: 'unavailable';
|
|
275
|
-
reason: string;
|
|
276
|
-
} | {
|
|
277
|
-
kind: 'error';
|
|
278
|
-
message: string;
|
|
279
|
-
};
|
|
280
|
-
type CanaryCorpusDigestResult = {
|
|
281
|
-
kind: 'digest';
|
|
282
|
-
corpusDigest: string;
|
|
283
|
-
} | {
|
|
284
|
-
kind: 'no-eval-root';
|
|
285
|
-
} | {
|
|
286
|
-
kind: 'unavailable';
|
|
287
|
-
reason: string;
|
|
288
|
-
};
|
|
289
|
-
interface CanaryAblation {
|
|
290
|
-
isNovel(input: {
|
|
291
|
-
product: string;
|
|
292
|
-
stateDigest: string;
|
|
293
|
-
}): Promise<boolean>;
|
|
294
|
-
stage(input: CanaryAblationInput & {
|
|
295
|
-
product: string;
|
|
296
|
-
stateDigest: string;
|
|
297
|
-
benefit: Benefit;
|
|
298
|
-
}): Promise<{
|
|
299
|
-
kind: 'staged' | 'already-staged' | 'not-novel' | 'no-eval-root' | 'target-unavailable' | 'write-conflict';
|
|
300
|
-
} | {
|
|
301
|
-
kind: 'unavailable';
|
|
302
|
-
reason: string;
|
|
303
|
-
} | {
|
|
304
|
-
kind: 'error';
|
|
305
|
-
message: string;
|
|
306
|
-
}>;
|
|
307
|
-
ablate(input: CanaryAblationInput): Promise<CanaryAblationResult>;
|
|
308
|
-
corpusDigest(): Promise<CanaryCorpusDigestResult>;
|
|
309
|
-
resolveCaps?(): Promise<{
|
|
310
|
-
maxAblations: number;
|
|
311
|
-
source: string;
|
|
312
|
-
}>;
|
|
313
|
-
}
|
|
314
108
|
interface CanaryCreditCounters {
|
|
315
|
-
credited: number;
|
|
316
109
|
rideAlongUncredited: number;
|
|
317
|
-
ablationCapExceeded: number;
|
|
318
|
-
ablationHeld: number;
|
|
319
|
-
ablationInconclusive: number;
|
|
320
|
-
ablationError: number;
|
|
321
110
|
rerunExcluded: number;
|
|
322
111
|
nonSignal: Record<NonSignalClass, number>;
|
|
323
|
-
staged: number;
|
|
324
|
-
stagingRefused: number;
|
|
325
|
-
}
|
|
326
|
-
interface CanaryCreditInput {
|
|
327
|
-
completedAblations: readonly CompletedCanaryAblation[];
|
|
328
|
-
saveCompletedAblation: (entry: CompletedCanaryAblation) => void;
|
|
329
|
-
maxAblations: number;
|
|
330
|
-
knowledgeRoot: string;
|
|
331
|
-
readBundleStateDigests: LocalCurationCapabilities['readCanonicalBundleStateDigests'];
|
|
332
|
-
attributions: readonly ProjectedCanaryAttribution[];
|
|
333
|
-
ablation?: CanaryAblation;
|
|
334
|
-
handoff: CompletedRunHandoff;
|
|
335
|
-
runDir: string;
|
|
336
|
-
observationIdOf: (outcome: CanaryOutcome) => string;
|
|
337
112
|
}
|
|
338
113
|
|
|
339
114
|
type NonSignalClass = 'environment' | 'timeout' | 'flake';
|
|
340
115
|
interface ProjectCanaryOutcomesInput {
|
|
341
116
|
handoff: CompletedRunHandoff;
|
|
342
|
-
/**
|
|
343
|
-
* The run's persisted analysis artifact when one exists (389-06, D-10). Data, not a port:
|
|
344
|
-
* `emitCanaryOutcomes` loads it from the fold's own `runDir`; the pure projection takes it
|
|
345
|
-
* as an argument so identical (handoff, artifact) bytes keep yielding identical outcomes.
|
|
346
|
-
*/
|
|
347
117
|
artifact?: RunAnalysisArtifact;
|
|
348
|
-
/**
|
|
349
|
-
* The circularity arbiter's per-record rows (422-16, D-20), when the handoff's own published
|
|
350
|
-
* sidecar is not the copy the caller holds — `emitCanaryOutcomes` supplies this only for the
|
|
351
|
-
* durable-recovery case below. ABSENT here means "read `handoff.misdirectionEvidence`", which
|
|
352
|
-
* is itself absent on every run written before 422. Data, never a port.
|
|
353
|
-
*/
|
|
354
|
-
misdirectionEvidence?: readonly MisdirectionRecordResult[];
|
|
355
118
|
resolveCandidateAnchors: (id: RecordId) => CandidateAnchorResolution;
|
|
356
119
|
}
|
|
357
120
|
type CandidateAnchorResolution = {
|
|
@@ -378,11 +141,7 @@ type EmitCanaryOutcomeResult = {
|
|
|
378
141
|
detail?: string;
|
|
379
142
|
};
|
|
380
143
|
type EmitCanaryOutcomeDetail = {
|
|
381
|
-
staged?: never;
|
|
382
144
|
benefit?: never;
|
|
383
|
-
crossCheck?: never;
|
|
384
|
-
corpusDigestMoved?: never;
|
|
385
|
-
misdirectionFlipped?: never;
|
|
386
145
|
candidateId?: never;
|
|
387
146
|
stepIndex?: never;
|
|
388
147
|
testId: string;
|
|
@@ -391,17 +150,7 @@ type EmitCanaryOutcomeDetail = {
|
|
|
391
150
|
kind: 'rerun-excluded';
|
|
392
151
|
};
|
|
393
152
|
} | {
|
|
394
|
-
staged?: true;
|
|
395
153
|
benefit: CanaryOutcome['benefit'];
|
|
396
|
-
crossCheck?: CrossCheck;
|
|
397
|
-
corpusDigestMoved?: true;
|
|
398
|
-
/**
|
|
399
|
-
* 422-16 (D-20): this row reached the harmed-outcome writer on the arbiter's CAUSAL evidence,
|
|
400
|
-
* not on anchor contact. Distinct from `crossCheck` beside it, which is the Gate-2 ABLATION's
|
|
401
|
-
* answer about a positive — two different cross-checks, and conflating them would read a
|
|
402
|
-
* removal that proved harm as a removal that proved benefit.
|
|
403
|
-
*/
|
|
404
|
-
misdirectionFlipped?: true;
|
|
405
154
|
candidateId: RecordId;
|
|
406
155
|
testId: string;
|
|
407
156
|
testIndex: number;
|
|
@@ -413,24 +162,14 @@ interface EmitCanaryOutcomeReport {
|
|
|
413
162
|
emitted: number;
|
|
414
163
|
refused: number;
|
|
415
164
|
unresolved: number;
|
|
416
|
-
/** D-12: per-source split of the planSource attribution stream; totals cover BOTH sources. */
|
|
417
165
|
procedure: {
|
|
418
166
|
emitted: number;
|
|
419
167
|
refused: number;
|
|
420
168
|
unresolved: number;
|
|
421
169
|
};
|
|
422
|
-
/**
|
|
423
|
-
* 422-16 (D-20): rows the arbiter's FLIPPED evidence sent to the harmed-outcome writer carry
|
|
424
|
-
* `misdirectionFlipped` on their DETAIL, and are counted from there rather than from a
|
|
425
|
-
* fourth total here. Two reasons, both deliberate. The consequence is the ordinary harmed
|
|
426
|
-
* outcome, so a parallel total would invite a reader to add it to `emitted`. And a required
|
|
427
|
-
* new member on this report would break every existing `emitCanaryOutcomes` double in the
|
|
428
|
-
* tree — the tail reads the rows it already renders, exactly as `candidateSummaryLine` does.
|
|
429
|
-
*/
|
|
430
170
|
details: EmitCanaryOutcomeDetail[];
|
|
431
171
|
}
|
|
432
172
|
interface EmitCanaryOutcomesOptions {
|
|
433
|
-
credit?: Partial<Pick<CanaryCreditInput, 'ablation' | 'maxAblations' | 'readBundleStateDigests'>>;
|
|
434
173
|
handoff: CompletedRunHandoff;
|
|
435
174
|
/** Absolute agent-qa-knowledge root used for store lookup and durable queue emission. */
|
|
436
175
|
knowledgeRoot: string;
|
|
@@ -438,40 +177,9 @@ interface EmitCanaryOutcomesOptions {
|
|
|
438
177
|
runDir: string;
|
|
439
178
|
emit: (outcome: CanaryOutcome) => void;
|
|
440
179
|
}
|
|
441
|
-
type AttributionSource = 'served-brief' | 'procedure-replay';
|
|
442
|
-
type ProjectedCanaryAttribution = {
|
|
443
|
-
kind: 'rerun-excluded';
|
|
444
|
-
testId: string;
|
|
445
|
-
testIndex: number;
|
|
446
|
-
} | {
|
|
447
|
-
kind: 'non-signal';
|
|
448
|
-
source: AttributionSource;
|
|
449
|
-
class: NonSignalClass;
|
|
450
|
-
candidateId: RecordId;
|
|
451
|
-
testId: string;
|
|
452
|
-
testIndex: number;
|
|
453
|
-
stepIndex: number;
|
|
454
|
-
} | {
|
|
455
|
-
kind: 'outcome';
|
|
456
|
-
source: AttributionSource;
|
|
457
|
-
outcome: CanaryOutcome;
|
|
458
|
-
/** 422-16 (D-20): selected by the arbiter's flipped row rather than by anchor contact. */
|
|
459
|
-
misdirectionFlipped?: true;
|
|
460
|
-
} | {
|
|
461
|
-
kind: 'unresolved';
|
|
462
|
-
source: AttributionSource;
|
|
463
|
-
candidateId: RecordId;
|
|
464
|
-
testId: string;
|
|
465
|
-
testIndex: number;
|
|
466
|
-
stepIndex: number;
|
|
467
|
-
reason: Extract<CandidateAnchorResolution, {
|
|
468
|
-
kind: 'unresolved';
|
|
469
|
-
}>['reason'];
|
|
470
|
-
detail?: string;
|
|
471
|
-
};
|
|
472
180
|
/** Pure Pin-1 projection: identical handoff + canonical snapshot yields identical outcome bytes. */
|
|
473
181
|
declare function projectCanaryOutcomes(input: ProjectCanaryOutcomesInput): CanaryOutcome[];
|
|
474
|
-
/** Resolves anchors read-only,
|
|
182
|
+
/** Resolves anchors read-only, sets each benefit, then emits every attribution in isolation. */
|
|
475
183
|
declare function emitCanaryOutcomes(opts: EmitCanaryOutcomesOptions): Promise<EmitCanaryOutcomeReport>;
|
|
476
184
|
|
|
477
185
|
interface EmitCurationCandidatesOptions {
|
|
@@ -488,7 +196,7 @@ type EmitCandidateOutcome = EnqueueCurationCandidateProjectionOutcome | {
|
|
|
488
196
|
};
|
|
489
197
|
interface EmitCandidateDetail {
|
|
490
198
|
candidateId?: string;
|
|
491
|
-
/** The projecting candidate's kind: per-kind outcome attribution from the report alone
|
|
199
|
+
/** The projecting candidate's kind: per-kind outcome attribution from the report alone. */
|
|
492
200
|
kind: CurationCandidateProjection['kind'];
|
|
493
201
|
outcome: EmitCandidateOutcome;
|
|
494
202
|
}
|
|
@@ -505,8 +213,6 @@ interface EmitReport {
|
|
|
505
213
|
*/
|
|
506
214
|
declare function emitCurationCandidates(opts: EmitCurationCandidatesOptions): EmitReport;
|
|
507
215
|
|
|
508
|
-
type ResolveTrustPriors = () => TrustPriorTable | undefined;
|
|
509
|
-
|
|
510
216
|
type RunCurationContext = {
|
|
511
217
|
readonly mask: MaskFn;
|
|
512
218
|
readonly knowledgeRoot: string;
|
|
@@ -532,14 +238,6 @@ interface CurationTailReceipt {
|
|
|
532
238
|
schemaVersion: typeof CURATION_TAIL_RECEIPT_SCHEMA_VERSION;
|
|
533
239
|
runId: string;
|
|
534
240
|
handoffDigest: string;
|
|
535
|
-
/**
|
|
536
|
-
* Run-outcome projection component: a version bump refolds already-receipted runs.
|
|
537
|
-
*
|
|
538
|
-
* This field alone only re-RUNS them (393 CR-02). The per-run fold behind it short-circuits on
|
|
539
|
-
* the durable applied-runs marker, so what actually re-derives the rows at a new version is the
|
|
540
|
-
* once-per-sweep `reconcileRunOutcomesProjection` below; the receipts then rewrite here at the
|
|
541
|
-
* new version through this same sweep.
|
|
542
|
-
*/
|
|
543
241
|
projectionVersion: number;
|
|
544
242
|
drainKind: 'drained' | 'handed-off';
|
|
545
243
|
/** The standing writer that accepted the handoff; null when this run drained inline. */
|
|
@@ -550,12 +248,9 @@ interface CurationTailReceipt {
|
|
|
550
248
|
type CurationTailModelDeps = Pick<BuildCuratorAuthorDependencies, 'profile' | 'auth'>;
|
|
551
249
|
interface CurationTailLogger {
|
|
552
250
|
warn(message: string): void;
|
|
553
|
-
/** 381-50: carries the per-run candidate summary line — diagnosis, never control flow. */
|
|
554
251
|
info?(message: string): void;
|
|
555
252
|
}
|
|
556
253
|
interface CurationTailOperations {
|
|
557
|
-
/** 2026-09-07, 418 D-01: eval-composing hosts bind this in Plan 05; BYOM omits it. */
|
|
558
|
-
canaryAblation?: CanaryAblation;
|
|
559
254
|
loadCompletedRunHandoff: typeof loadCompletedRunHandoff;
|
|
560
255
|
resolveKnowledgeRoot: typeof resolveKnowledgeRoot;
|
|
561
256
|
runSafetyScope: typeof runSafetyScope;
|
|
@@ -574,24 +269,11 @@ interface CurationTailOperations {
|
|
|
574
269
|
nudgeDaemon: typeof nudgeDaemon;
|
|
575
270
|
resolveRunCurationContext: typeof resolveRunCurationContext;
|
|
576
271
|
}
|
|
577
|
-
/**
|
|
578
|
-
* Writer authority the CALLER already holds (388-02, Pitfall 7).
|
|
579
|
-
*
|
|
580
|
-
* The fold acquires no lease of its own when this is present. That is not an optimisation: a
|
|
581
|
-
* process holding the writer lease that calls `acquireWriterLease` again is told `held` — by
|
|
582
|
-
* itself — and would hand its own work off to nobody (the 387-08 D-18 shape).
|
|
583
|
-
*/
|
|
584
272
|
interface CurationWriterAuthority {
|
|
585
273
|
leaseId: string;
|
|
586
274
|
epoch: number;
|
|
587
275
|
}
|
|
588
276
|
interface RunCurationTailOptions {
|
|
589
|
-
resolveTrustPriors?: ResolveTrustPriors;
|
|
590
|
-
maxAblations?: number;
|
|
591
|
-
/** Built once by the caller; synchronous per candidate inside writer authority. */
|
|
592
|
-
resolvePromotionPosture?: ResolvePromotionPosture;
|
|
593
|
-
/** 418 D-13: reuse prepared identity but bind each swept handoff's own ordinal. */
|
|
594
|
-
resolvePromotionPostureForRun?: (buildOrdinal: number, scope?: ScopeKey) => Promise<ResolvePromotionPosture | undefined>;
|
|
595
277
|
runId: string;
|
|
596
278
|
/** Absolute `.agent-qa` runtime root the run handoff lives under. */
|
|
597
279
|
agentQaRoot: string;
|
|
@@ -604,9 +286,7 @@ interface RunCurationTailOptions {
|
|
|
604
286
|
modelDeps?: CurationTailModelDeps;
|
|
605
287
|
signal?: AbortSignal;
|
|
606
288
|
log: CurationTailLogger;
|
|
607
|
-
/** 388-02: the sweep runs under the post-run tail's held lease when there is one. */
|
|
608
289
|
authority?: CurationWriterAuthority;
|
|
609
|
-
/** 388-02 (D-03): absolute wall-clock stop point shared with the one post-run budget. */
|
|
610
290
|
deadlineMs?: number;
|
|
611
291
|
/** Focused test seam. Production callers omit it. */
|
|
612
292
|
operations?: Partial<CurationTailOperations>;
|
|
@@ -621,24 +301,8 @@ interface RunVerdictFirstCurationTailOptions {
|
|
|
621
301
|
/** The complete verdict-derived process code, fixed before the tail begins. */
|
|
622
302
|
exitCode: number;
|
|
623
303
|
detach: boolean;
|
|
624
|
-
/**
|
|
625
|
-
* The durable hand-off, run BEFORE the detach short-circuit (388-02, D-03 / Pitfall 3).
|
|
626
|
-
*
|
|
627
|
-
* `--detach` means "do not spend this process on the work", not "do not record that the
|
|
628
|
-
* work exists". The enqueue is the record; the drain is the spending. Putting it inside
|
|
629
|
-
* `runTail` would have made a detached run the one posture that silently loses its
|
|
630
|
-
* analysis, which is the opposite of what detaching asks for.
|
|
631
|
-
*/
|
|
632
304
|
enqueue?(): Promise<void>;
|
|
633
305
|
runTail(): Promise<void>;
|
|
634
|
-
/**
|
|
635
|
-
* The user-facing close-out, run on BOTH paths (388-04, D-08).
|
|
636
|
-
*
|
|
637
|
-
* The detach short-circuit below returns before `runTail`, and the one posture that most
|
|
638
|
-
* needs a line about work left queued is exactly that one — so this cannot live inside the
|
|
639
|
-
* tail. It is told which gate was applied rather than inferring it, because the gate is
|
|
640
|
-
* this function's own knowledge and nothing downstream can see it.
|
|
641
|
-
*/
|
|
642
306
|
finish?(input: {
|
|
643
307
|
detached: boolean;
|
|
644
308
|
}): Promise<void>;
|
|
@@ -650,4 +314,4 @@ declare function runVerdictFirstCurationTail(opts: RunVerdictFirstCurationTailOp
|
|
|
650
314
|
|
|
651
315
|
declare const VERSION: string;
|
|
652
316
|
|
|
653
|
-
export { type BuildCuratorAuthorDependencies, CURATION_TAIL_RECEIPT_FILE, CURATION_TAIL_RECEIPT_SCHEMA_VERSION, CURATOR_MAX_NEIGHBORS, CURATOR_RETRY_CAP, ConfigFileNotFoundError, ConfigFileNotYamlError, type CurationTailLogger, type CurationTailModelDeps, type CurationTailOperations, type CurationTailReceipt, type EmitCanaryOutcomeDetail, type EmitCanaryOutcomeReport, type EmitCanaryOutcomeResult, type EmitCanaryOutcomesOptions, type ProjectCanaryOutcomesInput, type ResolvedTarget, type RunCurationTailOptions, type RunVerdictFirstCurationTailOptions, type TargetSummary, VERSION, buildCuratorAuthor, curationTailHandoffDigest, deepMerge, emitCanaryOutcomes, formatConfigDebug, listTargets, loadConfigFile, loadConfigPlugins, loadEnvOverrides, loadRequiredConfigFile, mergeConfigs, mergeUseBlocks, mergeWithTestConfig, normalizeConfigForValidation, projectCanaryOutcomes, purgeAll,
|
|
317
|
+
export { type BuildCuratorAuthorDependencies, CURATION_TAIL_RECEIPT_FILE, CURATION_TAIL_RECEIPT_SCHEMA_VERSION, CURATOR_MAX_NEIGHBORS, CURATOR_RETRY_CAP, ConfigFileNotFoundError, ConfigFileNotYamlError, type CurationTailLogger, type CurationTailModelDeps, type CurationTailOperations, type CurationTailReceipt, type EmitCanaryOutcomeDetail, type EmitCanaryOutcomeReport, type EmitCanaryOutcomeResult, type EmitCanaryOutcomesOptions, type ProjectCanaryOutcomesInput, type ResolvedTarget, type RunCurationTailOptions, type RunVerdictFirstCurationTailOptions, type TargetSummary, VERSION, buildCuratorAuthor, curationTailHandoffDigest, deepMerge, emitCanaryOutcomes, formatConfigDebug, listTargets, loadConfigFile, loadConfigPlugins, loadEnvOverrides, loadRequiredConfigFile, mergeConfigs, mergeUseBlocks, mergeWithTestConfig, normalizeConfigForValidation, projectCanaryOutcomes, purgeAll, readAnalyticsPrivacyDeclaration, resolveConfig, resolveConfiguredKnowledgeRoot, resolveRawConfigKnowledgeRoot, resolveTarget, runCurationTail, runVerdictFirstCurationTail };
|