@nathapp/nax 0.75.5 → 0.76.0
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/nax.js +1164 -410
- package/flows/nax-finish/commit-message.ts +136 -0
- package/flows/nax-finish/flow-ctx.ts +95 -0
- package/flows/nax-finish/nax-finish.flow.ts +129 -30
- package/flows/nax-finish/review-prompts.ts +42 -4
- package/flows/nax-finish/steps/context.ts +38 -0
- package/flows/nax-finish/steps/git.ts +26 -3
- package/flows/nax-finish/steps/result.ts +99 -10
- package/flows/nax-finish/types.ts +42 -0
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -17377,7 +17377,11 @@ var init_schemas_review = __esm(() => {
|
|
|
17377
17377
|
maxRequotes: 5
|
|
17378
17378
|
}),
|
|
17379
17379
|
excludePatterns: exports_external.array(exports_external.string()).optional(),
|
|
17380
|
-
demandInspectionTrail: exports_external.boolean().default(true)
|
|
17380
|
+
demandInspectionTrail: exports_external.boolean().default(true),
|
|
17381
|
+
recurrenceDemotion: exports_external.object({
|
|
17382
|
+
enabled: exports_external.boolean().default(false),
|
|
17383
|
+
maxBlockingRounds: exports_external.number().int().min(1).default(2)
|
|
17384
|
+
}).default({ enabled: false, maxBlockingRounds: 2 })
|
|
17381
17385
|
});
|
|
17382
17386
|
AdversarialReviewConfigSchema = exports_external.object({
|
|
17383
17387
|
model: ConfiguredModelSchema.default("balanced"),
|
|
@@ -17641,6 +17645,7 @@ var init_schemas3 = __esm(() => {
|
|
|
17641
17645
|
rules: [],
|
|
17642
17646
|
timeoutMs: 600000,
|
|
17643
17647
|
demandInspectionTrail: true,
|
|
17648
|
+
recurrenceDemotion: { enabled: false, maxBlockingRounds: 2 },
|
|
17644
17649
|
substantiation: {
|
|
17645
17650
|
requote: true,
|
|
17646
17651
|
maxRequotes: 5
|
|
@@ -17818,6 +17823,7 @@ var init_schemas3 = __esm(() => {
|
|
|
17818
17823
|
enabled: exports_external.boolean().default(false),
|
|
17819
17824
|
flowPath: exports_external.string().default("flows/nax-finish/nax-finish.flow.ts"),
|
|
17820
17825
|
defaultAgent: exports_external.string().nullable().default(null),
|
|
17826
|
+
model: exports_external.string().min(1, "model must be non-empty").nullable().default(null),
|
|
17821
17827
|
reviewers: exports_external.object({
|
|
17822
17828
|
spec: exports_external.string().nullable().default(null),
|
|
17823
17829
|
quality: exports_external.string().nullable().default(null)
|
|
@@ -17834,6 +17840,7 @@ var init_schemas3 = __esm(() => {
|
|
|
17834
17840
|
enabled: false,
|
|
17835
17841
|
flowPath: "flows/nax-finish/nax-finish.flow.ts",
|
|
17836
17842
|
defaultAgent: null,
|
|
17843
|
+
model: null,
|
|
17837
17844
|
reviewers: { spec: null, quality: null },
|
|
17838
17845
|
escalate: { telegram: true },
|
|
17839
17846
|
notify: { mode: "escalation" },
|
|
@@ -17844,6 +17851,7 @@ var init_schemas3 = __esm(() => {
|
|
|
17844
17851
|
enabled: false,
|
|
17845
17852
|
flowPath: "flows/nax-finish/nax-finish.flow.ts",
|
|
17846
17853
|
defaultAgent: null,
|
|
17854
|
+
model: null,
|
|
17847
17855
|
reviewers: { spec: null, quality: null },
|
|
17848
17856
|
escalate: { telegram: true },
|
|
17849
17857
|
notify: { mode: "escalation" },
|
|
@@ -19634,6 +19642,13 @@ function reshapeSelector(name, fn) {
|
|
|
19634
19642
|
return { name, select: fn };
|
|
19635
19643
|
}
|
|
19636
19644
|
|
|
19645
|
+
// src/config/project-key.ts
|
|
19646
|
+
import { basename as basename3 } from "path";
|
|
19647
|
+
function getProjectKey(config2, projectDir) {
|
|
19648
|
+
return config2.name?.trim() || basename3(projectDir);
|
|
19649
|
+
}
|
|
19650
|
+
var init_project_key = () => {};
|
|
19651
|
+
|
|
19637
19652
|
// src/config/selectors.ts
|
|
19638
19653
|
var reviewConfigSelector, planConfigSelector, decomposeConfigSelector, rectifyConfigSelector, acceptanceConfigSelector, acceptanceFixConfigSelector, acceptanceGenConfigSelector, tddConfigSelector, debateConfigSelector, routingConfigSelector, verifyConfigSelector, mutationCheckConfigSelector, rectificationGateConfigSelector, agentConfigSelector, agentManagerConfigSelector, interactionConfigSelector, precheckConfigSelector, qualityConfigSelector, autofixConfigSelector, executionGatesConfigSelector, testPatternConfigSelector, contextConfigSelector, contextToolRuntimeConfigSelector, promptLoaderConfigSelector, llmRoutingConfigSelector, finishConfigSelector;
|
|
19639
19654
|
var init_selectors = __esm(() => {
|
|
@@ -19654,7 +19669,7 @@ var init_selectors = __esm(() => {
|
|
|
19654
19669
|
mutationCheckConfigSelector = reshapeSelector("mutation-check", (c) => c.execution?.mutationCheck);
|
|
19655
19670
|
rectificationGateConfigSelector = pickSelector("rectification-gate", "execution", "models", "agent", "quality", "review");
|
|
19656
19671
|
agentConfigSelector = pickSelector("agent", "agent");
|
|
19657
|
-
agentManagerConfigSelector = pickSelector("agent-manager", "agent", "execution");
|
|
19672
|
+
agentManagerConfigSelector = pickSelector("agent-manager", "agent", "execution", "profile");
|
|
19658
19673
|
interactionConfigSelector = pickSelector("interaction", "interaction");
|
|
19659
19674
|
precheckConfigSelector = pickSelector("precheck", "precheck", "quality", "execution", "prompts", "review", "project");
|
|
19660
19675
|
qualityConfigSelector = pickSelector("quality", "quality", "execution");
|
|
@@ -19665,7 +19680,7 @@ var init_selectors = __esm(() => {
|
|
|
19665
19680
|
contextToolRuntimeConfigSelector = pickSelector("context-tool-runtime", "context", "execution", "project", "quality");
|
|
19666
19681
|
promptLoaderConfigSelector = pickSelector("prompt-loader", "prompts", "context", "project");
|
|
19667
19682
|
llmRoutingConfigSelector = pickSelector("llm-routing", "routing", "models", "agent", "tdd", "execution", "precheck");
|
|
19668
|
-
finishConfigSelector = pickSelector("finish", "finish", "interaction", "quality");
|
|
19683
|
+
finishConfigSelector = pickSelector("finish", "finish", "interaction", "quality", "agent");
|
|
19669
19684
|
});
|
|
19670
19685
|
|
|
19671
19686
|
// src/config/loader-runtime.ts
|
|
@@ -19945,6 +19960,7 @@ __export(exports_config, {
|
|
|
19945
19960
|
interactionConfigSelector: () => interactionConfigSelector,
|
|
19946
19961
|
globalConfigPath: () => globalConfigPath,
|
|
19947
19962
|
globalConfigDir: () => globalConfigDir,
|
|
19963
|
+
getProjectKey: () => getProjectKey,
|
|
19948
19964
|
getAcQualityRules: () => getAcQualityRules,
|
|
19949
19965
|
finishConfigSelector: () => finishConfigSelector,
|
|
19950
19966
|
findProjectDir: () => findProjectDir,
|
|
@@ -19996,6 +20012,7 @@ var init_config = __esm(() => {
|
|
|
19996
20012
|
init_path_security();
|
|
19997
20013
|
init_paths();
|
|
19998
20014
|
init_profile();
|
|
20015
|
+
init_project_key();
|
|
19999
20016
|
init_selectors();
|
|
20000
20017
|
init_test_strategy();
|
|
20001
20018
|
});
|
|
@@ -20108,6 +20125,11 @@ function estimateCostFromTokenUsage(usage, model) {
|
|
|
20108
20125
|
const cacheCreationCost = (usage.cacheCreationInputTokens ?? 0) * cacheCreationRate;
|
|
20109
20126
|
return inputCost + outputCost + cacheReadCost + cacheCreationCost;
|
|
20110
20127
|
}
|
|
20128
|
+
function resolvePricingSource(model) {
|
|
20129
|
+
if (model === undefined || model === "" || model === "unknown")
|
|
20130
|
+
return "unknown-model";
|
|
20131
|
+
return MODEL_PRICING[model] ? "model-rates" : "fallback-rates";
|
|
20132
|
+
}
|
|
20111
20133
|
var init_calculate = __esm(() => {
|
|
20112
20134
|
init_pricing();
|
|
20113
20135
|
});
|
|
@@ -21111,6 +21133,8 @@ class AcpSessionHandleImpl {
|
|
|
21111
21133
|
id;
|
|
21112
21134
|
agentName;
|
|
21113
21135
|
protocolIds;
|
|
21136
|
+
modelDef;
|
|
21137
|
+
modelTier;
|
|
21114
21138
|
_client;
|
|
21115
21139
|
_session;
|
|
21116
21140
|
_sessionName;
|
|
@@ -21128,6 +21152,8 @@ class AcpSessionHandleImpl {
|
|
|
21128
21152
|
this._resumed = opts.resumed;
|
|
21129
21153
|
this._timeoutSeconds = opts.timeoutSeconds;
|
|
21130
21154
|
this._modelDef = opts.modelDef;
|
|
21155
|
+
this.modelDef = opts.modelDef;
|
|
21156
|
+
this.modelTier = opts.modelTier;
|
|
21131
21157
|
this._permissionMode = opts.permissionMode;
|
|
21132
21158
|
}
|
|
21133
21159
|
}
|
|
@@ -21513,6 +21539,7 @@ class AcpAgentAdapter {
|
|
|
21513
21539
|
resumed: ensured.resumed,
|
|
21514
21540
|
timeoutSeconds,
|
|
21515
21541
|
modelDef,
|
|
21542
|
+
modelTier: opts.modelTier,
|
|
21516
21543
|
permissionMode: resolvedPermissions.mode
|
|
21517
21544
|
});
|
|
21518
21545
|
} catch (error48) {
|
|
@@ -21657,7 +21684,7 @@ class AcpAgentAdapter {
|
|
|
21657
21684
|
}
|
|
21658
21685
|
}
|
|
21659
21686
|
}
|
|
21660
|
-
var
|
|
21687
|
+
var INTERACTION_TIMEOUT_MS, AGENT_REGISTRY, DEFAULT_ENTRY, ACP_ADAPTER_NAMES;
|
|
21661
21688
|
var init_adapter = __esm(() => {
|
|
21662
21689
|
init_errors();
|
|
21663
21690
|
init_logger2();
|
|
@@ -22108,6 +22135,92 @@ function formatSessionName(req) {
|
|
|
22108
22135
|
}
|
|
22109
22136
|
var init_session_name = () => {};
|
|
22110
22137
|
|
|
22138
|
+
// src/agents/manager-dispatch.ts
|
|
22139
|
+
function modelAttribution(src) {
|
|
22140
|
+
return {
|
|
22141
|
+
...src.modelDef?.model !== undefined ? { model: src.modelDef.model } : {},
|
|
22142
|
+
...src.modelTier !== undefined ? { modelTier: src.modelTier } : {}
|
|
22143
|
+
};
|
|
22144
|
+
}
|
|
22145
|
+
function buildSessionTurnEvent(input) {
|
|
22146
|
+
const { handle, result, opts, startedAt } = input;
|
|
22147
|
+
return {
|
|
22148
|
+
kind: "session-turn",
|
|
22149
|
+
sessionName: handle.id,
|
|
22150
|
+
sessionRole: input.sessionRole,
|
|
22151
|
+
prompt: input.prompt,
|
|
22152
|
+
response: result.output,
|
|
22153
|
+
agentName: input.agentName,
|
|
22154
|
+
...modelAttribution(handle),
|
|
22155
|
+
...input.profile !== undefined ? { profile: input.profile } : {},
|
|
22156
|
+
stage: input.stage,
|
|
22157
|
+
storyId: opts.storyId,
|
|
22158
|
+
featureName: opts.featureName,
|
|
22159
|
+
workdir: opts.workdir,
|
|
22160
|
+
projectDir: opts.projectDir,
|
|
22161
|
+
resolvedPermissions: input.resolvedPermissions,
|
|
22162
|
+
tokenUsage: result.tokenUsage,
|
|
22163
|
+
estimatedCostUsd: result.estimatedCostUsd,
|
|
22164
|
+
exactCostUsd: result.exactCostUsd,
|
|
22165
|
+
durationMs: Date.now() - startedAt,
|
|
22166
|
+
timestamp: Date.now(),
|
|
22167
|
+
turn: result.internalRoundTrips ?? 1,
|
|
22168
|
+
protocolIds: {
|
|
22169
|
+
sessionId: handle.protocolIds?.sessionId ?? null,
|
|
22170
|
+
recordId: handle.protocolIds?.recordId ?? null
|
|
22171
|
+
},
|
|
22172
|
+
...result.interactions?.length ? { interactions: result.interactions } : {},
|
|
22173
|
+
origin: "runAsSession",
|
|
22174
|
+
...opts.callId !== undefined ? { callId: opts.callId } : {},
|
|
22175
|
+
...opts.scopeId !== undefined ? { scopeId: opts.scopeId } : {}
|
|
22176
|
+
};
|
|
22177
|
+
}
|
|
22178
|
+
function buildCompleteEvent(input) {
|
|
22179
|
+
const { options } = input;
|
|
22180
|
+
return {
|
|
22181
|
+
kind: "complete",
|
|
22182
|
+
sessionName: input.sessionName,
|
|
22183
|
+
sessionRole: options.sessionRole ?? "auto",
|
|
22184
|
+
prompt: input.prompt,
|
|
22185
|
+
response: input.response,
|
|
22186
|
+
agentName: input.agentName,
|
|
22187
|
+
...modelAttribution(options),
|
|
22188
|
+
...input.profile !== undefined ? { profile: input.profile } : {},
|
|
22189
|
+
stage: input.stage,
|
|
22190
|
+
storyId: options.storyId,
|
|
22191
|
+
featureName: options.featureName,
|
|
22192
|
+
workdir: options.workdir,
|
|
22193
|
+
resolvedPermissions: input.resolvedPermissions,
|
|
22194
|
+
tokenUsage: input.tokenUsage,
|
|
22195
|
+
estimatedCostUsd: input.estimatedCostUsd,
|
|
22196
|
+
exactCostUsd: input.exactCostUsd,
|
|
22197
|
+
durationMs: Date.now() - input.startedAt,
|
|
22198
|
+
timestamp: Date.now(),
|
|
22199
|
+
...options.callId !== undefined ? { callId: options.callId } : {},
|
|
22200
|
+
...options.scopeId !== undefined ? { scopeId: options.scopeId } : {}
|
|
22201
|
+
};
|
|
22202
|
+
}
|
|
22203
|
+
function buildDispatchErrorEvent(input) {
|
|
22204
|
+
return {
|
|
22205
|
+
kind: "error",
|
|
22206
|
+
origin: input.origin,
|
|
22207
|
+
agentName: input.agentName,
|
|
22208
|
+
stage: input.stage,
|
|
22209
|
+
storyId: input.storyId,
|
|
22210
|
+
errorCode: input.error instanceof NaxError ? input.error.code : "DISPATCH_ERROR",
|
|
22211
|
+
errorMessage: errorMessage(input.error),
|
|
22212
|
+
prompt: input.prompt,
|
|
22213
|
+
durationMs: Date.now() - input.startedAt,
|
|
22214
|
+
timestamp: Date.now(),
|
|
22215
|
+
resolvedPermissions: input.resolvedPermissions,
|
|
22216
|
+
...input.callId !== undefined ? { callId: input.callId } : {},
|
|
22217
|
+
...input.scopeId !== undefined ? { scopeId: input.scopeId } : {}
|
|
22218
|
+
};
|
|
22219
|
+
}
|
|
22220
|
+
var init_manager_dispatch = __esm(() => {
|
|
22221
|
+
init_errors();
|
|
22222
|
+
});
|
|
22223
|
+
|
|
22111
22224
|
// src/agents/retry/default-strategy.ts
|
|
22112
22225
|
var MAX_RETRIES = 3, defaultRetryStrategy;
|
|
22113
22226
|
var init_default_strategy = __esm(() => {
|
|
@@ -22735,52 +22848,33 @@ class AgentManager {
|
|
|
22735
22848
|
...rawResult,
|
|
22736
22849
|
protocolIds: rawResult.protocolIds ?? handle.protocolIds
|
|
22737
22850
|
};
|
|
22738
|
-
const event = {
|
|
22739
|
-
|
|
22740
|
-
sessionName: handle.id,
|
|
22851
|
+
const event = buildSessionTurnEvent({
|
|
22852
|
+
handle,
|
|
22741
22853
|
sessionRole,
|
|
22742
22854
|
prompt,
|
|
22743
|
-
|
|
22855
|
+
result,
|
|
22744
22856
|
agentName,
|
|
22745
22857
|
stage,
|
|
22746
|
-
|
|
22747
|
-
featureName: opts.featureName,
|
|
22748
|
-
workdir: opts.workdir,
|
|
22749
|
-
projectDir: opts.projectDir,
|
|
22858
|
+
opts,
|
|
22750
22859
|
resolvedPermissions,
|
|
22751
|
-
|
|
22752
|
-
|
|
22753
|
-
|
|
22754
|
-
durationMs: Date.now() - start,
|
|
22755
|
-
timestamp: Date.now(),
|
|
22756
|
-
turn: result.internalRoundTrips ?? 1,
|
|
22757
|
-
protocolIds: {
|
|
22758
|
-
sessionId: handle.protocolIds?.sessionId ?? null,
|
|
22759
|
-
recordId: handle.protocolIds?.recordId ?? null
|
|
22760
|
-
},
|
|
22761
|
-
...result.interactions?.length ? { interactions: result.interactions } : {},
|
|
22762
|
-
origin: "runAsSession",
|
|
22763
|
-
...opts.callId !== undefined ? { callId: opts.callId } : {},
|
|
22764
|
-
...opts.scopeId !== undefined ? { scopeId: opts.scopeId } : {}
|
|
22765
|
-
};
|
|
22860
|
+
profile: this._config.profile,
|
|
22861
|
+
startedAt: start
|
|
22862
|
+
});
|
|
22766
22863
|
this._dispatchEvents.emitDispatch(event);
|
|
22767
22864
|
return result;
|
|
22768
22865
|
} catch (err) {
|
|
22769
|
-
const errEvent = {
|
|
22770
|
-
kind: "error",
|
|
22866
|
+
const errEvent = buildDispatchErrorEvent({
|
|
22771
22867
|
origin: "runAsSession",
|
|
22772
22868
|
agentName,
|
|
22773
22869
|
stage,
|
|
22774
22870
|
storyId: opts.storyId,
|
|
22775
|
-
|
|
22776
|
-
errorMessage: errorMessage(err),
|
|
22871
|
+
error: err,
|
|
22777
22872
|
prompt,
|
|
22778
|
-
durationMs: Date.now() - start,
|
|
22779
|
-
timestamp: Date.now(),
|
|
22780
22873
|
resolvedPermissions,
|
|
22781
|
-
|
|
22782
|
-
|
|
22783
|
-
|
|
22874
|
+
callId: opts.callId,
|
|
22875
|
+
scopeId: opts.scopeId,
|
|
22876
|
+
startedAt: start
|
|
22877
|
+
});
|
|
22784
22878
|
this._dispatchEvents.emitDispatchError(errEvent);
|
|
22785
22879
|
throw err;
|
|
22786
22880
|
}
|
|
@@ -22799,44 +22893,35 @@ class AgentManager {
|
|
|
22799
22893
|
const start = Date.now();
|
|
22800
22894
|
try {
|
|
22801
22895
|
const outcome = await this.completeWithFallback(prompt, augmented, agentName);
|
|
22802
|
-
const event = {
|
|
22803
|
-
kind: "complete",
|
|
22896
|
+
const event = buildCompleteEvent({
|
|
22804
22897
|
sessionName,
|
|
22805
|
-
sessionRole: options.sessionRole ?? "auto",
|
|
22806
22898
|
prompt,
|
|
22807
22899
|
response: outcome.result.output,
|
|
22808
22900
|
agentName,
|
|
22809
22901
|
stage,
|
|
22810
|
-
|
|
22811
|
-
featureName: options.featureName,
|
|
22812
|
-
workdir: options.workdir,
|
|
22902
|
+
options,
|
|
22813
22903
|
resolvedPermissions,
|
|
22814
22904
|
tokenUsage: outcome.result.tokenUsage,
|
|
22815
22905
|
estimatedCostUsd: outcome.result.estimatedCostUsd,
|
|
22816
22906
|
exactCostUsd: outcome.result.exactCostUsd,
|
|
22817
|
-
|
|
22818
|
-
|
|
22819
|
-
|
|
22820
|
-
...options.scopeId !== undefined ? { scopeId: options.scopeId } : {}
|
|
22821
|
-
};
|
|
22907
|
+
profile: this._config.profile,
|
|
22908
|
+
startedAt: start
|
|
22909
|
+
});
|
|
22822
22910
|
this._dispatchEvents.emitDispatch(event);
|
|
22823
22911
|
return outcome.result;
|
|
22824
22912
|
} catch (err) {
|
|
22825
|
-
const errEvent = {
|
|
22826
|
-
kind: "error",
|
|
22913
|
+
const errEvent = buildDispatchErrorEvent({
|
|
22827
22914
|
origin: "completeAs",
|
|
22828
22915
|
agentName,
|
|
22829
22916
|
stage,
|
|
22830
22917
|
storyId: options.storyId,
|
|
22831
|
-
|
|
22832
|
-
errorMessage: errorMessage(err),
|
|
22918
|
+
error: err,
|
|
22833
22919
|
prompt,
|
|
22834
|
-
durationMs: Date.now() - start,
|
|
22835
|
-
timestamp: Date.now(),
|
|
22836
22920
|
resolvedPermissions,
|
|
22837
|
-
|
|
22838
|
-
|
|
22839
|
-
|
|
22921
|
+
callId: options.callId,
|
|
22922
|
+
scopeId: options.scopeId,
|
|
22923
|
+
startedAt: start
|
|
22924
|
+
});
|
|
22840
22925
|
this._dispatchEvents.emitDispatchError(errEvent);
|
|
22841
22926
|
throw err;
|
|
22842
22927
|
}
|
|
@@ -22859,6 +22944,7 @@ var init_manager = __esm(() => {
|
|
|
22859
22944
|
init_dispatch_events();
|
|
22860
22945
|
init_session_name();
|
|
22861
22946
|
init_bun_deps();
|
|
22947
|
+
init_manager_dispatch();
|
|
22862
22948
|
init_registry();
|
|
22863
22949
|
init_default_strategy();
|
|
22864
22950
|
init_hop_retry_policy();
|
|
@@ -22947,11 +23033,38 @@ var init_compose = __esm(() => {
|
|
|
22947
23033
|
|
|
22948
23034
|
// src/review/truncation.ts
|
|
22949
23035
|
function looksLikeTruncatedJson(raw) {
|
|
22950
|
-
|
|
23036
|
+
const text = raw.trimEnd();
|
|
23037
|
+
if (text.length === 0)
|
|
23038
|
+
return false;
|
|
23039
|
+
let depth = 0;
|
|
23040
|
+
let inString = false;
|
|
23041
|
+
let escaped = false;
|
|
23042
|
+
let opened = false;
|
|
23043
|
+
for (const ch of text) {
|
|
23044
|
+
if (escaped) {
|
|
23045
|
+
escaped = false;
|
|
23046
|
+
continue;
|
|
23047
|
+
}
|
|
23048
|
+
if (inString) {
|
|
23049
|
+
if (ch === "\\")
|
|
23050
|
+
escaped = true;
|
|
23051
|
+
else if (ch === '"')
|
|
23052
|
+
inString = false;
|
|
23053
|
+
continue;
|
|
23054
|
+
}
|
|
23055
|
+
if (ch === '"') {
|
|
23056
|
+
inString = true;
|
|
23057
|
+
continue;
|
|
23058
|
+
}
|
|
23059
|
+
if (ch === "{" || ch === "[") {
|
|
23060
|
+
depth++;
|
|
23061
|
+
opened = true;
|
|
23062
|
+
} else if (ch === "}" || ch === "]") {
|
|
23063
|
+
depth--;
|
|
23064
|
+
}
|
|
23065
|
+
}
|
|
23066
|
+
return opened && (inString || depth > 0);
|
|
22951
23067
|
}
|
|
22952
|
-
var init_truncation = __esm(() => {
|
|
22953
|
-
init_adapter();
|
|
22954
|
-
});
|
|
22955
23068
|
|
|
22956
23069
|
// src/utils/llm-json.ts
|
|
22957
23070
|
function extractJsonFromMarkdown(text) {
|
|
@@ -23094,9 +23207,9 @@ function makeParseRetryStrategy(opts) {
|
|
|
23094
23207
|
}
|
|
23095
23208
|
};
|
|
23096
23209
|
}
|
|
23210
|
+
var UNPARSED_PREVIEW_BYTES = 600;
|
|
23097
23211
|
var init_parse_retry = __esm(() => {
|
|
23098
23212
|
init_logger2();
|
|
23099
|
-
init_truncation();
|
|
23100
23213
|
init_types4();
|
|
23101
23214
|
});
|
|
23102
23215
|
|
|
@@ -23114,7 +23227,7 @@ function makeTieredParseRetryStrategy(opts) {
|
|
|
23114
23227
|
if (attempt >= opts.maxAttempts - 1) {
|
|
23115
23228
|
return { retry: false, fallback: opts.exhaustedFallback(inspection, ctx.lastOutput) };
|
|
23116
23229
|
}
|
|
23117
|
-
const isTruncated = ctx.lastOutput
|
|
23230
|
+
const isTruncated = looksLikeTruncatedJson(ctx.lastOutput);
|
|
23118
23231
|
const logger = opts._logger ?? getSafeLogger();
|
|
23119
23232
|
logger?.warn(opts.reviewerKind, `Parse retry \u2014 ${inspection.kind ?? "unknown"}`, {
|
|
23120
23233
|
storyId: ctx.storyId,
|
|
@@ -23128,7 +23241,6 @@ function makeTieredParseRetryStrategy(opts) {
|
|
|
23128
23241
|
}
|
|
23129
23242
|
var init_tiered_parse_retry = __esm(() => {
|
|
23130
23243
|
init_logger2();
|
|
23131
|
-
init_adapter();
|
|
23132
23244
|
init_types4();
|
|
23133
23245
|
});
|
|
23134
23246
|
|
|
@@ -23522,6 +23634,56 @@ var init_digest = __esm(() => {
|
|
|
23522
23634
|
SCOPE_ORDER2 = ["project", "feature", "story", "session", "retrieved"];
|
|
23523
23635
|
});
|
|
23524
23636
|
|
|
23637
|
+
// src/context/engine/manifest-builder.ts
|
|
23638
|
+
function buildManifest(inputs) {
|
|
23639
|
+
const {
|
|
23640
|
+
requestId,
|
|
23641
|
+
request,
|
|
23642
|
+
packed,
|
|
23643
|
+
usedTokens,
|
|
23644
|
+
digestTokens: digestTokens2,
|
|
23645
|
+
buildMs,
|
|
23646
|
+
providerResults,
|
|
23647
|
+
roleFiltered,
|
|
23648
|
+
belowMin,
|
|
23649
|
+
dedupeDropped,
|
|
23650
|
+
budgetExcludedIds,
|
|
23651
|
+
floorPackedIds,
|
|
23652
|
+
floorOverageIds
|
|
23653
|
+
} = inputs;
|
|
23654
|
+
const staleChunkIds = packed.filter((c) => c.staleCandidate).map((c) => c.id);
|
|
23655
|
+
const chunkSummaries = {};
|
|
23656
|
+
const chunkTokens = {};
|
|
23657
|
+
for (const c of packed) {
|
|
23658
|
+
chunkSummaries[c.id] = c.content.slice(0, CHUNK_SUMMARY_CHARS);
|
|
23659
|
+
chunkTokens[c.id] = c.tokens;
|
|
23660
|
+
}
|
|
23661
|
+
return {
|
|
23662
|
+
requestId,
|
|
23663
|
+
stage: request.stage,
|
|
23664
|
+
totalBudgetTokens: request.budgetTokens,
|
|
23665
|
+
usedTokens: usedTokens + digestTokens2,
|
|
23666
|
+
includedChunks: packed.map((c) => c.id),
|
|
23667
|
+
excludedChunks: [
|
|
23668
|
+
...roleFiltered.map((c) => ({ id: c.id, reason: "role-filter" })),
|
|
23669
|
+
...belowMin.map((c) => ({ id: c.id, reason: "below-min-score" })),
|
|
23670
|
+
...dedupeDropped.map((id) => ({ id, reason: "dedupe" })),
|
|
23671
|
+
...budgetExcludedIds.map((id) => ({ id, reason: "budget" }))
|
|
23672
|
+
],
|
|
23673
|
+
floorItems: floorPackedIds,
|
|
23674
|
+
floorOverageItems: floorOverageIds.length > 0 ? floorOverageIds : undefined,
|
|
23675
|
+
digestTokens: digestTokens2,
|
|
23676
|
+
buildMs,
|
|
23677
|
+
providerResults,
|
|
23678
|
+
repoRoot: request.repoRoot,
|
|
23679
|
+
packageDir: request.packageDir,
|
|
23680
|
+
...Object.keys(chunkSummaries).length > 0 && { chunkSummaries },
|
|
23681
|
+
...Object.keys(chunkTokens).length > 0 && { chunkTokens },
|
|
23682
|
+
...staleChunkIds.length > 0 && { staleChunks: staleChunkIds }
|
|
23683
|
+
};
|
|
23684
|
+
}
|
|
23685
|
+
var CHUNK_SUMMARY_CHARS = 300;
|
|
23686
|
+
|
|
23525
23687
|
// src/context/engine/packing.ts
|
|
23526
23688
|
function packChunks(chunks, budgetTokens, availableBudgetTokens) {
|
|
23527
23689
|
const effectiveBudget = availableBudgetTokens !== undefined ? Math.min(budgetTokens, availableBudgetTokens) : budgetTokens;
|
|
@@ -25742,7 +25904,7 @@ async function captureWorkingTreeChanges(workdir, baseRef, scopePrefix) {
|
|
|
25742
25904
|
return [];
|
|
25743
25905
|
const runDiff = async (args) => {
|
|
25744
25906
|
const fullArgs = scopePrefix ? [...args, "--", `${scopePrefix}/`] : args;
|
|
25745
|
-
const { stdout, exitCode } = await gitWithTimeout(fullArgs, workdir,
|
|
25907
|
+
const { stdout, exitCode } = await gitWithTimeout(fullArgs, workdir, _gitDeps.timeoutRetryGitTimeoutMs);
|
|
25746
25908
|
if (exitCode !== 0)
|
|
25747
25909
|
return [];
|
|
25748
25910
|
return stdout.trim().split(`
|
|
@@ -25791,16 +25953,20 @@ async function captureDiffSummary(workdir, baseRef, scopePrefix) {
|
|
|
25791
25953
|
return "";
|
|
25792
25954
|
}
|
|
25793
25955
|
}
|
|
25794
|
-
var
|
|
25956
|
+
var GIT_TIMEOUT_MS = 1e4, TIMEOUT_RETRY_GIT_TIMEOUT_MS = 3000, _gitDeps;
|
|
25795
25957
|
var init_git = __esm(() => {
|
|
25796
25958
|
init_logger2();
|
|
25797
25959
|
init_bun_deps();
|
|
25798
|
-
_gitDeps = {
|
|
25960
|
+
_gitDeps = {
|
|
25961
|
+
spawn,
|
|
25962
|
+
getSafeLogger,
|
|
25963
|
+
timeoutRetryGitTimeoutMs: TIMEOUT_RETRY_GIT_TIMEOUT_MS
|
|
25964
|
+
};
|
|
25799
25965
|
});
|
|
25800
25966
|
|
|
25801
25967
|
// src/utils/path-filters.ts
|
|
25802
25968
|
import { join as join8, relative as relative2 } from "path";
|
|
25803
|
-
function
|
|
25969
|
+
function basename4(path) {
|
|
25804
25970
|
const stripped = path.startsWith("./") ? path.slice(2) : path;
|
|
25805
25971
|
const idx = stripped.lastIndexOf("/");
|
|
25806
25972
|
return idx === -1 ? stripped : stripped.slice(idx + 1);
|
|
@@ -25930,7 +26096,7 @@ function isNaxInternalPath(path) {
|
|
|
25930
26096
|
return true;
|
|
25931
26097
|
if (path.includes("/.nax/"))
|
|
25932
26098
|
return true;
|
|
25933
|
-
return LOCKFILE_BASENAMES.has(
|
|
26099
|
+
return LOCKFILE_BASENAMES.has(basename4(path));
|
|
25934
26100
|
}
|
|
25935
26101
|
function filterNaxInternalPaths(paths, ignoreMatchers = []) {
|
|
25936
26102
|
return paths.filter((path) => !isNaxInternalPath(path) && !ignoreMatchers.some((matcher) => matcher.test(path)));
|
|
@@ -25974,8 +26140,8 @@ async function getGitRootMemo(workdir) {
|
|
|
25974
26140
|
return result ?? null;
|
|
25975
26141
|
}
|
|
25976
26142
|
function extractBasenamePattern(pattern) {
|
|
25977
|
-
const
|
|
25978
|
-
const parts =
|
|
26143
|
+
const basename5 = pattern.slice(pattern.lastIndexOf("/") + 1);
|
|
26144
|
+
const parts = basename5.split("*");
|
|
25979
26145
|
if (parts.length !== 2)
|
|
25980
26146
|
return null;
|
|
25981
26147
|
const [prefix, suffix] = parts;
|
|
@@ -25987,8 +26153,8 @@ function extractSearchTerms(sourceFile) {
|
|
|
25987
26153
|
const withoutPrefix = sourceFile.replace(/^(?:.*\/)?src\//, "");
|
|
25988
26154
|
const withoutExt = withoutPrefix.replace(/\.[^.]+$/, "");
|
|
25989
26155
|
const parts = withoutExt.split("/");
|
|
25990
|
-
const
|
|
25991
|
-
return [`/${
|
|
26156
|
+
const basename5 = parts[parts.length - 1];
|
|
26157
|
+
return [`/${basename5}`, withoutExt];
|
|
25992
26158
|
}
|
|
25993
26159
|
async function importGrepFallback(sourceFiles, workdir, testFilePatterns, maxScanFiles = MAX_GREP_TEST_FILES) {
|
|
25994
26160
|
if (sourceFiles.length === 0 || testFilePatterns.length === 0)
|
|
@@ -27808,33 +27974,21 @@ class ContextOrchestrator {
|
|
|
27808
27974
|
const digest = buildDigest(packed);
|
|
27809
27975
|
const dTokens = digestTokens(digest);
|
|
27810
27976
|
const buildMs = _orchestratorDeps.now() - startMs;
|
|
27811
|
-
const
|
|
27812
|
-
const chunkSummaries = {};
|
|
27813
|
-
for (const c of packed) {
|
|
27814
|
-
chunkSummaries[c.id] = c.content.slice(0, 300);
|
|
27815
|
-
}
|
|
27816
|
-
const manifest = {
|
|
27977
|
+
const manifest = buildManifest({
|
|
27817
27978
|
requestId,
|
|
27818
|
-
|
|
27819
|
-
|
|
27820
|
-
usedTokens
|
|
27821
|
-
includedChunks: packed.map((c) => c.id),
|
|
27822
|
-
excludedChunks: [
|
|
27823
|
-
...roleFiltered.map((c) => ({ id: c.id, reason: "role-filter" })),
|
|
27824
|
-
...belowMin.map((c) => ({ id: c.id, reason: "below-min-score" })),
|
|
27825
|
-
...dedupeDropped.map((id) => ({ id, reason: "dedupe" })),
|
|
27826
|
-
...budgetExcludedIds.map((id) => ({ id, reason: "budget" }))
|
|
27827
|
-
],
|
|
27828
|
-
floorItems: floorPackedIds,
|
|
27829
|
-
floorOverageItems: floorOverageIds.length > 0 ? floorOverageIds : undefined,
|
|
27979
|
+
request,
|
|
27980
|
+
packed,
|
|
27981
|
+
usedTokens,
|
|
27830
27982
|
digestTokens: dTokens,
|
|
27831
27983
|
buildMs,
|
|
27832
27984
|
providerResults,
|
|
27833
|
-
|
|
27834
|
-
|
|
27835
|
-
|
|
27836
|
-
|
|
27837
|
-
|
|
27985
|
+
roleFiltered,
|
|
27986
|
+
belowMin,
|
|
27987
|
+
dedupeDropped,
|
|
27988
|
+
budgetExcludedIds,
|
|
27989
|
+
floorPackedIds,
|
|
27990
|
+
floorOverageIds
|
|
27991
|
+
});
|
|
27838
27992
|
logger.debug("context-v2", "Bundle assembled", {
|
|
27839
27993
|
storyId: request.storyId,
|
|
27840
27994
|
stage: request.stage,
|
|
@@ -27891,6 +28045,7 @@ class ContextOrchestrator {
|
|
|
27891
28045
|
...prior.manifest,
|
|
27892
28046
|
requestId: _orchestratorDeps.uuid(),
|
|
27893
28047
|
includedChunks: packedChunks.map((c) => c.id),
|
|
28048
|
+
chunkTokens: Object.fromEntries(packedChunks.map((c) => [c.id, c.tokens])),
|
|
27894
28049
|
usedTokens: Math.max(0, prior.manifest.usedTokens - prior.manifest.digestTokens + dTokens + extraTokens),
|
|
27895
28050
|
digestTokens: dTokens,
|
|
27896
28051
|
buildMs: 0,
|
|
@@ -27928,7 +28083,7 @@ var init_orchestrator = __esm(() => {
|
|
|
27928
28083
|
});
|
|
27929
28084
|
|
|
27930
28085
|
// src/context/rules/canonical-loader.ts
|
|
27931
|
-
import { basename as
|
|
28086
|
+
import { basename as basename5, join as join12 } from "path";
|
|
27932
28087
|
function parseRuleAllowMarker(line) {
|
|
27933
28088
|
const allowed = new Set;
|
|
27934
28089
|
RULE_ALLOW_MARKER.lastIndex = 0;
|
|
@@ -28068,7 +28223,7 @@ async function loadCanonicalRules(workdir, options = {}) {
|
|
|
28068
28223
|
const filePaths = allFilePaths.filter((filePath) => {
|
|
28069
28224
|
const normalized = filePath.replaceAll("\\", "/");
|
|
28070
28225
|
const normalizedRulesDir = rulesDir.replaceAll("\\", "/");
|
|
28071
|
-
const relativePath = normalized.startsWith(`${normalizedRulesDir}/`) ? normalized.slice(normalizedRulesDir.length + 1) :
|
|
28226
|
+
const relativePath = normalized.startsWith(`${normalizedRulesDir}/`) ? normalized.slice(normalizedRulesDir.length + 1) : basename5(normalized);
|
|
28072
28227
|
return relativePath.split("/").length <= 2;
|
|
28073
28228
|
});
|
|
28074
28229
|
if (allFilePaths.length > filePaths.length) {
|
|
@@ -28084,8 +28239,8 @@ async function loadCanonicalRules(workdir, options = {}) {
|
|
|
28084
28239
|
for (const filePath of filePaths) {
|
|
28085
28240
|
const normalizedPath = filePath.replaceAll("\\", "/");
|
|
28086
28241
|
const normalizedRulesDir = rulesDir.replaceAll("\\", "/");
|
|
28087
|
-
const relativePath = normalizedPath.startsWith(`${normalizedRulesDir}/`) ? normalizedPath.slice(normalizedRulesDir.length + 1) :
|
|
28088
|
-
const fileName =
|
|
28242
|
+
const relativePath = normalizedPath.startsWith(`${normalizedRulesDir}/`) ? normalizedPath.slice(normalizedRulesDir.length + 1) : basename5(normalizedPath);
|
|
28243
|
+
const fileName = basename5(filePath);
|
|
28089
28244
|
let content;
|
|
28090
28245
|
try {
|
|
28091
28246
|
content = await _canonicalLoaderDeps.readFile(filePath);
|
|
@@ -29747,8 +29902,8 @@ function deriveTestPatterns(contextFiles, resolvedGlobs) {
|
|
|
29747
29902
|
const suffixes = resolvedGlobs ? extractGlobSuffixes(resolvedGlobs) : DEFAULT_TS_DERIVE_SUFFIXES;
|
|
29748
29903
|
const effectiveSuffixes = suffixes.length > 0 ? suffixes : DEFAULT_TS_DERIVE_SUFFIXES;
|
|
29749
29904
|
for (const filePath of contextFiles) {
|
|
29750
|
-
const
|
|
29751
|
-
const basenameNoExt =
|
|
29905
|
+
const basename6 = path.basename(filePath);
|
|
29906
|
+
const basenameNoExt = basename6.replace(/\.[^.]+$/, "");
|
|
29752
29907
|
for (const suffix of effectiveSuffixes) {
|
|
29753
29908
|
patterns.add(`${basenameNoExt}${suffix}`);
|
|
29754
29909
|
}
|
|
@@ -29810,8 +29965,8 @@ async function scanTestFiles(options) {
|
|
|
29810
29965
|
const files = [];
|
|
29811
29966
|
for await (const filePath of glob.scan({ cwd: scanDir, absolute: false })) {
|
|
29812
29967
|
if (allowedBasenames !== null) {
|
|
29813
|
-
const
|
|
29814
|
-
if (!allowedBasenames.has(
|
|
29968
|
+
const basename6 = path.basename(filePath);
|
|
29969
|
+
if (!allowedBasenames.has(basename6)) {
|
|
29815
29970
|
continue;
|
|
29816
29971
|
}
|
|
29817
29972
|
}
|
|
@@ -31570,6 +31725,11 @@ isolation scope: Only create or modify files in the test/ directory. Tests must
|
|
|
31570
31725
|
isolation scope: Create test files in test/. MAY read src/ files and MAY import from src/ to ensure correct types/interfaces. May create minimal stubs in src/ if needed to make imports work, but do NOT implement real logic.${footer}`;
|
|
31571
31726
|
}
|
|
31572
31727
|
if (role === "implementer") {
|
|
31728
|
+
if (mode === "lite") {
|
|
31729
|
+
return `${header}
|
|
31730
|
+
|
|
31731
|
+
isolation scope: Implement source code in src/ to make tests pass. You MAY add tests for acceptance criteria that have no coverage yet; do NOT weaken, delete, or skip existing tests. Run tests frequently to track progress.${footer}`;
|
|
31732
|
+
}
|
|
31573
31733
|
return `${header}
|
|
31574
31734
|
|
|
31575
31735
|
isolation scope: Implement source code in src/ to make tests pass. Do not modify test files. Run tests frequently to track progress.${footer}`;
|
|
@@ -32136,12 +32296,36 @@ Include the story ID when known \u2014 \`feat(<story-id>): <description>\`.
|
|
|
32136
32296
|
When the story is ambiguous, pick an interpretation, proceed, and document the choice in the commit body under \`Assumptions:\`. Do not invent requirements; do not silently choose when the story is genuinely under-specified \u2014 note it.`;
|
|
32137
32297
|
}
|
|
32138
32298
|
|
|
32299
|
+
// src/prompts/sections/test-quality.ts
|
|
32300
|
+
function buildTestQualitySection(role, variant, storyId) {
|
|
32301
|
+
const authors = AUTHORING_ROLES.has(role) || role === "implementer" && variant === "lite";
|
|
32302
|
+
if (!authors)
|
|
32303
|
+
return "";
|
|
32304
|
+
const storyIdLine = storyId ? `
|
|
32305
|
+
- Test names must use THIS story's ID (${storyId}) \u2014 never a sibling story's ID copied from a nearby test.` : "";
|
|
32306
|
+
return `# Review-Proof Tests
|
|
32307
|
+
|
|
32308
|
+
An adversarial reviewer will audit your tests after implementation and BLOCK the story on any test-gap finding. Each block costs a full rectification round. Write tests that survive that audit the first time:
|
|
32309
|
+
|
|
32310
|
+
- Every acceptance criterion needs a test that INVOKES the code at runtime and asserts its observable behavior (return value, thrown error, rendered/mounted output, emitted event, persisted state).
|
|
32311
|
+
- NEVER write source-inspection tests \u2014 asserting that a file contains a pattern, string, or symbol proves nothing about behavior and WILL be blocked as test-gap.
|
|
32312
|
+
- No placeholder or tautological tests: \`expect(true).toBe(true)\`, asserting on literals, empty bodies, \`.skip\`/\`.todo\` on an AC-covering test \u2014 all blocked as test-gap.
|
|
32313
|
+
- Every new exported symbol an AC depends on must be exercised by at least one test.
|
|
32314
|
+
- Cover the boundary and error paths the reviewer probes: empty/null/zero/negative inputs, and failure modes (errors must surface, not be swallowed).
|
|
32315
|
+
- For UI/page-level ACs, mount the page/component and simulate the interaction \u2014 a test that only exercises the underlying unit reads as no coverage for the AC's wiring.${storyIdLine}`;
|
|
32316
|
+
}
|
|
32317
|
+
var AUTHORING_ROLES;
|
|
32318
|
+
var init_test_quality = __esm(() => {
|
|
32319
|
+
AUTHORING_ROLES = new Set(["test-writer", "single-session", "tdd-simple", "batch"]);
|
|
32320
|
+
});
|
|
32321
|
+
|
|
32139
32322
|
// src/prompts/sections/index.ts
|
|
32140
32323
|
var init_sections2 = __esm(() => {
|
|
32141
32324
|
init_hermetic();
|
|
32142
32325
|
init_role_task();
|
|
32143
32326
|
init_story();
|
|
32144
32327
|
init_acceptance();
|
|
32328
|
+
init_test_quality();
|
|
32145
32329
|
});
|
|
32146
32330
|
|
|
32147
32331
|
// src/prompts/loader.ts
|
|
@@ -32284,7 +32468,7 @@ class TddPromptBuilder {
|
|
|
32284
32468
|
if (this.role === "verifier" && this.story_) {
|
|
32285
32469
|
acc.add(this.s("verdict", buildVerdictSection(this.story_)));
|
|
32286
32470
|
}
|
|
32287
|
-
const isolation = this.options.isolation;
|
|
32471
|
+
const isolation = this.role === "implementer" && this.options.variant === "lite" ? "lite" : this.options.isolation;
|
|
32288
32472
|
acc.add(this.s("isolation", buildIsolationSection(this.role, isolation, this.testCommand_)));
|
|
32289
32473
|
const tddLang = buildTddLanguageSection(this.loaderConfig_?.project?.language);
|
|
32290
32474
|
if (tddLang)
|
|
@@ -32300,6 +32484,9 @@ class TddPromptBuilder {
|
|
|
32300
32484
|
const guardrails = buildBehavioralGuardrailsSection(this.role, guardrailLevel, guardrailVariant, guardrailIsolation);
|
|
32301
32485
|
if (guardrails)
|
|
32302
32486
|
acc.add(this.s("guardrails", guardrails));
|
|
32487
|
+
const testQuality = buildTestQualitySection(this.role, this.options.variant, this.story_?.id);
|
|
32488
|
+
if (testQuality)
|
|
32489
|
+
acc.add(this.s("test-quality", testQuality));
|
|
32303
32490
|
if (this.role !== "verifier") {
|
|
32304
32491
|
const selfVerify = buildSelfVerificationSection(this.role, this.selfVerification_);
|
|
32305
32492
|
if (selfVerify)
|
|
@@ -32751,6 +32938,32 @@ var init_debate_builder = __esm(() => {
|
|
|
32751
32938
|
RE_REVIEW_JSON_DIRECTIVE = `Respond with JSON: { passed: boolean; findings: Array<${FINDING_SCHEMA}>; findingReasoning: { [ruleId: string]: string }; deltaSummary: string }`;
|
|
32752
32939
|
});
|
|
32753
32940
|
|
|
32941
|
+
// src/review/semantic-categories.ts
|
|
32942
|
+
function isSemanticCategory(value) {
|
|
32943
|
+
return SEMANTIC_CATEGORY_SET.has(value);
|
|
32944
|
+
}
|
|
32945
|
+
function normalizeSemanticCategory(raw) {
|
|
32946
|
+
if (typeof raw !== "string")
|
|
32947
|
+
return "";
|
|
32948
|
+
const normalized = raw.trim().toLowerCase();
|
|
32949
|
+
if (normalized === "")
|
|
32950
|
+
return "";
|
|
32951
|
+
return isSemanticCategory(normalized) ? normalized : "other";
|
|
32952
|
+
}
|
|
32953
|
+
var SEMANTIC_CATEGORIES, SEMANTIC_CATEGORY_SET, SEMANTIC_CATEGORY_ENUM_LINE;
|
|
32954
|
+
var init_semantic_categories = __esm(() => {
|
|
32955
|
+
SEMANTIC_CATEGORIES = [
|
|
32956
|
+
"unimplemented",
|
|
32957
|
+
"partial",
|
|
32958
|
+
"contradiction",
|
|
32959
|
+
"dead-path",
|
|
32960
|
+
"unwired",
|
|
32961
|
+
"other"
|
|
32962
|
+
];
|
|
32963
|
+
SEMANTIC_CATEGORY_SET = new Set(SEMANTIC_CATEGORIES);
|
|
32964
|
+
SEMANTIC_CATEGORY_ENUM_LINE = SEMANTIC_CATEGORIES.map((c) => `"${c}"`).join(" | ");
|
|
32965
|
+
});
|
|
32966
|
+
|
|
32754
32967
|
// src/prompts/builders/prior-iterations-builder.ts
|
|
32755
32968
|
function buildPriorIterationsBlock(iterations) {
|
|
32756
32969
|
if (iterations.length === 0)
|
|
@@ -32809,9 +33022,13 @@ function renderVerdictTemplate(iterations) {
|
|
|
32809
33022
|
When outcome is "unchanged", the prior hypothesis is FALSIFIED \u2014 the change did not affect what was tested. Choose a different category before producing a new verdict. Do NOT repeat fixes listed above.` : "";
|
|
32810
33023
|
return [
|
|
32811
33024
|
`**Required:** before adding any new finding, classify each of the ${total} prior finding(s) above as one of:`,
|
|
32812
|
-
"- `addressed` \u2014 the current diff resolves it (
|
|
32813
|
-
"- `still-blocking` \u2014 the implementer did not fix it; re-flag it with the IDENTICAL `file`, `line`, `category`, and substantively the same `message` wording",
|
|
32814
|
-
|
|
33025
|
+
"- `addressed` \u2014 the current diff resolves it; record it in `acks` (not `findings`), citing the diff line that fixes it in `note`",
|
|
33026
|
+
"- `still-blocking` \u2014 the implementer did not fix it; re-flag it in `findings` with the IDENTICAL `file`, `line`, `category`, and substantively the same `message` wording",
|
|
33027
|
+
"- `never-an-issue` \u2014 your prior judgment was wrong; record it in `acks` (not `findings`) and explain why in `note`",
|
|
33028
|
+
"",
|
|
33029
|
+
"Do NOT emit an acknowledgement as a finding. A resolved or withdrawn prior finding is not a defect \u2014",
|
|
33030
|
+
"reporting it as one inflates the finding count and buries the real defects. Only `still-blocking` belongs in `findings`.",
|
|
33031
|
+
"",
|
|
32815
33032
|
`Then surface any genuinely new findings.${unchangedNote}`
|
|
32816
33033
|
].join(`
|
|
32817
33034
|
`);
|
|
@@ -32875,13 +33092,36 @@ Flag issues only when you have confirmed:
|
|
|
32875
33092
|
3. New code has dead paths that will never execute (stubs, noops, unreachable branches)
|
|
32876
33093
|
4. New code is not wired into callers/exports (verified by grepping for usage)
|
|
32877
33094
|
|
|
32878
|
-
Do NOT flag: style issues, naming conventions, import ordering, file length, or anything lint handles
|
|
33095
|
+
Do NOT flag: style issues, naming conventions, import ordering, file length, or anything lint handles.
|
|
33096
|
+
|
|
33097
|
+
**Finding categories \u2014 every finding MUST carry exactly one \`category\`:**
|
|
33098
|
+
- \`unimplemented\` \u2014 an AC has no implementation at all.
|
|
33099
|
+
- \`partial\` \u2014 an AC is implemented for some inputs or paths, but not everything it specifies.
|
|
33100
|
+
- \`contradiction\` \u2014 the implementation does the opposite of what the AC specifies, or something the AC forbids.
|
|
33101
|
+
- \`dead-path\` \u2014 a stub, noop, or unreachable branch that will never execute.
|
|
33102
|
+
- \`unwired\` \u2014 new code exists but is not reachable: not exported, not called, not registered.
|
|
33103
|
+
- \`other\` \u2014 genuinely AC-related, but none of the above.
|
|
33104
|
+
|
|
33105
|
+
Pick the most specific axis that fits. Do not invent categories outside this list \u2014 an unrecognised value is recorded as \`other\` and loses its signal.`, SEMANTIC_OUTPUT_SCHEMA, ReviewPromptBuilder;
|
|
33106
|
+
var init_review_builder = __esm(() => {
|
|
33107
|
+
init_semantic_categories();
|
|
33108
|
+
init_sections2();
|
|
33109
|
+
SEMANTIC_ROLE = "You are a semantic code reviewer with access to the repository files. " + "Your job is to walk each acceptance criterion (AC) and judge whether the production code fulfills it \u2014 fully, partially, or not at all. " + "Test coverage gaps and convention/lint issues are out of scope \u2014 adversarial review and lint/typecheck handle those.";
|
|
33110
|
+
SEMANTIC_OUTPUT_SCHEMA = `Respond with JSON only \u2014 no explanation text before or after:
|
|
32879
33111
|
{
|
|
32880
33112
|
"passed": boolean,
|
|
32881
33113
|
"inspectedFiles": ["relative/path/you/actually/opened.ts"],
|
|
33114
|
+
"acks": [
|
|
33115
|
+
{
|
|
33116
|
+
"priorFinding": "<short identifier \u2014 file:line or a few words of its message>",
|
|
33117
|
+
"status": "addressed" | "never-an-issue",
|
|
33118
|
+
"note": "<why: the diff line that fixes it, or why the prior judgment was wrong>"
|
|
33119
|
+
}
|
|
33120
|
+
],
|
|
32882
33121
|
"findings": [
|
|
32883
33122
|
{
|
|
32884
33123
|
"severity": "error" | "warning" | "info" | "unverifiable",
|
|
33124
|
+
"category": ${SEMANTIC_CATEGORY_ENUM_LINE},
|
|
32885
33125
|
"file": "path/to/file",
|
|
32886
33126
|
"line": 42,
|
|
32887
33127
|
"issue": "description of the issue",
|
|
@@ -32899,14 +33139,13 @@ Do NOT flag: style issues, naming conventions, import ordering, file length, or
|
|
|
32899
33139
|
}
|
|
32900
33140
|
|
|
32901
33141
|
Notes:
|
|
33142
|
+
- \`acks\` records prior findings you are NOT re-flagging \u2014 resolved by the diff, or withdrawn. Omit it (or use \`[]\`) when there are no prior findings. An acknowledgement must never appear in \`findings\`.
|
|
33143
|
+
- \`category\` is required on every finding \u2014 one of the axes listed above.
|
|
32902
33144
|
- \`acIndex\` is required when severity is "error" (1-based, into the Acceptance Criteria list above).
|
|
32903
33145
|
- \`acQuote\` is optional advisory metadata for human auditors \u2014 not validated.
|
|
32904
33146
|
- Omit both for "warning", "info", "unverifiable".
|
|
32905
33147
|
- \`inspectedFiles\` must list the relative paths you actually opened while reviewing. A \`passed:true\` verdict with an empty or absent \`inspectedFiles\` is invalid \u2014 walk each AC against the real files before passing.
|
|
32906
|
-
If all ACs are correctly implemented after inspecting the code, respond with { "passed": true, "inspectedFiles": ["..."], "findings": [] }
|
|
32907
|
-
var init_review_builder = __esm(() => {
|
|
32908
|
-
init_sections2();
|
|
32909
|
-
SEMANTIC_ROLE = "You are a semantic code reviewer with access to the repository files. " + "Your job is to walk each acceptance criterion (AC) and judge whether the production code fulfills it \u2014 fully, partially, or not at all. " + "Test coverage gaps and convention/lint issues are out of scope \u2014 adversarial review and lint/typecheck handle those.";
|
|
33148
|
+
If all ACs are correctly implemented after inspecting the code, respond with { "passed": true, "inspectedFiles": ["..."], "findings": [] }.`;
|
|
32910
33149
|
ReviewPromptBuilder = class ReviewPromptBuilder {
|
|
32911
33150
|
buildSemanticReviewPrompt(story, semanticConfig, options) {
|
|
32912
33151
|
const acList = story.acceptanceCriteria.map((ac, i) => `${i + 1}. ${ac}`).join(`
|
|
@@ -32956,7 +33195,7 @@ Respond with a condensed summary:
|
|
|
32956
33195
|
- ${advisoryClause}
|
|
32957
33196
|
- Keep \`verifiedBy\` for every finding. If \`verifiedBy.observed\` is long, abbreviate it to one line \u2014 never drop the field.
|
|
32958
33197
|
Output ONLY a complete, valid JSON object. It must start with { and end with }.
|
|
32959
|
-
Schema: {"passed": boolean, "findings": [{"severity": string, "category":
|
|
33198
|
+
Schema: {"passed": boolean, "findings": [{"severity": string, "category": ${SEMANTIC_CATEGORY_ENUM_LINE}, "file": string, "line": number, "issue": string, "suggestion": string, "verifiedBy": {"command": string, "file": string, "line": number, "observed": string}}]}`;
|
|
32960
33199
|
}
|
|
32961
33200
|
static demandInspection() {
|
|
32962
33201
|
return `Your previous review returned \`passed:true\` with no findings and an empty (or absent) \`inspectedFiles\` list. That means you did not open any of the changed files \u2014 a verdict reached without reading the code is not valid.
|
|
@@ -33011,6 +33250,7 @@ ${drops.map((d, i) => `${i + 1}. [${d.finding.severity}] ${d.finding.issue}`).jo
|
|
|
33011
33250
|
Please re-review the code and re-issue any valid findings. For each finding you re-issue:
|
|
33012
33251
|
- You MUST include a valid \`acIndex\` (1-based index into the AC list below)
|
|
33013
33252
|
- You MUST include a \`verifiedBy\` field with verified evidence
|
|
33253
|
+
- You MUST include a \`category\`: ${SEMANTIC_CATEGORY_ENUM_LINE}
|
|
33014
33254
|
|
|
33015
33255
|
## Acceptance Criteria
|
|
33016
33256
|
${acList}
|
|
@@ -33145,6 +33385,7 @@ What new exported units lack corresponding test files?
|
|
|
33145
33385
|
- Bodies that always pass: \`expect(true).toBe(true)\`, \`expect(x).toBe(x)\`, \`expect(1).toBe(1)\`, an empty test body, or \`assert(true)\`.
|
|
33146
33386
|
- Tests skipped/disabled (\`it.skip\`, \`test.todo\`, \`xit\`, commented-out assertions) that an AC depends on.
|
|
33147
33387
|
- Assertions that never exercise the implementation (e.g. asserting on a literal, not on a value the production code produced).
|
|
33388
|
+
- Source-inspection tests: reading a source file and asserting it contains a pattern, string, or symbol instead of invoking the code and asserting its runtime behavior.
|
|
33148
33389
|
|
|
33149
33390
|
For each such finding: set \`acIndex\` to the AC the fake test purports to cover, \`acQuote\` to a verbatim substring of that AC, and \`verifiedBy.observed\` to the placeholder line itself (e.g. \`expect(true).toBe(true)\`). Do **not** downgrade these to \`warning\` \u2014 a green suite built on placeholder assertions is a failing implementation with hidden evidence.
|
|
33150
33391
|
|
|
@@ -33168,6 +33409,13 @@ Respond with ONLY a JSON object \u2014 no preamble, no explanation outside the J
|
|
|
33168
33409
|
{
|
|
33169
33410
|
"passed": true | false,
|
|
33170
33411
|
"inspectedFiles": ["relative/path/you/actually/opened.ts"],
|
|
33412
|
+
"acks": [
|
|
33413
|
+
{
|
|
33414
|
+
"priorFinding": "<short identifier of the prior finding \u2014 its file:line or a few words of its message>",
|
|
33415
|
+
"status": "addressed" | "never-an-issue",
|
|
33416
|
+
"note": "<why: the diff line that fixes it, or why the prior judgment was wrong>"
|
|
33417
|
+
}
|
|
33418
|
+
],
|
|
33171
33419
|
"findings": [
|
|
33172
33420
|
{
|
|
33173
33421
|
"severity": "error" | "warning" | "info" | "unverifiable",
|
|
@@ -33194,6 +33442,8 @@ Respond with ONLY a JSON object \u2014 no preamble, no explanation outside the J
|
|
|
33194
33442
|
|
|
33195
33443
|
**No rubber-stamping:** \`inspectedFiles\` must list the relative paths you actually opened with your tools while reviewing. A \`passed:true\` verdict with an empty or absent \`inspectedFiles\` is invalid \u2014 it means you never looked at the code. Fetch the diff and open the changed files before forming any verdict.
|
|
33196
33444
|
|
|
33445
|
+
\`acks\` records prior findings you are NOT re-flagging \u2014 resolved by the diff, or withdrawn. Omit it (or use \`[]\`) when there are no prior findings. An acknowledgement must never appear in \`findings\`.
|
|
33446
|
+
|
|
33197
33447
|
Severity guide:
|
|
33198
33448
|
- \`"error"\`: confident this will cause real failure or regression
|
|
33199
33449
|
- \`"warning"\`: fragile or incomplete but may ship without immediate breakage
|
|
@@ -33738,8 +33988,8 @@ function stripMarkdownInline(s) {
|
|
|
33738
33988
|
function extractLocusKeywords(finding) {
|
|
33739
33989
|
const keywords = [];
|
|
33740
33990
|
if (finding.file) {
|
|
33741
|
-
const
|
|
33742
|
-
const stem =
|
|
33991
|
+
const basename6 = finding.file.split("/").pop() ?? "";
|
|
33992
|
+
const stem = basename6.replace(/\.[^.]+$/, "");
|
|
33743
33993
|
for (const part of stem.split(/[-_]/)) {
|
|
33744
33994
|
if (part.length >= 3)
|
|
33745
33995
|
keywords.push(part.toLowerCase());
|
|
@@ -35888,6 +36138,35 @@ var init_acceptance_fix = __esm(() => {
|
|
|
35888
36138
|
};
|
|
35889
36139
|
});
|
|
35890
36140
|
|
|
36141
|
+
// src/review/acks.ts
|
|
36142
|
+
function extractAcks(raw) {
|
|
36143
|
+
if (!Array.isArray(raw))
|
|
36144
|
+
return [];
|
|
36145
|
+
const acks = [];
|
|
36146
|
+
for (const entry of raw) {
|
|
36147
|
+
if (acks.length >= MAX_ACKS)
|
|
36148
|
+
break;
|
|
36149
|
+
if (typeof entry === "string") {
|
|
36150
|
+
if (entry !== "")
|
|
36151
|
+
acks.push({ priorFinding: entry, status: "unknown" });
|
|
36152
|
+
continue;
|
|
36153
|
+
}
|
|
36154
|
+
if (typeof entry !== "object" || entry === null)
|
|
36155
|
+
continue;
|
|
36156
|
+
const e = entry;
|
|
36157
|
+
const known = e.status === "addressed" || e.status === "never-an-issue";
|
|
36158
|
+
const note = typeof e.note === "string" ? e.note.slice(0, MAX_NOTE_CHARS) : "";
|
|
36159
|
+
acks.push({
|
|
36160
|
+
priorFinding: typeof e.priorFinding === "string" ? e.priorFinding : "",
|
|
36161
|
+
status: known ? e.status : "unknown",
|
|
36162
|
+
...note !== "" && { note },
|
|
36163
|
+
...!known && typeof e.status === "string" && { rawStatus: e.status }
|
|
36164
|
+
});
|
|
36165
|
+
}
|
|
36166
|
+
return acks;
|
|
36167
|
+
}
|
|
36168
|
+
var MAX_ACKS = 50, MAX_NOTE_CHARS = 500;
|
|
36169
|
+
|
|
35891
36170
|
// src/review/ac-structural-counterfactual.ts
|
|
35892
36171
|
function analyzeStructuralCounterfactual(finding, acceptanceCriteria, diffFiles) {
|
|
35893
36172
|
const acIndexInRange = typeof finding.acIndex === "number" && finding.acIndex >= 1 && finding.acIndex <= acceptanceCriteria.length;
|
|
@@ -35925,7 +36204,12 @@ function validateAdversarialShape(parsed) {
|
|
|
35925
36204
|
return null;
|
|
35926
36205
|
if (!Array.isArray(obj.findings))
|
|
35927
36206
|
return null;
|
|
35928
|
-
|
|
36207
|
+
const acks = extractAcks(obj.acks);
|
|
36208
|
+
return {
|
|
36209
|
+
passed: obj.passed,
|
|
36210
|
+
findings: obj.findings,
|
|
36211
|
+
...acks.length > 0 && { acks }
|
|
36212
|
+
};
|
|
35929
36213
|
}
|
|
35930
36214
|
function formatFindings(findings) {
|
|
35931
36215
|
return findings.map((f) => `[${f.severity}][${f.category}] ${f.file}:${f.line} \u2014 ${f.issue}
|
|
@@ -35980,7 +36264,21 @@ function validateLLMShape(parsed) {
|
|
|
35980
36264
|
return null;
|
|
35981
36265
|
if (!Array.isArray(obj.findings))
|
|
35982
36266
|
return null;
|
|
35983
|
-
|
|
36267
|
+
const acks = extractAcks(obj.acks);
|
|
36268
|
+
return {
|
|
36269
|
+
passed: obj.passed,
|
|
36270
|
+
findings: obj.findings.filter(isFindingShaped).map(withNormalizedCategory),
|
|
36271
|
+
...acks.length > 0 && { acks }
|
|
36272
|
+
};
|
|
36273
|
+
}
|
|
36274
|
+
function isFindingShaped(f) {
|
|
36275
|
+
return typeof f === "object" && f !== null && !Array.isArray(f);
|
|
36276
|
+
}
|
|
36277
|
+
function withNormalizedCategory(f) {
|
|
36278
|
+
const category = normalizeSemanticCategory(f.category);
|
|
36279
|
+
if (category === "")
|
|
36280
|
+
return f;
|
|
36281
|
+
return { ...f, category };
|
|
35984
36282
|
}
|
|
35985
36283
|
function parseLLMResponse(raw) {
|
|
35986
36284
|
try {
|
|
@@ -36036,7 +36334,7 @@ function llmFindingToFinding(f, opts = {}) {
|
|
|
36036
36334
|
return {
|
|
36037
36335
|
source: "semantic-review",
|
|
36038
36336
|
severity: normalizeSeverity2(f.severity),
|
|
36039
|
-
category:
|
|
36337
|
+
category: normalizeSemanticCategory(f.category),
|
|
36040
36338
|
file: f.file,
|
|
36041
36339
|
line: f.line,
|
|
36042
36340
|
message: f.issue,
|
|
@@ -36051,6 +36349,7 @@ function toReviewFindings(findings, opts = {}) {
|
|
|
36051
36349
|
var UNVERIFIED_FINDING_PATTERNS;
|
|
36052
36350
|
var init_semantic_helpers = __esm(() => {
|
|
36053
36351
|
init_category_fix_target();
|
|
36352
|
+
init_semantic_categories();
|
|
36054
36353
|
init_severity();
|
|
36055
36354
|
UNVERIFIED_FINDING_PATTERNS = [
|
|
36056
36355
|
"cannot verify",
|
|
@@ -36189,6 +36488,89 @@ var init_finding_filters = __esm(() => {
|
|
|
36189
36488
|
init_ac_quote_validator();
|
|
36190
36489
|
});
|
|
36191
36490
|
|
|
36491
|
+
// src/review/recurrence-demotion.ts
|
|
36492
|
+
function normalizeIssueText(s) {
|
|
36493
|
+
return s.replace(/`/g, "").replace(/\s+/g, " ").trim().toLowerCase().slice(0, MAX_ISSUE_PREFIX);
|
|
36494
|
+
}
|
|
36495
|
+
function normalizeFingerprintPath(file3) {
|
|
36496
|
+
return (file3 ?? "").replace(/\\/g, "/").replace(/^(?:\.{1,2}\/)+/, "");
|
|
36497
|
+
}
|
|
36498
|
+
function fingerprintFor(file3, category, text, acIndex) {
|
|
36499
|
+
const normFile = normalizeFingerprintPath(file3);
|
|
36500
|
+
if (typeof acIndex === "number" && Number.isInteger(acIndex) && acIndex >= 1) {
|
|
36501
|
+
return `${normFile}|ac${acIndex}`;
|
|
36502
|
+
}
|
|
36503
|
+
return `${normFile}|${category ?? ""}|${normalizeIssueText(text).slice(0, FP_ISSUE_PREFIX)}`;
|
|
36504
|
+
}
|
|
36505
|
+
function lookupPriorAppearance(priorCounts, finding) {
|
|
36506
|
+
const acKey = finding.acIndex === undefined ? undefined : priorCounts.get(fingerprintFor(finding.file, finding.category, finding.issue, finding.acIndex));
|
|
36507
|
+
const proseKey = priorCounts.get(fingerprintFor(finding.file, finding.category, finding.issue));
|
|
36508
|
+
if (!acKey)
|
|
36509
|
+
return proseKey;
|
|
36510
|
+
if (!proseKey)
|
|
36511
|
+
return acKey;
|
|
36512
|
+
return acKey.count >= proseKey.count ? acKey : proseKey;
|
|
36513
|
+
}
|
|
36514
|
+
function countPriorAppearances(priorIterations, source = "adversarial-review") {
|
|
36515
|
+
const counts = new Map;
|
|
36516
|
+
for (const it of priorIterations) {
|
|
36517
|
+
const seenThisIter = new Map;
|
|
36518
|
+
for (const f of it.findingsAfter ?? []) {
|
|
36519
|
+
if (f.source !== source)
|
|
36520
|
+
continue;
|
|
36521
|
+
const acIndex = typeof f.meta?.acIndex === "number" ? f.meta.acIndex : undefined;
|
|
36522
|
+
seenThisIter.set(fingerprintFor(f.file, f.category, f.message), f.severity);
|
|
36523
|
+
if (acIndex !== undefined) {
|
|
36524
|
+
seenThisIter.set(fingerprintFor(f.file, f.category, f.message, acIndex), f.severity);
|
|
36525
|
+
}
|
|
36526
|
+
}
|
|
36527
|
+
for (const [fp, sev] of seenThisIter) {
|
|
36528
|
+
const cur = counts.get(fp);
|
|
36529
|
+
counts.set(fp, { count: (cur?.count ?? 0) + 1, lastSeverity: sev });
|
|
36530
|
+
}
|
|
36531
|
+
}
|
|
36532
|
+
return counts;
|
|
36533
|
+
}
|
|
36534
|
+
function tagCoverageGap(findings) {
|
|
36535
|
+
return findings.map((f) => ({ ...f, meta: { ...f.meta ?? {}, coverageGap: true } }));
|
|
36536
|
+
}
|
|
36537
|
+
function classifyRecurrence(accepted, priorIterations, cfg, testFileMatch, threshold, source = "adversarial-review") {
|
|
36538
|
+
const blocking = [];
|
|
36539
|
+
const advisory = [];
|
|
36540
|
+
const demoted = [];
|
|
36541
|
+
if (!cfg.enabled) {
|
|
36542
|
+
for (const f of accepted)
|
|
36543
|
+
(isBlockingSeverity(f.severity, threshold) ? blocking : advisory).push(f);
|
|
36544
|
+
return { blocking, advisory, demoted };
|
|
36545
|
+
}
|
|
36546
|
+
const priorCounts = countPriorAppearances(priorIterations, source);
|
|
36547
|
+
for (const f of accepted) {
|
|
36548
|
+
if (f.category === "test-gap" && testFileMatch(f.file) && isBlockingSeverity(f.severity, threshold)) {
|
|
36549
|
+
blocking.push(f);
|
|
36550
|
+
continue;
|
|
36551
|
+
}
|
|
36552
|
+
if (!isBlockingSeverity(f.severity, threshold)) {
|
|
36553
|
+
advisory.push(f);
|
|
36554
|
+
continue;
|
|
36555
|
+
}
|
|
36556
|
+
const prior = lookupPriorAppearance(priorCounts, f);
|
|
36557
|
+
const n = (prior?.count ?? 0) + 1;
|
|
36558
|
+
const prevWasBlocking = prior !== undefined && isBlockingSeverity(prior.lastSeverity, threshold);
|
|
36559
|
+
if (n >= cfg.maxBlockingRounds + 1) {
|
|
36560
|
+
demoted.push(f);
|
|
36561
|
+
} else if (n === 1 || prevWasBlocking) {
|
|
36562
|
+
blocking.push(f);
|
|
36563
|
+
} else {
|
|
36564
|
+
advisory.push(f);
|
|
36565
|
+
}
|
|
36566
|
+
}
|
|
36567
|
+
return { blocking, advisory, demoted };
|
|
36568
|
+
}
|
|
36569
|
+
var MAX_ISSUE_PREFIX = 160, FP_ISSUE_PREFIX = 48;
|
|
36570
|
+
var init_recurrence_demotion = __esm(() => {
|
|
36571
|
+
init_adversarial_helpers();
|
|
36572
|
+
});
|
|
36573
|
+
|
|
36192
36574
|
// src/review/requote-response.ts
|
|
36193
36575
|
function parseRequoteResponse(output) {
|
|
36194
36576
|
const parsed = tryParseLLMJson(output);
|
|
@@ -36236,6 +36618,17 @@ function isRecord(value) {
|
|
|
36236
36618
|
}
|
|
36237
36619
|
var init_requote_response = () => {};
|
|
36238
36620
|
|
|
36621
|
+
// src/operations/_review-fallback.ts
|
|
36622
|
+
function reviewExhaustedFallback(lastOutput, failOpen) {
|
|
36623
|
+
const unparsedPreview = previewOutput(lastOutput, UNPARSED_PREVIEW_BYTES);
|
|
36624
|
+
if (!/"passed"\s*:\s*false/.test(lastOutput))
|
|
36625
|
+
return { ...failOpen, unparsedPreview };
|
|
36626
|
+
return { ...failOpen, passed: false, failOpen: false, looksLikeFail: true, unparsedPreview };
|
|
36627
|
+
}
|
|
36628
|
+
var init__review_fallback = __esm(() => {
|
|
36629
|
+
init_retry();
|
|
36630
|
+
});
|
|
36631
|
+
|
|
36239
36632
|
// src/operations/semantic-review.ts
|
|
36240
36633
|
function withRepromptMarker(output, info) {
|
|
36241
36634
|
const parsed = tryParseLLMJson(output);
|
|
@@ -36323,6 +36716,7 @@ async function performSemanticReground(turn, firstParsed, drops, ctx) {
|
|
|
36323
36716
|
output: JSON.stringify({
|
|
36324
36717
|
passed: false,
|
|
36325
36718
|
findings: secondParsed.findings,
|
|
36719
|
+
...secondParsed.acks && { acks: secondParsed.acks },
|
|
36326
36720
|
_repromptInfo: { dropCount, outcome: "recovered-blocking", costUsd }
|
|
36327
36721
|
}),
|
|
36328
36722
|
estimatedCostUsd: costUsd
|
|
@@ -36335,6 +36729,7 @@ async function performSemanticReground(turn, firstParsed, drops, ctx) {
|
|
|
36335
36729
|
output: JSON.stringify({
|
|
36336
36730
|
passed: true,
|
|
36337
36731
|
findings: [...firstAdvisory, ...secondAdvisory],
|
|
36732
|
+
...secondParsed.acks && { acks: secondParsed.acks },
|
|
36338
36733
|
_repromptInfo: { dropCount, outcome: "recovered-advisory-only", costUsd }
|
|
36339
36734
|
}),
|
|
36340
36735
|
estimatedCostUsd: costUsd
|
|
@@ -36433,7 +36828,7 @@ var FAIL_OPEN, SEMANTIC_REQUOTE_RECOVERED_EVENT = "review.semantic.finding.requo
|
|
|
36433
36828
|
const passed = !requoted.findings.some((finding) => isBlockingSeverity(finding.severity, ctx.input.blockingThreshold ?? "error"));
|
|
36434
36829
|
return {
|
|
36435
36830
|
...turn,
|
|
36436
|
-
output: JSON.stringify({ passed, findings: requoted.findings }),
|
|
36831
|
+
output: JSON.stringify({ passed, findings: requoted.findings, ...parsed.acks && { acks: parsed.acks } }),
|
|
36437
36832
|
estimatedCostUsd: (turn.estimatedCostUsd ?? 0) + requoted.extraCostUsd
|
|
36438
36833
|
};
|
|
36439
36834
|
}
|
|
@@ -36454,7 +36849,9 @@ var init_semantic_review = __esm(() => {
|
|
|
36454
36849
|
init_logger2();
|
|
36455
36850
|
init_prompts();
|
|
36456
36851
|
init_finding_filters();
|
|
36852
|
+
init_recurrence_demotion();
|
|
36457
36853
|
init_requote_response();
|
|
36854
|
+
init__review_fallback();
|
|
36458
36855
|
FAIL_OPEN = {
|
|
36459
36856
|
passed: true,
|
|
36460
36857
|
findings: [],
|
|
@@ -36478,7 +36875,8 @@ var init_semantic_review = __esm(() => {
|
|
|
36478
36875
|
invalid: () => ReviewPromptBuilder.jsonRetry(),
|
|
36479
36876
|
truncated: () => ReviewPromptBuilder.jsonRetryCondensed({ blockingThreshold: input.blockingThreshold })
|
|
36480
36877
|
},
|
|
36481
|
-
exhaustedFallback: (lastOutput) =>
|
|
36878
|
+
exhaustedFallback: (lastOutput) => reviewExhaustedFallback(lastOutput, FAIL_OPEN),
|
|
36879
|
+
outputPreviewBytes: UNPARSED_PREVIEW_BYTES,
|
|
36482
36880
|
logContext: { blockingThreshold: input.blockingThreshold ?? "error" }
|
|
36483
36881
|
}),
|
|
36484
36882
|
hopBody: semanticReviewHopBody,
|
|
@@ -36507,9 +36905,11 @@ var init_semantic_review = __esm(() => {
|
|
|
36507
36905
|
findings: parsed.findings,
|
|
36508
36906
|
normalizedFindings: [],
|
|
36509
36907
|
acDropped: [],
|
|
36510
|
-
repromptEvent
|
|
36908
|
+
repromptEvent,
|
|
36909
|
+
...parsed.acks && { acks: parsed.acks }
|
|
36511
36910
|
};
|
|
36512
36911
|
}
|
|
36912
|
+
const unparsedPreview = previewOutput(output, UNPARSED_PREVIEW_BYTES);
|
|
36513
36913
|
if (/"passed"\s*:\s*false/.test(output)) {
|
|
36514
36914
|
return {
|
|
36515
36915
|
passed: false,
|
|
@@ -36517,10 +36917,11 @@ var init_semantic_review = __esm(() => {
|
|
|
36517
36917
|
normalizedFindings: [],
|
|
36518
36918
|
acDropped: [],
|
|
36519
36919
|
looksLikeFail: true,
|
|
36920
|
+
unparsedPreview,
|
|
36520
36921
|
repromptEvent
|
|
36521
36922
|
};
|
|
36522
36923
|
}
|
|
36523
|
-
return FAIL_OPEN;
|
|
36924
|
+
return { ...FAIL_OPEN, unparsedPreview };
|
|
36524
36925
|
},
|
|
36525
36926
|
async verify(parsed, input, _verifyCtx) {
|
|
36526
36927
|
if (parsed.failOpen || parsed.looksLikeFail)
|
|
@@ -36532,85 +36933,31 @@ var init_semantic_review = __esm(() => {
|
|
|
36532
36933
|
const sanitized = sanitizeRefModeFindings(findings, input.mode, threshold);
|
|
36533
36934
|
const substantiated = await substantiateSemanticEvidence(sanitized, input.mode, input.workdir, input.story.id, threshold, input.repoRoot);
|
|
36534
36935
|
const { accepted, dropped } = filterByAcGroundingMinimal(substantiated, input.story.acceptanceCriteria);
|
|
36535
|
-
const
|
|
36936
|
+
const isTestFile3 = semanticTestFileMatch(input);
|
|
36937
|
+
const recurrenceCfg = input.semanticConfig.recurrenceDemotion ?? { enabled: false, maxBlockingRounds: 2 };
|
|
36938
|
+
const {
|
|
36939
|
+
blocking,
|
|
36940
|
+
advisory: subThreshold,
|
|
36941
|
+
demoted
|
|
36942
|
+
} = classifyRecurrence(accepted, input.priorSemanticIterations ?? [], recurrenceCfg, isTestFile3, threshold, "semantic-review");
|
|
36943
|
+
const advisoryFindings = [
|
|
36944
|
+
...toReviewFindings(subThreshold.filter((f) => isBlockingSeverity(f.severity, threshold)), { isTestFile: isTestFile3 }),
|
|
36945
|
+
...tagCoverageGap(toReviewFindings(demoted, { isTestFile: isTestFile3 }))
|
|
36946
|
+
];
|
|
36536
36947
|
const passed = blocking.length === 0 && (parsed.passed || accepted.length > 0);
|
|
36537
36948
|
return {
|
|
36538
36949
|
...parsed,
|
|
36539
36950
|
passed,
|
|
36540
36951
|
findings: accepted,
|
|
36541
|
-
normalizedFindings: toReviewFindings(blocking, { isTestFile:
|
|
36952
|
+
normalizedFindings: toReviewFindings(blocking, { isTestFile: isTestFile3 }),
|
|
36953
|
+
advisoryFindings,
|
|
36542
36954
|
acDropped: dropped
|
|
36543
36955
|
};
|
|
36544
36956
|
}
|
|
36545
36957
|
};
|
|
36546
36958
|
});
|
|
36547
36959
|
|
|
36548
|
-
// src/
|
|
36549
|
-
function normalizeIssueText(s) {
|
|
36550
|
-
return s.replace(/`/g, "").replace(/\s+/g, " ").trim().toLowerCase().slice(0, MAX_ISSUE_PREFIX);
|
|
36551
|
-
}
|
|
36552
|
-
function fingerprintFor(file3, category, text) {
|
|
36553
|
-
const normFile = (file3 ?? "").replace(/\\/g, "/");
|
|
36554
|
-
return `${normFile}|${category ?? ""}|${normalizeIssueText(text).slice(0, FP_ISSUE_PREFIX)}`;
|
|
36555
|
-
}
|
|
36556
|
-
function countPriorAppearances(priorIterations) {
|
|
36557
|
-
const counts = new Map;
|
|
36558
|
-
for (const it of priorIterations) {
|
|
36559
|
-
const seenThisIter = new Map;
|
|
36560
|
-
for (const f of it.findingsAfter ?? []) {
|
|
36561
|
-
if (f.source !== "adversarial-review")
|
|
36562
|
-
continue;
|
|
36563
|
-
const fp = fingerprintFor(f.file, f.category, f.message);
|
|
36564
|
-
seenThisIter.set(fp, f.severity);
|
|
36565
|
-
}
|
|
36566
|
-
for (const [fp, sev] of seenThisIter) {
|
|
36567
|
-
const cur = counts.get(fp);
|
|
36568
|
-
counts.set(fp, { count: (cur?.count ?? 0) + 1, lastSeverity: sev });
|
|
36569
|
-
}
|
|
36570
|
-
}
|
|
36571
|
-
return counts;
|
|
36572
|
-
}
|
|
36573
|
-
function tagCoverageGap(findings) {
|
|
36574
|
-
return findings.map((f) => ({ ...f, meta: { ...f.meta ?? {}, coverageGap: true } }));
|
|
36575
|
-
}
|
|
36576
|
-
function classifyRecurrence(accepted, priorIterations, cfg, testFileMatch, threshold) {
|
|
36577
|
-
const blocking = [];
|
|
36578
|
-
const advisory = [];
|
|
36579
|
-
const demoted = [];
|
|
36580
|
-
if (!cfg.enabled) {
|
|
36581
|
-
for (const f of accepted)
|
|
36582
|
-
(isBlockingSeverity(f.severity, threshold) ? blocking : advisory).push(f);
|
|
36583
|
-
return { blocking, advisory, demoted };
|
|
36584
|
-
}
|
|
36585
|
-
const priorCounts = countPriorAppearances(priorIterations);
|
|
36586
|
-
for (const f of accepted) {
|
|
36587
|
-
if (f.category === "test-gap" && testFileMatch(f.file) && isBlockingSeverity(f.severity, threshold)) {
|
|
36588
|
-
blocking.push(f);
|
|
36589
|
-
continue;
|
|
36590
|
-
}
|
|
36591
|
-
if (!isBlockingSeverity(f.severity, threshold)) {
|
|
36592
|
-
advisory.push(f);
|
|
36593
|
-
continue;
|
|
36594
|
-
}
|
|
36595
|
-
const prior = priorCounts.get(fingerprintFor(f.file, f.category, f.issue));
|
|
36596
|
-
const n = (prior?.count ?? 0) + 1;
|
|
36597
|
-
const prevWasBlocking = prior !== undefined && isBlockingSeverity(prior.lastSeverity, threshold);
|
|
36598
|
-
if (n >= cfg.maxBlockingRounds + 1) {
|
|
36599
|
-
demoted.push(f);
|
|
36600
|
-
} else if (n === 1 || prevWasBlocking) {
|
|
36601
|
-
blocking.push(f);
|
|
36602
|
-
} else {
|
|
36603
|
-
advisory.push(f);
|
|
36604
|
-
}
|
|
36605
|
-
}
|
|
36606
|
-
return { blocking, advisory, demoted };
|
|
36607
|
-
}
|
|
36608
|
-
var MAX_ISSUE_PREFIX = 160, FP_ISSUE_PREFIX = 48;
|
|
36609
|
-
var init_recurrence_demotion = __esm(() => {
|
|
36610
|
-
init_adversarial_helpers();
|
|
36611
|
-
});
|
|
36612
|
-
|
|
36613
|
-
// src/operations/adversarial-review.ts
|
|
36960
|
+
// src/operations/adversarial-reprompt-marker.ts
|
|
36614
36961
|
function withRepromptMarker2(output, info) {
|
|
36615
36962
|
const parsed = tryParseLLMJson(output);
|
|
36616
36963
|
if (!parsed || typeof parsed !== "object")
|
|
@@ -36633,6 +36980,9 @@ function extractRepromptInfo2(raw) {
|
|
|
36633
36980
|
outcome: i.outcome
|
|
36634
36981
|
};
|
|
36635
36982
|
}
|
|
36983
|
+
var init_adversarial_reprompt_marker = () => {};
|
|
36984
|
+
|
|
36985
|
+
// src/operations/adversarial-review.ts
|
|
36636
36986
|
async function requoteBlockingAdversarialFindings(findings, ctx) {
|
|
36637
36987
|
const threshold = ctx.input.blockingThreshold ?? "error";
|
|
36638
36988
|
const maxRequotes = ctx.input.adversarialConfig.substantiation?.maxRequotes ?? DEFAULT_MAX_REQUOTES2;
|
|
@@ -36747,6 +37097,7 @@ async function performAdversarialReground(turn, firstParsed, drops, ctx) {
|
|
|
36747
37097
|
output: JSON.stringify({
|
|
36748
37098
|
passed: false,
|
|
36749
37099
|
findings: secondParsed.findings,
|
|
37100
|
+
...secondParsed.acks && { acks: secondParsed.acks },
|
|
36750
37101
|
_repromptInfo: { dropCount, outcome: "recovered-blocking", costUsd }
|
|
36751
37102
|
}),
|
|
36752
37103
|
estimatedCostUsd: costUsd
|
|
@@ -36759,6 +37110,7 @@ async function performAdversarialReground(turn, firstParsed, drops, ctx) {
|
|
|
36759
37110
|
output: JSON.stringify({
|
|
36760
37111
|
passed: true,
|
|
36761
37112
|
findings: [...firstAdvisory, ...secondAdvisory],
|
|
37113
|
+
...secondParsed.acks && { acks: secondParsed.acks },
|
|
36762
37114
|
_repromptInfo: { dropCount, outcome: "recovered-advisory-only", costUsd }
|
|
36763
37115
|
}),
|
|
36764
37116
|
estimatedCostUsd: costUsd
|
|
@@ -36794,7 +37146,8 @@ var FAIL_OPEN2, ADVERSARIAL_REQUOTE_RECOVERED_EVENT = "review.adversarial.findin
|
|
|
36794
37146
|
invalid: () => ReviewPromptBuilder.jsonRetry(),
|
|
36795
37147
|
truncated: () => ReviewPromptBuilder.jsonRetryCondensed({ blockingThreshold: input.blockingThreshold })
|
|
36796
37148
|
},
|
|
36797
|
-
exhaustedFallback: (lastOutput) =>
|
|
37149
|
+
exhaustedFallback: (lastOutput) => reviewExhaustedFallback(lastOutput, FAIL_OPEN2),
|
|
37150
|
+
outputPreviewBytes: UNPARSED_PREVIEW_BYTES,
|
|
36798
37151
|
logContext: { blockingThreshold: input.blockingThreshold ?? "error" }
|
|
36799
37152
|
}), adversarialReviewOp;
|
|
36800
37153
|
var init_adversarial_review = __esm(() => {
|
|
@@ -36806,6 +37159,8 @@ var init_adversarial_review = __esm(() => {
|
|
|
36806
37159
|
init_finding_filters();
|
|
36807
37160
|
init_recurrence_demotion();
|
|
36808
37161
|
init_requote_response();
|
|
37162
|
+
init__review_fallback();
|
|
37163
|
+
init_adversarial_reprompt_marker();
|
|
36809
37164
|
FAIL_OPEN2 = {
|
|
36810
37165
|
passed: true,
|
|
36811
37166
|
findings: [],
|
|
@@ -36850,7 +37205,7 @@ var init_adversarial_review = __esm(() => {
|
|
|
36850
37205
|
const passed = !requoted.findings.some((finding) => isBlockingSeverity(finding.severity, ctx.input.blockingThreshold ?? "error"));
|
|
36851
37206
|
return {
|
|
36852
37207
|
...turn,
|
|
36853
|
-
output: JSON.stringify({ passed, findings: requoted.findings }),
|
|
37208
|
+
output: JSON.stringify({ passed, findings: requoted.findings, ...parsed.acks && { acks: parsed.acks } }),
|
|
36854
37209
|
estimatedCostUsd: (turn.estimatedCostUsd ?? 0) + requoted.extraCostUsd
|
|
36855
37210
|
};
|
|
36856
37211
|
}
|
|
@@ -36885,7 +37240,8 @@ var init_adversarial_review = __esm(() => {
|
|
|
36885
37240
|
findings: parsed.findings,
|
|
36886
37241
|
normalizedFindings: [],
|
|
36887
37242
|
acDropped: [],
|
|
36888
|
-
repromptEvent
|
|
37243
|
+
repromptEvent,
|
|
37244
|
+
...parsed.acks && { acks: parsed.acks }
|
|
36889
37245
|
};
|
|
36890
37246
|
}
|
|
36891
37247
|
if (/"passed"\s*:\s*false/.test(output) && !/"findings"\s*:\s*\[\s*\{/.test(output)) {
|
|
@@ -38726,7 +39082,6 @@ var init_ground = __esm(() => {
|
|
|
38726
39082
|
init_errors();
|
|
38727
39083
|
init_logger2();
|
|
38728
39084
|
init_prompts();
|
|
38729
|
-
init_truncation();
|
|
38730
39085
|
groundOp = {
|
|
38731
39086
|
kind: "run",
|
|
38732
39087
|
name: "ground",
|
|
@@ -42163,11 +42518,11 @@ var init_findings = __esm(() => {
|
|
|
42163
42518
|
init_cycle();
|
|
42164
42519
|
});
|
|
42165
42520
|
|
|
42166
|
-
// src/review/
|
|
42167
|
-
function
|
|
42521
|
+
// src/review/review-iteration-store.ts
|
|
42522
|
+
function getReviewIterations(store, storyId) {
|
|
42168
42523
|
return store.get(storyId) ?? [];
|
|
42169
42524
|
}
|
|
42170
|
-
function
|
|
42525
|
+
function recordReviewIteration(store, storyId, roundFindings) {
|
|
42171
42526
|
const prior = store.get(storyId) ?? [];
|
|
42172
42527
|
const findingsBefore = prior.length > 0 ? prior[prior.length - 1].findingsAfter : [];
|
|
42173
42528
|
const findingsAfter = [...roundFindings];
|
|
@@ -42183,7 +42538,7 @@ function recordAdversarialIteration(store, storyId, roundFindings) {
|
|
|
42183
42538
|
};
|
|
42184
42539
|
store.set(storyId, [...prior, iteration]);
|
|
42185
42540
|
}
|
|
42186
|
-
var
|
|
42541
|
+
var init_review_iteration_store = __esm(() => {
|
|
42187
42542
|
init_findings();
|
|
42188
42543
|
});
|
|
42189
42544
|
|
|
@@ -42222,12 +42577,50 @@ function recordAdversarialAudit(opts) {
|
|
|
42222
42577
|
blockingThreshold: opts.blockingThreshold,
|
|
42223
42578
|
result: opts.result,
|
|
42224
42579
|
advisoryFindings: opts.advisoryFindings,
|
|
42580
|
+
acks: opts.acks,
|
|
42225
42581
|
diffAvailable: opts.diffAvailable,
|
|
42226
42582
|
adversarialDropAnalysis: opts.adversarialDropAnalysis,
|
|
42227
42583
|
adversarialAcceptAnalysis: opts.adversarialAcceptAnalysis
|
|
42228
42584
|
});
|
|
42229
42585
|
}
|
|
42230
42586
|
|
|
42587
|
+
// src/review/adversarial-counterfactual-telemetry.ts
|
|
42588
|
+
function buildCounterfactualTelemetry({
|
|
42589
|
+
acDropped,
|
|
42590
|
+
blockingFindings,
|
|
42591
|
+
acceptanceCriteria,
|
|
42592
|
+
diffFiles
|
|
42593
|
+
}) {
|
|
42594
|
+
const adversarialDropAnalysis = acDropped.map((d) => ({
|
|
42595
|
+
finding: {
|
|
42596
|
+
file: d.finding.file ?? "<unknown>",
|
|
42597
|
+
line: d.finding.line ?? 0,
|
|
42598
|
+
severity: d.finding.severity,
|
|
42599
|
+
category: d.finding.category ?? "<unknown>",
|
|
42600
|
+
issue: d.finding.issue
|
|
42601
|
+
},
|
|
42602
|
+
dropCode: d.code,
|
|
42603
|
+
acIndex: d.finding.acIndex,
|
|
42604
|
+
rawCategory: d.finding.category ?? "",
|
|
42605
|
+
counterfactual: analyzeStructuralCounterfactual({ acIndex: d.finding.acIndex, category: d.finding.category, file: d.finding.file }, acceptanceCriteria, diffFiles)
|
|
42606
|
+
}));
|
|
42607
|
+
const adversarialAcceptAnalysis = blockingFindings.map((f) => ({
|
|
42608
|
+
finding: {
|
|
42609
|
+
file: f.file,
|
|
42610
|
+
line: f.line,
|
|
42611
|
+
severity: f.severity,
|
|
42612
|
+
category: f.category
|
|
42613
|
+
},
|
|
42614
|
+
acIndex: f.acIndex,
|
|
42615
|
+
rawCategory: f.category,
|
|
42616
|
+
counterfactual: analyzeStructuralCounterfactual({ acIndex: f.acIndex, category: f.category, file: f.file }, acceptanceCriteria, diffFiles)
|
|
42617
|
+
}));
|
|
42618
|
+
return { adversarialDropAnalysis, adversarialAcceptAnalysis };
|
|
42619
|
+
}
|
|
42620
|
+
var init_adversarial_counterfactual_telemetry = __esm(() => {
|
|
42621
|
+
init_ac_structural_counterfactual();
|
|
42622
|
+
});
|
|
42623
|
+
|
|
42231
42624
|
// src/review/diff-utils.ts
|
|
42232
42625
|
var {spawn: spawn3 } = globalThis.Bun;
|
|
42233
42626
|
async function resolveNaxIgnorePathspecExcludes(workdir, options) {
|
|
@@ -42626,7 +43019,7 @@ var package_default;
|
|
|
42626
43019
|
var init_package = __esm(() => {
|
|
42627
43020
|
package_default = {
|
|
42628
43021
|
name: "@nathapp/nax",
|
|
42629
|
-
version: "0.
|
|
43022
|
+
version: "0.76.0",
|
|
42630
43023
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
42631
43024
|
type: "module",
|
|
42632
43025
|
bin: {
|
|
@@ -42730,8 +43123,8 @@ var init_version = __esm(() => {
|
|
|
42730
43123
|
NAX_VERSION = package_default.version;
|
|
42731
43124
|
NAX_COMMIT = (() => {
|
|
42732
43125
|
try {
|
|
42733
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
42734
|
-
return "
|
|
43126
|
+
if (/^[0-9a-f]{6,10}$/.test("e7721ea5"))
|
|
43127
|
+
return "e7721ea5";
|
|
42735
43128
|
} catch {}
|
|
42736
43129
|
try {
|
|
42737
43130
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -42778,6 +43171,9 @@ function toPersistedEntry(entry, epochMs) {
|
|
|
42778
43171
|
blockingThreshold: entry.blockingThreshold ?? "error",
|
|
42779
43172
|
result: entry.result,
|
|
42780
43173
|
advisoryFindings: entry.advisoryFindings ?? null,
|
|
43174
|
+
acks: entry.acks ?? null,
|
|
43175
|
+
acDropped: entry.acDropped ?? null,
|
|
43176
|
+
...entry.parsed ? {} : { unparsedPreview: entry.unparsedPreview ?? null },
|
|
42781
43177
|
diffAvailable: entry.diffAvailable ?? null,
|
|
42782
43178
|
adversarialDropAnalysis: entry.adversarialDropAnalysis ?? null,
|
|
42783
43179
|
adversarialAcceptAnalysis: entry.adversarialAcceptAnalysis ?? null
|
|
@@ -43146,6 +43542,7 @@ async function runAdversarialReview(opts) {
|
|
|
43146
43542
|
...tagCoverageGap(toAdversarialReviewFindings(demoted, { isTestFile: testFileMatch }))
|
|
43147
43543
|
];
|
|
43148
43544
|
const acDropped = opResult.acDropped ?? [];
|
|
43545
|
+
const acks = opResult.acks;
|
|
43149
43546
|
let diffFiles;
|
|
43150
43547
|
let diffAvailable;
|
|
43151
43548
|
if (diff && diff.length > 0) {
|
|
@@ -43163,30 +43560,12 @@ async function runAdversarialReview(opts) {
|
|
|
43163
43560
|
diffAvailable = true;
|
|
43164
43561
|
}
|
|
43165
43562
|
}
|
|
43166
|
-
const adversarialDropAnalysis
|
|
43167
|
-
|
|
43168
|
-
|
|
43169
|
-
|
|
43170
|
-
|
|
43171
|
-
|
|
43172
|
-
issue: d.finding.issue
|
|
43173
|
-
},
|
|
43174
|
-
dropCode: d.code,
|
|
43175
|
-
acIndex: d.finding.acIndex,
|
|
43176
|
-
rawCategory: d.finding.category ?? "",
|
|
43177
|
-
counterfactual: analyzeStructuralCounterfactual({ acIndex: d.finding.acIndex, category: d.finding.category, file: d.finding.file }, story.acceptanceCriteria, diffFiles)
|
|
43178
|
-
}));
|
|
43179
|
-
const adversarialAcceptAnalysis = blockingFindings.map((f) => ({
|
|
43180
|
-
finding: {
|
|
43181
|
-
file: f.file,
|
|
43182
|
-
line: f.line,
|
|
43183
|
-
severity: f.severity,
|
|
43184
|
-
category: f.category
|
|
43185
|
-
},
|
|
43186
|
-
acIndex: f.acIndex,
|
|
43187
|
-
rawCategory: f.category,
|
|
43188
|
-
counterfactual: analyzeStructuralCounterfactual({ acIndex: f.acIndex, category: f.category, file: f.file }, story.acceptanceCriteria, diffFiles)
|
|
43189
|
-
}));
|
|
43563
|
+
const { adversarialDropAnalysis, adversarialAcceptAnalysis } = buildCounterfactualTelemetry({
|
|
43564
|
+
acDropped,
|
|
43565
|
+
blockingFindings,
|
|
43566
|
+
acceptanceCriteria: story.acceptanceCriteria,
|
|
43567
|
+
diffFiles
|
|
43568
|
+
});
|
|
43190
43569
|
if (advisoryFindings.length > 0) {
|
|
43191
43570
|
logger?.debug("review", `Adversarial review: ${advisoryFindings.length} advisory findings (below threshold '${threshold}')`, {
|
|
43192
43571
|
storyId: story.id,
|
|
@@ -43228,7 +43607,8 @@ async function runAdversarialReview(opts) {
|
|
|
43228
43607
|
advisoryFindings: advisoryFindings.length > 0 ? advisoryReviewFindings : undefined,
|
|
43229
43608
|
diffAvailable,
|
|
43230
43609
|
adversarialDropAnalysis,
|
|
43231
|
-
adversarialAcceptAnalysis
|
|
43610
|
+
adversarialAcceptAnalysis,
|
|
43611
|
+
acks
|
|
43232
43612
|
});
|
|
43233
43613
|
const output = blockingFindings.length > 0 ? `Adversarial review failed:
|
|
43234
43614
|
|
|
@@ -43262,6 +43642,7 @@ ${formatFindings(blockingFindings)}` : "Adversarial review failed (no findings)"
|
|
|
43262
43642
|
storyId: story.id,
|
|
43263
43643
|
featureName,
|
|
43264
43644
|
parsed: true,
|
|
43645
|
+
acks,
|
|
43265
43646
|
failOpen: false,
|
|
43266
43647
|
passed: true,
|
|
43267
43648
|
passReason: "ac_quote_not_substring_demoted",
|
|
@@ -43299,6 +43680,7 @@ ${formatFindings(blockingFindings)}` : "Adversarial review failed (no findings)"
|
|
|
43299
43680
|
storyId: story.id,
|
|
43300
43681
|
featureName,
|
|
43301
43682
|
parsed: true,
|
|
43683
|
+
acks,
|
|
43302
43684
|
failOpen: false,
|
|
43303
43685
|
passed: false,
|
|
43304
43686
|
blockingThreshold: threshold,
|
|
@@ -43329,6 +43711,7 @@ ${dropSummary}`,
|
|
|
43329
43711
|
storyId: story.id,
|
|
43330
43712
|
featureName,
|
|
43331
43713
|
parsed: true,
|
|
43714
|
+
acks,
|
|
43332
43715
|
failOpen: false,
|
|
43333
43716
|
passed: true,
|
|
43334
43717
|
blockingThreshold: threshold,
|
|
@@ -43359,7 +43742,7 @@ var init_adversarial = __esm(() => {
|
|
|
43359
43742
|
init_logger2();
|
|
43360
43743
|
init_adversarial_review();
|
|
43361
43744
|
init_call();
|
|
43362
|
-
|
|
43745
|
+
init_adversarial_counterfactual_telemetry();
|
|
43363
43746
|
init_adversarial_helpers();
|
|
43364
43747
|
init_diff_utils();
|
|
43365
43748
|
init_finding_projection();
|
|
@@ -43819,7 +44202,8 @@ function recordSemanticDebateAudit(opts) {
|
|
|
43819
44202
|
passed: opts.passed,
|
|
43820
44203
|
blockingThreshold: opts.blockingThreshold,
|
|
43821
44204
|
result: opts.result,
|
|
43822
|
-
advisoryFindings: opts.advisoryFindings
|
|
44205
|
+
advisoryFindings: opts.advisoryFindings,
|
|
44206
|
+
acks: opts.acks
|
|
43823
44207
|
});
|
|
43824
44208
|
}
|
|
43825
44209
|
async function runSemanticDebate(opts) {
|
|
@@ -43873,12 +44257,16 @@ async function runSemanticDebate(opts) {
|
|
|
43873
44257
|
const debateCost = debateResult.totalCostUsd ?? 0;
|
|
43874
44258
|
const resolverPassed = debateResult.outcome === "passed";
|
|
43875
44259
|
const allFindings = [];
|
|
44260
|
+
const acks = [];
|
|
43876
44261
|
for (const p of debateResult.proposals) {
|
|
43877
44262
|
const parsed = parseLLMResponse(p.output);
|
|
43878
44263
|
if (parsed) {
|
|
43879
44264
|
allFindings.push(...parsed.findings);
|
|
44265
|
+
if (parsed.acks)
|
|
44266
|
+
acks.push(...parsed.acks.slice(0, MAX_ACKS - acks.length));
|
|
43880
44267
|
}
|
|
43881
44268
|
}
|
|
44269
|
+
const debateAcks = acks.length > 0 ? acks : undefined;
|
|
43882
44270
|
const seen = new Set;
|
|
43883
44271
|
const deduped = [];
|
|
43884
44272
|
for (const f of allFindings) {
|
|
@@ -43912,6 +44300,7 @@ async function runSemanticDebate(opts) {
|
|
|
43912
44300
|
storyId: story.id,
|
|
43913
44301
|
featureName,
|
|
43914
44302
|
parsed: true,
|
|
44303
|
+
acks: debateAcks,
|
|
43915
44304
|
passed: false,
|
|
43916
44305
|
blockingThreshold: debateThreshold,
|
|
43917
44306
|
result: {
|
|
@@ -43944,6 +44333,7 @@ ${formatFindings2(debateBlocking)}`,
|
|
|
43944
44333
|
storyId: story.id,
|
|
43945
44334
|
featureName,
|
|
43946
44335
|
parsed: true,
|
|
44336
|
+
acks: debateAcks,
|
|
43947
44337
|
passed: true,
|
|
43948
44338
|
blockingThreshold: debateThreshold,
|
|
43949
44339
|
result: {
|
|
@@ -43970,6 +44360,7 @@ ${formatFindings2(debateBlocking)}`,
|
|
|
43970
44360
|
storyId: story.id,
|
|
43971
44361
|
featureName,
|
|
43972
44362
|
parsed: true,
|
|
44363
|
+
acks: debateAcks,
|
|
43973
44364
|
passed: true,
|
|
43974
44365
|
blockingThreshold: debateThreshold,
|
|
43975
44366
|
result: {
|
|
@@ -44013,7 +44404,8 @@ function recordSemanticAudit(opts) {
|
|
|
44013
44404
|
passed: opts.passed,
|
|
44014
44405
|
blockingThreshold: opts.blockingThreshold,
|
|
44015
44406
|
result: opts.result,
|
|
44016
|
-
advisoryFindings: opts.advisoryFindings
|
|
44407
|
+
advisoryFindings: opts.advisoryFindings,
|
|
44408
|
+
acks: opts.acks
|
|
44017
44409
|
});
|
|
44018
44410
|
}
|
|
44019
44411
|
async function runSemanticReview(opts) {
|
|
@@ -44291,6 +44683,7 @@ async function runSemanticReview(opts) {
|
|
|
44291
44683
|
}
|
|
44292
44684
|
const threshold = blockingThreshold ?? "error";
|
|
44293
44685
|
const allFindings = opResult.findings;
|
|
44686
|
+
const acks = opResult.acks;
|
|
44294
44687
|
const blockingFindings = allFindings.filter((f) => isBlockingSeverity(f.severity, threshold));
|
|
44295
44688
|
const advisoryFindings = allFindings.filter((f) => !isBlockingSeverity(f.severity, threshold));
|
|
44296
44689
|
if (advisoryFindings.length > 0) {
|
|
@@ -44328,6 +44721,7 @@ ${formatFindings2(blockingFindings)}`;
|
|
|
44328
44721
|
failOpen: false,
|
|
44329
44722
|
passed: false,
|
|
44330
44723
|
blockingThreshold: threshold,
|
|
44724
|
+
acks,
|
|
44331
44725
|
result: {
|
|
44332
44726
|
passed: false,
|
|
44333
44727
|
findings: llmFindingsToReviewFindings(allFindings, { source: "semantic-review", isTestFile: testFileMatch })
|
|
@@ -44358,6 +44752,7 @@ ${formatFindings2(blockingFindings)}`;
|
|
|
44358
44752
|
storyId: story.id,
|
|
44359
44753
|
featureName,
|
|
44360
44754
|
parsed: true,
|
|
44755
|
+
acks,
|
|
44361
44756
|
failOpen: false,
|
|
44362
44757
|
passed: false,
|
|
44363
44758
|
blockingThreshold: threshold,
|
|
@@ -44383,6 +44778,7 @@ ${formatFindings2(blockingFindings)}`;
|
|
|
44383
44778
|
storyId: story.id,
|
|
44384
44779
|
featureName,
|
|
44385
44780
|
parsed: true,
|
|
44781
|
+
acks,
|
|
44386
44782
|
failOpen: false,
|
|
44387
44783
|
passed: true,
|
|
44388
44784
|
blockingThreshold: threshold,
|
|
@@ -44749,7 +45145,7 @@ var init_review = __esm(() => {
|
|
|
44749
45145
|
init_category_fix_target();
|
|
44750
45146
|
init_finding_filters();
|
|
44751
45147
|
init_ac_quote_validator();
|
|
44752
|
-
|
|
45148
|
+
init_review_iteration_store();
|
|
44753
45149
|
init_ac_structural_counterfactual();
|
|
44754
45150
|
init_adversarial();
|
|
44755
45151
|
init_semantic_evidence();
|
|
@@ -44761,6 +45157,7 @@ var init_review = __esm(() => {
|
|
|
44761
45157
|
init_runner2();
|
|
44762
45158
|
init_requote_response();
|
|
44763
45159
|
init_severity();
|
|
45160
|
+
init_semantic_categories();
|
|
44764
45161
|
});
|
|
44765
45162
|
|
|
44766
45163
|
// src/prompts/builders/rectifier-builder-helpers.ts
|
|
@@ -46778,6 +47175,7 @@ function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
|
46778
47175
|
workdir,
|
|
46779
47176
|
pipelineStage: stage,
|
|
46780
47177
|
modelDef,
|
|
47178
|
+
...resolvedRunOptions.modelDef !== undefined ? {} : { modelTier: effectiveTier },
|
|
46781
47179
|
timeoutSeconds: resolvedRunOptions.timeoutSeconds ?? config2.execution?.sessionTimeoutSeconds ?? DEFAULT_CONFIG.execution.sessionTimeoutSeconds,
|
|
46782
47180
|
featureName,
|
|
46783
47181
|
storyId: story.id,
|
|
@@ -46785,6 +47183,7 @@ function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
|
46785
47183
|
});
|
|
46786
47184
|
}
|
|
46787
47185
|
} else {
|
|
47186
|
+
const pinned = hopKind.kind === "primary" && resolvedRunOptions.modelDef !== undefined;
|
|
46788
47187
|
const modelDef = hopKind.kind === "primary" ? resolvedRunOptions.modelDef ?? resolveModelForAgent(config2.models, agentName, effectiveTier, defaultAgent) : resolveModelForAgent(config2.models, agentName, effectiveTier, defaultAgent);
|
|
46789
47188
|
handle = await sessionManager.openSession(sessionName, {
|
|
46790
47189
|
agentName,
|
|
@@ -46792,6 +47191,7 @@ function buildHopCallback(ctx, sessionId, _initialOptions) {
|
|
|
46792
47191
|
workdir,
|
|
46793
47192
|
pipelineStage: stage,
|
|
46794
47193
|
modelDef,
|
|
47194
|
+
...pinned ? {} : { modelTier: effectiveTier },
|
|
46795
47195
|
timeoutSeconds: resolvedRunOptions.timeoutSeconds ?? config2.execution?.sessionTimeoutSeconds ?? DEFAULT_CONFIG.execution.sessionTimeoutSeconds,
|
|
46796
47196
|
featureName,
|
|
46797
47197
|
storyId: story.id,
|
|
@@ -46954,6 +47354,7 @@ async function callOp(ctx, op, input) {
|
|
|
46954
47354
|
const sessionName = sessionRole2 && ctx.packageDir ? computeAcpHandle(ctx.packageDir, ctx.featureName, ctx.storyId, sessionRole2) : undefined;
|
|
46955
47355
|
const completeOptions = {
|
|
46956
47356
|
modelDef: resolved.modelDef,
|
|
47357
|
+
...resolved.modelTier !== undefined ? { modelTier: resolved.modelTier } : {},
|
|
46957
47358
|
jsonMode: completeOp.jsonMode ?? false,
|
|
46958
47359
|
pipelineStage: op.stage,
|
|
46959
47360
|
storyId: ctx.storyId,
|
|
@@ -48009,7 +48410,7 @@ var init_agent_stream_logging = __esm(() => {
|
|
|
48009
48410
|
});
|
|
48010
48411
|
|
|
48011
48412
|
// src/runtime/middleware/cost.ts
|
|
48012
|
-
function attachCostSubscriber(bus, aggregator, runId) {
|
|
48413
|
+
function attachCostSubscriber(bus, aggregator, runId, projectKey) {
|
|
48013
48414
|
const offDispatch = bus.onDispatch((event) => {
|
|
48014
48415
|
const tu = event.tokenUsage;
|
|
48015
48416
|
const wireExactCostUsd = event.exactCostUsd;
|
|
@@ -48022,9 +48423,15 @@ function attachCostSubscriber(bus, aggregator, runId) {
|
|
|
48022
48423
|
const costEvent = {
|
|
48023
48424
|
ts: event.timestamp,
|
|
48024
48425
|
runId,
|
|
48426
|
+
...projectKey !== undefined ? { projectKey } : {},
|
|
48427
|
+
schemaVersion: COST_ROW_SCHEMA_VERSION,
|
|
48025
48428
|
agentName: event.agentName,
|
|
48026
|
-
model: "unknown",
|
|
48429
|
+
model: event.model ?? "unknown",
|
|
48430
|
+
...event.modelTier !== undefined ? { modelTier: event.modelTier } : {},
|
|
48431
|
+
...event.profile !== undefined ? { profile: event.profile } : {},
|
|
48027
48432
|
stage: event.stage,
|
|
48433
|
+
sessionRole: event.sessionRole,
|
|
48434
|
+
...event.featureName !== undefined ? { featureName: event.featureName } : {},
|
|
48028
48435
|
storyId: event.storyId,
|
|
48029
48436
|
callId: event.callId,
|
|
48030
48437
|
scopeId: event.scopeId,
|
|
@@ -48038,14 +48445,18 @@ function attachCostSubscriber(bus, aggregator, runId) {
|
|
|
48038
48445
|
exactCostUsd,
|
|
48039
48446
|
costUsd: exactCostUsd,
|
|
48040
48447
|
confidence,
|
|
48448
|
+
pricingSource: hasWireExactCost ? "wire" : resolvePricingSource(event.model),
|
|
48041
48449
|
durationMs: event.durationMs
|
|
48042
48450
|
};
|
|
48043
48451
|
aggregator.record(costEvent);
|
|
48044
48452
|
});
|
|
48045
48453
|
const offError = bus.onDispatchError((event) => {
|
|
48046
48454
|
const errorEvent = {
|
|
48455
|
+
kind: "error",
|
|
48047
48456
|
ts: event.timestamp,
|
|
48048
48457
|
runId,
|
|
48458
|
+
...projectKey !== undefined ? { projectKey } : {},
|
|
48459
|
+
schemaVersion: COST_ROW_SCHEMA_VERSION,
|
|
48049
48460
|
agentName: event.agentName,
|
|
48050
48461
|
stage: event.stage,
|
|
48051
48462
|
storyId: event.storyId,
|
|
@@ -48074,6 +48485,10 @@ function attachCostSubscriber(bus, aggregator, runId) {
|
|
|
48074
48485
|
offCompleted();
|
|
48075
48486
|
};
|
|
48076
48487
|
}
|
|
48488
|
+
var COST_ROW_SCHEMA_VERSION = 2;
|
|
48489
|
+
var init_cost2 = __esm(() => {
|
|
48490
|
+
init_agents();
|
|
48491
|
+
});
|
|
48077
48492
|
|
|
48078
48493
|
// src/runtime/middleware/audit.ts
|
|
48079
48494
|
function attachAuditSubscriber(bus, auditor, runId) {
|
|
@@ -48172,6 +48587,9 @@ function attachReviewAuditSubscriber(bus, auditor, runId) {
|
|
|
48172
48587
|
blockingThreshold: event.blockingThreshold,
|
|
48173
48588
|
result: event.result,
|
|
48174
48589
|
advisoryFindings: event.advisoryFindings,
|
|
48590
|
+
acks: event.acks ? [...event.acks] : undefined,
|
|
48591
|
+
acDropped: event.acDropped ? [...event.acDropped] : undefined,
|
|
48592
|
+
unparsedPreview: event.unparsedPreview,
|
|
48175
48593
|
diffAvailable: event.diffAvailable,
|
|
48176
48594
|
adversarialDropAnalysis: event.adversarialDropAnalysis,
|
|
48177
48595
|
adversarialAcceptAnalysis: event.adversarialAcceptAnalysis
|
|
@@ -48187,7 +48605,7 @@ function attachReviewAuditSubscriber(bus, auditor, runId) {
|
|
|
48187
48605
|
function scheduleTickIfNeeded(tickRef, tick, intervalMs) {
|
|
48188
48606
|
if (tickRef.handle !== null)
|
|
48189
48607
|
return;
|
|
48190
|
-
tickRef.handle = setTimeout(tick, intervalMs);
|
|
48608
|
+
tickRef.handle = _idleWatchdogDeps.setTimeout(tick, intervalMs);
|
|
48191
48609
|
}
|
|
48192
48610
|
function handleObserveTimeout(state, reason, idleDurationMs, nonToolCallIdleMs) {
|
|
48193
48611
|
if (state.warnedForCurrentIdlePeriod)
|
|
@@ -48215,7 +48633,7 @@ async function handleCancelTimeout(state, reason, controllerRegistry, maxRetryAt
|
|
|
48215
48633
|
return;
|
|
48216
48634
|
}
|
|
48217
48635
|
state.cancelAttempts++;
|
|
48218
|
-
state.lastActivityAt =
|
|
48636
|
+
state.lastActivityAt = _idleWatchdogDeps.now();
|
|
48219
48637
|
getSafeLogger()?.warn("idle-watchdog", reason === "tool_call_only_idle_timeout_exceeded" ? "Canceling tool-call-only idle call" : "Canceling idle call", {
|
|
48220
48638
|
storyId: state.storyId,
|
|
48221
48639
|
key: reason,
|
|
@@ -48249,17 +48667,17 @@ function handleWarnThenCancelTimeout(state, reason, controllerRegistry, maxRetry
|
|
|
48249
48667
|
});
|
|
48250
48668
|
state.inGracePeriod = true;
|
|
48251
48669
|
state.graceReason = reason;
|
|
48252
|
-
state.graceTimer = setTimeout(async () => {
|
|
48670
|
+
state.graceTimer = _idleWatchdogDeps.setTimeout(async () => {
|
|
48253
48671
|
if (!activeStates.has(state.callId))
|
|
48254
48672
|
return;
|
|
48255
48673
|
state.inGracePeriod = false;
|
|
48256
48674
|
state.graceTimer = undefined;
|
|
48257
48675
|
state.graceReason = undefined;
|
|
48258
|
-
const currentReason = getTimeoutReason(state,
|
|
48676
|
+
const currentReason = getTimeoutReason(state, _idleWatchdogDeps.now(), idleTimeoutMs, toolCallOnlyTimeoutMs);
|
|
48259
48677
|
if (currentReason !== reason)
|
|
48260
48678
|
return;
|
|
48261
48679
|
state.cancelAttempts++;
|
|
48262
|
-
state.lastActivityAt =
|
|
48680
|
+
state.lastActivityAt = _idleWatchdogDeps.now();
|
|
48263
48681
|
const cancel = controllerRegistry.get(state.callId);
|
|
48264
48682
|
if (cancel)
|
|
48265
48683
|
await cancel().catch(() => {});
|
|
@@ -48267,7 +48685,7 @@ function handleWarnThenCancelTimeout(state, reason, controllerRegistry, maxRetry
|
|
|
48267
48685
|
}
|
|
48268
48686
|
function clearGrace(state) {
|
|
48269
48687
|
if (state.inGracePeriod && state.graceTimer !== undefined) {
|
|
48270
|
-
clearTimeout(state.graceTimer);
|
|
48688
|
+
_idleWatchdogDeps.clearTimeout(state.graceTimer);
|
|
48271
48689
|
state.graceTimer = undefined;
|
|
48272
48690
|
state.inGracePeriod = false;
|
|
48273
48691
|
state.graceReason = undefined;
|
|
@@ -48303,7 +48721,7 @@ function attachAgentIdleWatchdog(agentStreamEvents, controllerRegistry, config2)
|
|
|
48303
48721
|
const tickRef = { handle: null };
|
|
48304
48722
|
function tick() {
|
|
48305
48723
|
tickRef.handle = null;
|
|
48306
|
-
const now =
|
|
48724
|
+
const now = _idleWatchdogDeps.now();
|
|
48307
48725
|
for (const [, state] of activeStates) {
|
|
48308
48726
|
if (state.inGracePeriod)
|
|
48309
48727
|
continue;
|
|
@@ -48326,7 +48744,7 @@ function attachAgentIdleWatchdog(agentStreamEvents, controllerRegistry, config2)
|
|
|
48326
48744
|
const unsubscribe = agentStreamEvents.onAgentStream((event) => {
|
|
48327
48745
|
switch (event.kind) {
|
|
48328
48746
|
case "agent.call_started": {
|
|
48329
|
-
const now =
|
|
48747
|
+
const now = _idleWatchdogDeps.now();
|
|
48330
48748
|
activeStates.set(event.callId, {
|
|
48331
48749
|
callId: event.callId,
|
|
48332
48750
|
agentName: event.agentName,
|
|
@@ -48398,11 +48816,11 @@ function attachAgentIdleWatchdog(agentStreamEvents, controllerRegistry, config2)
|
|
|
48398
48816
|
const state = activeStates.get(event.callId);
|
|
48399
48817
|
if (state) {
|
|
48400
48818
|
if (state.graceTimer !== undefined)
|
|
48401
|
-
clearTimeout(state.graceTimer);
|
|
48819
|
+
_idleWatchdogDeps.clearTimeout(state.graceTimer);
|
|
48402
48820
|
activeStates.delete(event.callId);
|
|
48403
48821
|
}
|
|
48404
48822
|
if (activeStates.size === 0 && tickRef.handle !== null) {
|
|
48405
|
-
clearTimeout(tickRef.handle);
|
|
48823
|
+
_idleWatchdogDeps.clearTimeout(tickRef.handle);
|
|
48406
48824
|
tickRef.handle = null;
|
|
48407
48825
|
}
|
|
48408
48826
|
break;
|
|
@@ -48413,17 +48831,23 @@ function attachAgentIdleWatchdog(agentStreamEvents, controllerRegistry, config2)
|
|
|
48413
48831
|
unsubscribe();
|
|
48414
48832
|
for (const state of activeStates.values()) {
|
|
48415
48833
|
if (state.graceTimer !== undefined)
|
|
48416
|
-
clearTimeout(state.graceTimer);
|
|
48834
|
+
_idleWatchdogDeps.clearTimeout(state.graceTimer);
|
|
48417
48835
|
}
|
|
48418
48836
|
activeStates.clear();
|
|
48419
48837
|
if (tickRef.handle !== null) {
|
|
48420
|
-
clearTimeout(tickRef.handle);
|
|
48838
|
+
_idleWatchdogDeps.clearTimeout(tickRef.handle);
|
|
48421
48839
|
tickRef.handle = null;
|
|
48422
48840
|
}
|
|
48423
48841
|
};
|
|
48424
48842
|
}
|
|
48843
|
+
var _idleWatchdogDeps;
|
|
48425
48844
|
var init_idle_watchdog = __esm(() => {
|
|
48426
48845
|
init_logger2();
|
|
48846
|
+
_idleWatchdogDeps = {
|
|
48847
|
+
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
48848
|
+
clearTimeout: (id) => clearTimeout(id),
|
|
48849
|
+
now: () => Date.now()
|
|
48850
|
+
};
|
|
48427
48851
|
});
|
|
48428
48852
|
|
|
48429
48853
|
// src/runtime/middleware/index.ts
|
|
@@ -48431,6 +48855,7 @@ var init_middleware = __esm(() => {
|
|
|
48431
48855
|
init_cancellation();
|
|
48432
48856
|
init_logging();
|
|
48433
48857
|
init_agent_stream_logging();
|
|
48858
|
+
init_cost2();
|
|
48434
48859
|
init_idle_watchdog();
|
|
48435
48860
|
});
|
|
48436
48861
|
|
|
@@ -48988,6 +49413,11 @@ var init_manager_sweep = __esm(() => {
|
|
|
48988
49413
|
DEFAULT_ORPHAN_TTL_MS = 4 * 60 * 60 * 1000;
|
|
48989
49414
|
});
|
|
48990
49415
|
|
|
49416
|
+
// src/session/model-selection.ts
|
|
49417
|
+
function selectModel(opts) {
|
|
49418
|
+
return { modelDef: opts.modelDef, ...opts.modelTier ? { modelTier: opts.modelTier } : {} };
|
|
49419
|
+
}
|
|
49420
|
+
|
|
48991
49421
|
// src/session/naming.ts
|
|
48992
49422
|
var exports_naming = {};
|
|
48993
49423
|
__export(exports_naming, {
|
|
@@ -49293,7 +49723,7 @@ class SessionManager {
|
|
|
49293
49723
|
agentName: opts.agentName,
|
|
49294
49724
|
workdir: opts.workdir,
|
|
49295
49725
|
resolvedPermissions,
|
|
49296
|
-
|
|
49726
|
+
...selectModel(opts),
|
|
49297
49727
|
timeoutSeconds: opts.timeoutSeconds,
|
|
49298
49728
|
onPidSpawned: this._pidRegistry ? (pid) => this._pidRegistry?.register(pid) : undefined,
|
|
49299
49729
|
onPidExited: this._pidRegistry ? (pid) => this._pidRegistry?.unregister(pid) : undefined,
|
|
@@ -49568,8 +49998,8 @@ async function triageFlakyFindings(input) {
|
|
|
49568
49998
|
result.push({ ...f });
|
|
49569
49999
|
return { findings: result, quarantineReport: { keys, reasons } };
|
|
49570
50000
|
}
|
|
49571
|
-
const changedTestSet = new Set(diff.changedTestFiles.map(
|
|
49572
|
-
const mappedTestSet = new Set(diff.mappedTestFiles.map(
|
|
50001
|
+
const changedTestSet = new Set(diff.changedTestFiles.map(basename6));
|
|
50002
|
+
const mappedTestSet = new Set(diff.mappedTestFiles.map(basename6));
|
|
49573
50003
|
const candidates = findings.filter((f) => isProbeCandidate(f, changedTestSet, mappedTestSet));
|
|
49574
50004
|
if (candidates.length > flakeDetection.maxProbesPerGate) {
|
|
49575
50005
|
logger?.info("flake-triage", `Skipping flake triage \u2014 ${candidates.length} candidates exceed maxProbesPerGate=${flakeDetection.maxProbesPerGate}`);
|
|
@@ -49633,7 +50063,7 @@ async function triageFlakyFindings(input) {
|
|
|
49633
50063
|
}
|
|
49634
50064
|
return { findings: result, quarantineReport: { keys, reasons } };
|
|
49635
50065
|
}
|
|
49636
|
-
function
|
|
50066
|
+
function basename6(path7) {
|
|
49637
50067
|
const i = path7.lastIndexOf("/");
|
|
49638
50068
|
return i === -1 ? path7 : path7.slice(i + 1);
|
|
49639
50069
|
}
|
|
@@ -49644,7 +50074,7 @@ function isProbeCandidate(finding, changedTestSet, mappedTestSet) {
|
|
|
49644
50074
|
return false;
|
|
49645
50075
|
if (!finding.rule)
|
|
49646
50076
|
return false;
|
|
49647
|
-
const base =
|
|
50077
|
+
const base = basename6(finding.file);
|
|
49648
50078
|
if (changedTestSet.has(base) || changedTestSet.has(finding.file))
|
|
49649
50079
|
return false;
|
|
49650
50080
|
if (mappedTestSet.has(base) || mappedTestSet.has(finding.file))
|
|
@@ -49778,6 +50208,7 @@ __export(exports_runtime, {
|
|
|
49778
50208
|
attachAgentIdleWatchdog: () => attachAgentIdleWatchdog,
|
|
49779
50209
|
_reviewAuditDeps: () => _reviewAuditDeps,
|
|
49780
50210
|
_promptAuditorDeps: () => _promptAuditorDeps,
|
|
50211
|
+
_idleWatchdogDeps: () => _idleWatchdogDeps,
|
|
49781
50212
|
_costAggDeps: () => _costAggDeps,
|
|
49782
50213
|
ReviewAuditor: () => ReviewAuditor,
|
|
49783
50214
|
PromptAuditor: () => PromptAuditor,
|
|
@@ -49787,7 +50218,7 @@ __export(exports_runtime, {
|
|
|
49787
50218
|
CostAggregator: () => CostAggregator,
|
|
49788
50219
|
AgentStreamEventBus: () => AgentStreamEventBus
|
|
49789
50220
|
});
|
|
49790
|
-
import { basename as
|
|
50221
|
+
import { basename as basename7, join as join31 } from "path";
|
|
49791
50222
|
function createRuntime(config2, workdir, opts) {
|
|
49792
50223
|
const runId = crypto.randomUUID();
|
|
49793
50224
|
const controller = new AbortController;
|
|
@@ -49799,7 +50230,7 @@ function createRuntime(config2, workdir, opts) {
|
|
|
49799
50230
|
const configLoader = createConfigLoader(config2);
|
|
49800
50231
|
const dispatchEvents = new DispatchEventBus;
|
|
49801
50232
|
const agentStreamEvents = opts?.agentStreamEvents ?? new AgentStreamEventBus;
|
|
49802
|
-
const projectKey = config2.name?.trim() ||
|
|
50233
|
+
const projectKey = config2.name?.trim() || basename7(workdir);
|
|
49803
50234
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
49804
50235
|
const globalDir = globalOutputDir();
|
|
49805
50236
|
const curatorRollupPathValue = curatorRollupPath(globalDir, config2.curator?.rollupPath);
|
|
@@ -49854,7 +50285,7 @@ function createRuntime(config2, workdir, opts) {
|
|
|
49854
50285
|
agentManager.configureRuntime({ pidRegistry });
|
|
49855
50286
|
}
|
|
49856
50287
|
const offLogging = attachLoggingSubscriber(dispatchEvents, runId);
|
|
49857
|
-
const offCost = attachCostSubscriber(dispatchEvents, costAggregator, runId);
|
|
50288
|
+
const offCost = attachCostSubscriber(dispatchEvents, costAggregator, runId, getProjectKey(config2, workdir));
|
|
49858
50289
|
const offAudit = attachAuditSubscriber(dispatchEvents, promptAuditor, runId);
|
|
49859
50290
|
const offReviewAudit = attachReviewAuditSubscriber(dispatchEvents, reviewAuditor, runId);
|
|
49860
50291
|
const offAgentStreamLogging = attachAgentStreamLogging(agentStreamEvents, runId);
|
|
@@ -49863,6 +50294,7 @@ function createRuntime(config2, workdir, opts) {
|
|
|
49863
50294
|
const logger = getLogger();
|
|
49864
50295
|
const quarantineMemo = createQuarantineMemo();
|
|
49865
50296
|
const adversarialIterations = new Map;
|
|
50297
|
+
const semanticIterations = new Map;
|
|
49866
50298
|
const rectificationOscillations = new Map;
|
|
49867
50299
|
let closed = false;
|
|
49868
50300
|
return {
|
|
@@ -49886,6 +50318,7 @@ function createRuntime(config2, workdir, opts) {
|
|
|
49886
50318
|
logger,
|
|
49887
50319
|
quarantineMemo,
|
|
49888
50320
|
adversarialIterations,
|
|
50321
|
+
semanticIterations,
|
|
49889
50322
|
rectificationOscillations,
|
|
49890
50323
|
get signal() {
|
|
49891
50324
|
return controller.signal;
|
|
@@ -52781,7 +53214,8 @@ var init_telegram = __esm(() => {
|
|
|
52781
53214
|
init_zod();
|
|
52782
53215
|
init_logger2();
|
|
52783
53216
|
_telegramPluginDeps = {
|
|
52784
|
-
fetch: globalThis.fetch.bind(globalThis)
|
|
53217
|
+
fetch: globalThis.fetch.bind(globalThis),
|
|
53218
|
+
basePollBackoffMs: 1000
|
|
52785
53219
|
};
|
|
52786
53220
|
NUMERIC_CHAT_ID = /^-?\d+$/;
|
|
52787
53221
|
TelegramConfigSchema = exports_external.object({
|
|
@@ -52795,7 +53229,7 @@ var init_telegram = __esm(() => {
|
|
|
52795
53229
|
chatId = null;
|
|
52796
53230
|
pendingMessages = new Map;
|
|
52797
53231
|
lastUpdateId = 0;
|
|
52798
|
-
backoffMs =
|
|
53232
|
+
backoffMs = _telegramPluginDeps.basePollBackoffMs;
|
|
52799
53233
|
maxBackoffMs = 30000;
|
|
52800
53234
|
static MAX_DRAIN_PAGES = 10;
|
|
52801
53235
|
static INTERACTIVE_REQUEST_TYPES = new Set([
|
|
@@ -52905,7 +53339,7 @@ ${partLabel}${chunks[i]}`;
|
|
|
52905
53339
|
this.clearInlineKeyboard(update.callback_query.message.message_id);
|
|
52906
53340
|
}
|
|
52907
53341
|
this.pendingMessages.delete(requestId);
|
|
52908
|
-
this.backoffMs =
|
|
53342
|
+
this.backoffMs = _telegramPluginDeps.basePollBackoffMs;
|
|
52909
53343
|
return response;
|
|
52910
53344
|
}
|
|
52911
53345
|
if (update.callback_query) {
|
|
@@ -52976,7 +53410,7 @@ ${partLabel}${chunks[i]}`;
|
|
|
52976
53410
|
rejected: raw.length - updates.length
|
|
52977
53411
|
});
|
|
52978
53412
|
}
|
|
52979
|
-
this.backoffMs =
|
|
53413
|
+
this.backoffMs = _telegramPluginDeps.basePollBackoffMs;
|
|
52980
53414
|
return { ok: true, updates, rawCount: raw.length };
|
|
52981
53415
|
} catch (err) {
|
|
52982
53416
|
this.backoffMs = Math.min(this.backoffMs * 2, this.maxBackoffMs);
|
|
@@ -55788,10 +56222,10 @@ __export(exports_status_cost, {
|
|
|
55788
56222
|
displayCostMetrics: () => displayCostMetrics,
|
|
55789
56223
|
_costReportEmitDeps: () => _costReportEmitDeps
|
|
55790
56224
|
});
|
|
55791
|
-
import { basename as
|
|
56225
|
+
import { basename as basename8 } from "path";
|
|
55792
56226
|
async function resolveProject(workdir) {
|
|
55793
56227
|
const config2 = await loadConfig(workdir).catch(() => null);
|
|
55794
|
-
const project = config2?.name?.trim() ||
|
|
56228
|
+
const project = config2?.name?.trim() || basename8(workdir);
|
|
55795
56229
|
const outputDir = projectOutputDir(project, config2?.outputDir);
|
|
55796
56230
|
return { project, outputDir };
|
|
55797
56231
|
}
|
|
@@ -56056,7 +56490,7 @@ __export(exports_status_features, {
|
|
|
56056
56490
|
_statusFeaturesDeps: () => _statusFeaturesDeps
|
|
56057
56491
|
});
|
|
56058
56492
|
import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
|
|
56059
|
-
import { basename as
|
|
56493
|
+
import { basename as basename9, join as join45, resolve as resolve14 } from "path";
|
|
56060
56494
|
function isPidAlive(pid) {
|
|
56061
56495
|
try {
|
|
56062
56496
|
process.kill(pid, 0);
|
|
@@ -56079,7 +56513,7 @@ async function loadStatusFile(featureDir) {
|
|
|
56079
56513
|
}
|
|
56080
56514
|
async function loadProjectStatusFile(projectDir) {
|
|
56081
56515
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
56082
|
-
const projectKey = config2?.name?.trim() ||
|
|
56516
|
+
const projectKey = config2?.name?.trim() || basename9(projectDir);
|
|
56083
56517
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
56084
56518
|
const statusPath = join45(outputDir, "status.json");
|
|
56085
56519
|
if (!existsSync17(statusPath)) {
|
|
@@ -56150,7 +56584,7 @@ async function getFeatureSummary(featureName, featureDir) {
|
|
|
56150
56584
|
}
|
|
56151
56585
|
async function displayAllFeatures(projectDir) {
|
|
56152
56586
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
56153
|
-
const projectKey = config2?.name?.trim() ||
|
|
56587
|
+
const projectKey = config2?.name?.trim() || basename9(projectDir);
|
|
56154
56588
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
56155
56589
|
const featuresDir = join45(outputDir, "features");
|
|
56156
56590
|
if (!existsSync17(featuresDir)) {
|
|
@@ -56373,7 +56807,7 @@ async function displayFeatureStatus(options = {}) {
|
|
|
56373
56807
|
if (options.dir) {
|
|
56374
56808
|
const projectDir = resolve14(options.dir);
|
|
56375
56809
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
56376
|
-
const projectKey = config2?.name?.trim() ||
|
|
56810
|
+
const projectKey = config2?.name?.trim() || basename9(projectDir);
|
|
56377
56811
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
56378
56812
|
featureDir = join45(outputDir, "features", options.feature);
|
|
56379
56813
|
} else {
|
|
@@ -57004,7 +57438,7 @@ var init_acceptance3 = __esm(() => {
|
|
|
57004
57438
|
const allOutputParts = [];
|
|
57005
57439
|
let anyError = false;
|
|
57006
57440
|
let errorExitCode = 0;
|
|
57007
|
-
let
|
|
57441
|
+
let hardeningPromoted = 0;
|
|
57008
57442
|
for (const { testPath, packageDir, testFramework, commandOverride } of testGroups) {
|
|
57009
57443
|
const testFile = Bun.file(testPath);
|
|
57010
57444
|
const exists = await testFile.exists();
|
|
@@ -57105,7 +57539,7 @@ ${stderr}`;
|
|
|
57105
57539
|
runtime: ctx.runtime,
|
|
57106
57540
|
abortSignal: ctx.abortSignal
|
|
57107
57541
|
});
|
|
57108
|
-
|
|
57542
|
+
hardeningPromoted = result.promoted.length;
|
|
57109
57543
|
} catch (err) {
|
|
57110
57544
|
logger.debug("acceptance", "Hardening pass failed (non-blocking)", {
|
|
57111
57545
|
storyId: ctx.story.id,
|
|
@@ -57118,7 +57552,8 @@ ${stderr}`;
|
|
|
57118
57552
|
packageDir: ctx.workdir,
|
|
57119
57553
|
passed: true,
|
|
57120
57554
|
failedACs: [],
|
|
57121
|
-
retries:
|
|
57555
|
+
retries: ctx.acceptanceRetries ?? 0,
|
|
57556
|
+
hardeningPromoted,
|
|
57122
57557
|
durationMs
|
|
57123
57558
|
});
|
|
57124
57559
|
return { action: "continue" };
|
|
@@ -57134,7 +57569,8 @@ ${stderr}`;
|
|
|
57134
57569
|
packageDir: ctx.workdir,
|
|
57135
57570
|
passed: false,
|
|
57136
57571
|
failedACs: allFailedACs,
|
|
57137
|
-
retries:
|
|
57572
|
+
retries: ctx.acceptanceRetries ?? 0,
|
|
57573
|
+
hardeningPromoted,
|
|
57138
57574
|
durationMs
|
|
57139
57575
|
});
|
|
57140
57576
|
if (anyError) {
|
|
@@ -59246,11 +59682,12 @@ function toReviewDecisionPayload(opName, output) {
|
|
|
59246
59682
|
const reviewer = opName === "semantic-review" ? "semantic" : opName === "adversarial-review" ? "adversarial" : null;
|
|
59247
59683
|
if (!reviewer)
|
|
59248
59684
|
return null;
|
|
59685
|
+
const unparsedPreview = typeof record2.unparsedPreview === "string" ? record2.unparsedPreview : undefined;
|
|
59249
59686
|
if (record2.failOpen === true) {
|
|
59250
|
-
return { reviewer, parsed: false, passed: true, failOpen: true, result: null };
|
|
59687
|
+
return { reviewer, parsed: false, passed: true, failOpen: true, result: null, unparsedPreview };
|
|
59251
59688
|
}
|
|
59252
59689
|
if (record2.looksLikeFail === true) {
|
|
59253
|
-
return { reviewer, parsed: false, passed: false, looksLikeFail: true, result: null };
|
|
59690
|
+
return { reviewer, parsed: false, passed: false, looksLikeFail: true, result: null, unparsedPreview };
|
|
59254
59691
|
}
|
|
59255
59692
|
if (typeof record2.passed !== "boolean" || !Array.isArray(record2.findings)) {
|
|
59256
59693
|
return null;
|
|
@@ -59272,7 +59709,8 @@ function toReviewDecisionPayload(opName, output) {
|
|
|
59272
59709
|
parsed: true,
|
|
59273
59710
|
passed: record2.passed,
|
|
59274
59711
|
result: { passed: record2.passed, findings: record2.findings },
|
|
59275
|
-
acDropped
|
|
59712
|
+
acDropped,
|
|
59713
|
+
...Array.isArray(record2.advisoryFindings) ? { advisoryFindings: record2.advisoryFindings } : {}
|
|
59276
59714
|
};
|
|
59277
59715
|
}
|
|
59278
59716
|
function emitReviewDecision(ctx, opName, output) {
|
|
@@ -59293,7 +59731,10 @@ function emitReviewDecision(ctx, opName, output) {
|
|
|
59293
59731
|
looksLikeFail: payload.parsed ? undefined : payload.looksLikeFail,
|
|
59294
59732
|
failOpen: payload.parsed ? false : payload.failOpen,
|
|
59295
59733
|
passed: payload.passed,
|
|
59296
|
-
result: payload.result
|
|
59734
|
+
result: payload.result,
|
|
59735
|
+
advisoryFindings: payload.parsed ? payload.advisoryFindings : undefined,
|
|
59736
|
+
acDropped: payload.parsed ? payload.acDropped : undefined,
|
|
59737
|
+
unparsedPreview: payload.parsed ? undefined : payload.unparsedPreview
|
|
59297
59738
|
});
|
|
59298
59739
|
}
|
|
59299
59740
|
function logUnifiedReviewPhaseStart(storyId, opName) {
|
|
@@ -59430,13 +59871,19 @@ async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = fa
|
|
|
59430
59871
|
dispatchInput = await refreshReviewInputForDispatch(opName, dispatchInput);
|
|
59431
59872
|
let advIterationBefore = 0;
|
|
59432
59873
|
if (opName === "adversarial-review" && ctx.storyId) {
|
|
59433
|
-
const priorIterations =
|
|
59874
|
+
const priorIterations = getReviewIterations(ctx.runtime.adversarialIterations, ctx.storyId);
|
|
59434
59875
|
advIterationBefore = priorIterations.length;
|
|
59435
59876
|
dispatchInput = {
|
|
59436
59877
|
...dispatchInput,
|
|
59437
59878
|
priorAdversarialIterations: priorIterations
|
|
59438
59879
|
};
|
|
59439
59880
|
}
|
|
59881
|
+
if (opName === "semantic-review" && ctx.storyId) {
|
|
59882
|
+
dispatchInput = {
|
|
59883
|
+
...dispatchInput,
|
|
59884
|
+
priorSemanticIterations: getReviewIterations(ctx.runtime.semanticIterations, ctx.storyId)
|
|
59885
|
+
};
|
|
59886
|
+
}
|
|
59440
59887
|
if (isTddPhase) {
|
|
59441
59888
|
logger?.info("tdd", `-> Session: ${opName}`, { storyId: ctx.storyId, role: opName, ...progressData });
|
|
59442
59889
|
} else if (isThreeSession && opName === "full-suite-gate") {
|
|
@@ -59458,11 +59905,18 @@ async function runPhase(ctx, slot, phaseCosts, phaseOutputs, isThreeSession = fa
|
|
|
59458
59905
|
emitReviewDecision(ctx, opName, output);
|
|
59459
59906
|
if (opName === "adversarial-review" && ctx.storyId) {
|
|
59460
59907
|
const advOut = output;
|
|
59461
|
-
|
|
59908
|
+
recordReviewIteration(ctx.runtime.adversarialIterations, ctx.storyId, [
|
|
59462
59909
|
...advOut.normalizedFindings ?? [],
|
|
59463
59910
|
...advOut.advisoryFindings ?? []
|
|
59464
59911
|
]);
|
|
59465
59912
|
}
|
|
59913
|
+
if (opName === "semantic-review" && ctx.storyId) {
|
|
59914
|
+
const semOut = output;
|
|
59915
|
+
recordReviewIteration(ctx.runtime.semanticIterations, ctx.storyId, [
|
|
59916
|
+
...semOut.normalizedFindings ?? [],
|
|
59917
|
+
...semOut.advisoryFindings ?? []
|
|
59918
|
+
]);
|
|
59919
|
+
}
|
|
59466
59920
|
logUnifiedReviewPhaseResult(ctx.storyId, opName, output);
|
|
59467
59921
|
logDeterministicPhaseOutcome(ctx.storyId, opName, output, Date.now() - phaseStartedAt, isTddPhase, slot.op.stage, progressData);
|
|
59468
59922
|
outcome = derivePhaseOutcome(output);
|
|
@@ -60224,6 +60678,7 @@ var init_story_orchestrator = __esm(() => {
|
|
|
60224
60678
|
init_rectification();
|
|
60225
60679
|
init_nbf_flake_triage();
|
|
60226
60680
|
init_run_phase();
|
|
60681
|
+
init_review_decision();
|
|
60227
60682
|
init_types9();
|
|
60228
60683
|
});
|
|
60229
60684
|
|
|
@@ -60573,7 +61028,6 @@ async function assemblePlanInputsFromCtx(ctx) {
|
|
|
60573
61028
|
diff: prepared.diff,
|
|
60574
61029
|
excludePatterns: prepared.excludePatterns,
|
|
60575
61030
|
featureCtxBlock: buildFeatureCtxBlock(ctx, "reviewer-semantic"),
|
|
60576
|
-
priorSemanticIterations: ctx.priorSemanticIterations,
|
|
60577
61031
|
resolvedTestPatterns,
|
|
60578
61032
|
blockingThreshold: ctx.config.review.blockingThreshold,
|
|
60579
61033
|
_refresh: {
|
|
@@ -60611,7 +61065,6 @@ async function assemblePlanInputsFromCtx(ctx) {
|
|
|
60611
61065
|
testGlobs: prepared.testGlobs,
|
|
60612
61066
|
refExcludePatterns: prepared.refExcludePatterns,
|
|
60613
61067
|
featureCtxBlock: buildFeatureCtxBlock(ctx, "reviewer-adversarial"),
|
|
60614
|
-
priorAdversarialIterations: ctx.priorAdversarialIterations,
|
|
60615
61068
|
resolvedTestPatterns,
|
|
60616
61069
|
blockingThreshold: ctx.config.review.blockingThreshold,
|
|
60617
61070
|
_refresh: {
|
|
@@ -62113,7 +62566,7 @@ async function fanOutReporters(reporters, hook, invoke) {
|
|
|
62113
62566
|
}
|
|
62114
62567
|
}
|
|
62115
62568
|
}
|
|
62116
|
-
function wireReporters(bus, pluginRegistry, runId, startTime) {
|
|
62569
|
+
function wireReporters(bus, pluginRegistry, runId, startTime, projectKey) {
|
|
62117
62570
|
const logger = getSafeLogger();
|
|
62118
62571
|
const safe = (name, fn) => {
|
|
62119
62572
|
return fn().catch((err) => logger?.warn("reporters-subscriber", `Reporter "${name}" error`, { error: String(err) })).catch(() => {});
|
|
@@ -62163,7 +62616,8 @@ function wireReporters(bus, pluginRegistry, runId, startTime) {
|
|
|
62163
62616
|
runId,
|
|
62164
62617
|
feature: ev.feature,
|
|
62165
62618
|
totalStories: ev.totalStories,
|
|
62166
|
-
startTime: new Date(startTime).toISOString()
|
|
62619
|
+
startTime: new Date(startTime).toISOString(),
|
|
62620
|
+
project: projectKey
|
|
62167
62621
|
});
|
|
62168
62622
|
} catch (err) {
|
|
62169
62623
|
logger?.warn("plugins", `Reporter '${r.name}' onRunStart failed`, { error: err });
|
|
@@ -62690,7 +63144,8 @@ var init_gitignore = __esm(() => {
|
|
|
62690
63144
|
"**/_nax_acceptance_test.py",
|
|
62691
63145
|
"**/_nax_suggested_test.py",
|
|
62692
63146
|
"**/.nax/features/*/",
|
|
62693
|
-
".nax/prompt-audit/"
|
|
63147
|
+
".nax/prompt-audit/",
|
|
63148
|
+
".nax/finish-audit/"
|
|
62694
63149
|
];
|
|
62695
63150
|
});
|
|
62696
63151
|
|
|
@@ -62703,7 +63158,7 @@ __export(exports_init_context, {
|
|
|
62703
63158
|
generatePackageContextTemplate: () => generatePackageContextTemplate,
|
|
62704
63159
|
generateContextTemplate: () => generateContextTemplate
|
|
62705
63160
|
});
|
|
62706
|
-
import { basename as
|
|
63161
|
+
import { basename as basename11, join as join54 } from "path";
|
|
62707
63162
|
async function bunFileExists(path16) {
|
|
62708
63163
|
return Bun.file(path16).exists();
|
|
62709
63164
|
}
|
|
@@ -62798,7 +63253,7 @@ async function scanProject(projectRoot) {
|
|
|
62798
63253
|
const readmeSnippet = await readReadmeSnippet(projectRoot);
|
|
62799
63254
|
const entryPoints = await detectEntryPoints(projectRoot);
|
|
62800
63255
|
const configFiles = await detectConfigFiles(projectRoot);
|
|
62801
|
-
const projectName = packageManifest?.name ||
|
|
63256
|
+
const projectName = packageManifest?.name || basename11(projectRoot);
|
|
62802
63257
|
return {
|
|
62803
63258
|
projectName,
|
|
62804
63259
|
fileTree,
|
|
@@ -64221,7 +64676,8 @@ async function collectFromMetrics(context) {
|
|
|
64221
64676
|
const success2 = boolValue(story.success, false);
|
|
64222
64677
|
const storyId = stringValue(story.storyId ?? story.id, "unknown");
|
|
64223
64678
|
const obs = {
|
|
64224
|
-
schemaVersion:
|
|
64679
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64680
|
+
projectKey: context.projectKey,
|
|
64225
64681
|
runId: context.runId,
|
|
64226
64682
|
featureId: stringValue(currentRun.feature, context.feature),
|
|
64227
64683
|
storyId,
|
|
@@ -64241,6 +64697,24 @@ async function collectFromMetrics(context) {
|
|
|
64241
64697
|
} catch {}
|
|
64242
64698
|
return observations;
|
|
64243
64699
|
}
|
|
64700
|
+
function withinRun(timestamp, runStartedAt) {
|
|
64701
|
+
if (runStartedAt === undefined)
|
|
64702
|
+
return true;
|
|
64703
|
+
const ts = typeof timestamp === "string" ? Date.parse(timestamp) : Number.NaN;
|
|
64704
|
+
if (Number.isNaN(ts))
|
|
64705
|
+
return true;
|
|
64706
|
+
return ts >= runStartedAt;
|
|
64707
|
+
}
|
|
64708
|
+
async function writtenThisRun(filePath, runStartedAt) {
|
|
64709
|
+
if (runStartedAt === undefined)
|
|
64710
|
+
return true;
|
|
64711
|
+
try {
|
|
64712
|
+
const stat = await Bun.file(filePath).stat();
|
|
64713
|
+
return stat.mtimeMs >= runStartedAt;
|
|
64714
|
+
} catch {
|
|
64715
|
+
return true;
|
|
64716
|
+
}
|
|
64717
|
+
}
|
|
64244
64718
|
function findingRuleId(finding) {
|
|
64245
64719
|
return stringValue(finding.ruleId ?? finding.rule ?? finding.checkId ?? finding.category, "unknown");
|
|
64246
64720
|
}
|
|
@@ -64266,17 +64740,22 @@ async function collectFromReviewAudit(context) {
|
|
|
64266
64740
|
const audit = asRecord3(await readJsonFile(fullPath));
|
|
64267
64741
|
if (!audit)
|
|
64268
64742
|
continue;
|
|
64743
|
+
const featureId = stringValue(audit.featureName ?? audit.featureId, context.feature);
|
|
64744
|
+
if (context.runStartedAt !== undefined && featureId !== context.feature)
|
|
64745
|
+
continue;
|
|
64746
|
+
if (!withinRun(audit.timestamp, context.runStartedAt))
|
|
64747
|
+
continue;
|
|
64269
64748
|
const result = asRecord3(audit.result);
|
|
64270
64749
|
const findings = asArray(result?.findings);
|
|
64271
64750
|
const storyId = stringValue(audit.storyId, "unknown");
|
|
64272
|
-
const featureId = stringValue(audit.featureName ?? audit.featureId, context.feature);
|
|
64273
64751
|
for (const rawFinding of findings) {
|
|
64274
64752
|
const finding = asRecord3(rawFinding);
|
|
64275
64753
|
if (!finding)
|
|
64276
64754
|
continue;
|
|
64277
64755
|
const ruleId = findingRuleId(finding);
|
|
64278
64756
|
const obs = {
|
|
64279
|
-
schemaVersion:
|
|
64757
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64758
|
+
projectKey: context.projectKey,
|
|
64280
64759
|
runId: context.runId,
|
|
64281
64760
|
featureId,
|
|
64282
64761
|
storyId,
|
|
@@ -64287,6 +64766,7 @@ async function collectFromReviewAudit(context) {
|
|
|
64287
64766
|
ruleId,
|
|
64288
64767
|
checkId: ruleId,
|
|
64289
64768
|
severity: stringValue(finding.severity, "info"),
|
|
64769
|
+
category: optionalString(finding.category),
|
|
64290
64770
|
file: stringValue(finding.file),
|
|
64291
64771
|
line: numberValue(finding.line, 0),
|
|
64292
64772
|
message: findingMessage(finding)
|
|
@@ -64302,6 +64782,7 @@ async function collectFromReviewAudit(context) {
|
|
|
64302
64782
|
async function collectFromContextManifests(context) {
|
|
64303
64783
|
const observations = [];
|
|
64304
64784
|
const featuresDir = path18.join(context.workdir, ".nax", "features");
|
|
64785
|
+
let skippedManifests = 0;
|
|
64305
64786
|
try {
|
|
64306
64787
|
const glob = new Bun.Glob("*/stories/*/context-manifest-*.json");
|
|
64307
64788
|
for await (const file3 of glob.scan({ cwd: featuresDir, absolute: false })) {
|
|
@@ -64310,15 +64791,21 @@ async function collectFromContextManifests(context) {
|
|
|
64310
64791
|
const parts = file3.split("/");
|
|
64311
64792
|
const featureId = parts[0] ?? context.feature;
|
|
64312
64793
|
const storyId = parts[2] ?? "unknown";
|
|
64794
|
+
if (!await writtenThisRun(fullPath, context.runStartedAt)) {
|
|
64795
|
+
skippedManifests += 1;
|
|
64796
|
+
continue;
|
|
64797
|
+
}
|
|
64313
64798
|
const manifest = asRecord3(await readJsonFile(fullPath));
|
|
64314
64799
|
if (!manifest)
|
|
64315
64800
|
continue;
|
|
64316
64801
|
const ts = now();
|
|
64317
64802
|
const chunkSummaries = asRecord3(manifest.chunkSummaries) ?? {};
|
|
64803
|
+
const chunkTokens = asRecord3(manifest.chunkTokens) ?? {};
|
|
64318
64804
|
for (const chunkId of asArray(manifest.includedChunks)) {
|
|
64319
64805
|
const id = String(chunkId);
|
|
64320
64806
|
const obs = {
|
|
64321
|
-
schemaVersion:
|
|
64807
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64808
|
+
projectKey: context.projectKey,
|
|
64322
64809
|
runId: context.runId,
|
|
64323
64810
|
featureId,
|
|
64324
64811
|
storyId,
|
|
@@ -64328,7 +64815,7 @@ async function collectFromContextManifests(context) {
|
|
|
64328
64815
|
payload: {
|
|
64329
64816
|
chunkId: id,
|
|
64330
64817
|
label: stringValue(chunkSummaries[id], id),
|
|
64331
|
-
tokens: 0
|
|
64818
|
+
tokens: numberValue(chunkTokens[id], 0)
|
|
64332
64819
|
}
|
|
64333
64820
|
};
|
|
64334
64821
|
observations.push(obs);
|
|
@@ -64339,7 +64826,8 @@ async function collectFromContextManifests(context) {
|
|
|
64339
64826
|
continue;
|
|
64340
64827
|
const id = stringValue(excluded.id, "unknown");
|
|
64341
64828
|
const obs = {
|
|
64342
|
-
schemaVersion:
|
|
64829
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64830
|
+
projectKey: context.projectKey,
|
|
64343
64831
|
runId: context.runId,
|
|
64344
64832
|
featureId,
|
|
64345
64833
|
storyId,
|
|
@@ -64359,7 +64847,8 @@ async function collectFromContextManifests(context) {
|
|
|
64359
64847
|
if (!provider || stringValue(provider.status) !== "empty")
|
|
64360
64848
|
continue;
|
|
64361
64849
|
const obs = {
|
|
64362
|
-
schemaVersion:
|
|
64850
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64851
|
+
projectKey: context.projectKey,
|
|
64363
64852
|
runId: context.runId,
|
|
64364
64853
|
featureId,
|
|
64365
64854
|
storyId,
|
|
@@ -64390,7 +64879,8 @@ function entryFeatureId(context, entry, data) {
|
|
|
64390
64879
|
function collectPullCall(context, entry, data) {
|
|
64391
64880
|
const toolName = stringValue(data.tool ?? data.toolName, "unknown");
|
|
64392
64881
|
return {
|
|
64393
|
-
schemaVersion:
|
|
64882
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64883
|
+
projectKey: context.projectKey,
|
|
64394
64884
|
runId: context.runId,
|
|
64395
64885
|
featureId: entryFeatureId(context, entry, data),
|
|
64396
64886
|
storyId: entryStoryId(entry, data),
|
|
@@ -64409,7 +64899,8 @@ function collectPullCall(context, entry, data) {
|
|
|
64409
64899
|
}
|
|
64410
64900
|
function collectAcceptanceVerdict(context, entry, data) {
|
|
64411
64901
|
return {
|
|
64412
|
-
schemaVersion:
|
|
64902
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64903
|
+
projectKey: context.projectKey,
|
|
64413
64904
|
runId: context.runId,
|
|
64414
64905
|
featureId: entryFeatureId(context, entry, data),
|
|
64415
64906
|
storyId: entryStoryId(entry, data),
|
|
@@ -64427,7 +64918,8 @@ function collectAcceptanceVerdict(context, entry, data) {
|
|
|
64427
64918
|
}
|
|
64428
64919
|
function collectRectify(context, entry, data) {
|
|
64429
64920
|
return {
|
|
64430
|
-
schemaVersion:
|
|
64921
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64922
|
+
projectKey: context.projectKey,
|
|
64431
64923
|
runId: context.runId,
|
|
64432
64924
|
featureId: entryFeatureId(context, entry, data),
|
|
64433
64925
|
storyId: entryStoryId(entry, data),
|
|
@@ -64442,7 +64934,8 @@ function collectRectify(context, entry, data) {
|
|
|
64442
64934
|
}
|
|
64443
64935
|
function collectEscalation(context, entry, data) {
|
|
64444
64936
|
return {
|
|
64445
|
-
schemaVersion:
|
|
64937
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64938
|
+
projectKey: context.projectKey,
|
|
64446
64939
|
runId: context.runId,
|
|
64447
64940
|
featureId: entryFeatureId(context, entry, data),
|
|
64448
64941
|
storyId: entryStoryId(entry, data),
|
|
@@ -64458,7 +64951,8 @@ function collectEscalation(context, entry, data) {
|
|
|
64458
64951
|
function collectFixCycleIteration(context, entry, data) {
|
|
64459
64952
|
const outcome = optionalString(data.outcome);
|
|
64460
64953
|
return {
|
|
64461
|
-
schemaVersion:
|
|
64954
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64955
|
+
projectKey: context.projectKey,
|
|
64462
64956
|
runId: context.runId,
|
|
64463
64957
|
featureId: entryFeatureId(context, entry, data),
|
|
64464
64958
|
storyId: entryStoryId(entry, data),
|
|
@@ -64478,7 +64972,8 @@ function collectFixCycleIteration(context, entry, data) {
|
|
|
64478
64972
|
}
|
|
64479
64973
|
function collectFixCycleExit(context, entry, data) {
|
|
64480
64974
|
return {
|
|
64481
|
-
schemaVersion:
|
|
64975
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64976
|
+
projectKey: context.projectKey,
|
|
64482
64977
|
runId: context.runId,
|
|
64483
64978
|
featureId: entryFeatureId(context, entry, data),
|
|
64484
64979
|
storyId: entryStoryId(entry, data),
|
|
@@ -64493,7 +64988,8 @@ function collectFixCycleExit(context, entry, data) {
|
|
|
64493
64988
|
}
|
|
64494
64989
|
function collectFixCycleRetry(context, entry, data) {
|
|
64495
64990
|
return {
|
|
64496
|
-
schemaVersion:
|
|
64991
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
64992
|
+
projectKey: context.projectKey,
|
|
64497
64993
|
runId: context.runId,
|
|
64498
64994
|
featureId: entryFeatureId(context, entry, data),
|
|
64499
64995
|
storyId: entryStoryId(entry, data),
|
|
@@ -64547,6 +65043,7 @@ async function collectObservations(context) {
|
|
|
64547
65043
|
]);
|
|
64548
65044
|
return [...metricsObs, ...auditObs, ...manifestObs, ...jsonlObs];
|
|
64549
65045
|
}
|
|
65046
|
+
var OBSERVATION_SCHEMA_VERSION = 3;
|
|
64550
65047
|
var init_collect = () => {};
|
|
64551
65048
|
|
|
64552
65049
|
// src/plugins/builtin/curator/heuristics.ts
|
|
@@ -64563,6 +65060,12 @@ function mergeThresholds(thresholds) {
|
|
|
64563
65060
|
function uniqueStoryIds(storyIds) {
|
|
64564
65061
|
return [...new Set(storyIds)];
|
|
64565
65062
|
}
|
|
65063
|
+
function crossFeatureKey(category, message) {
|
|
65064
|
+
return `${category ?? ""}|${normalizeIssueText(message).slice(0, CROSS_FEATURE_MESSAGE_PREFIX)}`;
|
|
65065
|
+
}
|
|
65066
|
+
function truncate3(s, max) {
|
|
65067
|
+
return s.length > max ? `${s.slice(0, max - 1)}\u2026` : s;
|
|
65068
|
+
}
|
|
64566
65069
|
function firstLine2(message) {
|
|
64567
65070
|
return message.split(`
|
|
64568
65071
|
`)[0] ?? message;
|
|
@@ -64571,37 +65074,45 @@ function h1RepeatedReviewFinding(observations, threshold) {
|
|
|
64571
65074
|
const findings = observations.filter((o) => o.kind === "review-finding");
|
|
64572
65075
|
const groups = new Map;
|
|
64573
65076
|
for (const obs of findings) {
|
|
64574
|
-
const
|
|
64575
|
-
|
|
64576
|
-
|
|
64577
|
-
|
|
64578
|
-
|
|
64579
|
-
|
|
64580
|
-
|
|
64581
|
-
|
|
64582
|
-
|
|
64583
|
-
} else {
|
|
64584
|
-
const sampleKey = firstLine2(message);
|
|
64585
|
-
groups.set(ruleId, { storyIds: [obs.storyId], samples: sampleKey ? [sampleKey] : [] });
|
|
65077
|
+
const { file: file3, category, message } = obs.payload;
|
|
65078
|
+
if (!file3 && !category)
|
|
65079
|
+
continue;
|
|
65080
|
+
const key = crossFeatureKey(category, message);
|
|
65081
|
+
let group = groups.get(key);
|
|
65082
|
+
if (!group) {
|
|
65083
|
+
group = { featureIds: new Set, sites: new Set, files: new Set, samples: [] };
|
|
65084
|
+
groups.set(key, group);
|
|
65085
|
+
group.category = category;
|
|
64586
65086
|
}
|
|
65087
|
+
group.featureIds.add(obs.featureId);
|
|
65088
|
+
group.sites.add(`${obs.featureId}/${obs.storyId}`);
|
|
65089
|
+
if (file3)
|
|
65090
|
+
group.files.add(file3);
|
|
65091
|
+
const sample = firstLine2(message);
|
|
65092
|
+
if (sample && group.samples.length < 2 && !group.samples.includes(sample))
|
|
65093
|
+
group.samples.push(sample);
|
|
64587
65094
|
}
|
|
64588
65095
|
const proposals = [];
|
|
64589
|
-
for (const
|
|
64590
|
-
|
|
65096
|
+
for (const group of groups.values()) {
|
|
65097
|
+
const featureCount = group.featureIds.size;
|
|
65098
|
+
if (featureCount < threshold)
|
|
64591
65099
|
continue;
|
|
64592
|
-
const
|
|
64593
|
-
const
|
|
64594
|
-
const
|
|
64595
|
-
const
|
|
64596
|
-
|
|
65100
|
+
const features = [...group.featureIds];
|
|
65101
|
+
const sites = [...group.sites];
|
|
65102
|
+
const files = [...group.files];
|
|
65103
|
+
const gist = group.samples[0] ? truncate3(group.samples[0], DESCRIPTION_GIST_CHARS) : "(no description)";
|
|
65104
|
+
const categoryLabel = group.category ? `${group.category}: ` : "";
|
|
65105
|
+
const fileSection = files.length > 0 ? ` Files: ${files.slice(0, MAX_EVIDENCE_FILES).join(", ")}.` : "";
|
|
65106
|
+
const sampleSection = group.samples.length > 0 ? `
|
|
65107
|
+
Examples: ${group.samples.join(" | ")}` : "";
|
|
64597
65108
|
proposals.push({
|
|
64598
65109
|
id: "H1",
|
|
64599
|
-
severity:
|
|
65110
|
+
severity: featureCount >= threshold * HIGH_SEVERITY_MULTIPLE ? "HIGH" : "MED",
|
|
64600
65111
|
target: { canonicalFile: ".nax/rules/curator-suggestions.md", action: "add" },
|
|
64601
|
-
description: `
|
|
64602
|
-
evidence: `
|
|
65112
|
+
description: `Recurring across ${featureCount} features \u2014 ${categoryLabel}${gist}`,
|
|
65113
|
+
evidence: `Seen in ${featureCount} features: ${features.join(", ")} (sites: ${sites.join(", ")}).${fileSection}${sampleSection}`,
|
|
64603
65114
|
sourceKinds: ["review-finding"],
|
|
64604
|
-
storyIds:
|
|
65115
|
+
storyIds: sites
|
|
64605
65116
|
});
|
|
64606
65117
|
}
|
|
64607
65118
|
return proposals;
|
|
@@ -64772,8 +65283,9 @@ function runHeuristics(observations, thresholds) {
|
|
|
64772
65283
|
...h6FixCycleUnchanged(observations, t.unchangedOutcome)
|
|
64773
65284
|
];
|
|
64774
65285
|
}
|
|
64775
|
-
var DEFAULT_THRESHOLDS2;
|
|
65286
|
+
var DEFAULT_THRESHOLDS2, HIGH_SEVERITY_MULTIPLE = 2, DESCRIPTION_GIST_CHARS = 90, MAX_EVIDENCE_FILES = 4, CROSS_FEATURE_MESSAGE_PREFIX = 48;
|
|
64776
65287
|
var init_heuristics = __esm(() => {
|
|
65288
|
+
init_review();
|
|
64777
65289
|
DEFAULT_THRESHOLDS2 = {
|
|
64778
65290
|
repeatedFinding: 2,
|
|
64779
65291
|
emptyKeyword: 2,
|
|
@@ -64846,7 +65358,7 @@ function renderProposals(proposals, runId, observationCount) {
|
|
|
64846
65358
|
const storyList = p.storyIds.join(", ");
|
|
64847
65359
|
lines.push(`- [ ] [${p.severity}] ${p.id}: ${p.description} \u2014 stories: ${storyList}`);
|
|
64848
65360
|
if (p.evidence) {
|
|
64849
|
-
lines.push(` _Evidence: ${p.evidence}_`);
|
|
65361
|
+
lines.push(` _Evidence: ${p.evidence.replace(/\s*\n\s*/g, " \xB7 ")}_`);
|
|
64850
65362
|
}
|
|
64851
65363
|
}
|
|
64852
65364
|
lines.push("");
|
|
@@ -64856,6 +65368,26 @@ function renderProposals(proposals, runId, observationCount) {
|
|
|
64856
65368
|
`);
|
|
64857
65369
|
}
|
|
64858
65370
|
|
|
65371
|
+
// src/plugins/builtin/curator/jsonl-stream.ts
|
|
65372
|
+
async function* streamJsonlLines(file3) {
|
|
65373
|
+
const decoder = new TextDecoder;
|
|
65374
|
+
let carry = "";
|
|
65375
|
+
for await (const chunk of file3.stream()) {
|
|
65376
|
+
carry += decoder.decode(chunk, { stream: true });
|
|
65377
|
+
let nl = carry.indexOf(`
|
|
65378
|
+
`);
|
|
65379
|
+
while (nl !== -1) {
|
|
65380
|
+
yield carry.slice(0, nl);
|
|
65381
|
+
carry = carry.slice(nl + 1);
|
|
65382
|
+
nl = carry.indexOf(`
|
|
65383
|
+
`);
|
|
65384
|
+
}
|
|
65385
|
+
}
|
|
65386
|
+
carry += decoder.decode();
|
|
65387
|
+
if (carry.length > 0)
|
|
65388
|
+
yield carry;
|
|
65389
|
+
}
|
|
65390
|
+
|
|
64859
65391
|
// src/plugins/builtin/curator/rollup.ts
|
|
64860
65392
|
import { appendFile as appendFile3, mkdir as mkdir9, writeFile } from "fs/promises";
|
|
64861
65393
|
import * as path19 from "path";
|
|
@@ -64876,7 +65408,72 @@ async function appendToRollup(observations, rollupPath) {
|
|
|
64876
65408
|
await appendFile3(rollupPath, newLines);
|
|
64877
65409
|
} catch {}
|
|
64878
65410
|
}
|
|
64879
|
-
|
|
65411
|
+
function emptyWindow() {
|
|
65412
|
+
return { observations: [], runIds: [], truncated: false, unattributedRows: 0 };
|
|
65413
|
+
}
|
|
65414
|
+
async function parseTail(file3, startedMidFile, projectKey) {
|
|
65415
|
+
const observations = [];
|
|
65416
|
+
let unattributed = 0;
|
|
65417
|
+
let first = true;
|
|
65418
|
+
for await (const line of streamJsonlLines(file3)) {
|
|
65419
|
+
if (first) {
|
|
65420
|
+
first = false;
|
|
65421
|
+
if (startedMidFile)
|
|
65422
|
+
continue;
|
|
65423
|
+
}
|
|
65424
|
+
if (!line.trim())
|
|
65425
|
+
continue;
|
|
65426
|
+
try {
|
|
65427
|
+
const obs = JSON.parse(line);
|
|
65428
|
+
if (obs.projectKey === projectKey)
|
|
65429
|
+
observations.push(obs);
|
|
65430
|
+
else if (!obs.projectKey)
|
|
65431
|
+
unattributed += 1;
|
|
65432
|
+
} catch {}
|
|
65433
|
+
}
|
|
65434
|
+
return { observations, unattributed };
|
|
65435
|
+
}
|
|
65436
|
+
function newestRunIds(observations, windowRuns) {
|
|
65437
|
+
const keep = new Set;
|
|
65438
|
+
for (let i = observations.length - 1;i >= 0 && keep.size < windowRuns; i -= 1) {
|
|
65439
|
+
const runId = observations[i]?.runId;
|
|
65440
|
+
if (runId)
|
|
65441
|
+
keep.add(runId);
|
|
65442
|
+
}
|
|
65443
|
+
return keep;
|
|
65444
|
+
}
|
|
65445
|
+
async function readHeuristicWindow(rollupPath, windowRuns, options) {
|
|
65446
|
+
const maxTail = Math.max(1, options.maxTailBytes ?? MAX_WINDOW_TAIL_BYTES);
|
|
65447
|
+
try {
|
|
65448
|
+
const file3 = Bun.file(rollupPath);
|
|
65449
|
+
if (!await file3.exists())
|
|
65450
|
+
return emptyWindow();
|
|
65451
|
+
const size = file3.size;
|
|
65452
|
+
let tail = Math.max(1, Math.min(options.tailBytes ?? INITIAL_WINDOW_TAIL_BYTES, maxTail));
|
|
65453
|
+
while (true) {
|
|
65454
|
+
const start = Math.max(0, size - tail);
|
|
65455
|
+
const { observations, unattributed } = await parseTail(start > 0 ? file3.slice(start) : file3, start > 0, options.projectKey);
|
|
65456
|
+
const keep = newestRunIds(observations, windowRuns);
|
|
65457
|
+
const exhausted = start === 0;
|
|
65458
|
+
if (keep.size >= windowRuns || exhausted || tail >= maxTail) {
|
|
65459
|
+
return {
|
|
65460
|
+
observations: observations.filter((o) => keep.has(o.runId)),
|
|
65461
|
+
runIds: [...keep],
|
|
65462
|
+
truncated: keep.size < windowRuns && !exhausted,
|
|
65463
|
+
unattributedRows: unattributed
|
|
65464
|
+
};
|
|
65465
|
+
}
|
|
65466
|
+
tail = Math.max(1, Math.min(tail * 2, maxTail));
|
|
65467
|
+
}
|
|
65468
|
+
} catch {
|
|
65469
|
+
return emptyWindow();
|
|
65470
|
+
}
|
|
65471
|
+
}
|
|
65472
|
+
var INITIAL_WINDOW_TAIL_BYTES, MAX_WINDOW_TAIL_BYTES;
|
|
65473
|
+
var init_rollup = __esm(() => {
|
|
65474
|
+
INITIAL_WINDOW_TAIL_BYTES = 8 * 1024 * 1024;
|
|
65475
|
+
MAX_WINDOW_TAIL_BYTES = 64 * 1024 * 1024;
|
|
65476
|
+
});
|
|
64880
65477
|
|
|
64881
65478
|
// src/plugins/builtin/curator/index.ts
|
|
64882
65479
|
import { mkdir as mkdir10 } from "fs/promises";
|
|
@@ -64921,11 +65518,12 @@ function getCuratorThresholds(context) {
|
|
|
64921
65518
|
unchangedOutcome: raw.unchangedOutcome ?? DEFAULT_THRESHOLDS3.unchangedOutcome
|
|
64922
65519
|
};
|
|
64923
65520
|
}
|
|
64924
|
-
var PLUGIN_NAME3 = "nax-curator", PLUGIN_VERSION3 = "0.1.0", DEFAULT_THRESHOLDS3, curatorAction, curatorPlugin;
|
|
65521
|
+
var PLUGIN_NAME3 = "nax-curator", PLUGIN_VERSION3 = "0.1.0", DEFAULT_THRESHOLDS3, curatorAction, HEURISTIC_WINDOW_RUNS = 20, curatorPlugin;
|
|
64925
65522
|
var init_curator = __esm(() => {
|
|
64926
65523
|
init_collect();
|
|
64927
65524
|
init_heuristics();
|
|
64928
65525
|
init_rollup();
|
|
65526
|
+
init_rollup();
|
|
64929
65527
|
DEFAULT_THRESHOLDS3 = {
|
|
64930
65528
|
repeatedFinding: 2,
|
|
64931
65529
|
emptyKeyword: 2,
|
|
@@ -64958,12 +65556,22 @@ var init_curator = __esm(() => {
|
|
|
64958
65556
|
await Bun.write(observationsPath, observations.map((o) => JSON.stringify(o)).join(`
|
|
64959
65557
|
`) + (observations.length > 0 ? `
|
|
64960
65558
|
` : ""));
|
|
65559
|
+
await appendToRollup(observations, rollupPath);
|
|
64961
65560
|
const thresholds = getCuratorThresholds(context);
|
|
64962
|
-
const
|
|
65561
|
+
const window2 = await readHeuristicWindow(rollupPath, HEURISTIC_WINDOW_RUNS, {
|
|
65562
|
+
projectKey: curatorContext.projectKey
|
|
65563
|
+
});
|
|
65564
|
+
if (window2.truncated) {
|
|
65565
|
+
context.logger.warn("Curator window truncated at the byte ceiling", {
|
|
65566
|
+
runsFound: window2.runIds.length,
|
|
65567
|
+
runsRequested: HEURISTIC_WINDOW_RUNS,
|
|
65568
|
+
unattributedRows: window2.unattributedRows
|
|
65569
|
+
});
|
|
65570
|
+
}
|
|
65571
|
+
const proposals = runHeuristics(window2.observations.length > 0 ? window2.observations : observations, thresholds);
|
|
64963
65572
|
const markdown = renderProposals(proposals, context.runId, observations.length);
|
|
64964
65573
|
const proposalsMdPath = path20.join(runDir, "curator-proposals.md");
|
|
64965
65574
|
await Bun.write(proposalsMdPath, markdown);
|
|
64966
|
-
await appendToRollup(observations, rollupPath);
|
|
64967
65575
|
}
|
|
64968
65576
|
return {
|
|
64969
65577
|
success: true,
|
|
@@ -64996,15 +65604,21 @@ function selectFinish(config2) {
|
|
|
64996
65604
|
return;
|
|
64997
65605
|
return finishConfigSelector.select(config2)?.finish;
|
|
64998
65606
|
}
|
|
65607
|
+
function resolveFlowAgent(config2, explicit) {
|
|
65608
|
+
if (typeof explicit === "string" && explicit.length > 0)
|
|
65609
|
+
return explicit;
|
|
65610
|
+
return resolveDefaultAgent(config2 ?? {});
|
|
65611
|
+
}
|
|
64999
65612
|
function getFinishAutoFlowConfig(ctx) {
|
|
65000
65613
|
const autoFlow = selectFinish(ctx.config)?.autoFlow;
|
|
65001
65614
|
if (!autoFlow)
|
|
65002
|
-
return DEFAULT_FINISH_AUTO_FLOW_CONFIG;
|
|
65615
|
+
return { ...DEFAULT_FINISH_AUTO_FLOW_CONFIG, defaultAgent: resolveFlowAgent(ctx.config, null) };
|
|
65003
65616
|
const defaults = DEFAULT_FINISH_AUTO_FLOW_CONFIG;
|
|
65004
65617
|
return {
|
|
65005
65618
|
enabled: autoFlow.enabled === true,
|
|
65006
65619
|
flowPath: autoFlow.flowPath ?? defaults.flowPath,
|
|
65007
|
-
defaultAgent: autoFlow.defaultAgent
|
|
65620
|
+
defaultAgent: resolveFlowAgent(ctx.config, autoFlow.defaultAgent),
|
|
65621
|
+
model: autoFlow.model ?? null,
|
|
65008
65622
|
reviewers: {
|
|
65009
65623
|
spec: autoFlow.reviewers?.spec ?? null,
|
|
65010
65624
|
quality: autoFlow.reviewers?.quality ?? null
|
|
@@ -65028,11 +65642,12 @@ function telegramCreds(config2) {
|
|
|
65028
65642
|
}
|
|
65029
65643
|
var DEFAULT_FINISH_AUTO_FLOW_CONFIG;
|
|
65030
65644
|
var init_config2 = __esm(() => {
|
|
65645
|
+
init_agents();
|
|
65031
65646
|
init_config();
|
|
65032
65647
|
DEFAULT_FINISH_AUTO_FLOW_CONFIG = {
|
|
65033
65648
|
enabled: false,
|
|
65034
65649
|
flowPath: "flows/nax-finish/nax-finish.flow.ts",
|
|
65035
|
-
|
|
65650
|
+
model: null,
|
|
65036
65651
|
reviewers: { spec: null, quality: null },
|
|
65037
65652
|
escalate: { telegram: true },
|
|
65038
65653
|
notify: { mode: "escalation" },
|
|
@@ -65127,14 +65742,21 @@ async function defaultRun2(cmd, opts) {
|
|
|
65127
65742
|
clearTimeout(timer);
|
|
65128
65743
|
}
|
|
65129
65744
|
}
|
|
65130
|
-
|
|
65131
|
-
const
|
|
65745
|
+
function finishAuditDir(ctx) {
|
|
65746
|
+
const root = ctx.outputDir ?? path21.join(ctx.workdir, ".nax");
|
|
65747
|
+
return path21.join(root, "finish-audit", ctx.feature);
|
|
65748
|
+
}
|
|
65749
|
+
function finishResultPath(ctx, runId) {
|
|
65750
|
+
return path21.join(finishAuditDir(ctx), `${runId}.result.json`);
|
|
65751
|
+
}
|
|
65752
|
+
async function defaultReadResult(resultPath) {
|
|
65753
|
+
const f = Bun.file(resultPath);
|
|
65132
65754
|
if (!await f.exists())
|
|
65133
65755
|
return null;
|
|
65134
65756
|
return JSON.parse(await f.text());
|
|
65135
65757
|
}
|
|
65136
|
-
async function defaultClearResult(
|
|
65137
|
-
const file3 = Bun.file(
|
|
65758
|
+
async function defaultClearResult(resultPath) {
|
|
65759
|
+
const file3 = Bun.file(resultPath);
|
|
65138
65760
|
if (await file3.exists())
|
|
65139
65761
|
await file3.delete();
|
|
65140
65762
|
}
|
|
@@ -65159,18 +65781,19 @@ async function resolveFlowPath(workdir, flowPath, deps = _naxFinishDeps) {
|
|
|
65159
65781
|
}
|
|
65160
65782
|
return null;
|
|
65161
65783
|
}
|
|
65162
|
-
function buildFlowArgv(flowPath, inputJson,
|
|
65163
|
-
const stepTimeout = stepMs && stepMs > 0 ? ["--timeout", String(Math.ceil(stepMs / 1000))] : [];
|
|
65784
|
+
function buildFlowArgv(flowPath, inputJson, opts = {}) {
|
|
65785
|
+
const stepTimeout = opts.stepMs && opts.stepMs > 0 ? ["--timeout", String(Math.ceil(opts.stepMs / 1000))] : [];
|
|
65164
65786
|
return [
|
|
65165
65787
|
"acpx",
|
|
65166
65788
|
"--approve-all",
|
|
65167
65789
|
...stepTimeout,
|
|
65790
|
+
...opts.model ? ["--model", opts.model] : [],
|
|
65168
65791
|
"flow",
|
|
65169
65792
|
"run",
|
|
65170
65793
|
flowPath,
|
|
65171
65794
|
"--input-json",
|
|
65172
65795
|
inputJson,
|
|
65173
|
-
...defaultAgent ? ["--default-agent", defaultAgent] : []
|
|
65796
|
+
...opts.defaultAgent ? ["--default-agent", opts.defaultAgent] : []
|
|
65174
65797
|
];
|
|
65175
65798
|
}
|
|
65176
65799
|
function buildFlowEnv(cfg) {
|
|
@@ -65208,22 +65831,29 @@ async function executeFinishFlow(options) {
|
|
|
65208
65831
|
escalateTelegram
|
|
65209
65832
|
};
|
|
65210
65833
|
}
|
|
65211
|
-
|
|
65834
|
+
const resultPath = finishResultPath(ctx, ctx.runId);
|
|
65835
|
+
await _naxFinishDeps.clearResult(resultPath);
|
|
65212
65836
|
const input = {
|
|
65213
65837
|
feature: ctx.feature,
|
|
65214
65838
|
workdir: ctx.workdir,
|
|
65215
65839
|
branch: ctx.branch,
|
|
65216
65840
|
prdPath: ctx.prdPath,
|
|
65841
|
+
auditDir: finishAuditDir(ctx),
|
|
65842
|
+
runId: ctx.runId,
|
|
65217
65843
|
escalateTelegram,
|
|
65218
65844
|
timeouts: { acceptanceMs: cfg.timeouts.acceptanceMs, gateMs: cfg.timeouts.gateMs }
|
|
65219
65845
|
};
|
|
65220
|
-
const cmd = buildFlowArgv(flowPath, JSON.stringify(input),
|
|
65846
|
+
const cmd = buildFlowArgv(flowPath, JSON.stringify(input), {
|
|
65847
|
+
defaultAgent: cfg.defaultAgent,
|
|
65848
|
+
stepMs: cfg.timeouts.stepMs,
|
|
65849
|
+
model: cfg.model
|
|
65850
|
+
});
|
|
65221
65851
|
const res = await _naxFinishDeps.run(cmd, {
|
|
65222
65852
|
cwd: ctx.workdir,
|
|
65223
65853
|
env: buildFlowEnv(cfg),
|
|
65224
65854
|
timeoutMs: cfg.timeouts.flowMs
|
|
65225
65855
|
});
|
|
65226
|
-
const result = await _naxFinishDeps.readResult(
|
|
65856
|
+
const result = await _naxFinishDeps.readResult(resultPath);
|
|
65227
65857
|
if (!result)
|
|
65228
65858
|
return missingResultOutcome(ctx, res, escalateTelegram);
|
|
65229
65859
|
return {
|
|
@@ -65601,7 +66231,7 @@ function startHeartbeat(opts) {
|
|
|
65601
66231
|
let stopped = false;
|
|
65602
66232
|
let timer;
|
|
65603
66233
|
const armTimer = () => {
|
|
65604
|
-
timer = setTimeout(() => {
|
|
66234
|
+
timer = _heartbeatDeps.setTimeout(() => {
|
|
65605
66235
|
try {
|
|
65606
66236
|
Promise.resolve(onTick(getSnapshot())).catch((err) => getSafeLogger()?.warn(STAGE2, "Heartbeat tick failed", {
|
|
65607
66237
|
error: err instanceof Error ? err.message : String(err)
|
|
@@ -65620,7 +66250,7 @@ function startHeartbeat(opts) {
|
|
|
65620
66250
|
stop() {
|
|
65621
66251
|
stopped = true;
|
|
65622
66252
|
if (timer !== undefined)
|
|
65623
|
-
clearTimeout(timer);
|
|
66253
|
+
_heartbeatDeps.clearTimeout(timer);
|
|
65624
66254
|
}
|
|
65625
66255
|
};
|
|
65626
66256
|
}
|
|
@@ -65664,10 +66294,14 @@ function buildHeartbeatMetricsPayload(p) {
|
|
|
65664
66294
|
]
|
|
65665
66295
|
};
|
|
65666
66296
|
}
|
|
65667
|
-
var STAGE2 = "otel-reporter-heartbeat";
|
|
66297
|
+
var STAGE2 = "otel-reporter-heartbeat", _heartbeatDeps;
|
|
65668
66298
|
var init_heartbeat = __esm(() => {
|
|
65669
66299
|
init_logger2();
|
|
65670
66300
|
init_otlp();
|
|
66301
|
+
_heartbeatDeps = {
|
|
66302
|
+
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
66303
|
+
clearTimeout: (id) => clearTimeout(id)
|
|
66304
|
+
};
|
|
65671
66305
|
});
|
|
65672
66306
|
|
|
65673
66307
|
// src/plugins/builtin/otel-reporter/ids.ts
|
|
@@ -65697,7 +66331,7 @@ function toLogRecord(entry) {
|
|
|
65697
66331
|
const nonScalars = {};
|
|
65698
66332
|
for (const [key, value] of Object.entries(data)) {
|
|
65699
66333
|
if (typeof value === "string") {
|
|
65700
|
-
attributes.push(attr(`nax.data.${key}`,
|
|
66334
|
+
attributes.push(attr(`nax.data.${key}`, truncate4(value)));
|
|
65701
66335
|
} else if (typeof value === "number") {
|
|
65702
66336
|
if (Number.isFinite(value)) {
|
|
65703
66337
|
attributes.push(attr(`nax.data.${key}`, value));
|
|
@@ -65711,7 +66345,7 @@ function toLogRecord(entry) {
|
|
|
65711
66345
|
}
|
|
65712
66346
|
}
|
|
65713
66347
|
if (Object.keys(nonScalars).length > 0) {
|
|
65714
|
-
attributes.push(attr("nax.data_json",
|
|
66348
|
+
attributes.push(attr("nax.data_json", truncate4(JSON.stringify(nonScalars))));
|
|
65715
66349
|
}
|
|
65716
66350
|
return {
|
|
65717
66351
|
body: { stringValue: entry.message },
|
|
@@ -65734,7 +66368,7 @@ function buildLogsPayload(entries, resource) {
|
|
|
65734
66368
|
]
|
|
65735
66369
|
};
|
|
65736
66370
|
}
|
|
65737
|
-
function
|
|
66371
|
+
function truncate4(value) {
|
|
65738
66372
|
if (value.length <= DATA_JSON_MAX)
|
|
65739
66373
|
return value;
|
|
65740
66374
|
const marker = TRUNCATION_MARKER;
|
|
@@ -66702,11 +67336,11 @@ function getSafeLogger6() {
|
|
|
66702
67336
|
return getSafeLogger();
|
|
66703
67337
|
}
|
|
66704
67338
|
function extractPluginName(pluginPath) {
|
|
66705
|
-
const
|
|
66706
|
-
if (
|
|
67339
|
+
const basename13 = path22.basename(pluginPath);
|
|
67340
|
+
if (basename13 === "index.ts" || basename13 === "index.js" || basename13 === "index.mjs") {
|
|
66707
67341
|
return path22.basename(path22.dirname(pluginPath));
|
|
66708
67342
|
}
|
|
66709
|
-
return
|
|
67343
|
+
return basename13.replace(/\.(ts|js|mjs)$/, "");
|
|
66710
67344
|
}
|
|
66711
67345
|
async function loadPlugins(globalDir, projectDir, configPlugins, projectRoot, disabledPlugins, isTestFileFn, reporters) {
|
|
66712
67346
|
const loadedPlugins = [];
|
|
@@ -67379,9 +68013,9 @@ var init_hooks = __esm(() => {
|
|
|
67379
68013
|
// src/execution/crash-heartbeat.ts
|
|
67380
68014
|
import { appendFileSync as appendFileSync2 } from "fs";
|
|
67381
68015
|
async function heartbeatLoop(gen, statusWriter, getTotalCost, getIterations, jsonlFilePath) {
|
|
67382
|
-
const logger =
|
|
68016
|
+
const logger = _heartbeatDeps2.getSafeLogger();
|
|
67383
68017
|
while (gen === _heartbeatGen && _heartbeatActive) {
|
|
67384
|
-
await
|
|
68018
|
+
await _heartbeatDeps2.sleep(60000);
|
|
67385
68019
|
if (gen !== _heartbeatGen || !_heartbeatActive)
|
|
67386
68020
|
break;
|
|
67387
68021
|
try {
|
|
@@ -67410,11 +68044,11 @@ async function heartbeatLoop(gen, statusWriter, getTotalCost, getIterations, jso
|
|
|
67410
68044
|
}
|
|
67411
68045
|
}
|
|
67412
68046
|
function startHeartbeat2(statusWriter, getTotalCost, getIterations, jsonlFilePath) {
|
|
67413
|
-
const logger =
|
|
68047
|
+
const logger = _heartbeatDeps2.getSafeLogger();
|
|
67414
68048
|
_heartbeatActive = true;
|
|
67415
68049
|
const gen = ++_heartbeatGen;
|
|
67416
68050
|
heartbeatLoop(gen, statusWriter, getTotalCost, getIterations, jsonlFilePath).catch((err) => {
|
|
67417
|
-
|
|
68051
|
+
_heartbeatDeps2.getSafeLogger()?.warn("crash-recovery", "Heartbeat loop crashed; status updates stopped", {
|
|
67418
68052
|
error: err instanceof Error ? err.message : String(err)
|
|
67419
68053
|
});
|
|
67420
68054
|
});
|
|
@@ -67427,10 +68061,10 @@ function stopHeartbeat() {
|
|
|
67427
68061
|
getSafeLogger()?.debug("crash-recovery", "Heartbeat stopped");
|
|
67428
68062
|
}
|
|
67429
68063
|
}
|
|
67430
|
-
var
|
|
68064
|
+
var _heartbeatDeps2, _heartbeatGen = 0, _heartbeatActive = false;
|
|
67431
68065
|
var init_crash_heartbeat = __esm(() => {
|
|
67432
68066
|
init_logger2();
|
|
67433
|
-
|
|
68067
|
+
_heartbeatDeps2 = {
|
|
67434
68068
|
sleep: async (ms) => Bun.sleep(ms),
|
|
67435
68069
|
getSafeLogger
|
|
67436
68070
|
};
|
|
@@ -68205,6 +68839,7 @@ function buildAcceptanceContext(ctx, prd) {
|
|
|
68205
68839
|
agentManager: ctx.agentManager,
|
|
68206
68840
|
sessionManager: ctx.sessionManager,
|
|
68207
68841
|
acceptanceTestPaths: ctx.acceptanceTestPaths,
|
|
68842
|
+
acceptanceRetries: ctx.acceptanceRetries ?? 0,
|
|
68208
68843
|
runtime: ctx.runtime,
|
|
68209
68844
|
abortSignal: ctx.abortSignal
|
|
68210
68845
|
};
|
|
@@ -68301,8 +68936,9 @@ async function runAcceptanceLoop(ctx) {
|
|
|
68301
68936
|
logger?.info("acceptance", "All stories complete, running acceptance validation");
|
|
68302
68937
|
const { acceptanceStage: acceptanceStage2 } = await _runAcceptanceTestsOnceDeps.importAcceptanceStage();
|
|
68303
68938
|
while (acceptanceRetries < maxRetries) {
|
|
68939
|
+
const attemptCtx = { ...ctx, acceptanceRetries };
|
|
68304
68940
|
const firstStory = prd.userStories[0];
|
|
68305
|
-
const acceptanceContext = buildAcceptanceContext(
|
|
68941
|
+
const acceptanceContext = buildAcceptanceContext(attemptCtx, prd);
|
|
68306
68942
|
const acceptanceResult = await acceptanceStage2.execute(acceptanceContext);
|
|
68307
68943
|
if (acceptanceResult.action === "continue") {
|
|
68308
68944
|
logger?.info("acceptance", "Acceptance validation passed!");
|
|
@@ -68393,17 +69029,14 @@ async function runAcceptanceLoop(ctx) {
|
|
|
68393
69029
|
confidence: diagnosis.confidence,
|
|
68394
69030
|
attempt: acceptanceRetries
|
|
68395
69031
|
});
|
|
68396
|
-
const cycleResult = await runAcceptanceFixCycle(
|
|
68397
|
-
packageDir: pkg.packageDir,
|
|
68398
|
-
testPath: effectivePath
|
|
68399
|
-
});
|
|
69032
|
+
const cycleResult = await runAcceptanceFixCycle(attemptCtx, prd, pkgFailures, diagnosis, effectivePath, testCommand, { packageDir: pkg.packageDir, testPath: effectivePath });
|
|
68400
69033
|
totalCost2 += cycleResult.costUsd ?? 0;
|
|
68401
69034
|
totalInternalIterations += cycleResult.iterations.length;
|
|
68402
69035
|
const pkgResolved = cycleResult.exitReason === "resolved" || cycleResult.finalFindings.length === 0;
|
|
68403
69036
|
if (!pkgResolved)
|
|
68404
69037
|
remainingFindings.push(...cycleResult.finalFindings);
|
|
68405
69038
|
}
|
|
68406
|
-
const finalCheck = await runAcceptanceTestsOnce(
|
|
69039
|
+
const finalCheck = await runAcceptanceTestsOnce(attemptCtx, prd);
|
|
68407
69040
|
const success2 = finalCheck.passed && remainingFindings.length === 0;
|
|
68408
69041
|
const failureMessages = !success2 ? finalCheck.failedACs.length > 0 ? finalCheck.failedACs : remainingFindings.length > 0 ? remainingFindings.map((f) => f.message) : ["acceptance validation failed (unknown cause)"] : undefined;
|
|
68409
69042
|
return buildResult(success2, prd, totalCost2, iterations, storiesCompleted, prdDirty, failureMessages, acceptanceRetries + totalInternalIterations);
|
|
@@ -69711,10 +70344,10 @@ var init_ensure_package_dirs = __esm(() => {
|
|
|
69711
70344
|
|
|
69712
70345
|
// src/pipeline/subscribers/events-writer.ts
|
|
69713
70346
|
import { appendFile as appendFile5, mkdir as mkdir13 } from "fs/promises";
|
|
69714
|
-
import { basename as
|
|
70347
|
+
import { basename as basename16, join as join86 } from "path";
|
|
69715
70348
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
69716
70349
|
const logger = getSafeLogger();
|
|
69717
|
-
const project =
|
|
70350
|
+
const project = basename16(workdir);
|
|
69718
70351
|
const eventsDir = join86(getEventsRootDir(), project);
|
|
69719
70352
|
const eventsFile = join86(eventsDir, "events.jsonl");
|
|
69720
70353
|
let dirReady = false;
|
|
@@ -69897,10 +70530,10 @@ var init_interaction2 = __esm(() => {
|
|
|
69897
70530
|
|
|
69898
70531
|
// src/pipeline/subscribers/registry.ts
|
|
69899
70532
|
import { mkdir as mkdir14, writeFile as writeFile2 } from "fs/promises";
|
|
69900
|
-
import { basename as
|
|
70533
|
+
import { basename as basename17, join as join87 } from "path";
|
|
69901
70534
|
function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
69902
70535
|
const logger = getSafeLogger();
|
|
69903
|
-
const project =
|
|
70536
|
+
const project = basename17(workdir);
|
|
69904
70537
|
const runDir = join87(getRunsDir(), `${project}-${feature}-${runId}`);
|
|
69905
70538
|
const metaFile = join87(runDir, "meta.json");
|
|
69906
70539
|
const unsub = bus.on("run:started", (_ev) => {
|
|
@@ -71317,8 +71950,6 @@ function releaseHeavyPipelineContext(ctx) {
|
|
|
71317
71950
|
ctx.constitution = undefined;
|
|
71318
71951
|
ctx.acceptanceFailures = undefined;
|
|
71319
71952
|
ctx.autofixPriorIterations = undefined;
|
|
71320
|
-
ctx.priorSemanticIterations = undefined;
|
|
71321
|
-
ctx.priorAdversarialIterations = undefined;
|
|
71322
71953
|
ctx.reviewFindings = undefined;
|
|
71323
71954
|
ctx.selfVerification = undefined;
|
|
71324
71955
|
ctx.tddIsolations = undefined;
|
|
@@ -71977,7 +72608,7 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
71977
72608
|
_prevRunUnsubscribers = [];
|
|
71978
72609
|
const thisRunUnsubscribers = [
|
|
71979
72610
|
wireHooks(pipelineEventBus, ctx.hooks, ctx.workdir, ctx.feature),
|
|
71980
|
-
wireReporters(pipelineEventBus, ctx.pluginRegistry, ctx.runId, ctx.startTime),
|
|
72611
|
+
wireReporters(pipelineEventBus, ctx.pluginRegistry, ctx.runId, ctx.startTime, ctx.runtime.projectKey),
|
|
71981
72612
|
wireInteraction(pipelineEventBus, ctx.interactionChain, ctx.config),
|
|
71982
72613
|
wireEventsWriter(pipelineEventBus, ctx.feature, ctx.runId, ctx.workdir),
|
|
71983
72614
|
wireRegistry(pipelineEventBus, ctx.feature, ctx.runId, ctx.workdir, ctx.runtime.outputDir)
|
|
@@ -73822,7 +74453,8 @@ function buildPostRunContext(opts, durationMs, logger) {
|
|
|
73822
74453
|
projectKey,
|
|
73823
74454
|
curatorRollupPath: curatorRollupPath2,
|
|
73824
74455
|
logFilePath,
|
|
73825
|
-
config: config2
|
|
74456
|
+
config: config2,
|
|
74457
|
+
startTime
|
|
73826
74458
|
} = opts;
|
|
73827
74459
|
const counts = countStories(prd);
|
|
73828
74460
|
return {
|
|
@@ -73833,6 +74465,7 @@ function buildPostRunContext(opts, durationMs, logger) {
|
|
|
73833
74465
|
branch,
|
|
73834
74466
|
version: version2,
|
|
73835
74467
|
totalDurationMs: durationMs,
|
|
74468
|
+
runStartedAt: startTime,
|
|
73836
74469
|
totalCost: totalCost2,
|
|
73837
74470
|
storySummary: {
|
|
73838
74471
|
completed: storiesCompleted,
|
|
@@ -74160,6 +74793,7 @@ var exports_execution = {};
|
|
|
74160
74793
|
__export(exports_execution, {
|
|
74161
74794
|
writeExitSummary: () => writeExitSummary,
|
|
74162
74795
|
withIncreasingFailuresBail: () => withIncreasingFailuresBail,
|
|
74796
|
+
toReviewDecisionPayload: () => toReviewDecisionPayload,
|
|
74163
74797
|
synthesizeBackfillMetric: () => synthesizeBackfillMetric,
|
|
74164
74798
|
stopHeartbeat: () => stopHeartbeat,
|
|
74165
74799
|
startHeartbeat: () => startHeartbeat2,
|
|
@@ -103797,6 +104431,88 @@ var init_bakeoff = __esm(() => {
|
|
|
103797
104431
|
init_report2();
|
|
103798
104432
|
});
|
|
103799
104433
|
|
|
104434
|
+
// src/plugins/builtin/curator/rollup-prune.ts
|
|
104435
|
+
import { rename as rename4, unlink as unlink4, writeFile as writeFile3 } from "fs/promises";
|
|
104436
|
+
import { appendFile as appendFile6 } from "fs/promises";
|
|
104437
|
+
async function scanProjectRunIds(rollupPath, projectKey) {
|
|
104438
|
+
const maxTsByRunId = new Map;
|
|
104439
|
+
for await (const line of streamJsonlLines(Bun.file(rollupPath))) {
|
|
104440
|
+
if (!line.trim())
|
|
104441
|
+
continue;
|
|
104442
|
+
let obs;
|
|
104443
|
+
try {
|
|
104444
|
+
obs = JSON.parse(line);
|
|
104445
|
+
} catch {
|
|
104446
|
+
continue;
|
|
104447
|
+
}
|
|
104448
|
+
if (obs.projectKey !== projectKey)
|
|
104449
|
+
continue;
|
|
104450
|
+
const existing = maxTsByRunId.get(obs.runId);
|
|
104451
|
+
if (existing === undefined || obs.ts > existing)
|
|
104452
|
+
maxTsByRunId.set(obs.runId, obs.ts);
|
|
104453
|
+
}
|
|
104454
|
+
return [...maxTsByRunId.entries()].sort((a, b) => a[1] > b[1] ? -1 : a[1] < b[1] ? 1 : 0).map(([runId]) => runId);
|
|
104455
|
+
}
|
|
104456
|
+
async function pruneRollup(input) {
|
|
104457
|
+
const { rollupPath, projectKey, keepRunIds, dropUnattributed = false } = input;
|
|
104458
|
+
const tmpPath = `${rollupPath}.gc-tmp`;
|
|
104459
|
+
const result2 = { kept: 0, dropped: 0, keptOtherProjects: 0, keptUnattributed: 0 };
|
|
104460
|
+
let buffer = "";
|
|
104461
|
+
const flush = async () => {
|
|
104462
|
+
if (buffer.length === 0)
|
|
104463
|
+
return;
|
|
104464
|
+
await appendFile6(tmpPath, buffer);
|
|
104465
|
+
buffer = "";
|
|
104466
|
+
};
|
|
104467
|
+
try {
|
|
104468
|
+
await writeFile3(tmpPath, "");
|
|
104469
|
+
for await (const line of streamJsonlLines(Bun.file(rollupPath))) {
|
|
104470
|
+
if (!line.trim())
|
|
104471
|
+
continue;
|
|
104472
|
+
let obs = null;
|
|
104473
|
+
try {
|
|
104474
|
+
obs = JSON.parse(line);
|
|
104475
|
+
} catch {
|
|
104476
|
+
obs = null;
|
|
104477
|
+
}
|
|
104478
|
+
let keep;
|
|
104479
|
+
if (obs === null) {
|
|
104480
|
+
keep = !dropUnattributed;
|
|
104481
|
+
if (keep)
|
|
104482
|
+
result2.keptUnattributed++;
|
|
104483
|
+
} else if (obs.projectKey === undefined) {
|
|
104484
|
+
keep = !dropUnattributed;
|
|
104485
|
+
if (keep)
|
|
104486
|
+
result2.keptUnattributed++;
|
|
104487
|
+
} else if (obs.projectKey !== projectKey) {
|
|
104488
|
+
keep = true;
|
|
104489
|
+
result2.keptOtherProjects++;
|
|
104490
|
+
} else {
|
|
104491
|
+
keep = keepRunIds.has(obs.runId);
|
|
104492
|
+
}
|
|
104493
|
+
if (!keep) {
|
|
104494
|
+
result2.dropped++;
|
|
104495
|
+
continue;
|
|
104496
|
+
}
|
|
104497
|
+
result2.kept++;
|
|
104498
|
+
buffer += `${line}
|
|
104499
|
+
`;
|
|
104500
|
+
if (buffer.length >= FLUSH_BYTES)
|
|
104501
|
+
await flush();
|
|
104502
|
+
}
|
|
104503
|
+
await flush();
|
|
104504
|
+
await rename4(tmpPath, rollupPath);
|
|
104505
|
+
} catch (err) {
|
|
104506
|
+
await unlink4(tmpPath).catch(() => {});
|
|
104507
|
+
throw err;
|
|
104508
|
+
}
|
|
104509
|
+
return result2;
|
|
104510
|
+
}
|
|
104511
|
+
var FLUSH_BYTES;
|
|
104512
|
+
var init_rollup_prune = __esm(() => {
|
|
104513
|
+
FLUSH_BYTES = 4 * 1024 * 1024;
|
|
104514
|
+
});
|
|
104515
|
+
|
|
103800
104516
|
// src/commands/curator.ts
|
|
103801
104517
|
var exports_curator = {};
|
|
103802
104518
|
__export(exports_curator, {
|
|
@@ -103804,14 +104520,12 @@ __export(exports_curator, {
|
|
|
103804
104520
|
curatorGc: () => curatorGc,
|
|
103805
104521
|
curatorDryrun: () => curatorDryrun,
|
|
103806
104522
|
curatorCommit: () => curatorCommit,
|
|
104523
|
+
_testing: () => _testing,
|
|
103807
104524
|
_curatorCmdDeps: () => _curatorCmdDeps
|
|
103808
104525
|
});
|
|
103809
104526
|
import { readdirSync as readdirSync9 } from "fs";
|
|
103810
|
-
import { unlink as
|
|
103811
|
-
import {
|
|
103812
|
-
function getProjectKey(config2, projectDir) {
|
|
103813
|
-
return config2.name?.trim() || basename18(projectDir);
|
|
103814
|
-
}
|
|
104527
|
+
import { unlink as unlink5 } from "fs/promises";
|
|
104528
|
+
import { join as join100 } from "path";
|
|
103815
104529
|
function listRunIds(runsDir) {
|
|
103816
104530
|
try {
|
|
103817
104531
|
return readdirSync9(runsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
@@ -104053,34 +104767,25 @@ async function curatorGc(options) {
|
|
|
104053
104767
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
104054
104768
|
const gDir = _curatorCmdDeps.globalOutputDir();
|
|
104055
104769
|
const rollupPath = _curatorCmdDeps.curatorRollupPath(gDir, config2.curator?.rollupPath);
|
|
104056
|
-
|
|
104057
|
-
if (rollupText === null) {
|
|
104770
|
+
if (!await _curatorCmdDeps.fileExists(rollupPath)) {
|
|
104058
104771
|
console.log(`[gc] No rollup file found at ${rollupPath}. Nothing to prune.`);
|
|
104059
104772
|
return;
|
|
104060
104773
|
}
|
|
104061
|
-
const
|
|
104062
|
-
|
|
104063
|
-
const observations = lines.map((l) => JSON.parse(l));
|
|
104064
|
-
const maxTsByRunId = new Map;
|
|
104065
|
-
for (const obs of observations) {
|
|
104066
|
-
const existing = maxTsByRunId.get(obs.runId);
|
|
104067
|
-
if (!existing || obs.ts > existing) {
|
|
104068
|
-
maxTsByRunId.set(obs.runId, obs.ts);
|
|
104069
|
-
}
|
|
104070
|
-
}
|
|
104774
|
+
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
104775
|
+
const uniqueRunIds = await _curatorCmdDeps.scanProjectRunIds(rollupPath, projectKey);
|
|
104071
104776
|
const keep = options.keep ?? DEFAULT_KEEP;
|
|
104072
|
-
const
|
|
104073
|
-
if (uniqueRunIds.length <= keep) {
|
|
104074
|
-
console.log(`[gc] ${uniqueRunIds.length} unique run(s) in rollup \u2014 at or below keep=${keep}. Nothing to prune.`);
|
|
104777
|
+
const sweep = options.sweepUnattributed === true;
|
|
104778
|
+
if (uniqueRunIds.length <= keep && !sweep) {
|
|
104779
|
+
console.log(`[gc] ${uniqueRunIds.length} unique run(s) for ${projectKey} in rollup \u2014 at or below keep=${keep}. Nothing to prune.`);
|
|
104075
104780
|
return;
|
|
104076
104781
|
}
|
|
104077
104782
|
const keepSet = new Set(uniqueRunIds.slice(0, keep));
|
|
104078
|
-
const
|
|
104079
|
-
|
|
104080
|
-
|
|
104081
|
-
|
|
104082
|
-
|
|
104083
|
-
|
|
104783
|
+
const result2 = await _curatorCmdDeps.pruneRollup({
|
|
104784
|
+
rollupPath,
|
|
104785
|
+
projectKey,
|
|
104786
|
+
keepRunIds: keepSet,
|
|
104787
|
+
dropUnattributed: sweep
|
|
104788
|
+
});
|
|
104084
104789
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
104085
104790
|
const perRunsDir = join100(outputDir, "runs");
|
|
104086
104791
|
for (const runId of uniqueRunIds) {
|
|
@@ -104090,12 +104795,20 @@ async function curatorGc(options) {
|
|
|
104090
104795
|
await _curatorCmdDeps.removeFile(join100(runDir, "curator-proposals.md"));
|
|
104091
104796
|
}
|
|
104092
104797
|
}
|
|
104093
|
-
|
|
104798
|
+
const droppedRuns = Math.max(0, uniqueRunIds.length - keepSet.size);
|
|
104799
|
+
console.log(`[gc] Pruned rollup for ${projectKey}: kept ${keepSet.size} of ${uniqueRunIds.length} run(s), dropped ${result2.dropped} row(s).`);
|
|
104800
|
+
console.log(`[gc] Preserved ${result2.keptOtherProjects} row(s) from other projects and ${result2.keptUnattributed} unattributed row(s).`);
|
|
104801
|
+
if (!sweep && result2.keptUnattributed > 0) {
|
|
104802
|
+
console.log(`[gc] ${result2.keptUnattributed} unattributed row(s) predate project scoping (#1429) and can never be read back. Run with --sweep-unattributed to drop them machine-wide.`);
|
|
104803
|
+
}
|
|
104804
|
+
if (droppedRuns === 0 && sweep)
|
|
104805
|
+
console.log("[gc] No run-level pruning was needed; only the unattributed sweep ran.");
|
|
104094
104806
|
}
|
|
104095
|
-
var _curatorCmdDeps, DEFAULT_KEEP = 50;
|
|
104807
|
+
var _curatorCmdDeps, _testing, DEFAULT_KEEP = 50;
|
|
104096
104808
|
var init_curator2 = __esm(() => {
|
|
104097
104809
|
init_config();
|
|
104098
104810
|
init_heuristics();
|
|
104811
|
+
init_rollup_prune();
|
|
104099
104812
|
init_paths2();
|
|
104100
104813
|
init_common();
|
|
104101
104814
|
_curatorCmdDeps = {
|
|
@@ -104105,6 +104818,9 @@ var init_curator2 = __esm(() => {
|
|
|
104105
104818
|
globalOutputDir: () => globalOutputDir(),
|
|
104106
104819
|
curatorRollupPath: (gDir, override) => curatorRollupPath(gDir, override),
|
|
104107
104820
|
readFile: async (p) => Bun.file(p).text(),
|
|
104821
|
+
fileExists: async (p) => Bun.file(p).exists(),
|
|
104822
|
+
scanProjectRunIds: (rollupPath, projectKey) => scanProjectRunIds(rollupPath, projectKey),
|
|
104823
|
+
pruneRollup: (input) => pruneRollup(input),
|
|
104108
104824
|
writeFile: async (p, content) => {
|
|
104109
104825
|
await Bun.write(p, content);
|
|
104110
104826
|
},
|
|
@@ -104115,7 +104831,7 @@ var init_curator2 = __esm(() => {
|
|
|
104115
104831
|
},
|
|
104116
104832
|
removeFile: async (p) => {
|
|
104117
104833
|
try {
|
|
104118
|
-
await
|
|
104834
|
+
await unlink5(p);
|
|
104119
104835
|
} catch {}
|
|
104120
104836
|
},
|
|
104121
104837
|
openInEditor: async (filePath) => {
|
|
@@ -104126,13 +104842,14 @@ var init_curator2 = __esm(() => {
|
|
|
104126
104842
|
}
|
|
104127
104843
|
}
|
|
104128
104844
|
};
|
|
104845
|
+
_testing = { parseCheckedProposals };
|
|
104129
104846
|
});
|
|
104130
104847
|
|
|
104131
104848
|
// bin/nax.ts
|
|
104132
104849
|
init_source();
|
|
104133
104850
|
import { existsSync as existsSync39, mkdirSync as mkdirSync8 } from "fs";
|
|
104134
104851
|
import { homedir as homedir3 } from "os";
|
|
104135
|
-
import { basename as
|
|
104852
|
+
import { basename as basename20, join as join101 } from "path";
|
|
104136
104853
|
|
|
104137
104854
|
// node_modules/commander/esm.mjs
|
|
104138
104855
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -104789,12 +105506,12 @@ init_errors();
|
|
|
104789
105506
|
init_logger2();
|
|
104790
105507
|
init_runtime();
|
|
104791
105508
|
import { existsSync as existsSync18, readdirSync as readdirSync4 } from "fs";
|
|
104792
|
-
import { basename as
|
|
105509
|
+
import { basename as basename10, join as join46 } from "path";
|
|
104793
105510
|
async function resolveOutputDir(workdir, override) {
|
|
104794
105511
|
if (override)
|
|
104795
105512
|
return override;
|
|
104796
105513
|
const config2 = await loadConfig(workdir).catch(() => null);
|
|
104797
|
-
const projectKey = config2?.name?.trim() ||
|
|
105514
|
+
const projectKey = config2?.name?.trim() || basename10(workdir);
|
|
104798
105515
|
return projectOutputDir(projectKey, config2?.outputDir);
|
|
104799
105516
|
}
|
|
104800
105517
|
async function parseRunLog(logPath) {
|
|
@@ -105829,7 +106546,7 @@ async function contextInspectCommand(options) {
|
|
|
105829
106546
|
init_canonical_loader();
|
|
105830
106547
|
init_errors();
|
|
105831
106548
|
import { mkdir as mkdir11 } from "fs/promises";
|
|
105832
|
-
import { basename as
|
|
106549
|
+
import { basename as basename14, join as join72 } from "path";
|
|
105833
106550
|
var _rulesCLIDeps = {
|
|
105834
106551
|
readFile: async (path24) => Bun.file(path24).text(),
|
|
105835
106552
|
writeFile: async (path24, content) => {
|
|
@@ -105914,6 +106631,32 @@ function neutralizeContent(content) {
|
|
|
105914
106631
|
apply(/\p{Extended_Pictographic}/gu, "");
|
|
105915
106632
|
return { content: result.trim(), replacements };
|
|
105916
106633
|
}
|
|
106634
|
+
function translateLegacyFrontmatter(content) {
|
|
106635
|
+
const fm = /^---\n([\s\S]*?)\n---\n/.exec(content);
|
|
106636
|
+
if (!fm?.[1])
|
|
106637
|
+
return { content, translated: false };
|
|
106638
|
+
const block = fm[1];
|
|
106639
|
+
if (!/^paths:/m.test(block) || /^appliesTo:/m.test(block))
|
|
106640
|
+
return { content, translated: false };
|
|
106641
|
+
const rewritten = block.replace(/^paths:/m, "appliesTo:");
|
|
106642
|
+
const head = content.slice(0, fm.index);
|
|
106643
|
+
const tail = content.slice(fm.index + fm[0].length);
|
|
106644
|
+
return { content: `${head}---
|
|
106645
|
+
${rewritten}
|
|
106646
|
+
---
|
|
106647
|
+
${tail}`, translated: true };
|
|
106648
|
+
}
|
|
106649
|
+
function withReviewNotice(content, replacements) {
|
|
106650
|
+
if (replacements <= 0)
|
|
106651
|
+
return content;
|
|
106652
|
+
const notice = `<!-- NOTE: ${replacements} neutralization(s) applied \u2014 review before committing -->
|
|
106653
|
+
|
|
106654
|
+
`;
|
|
106655
|
+
const fm = /^---\n[\s\S]*?\n---\n/.exec(content);
|
|
106656
|
+
if (!fm)
|
|
106657
|
+
return notice + content;
|
|
106658
|
+
return content.slice(0, fm[0].length) + notice + content.slice(fm[0].length).replace(/^\n+/, "");
|
|
106659
|
+
}
|
|
105917
106660
|
async function collectMigrationSources(workdir) {
|
|
105918
106661
|
const sources = [];
|
|
105919
106662
|
const claudeMdPath = join72(workdir, "CLAUDE.md");
|
|
@@ -105929,7 +106672,7 @@ async function collectMigrationSources(workdir) {
|
|
|
105929
106672
|
try {
|
|
105930
106673
|
const content = await _rulesCLIDeps.readFile(filePath);
|
|
105931
106674
|
if (content.trim()) {
|
|
105932
|
-
sources.push({ sourcePath: filePath, targetFileName:
|
|
106675
|
+
sources.push({ sourcePath: filePath, targetFileName: basename14(filePath), content });
|
|
105933
106676
|
}
|
|
105934
106677
|
} catch {}
|
|
105935
106678
|
}
|
|
@@ -105960,11 +106703,9 @@ async function rulesMigrateCommand(options) {
|
|
|
105960
106703
|
skipped++;
|
|
105961
106704
|
continue;
|
|
105962
106705
|
}
|
|
105963
|
-
const { content:
|
|
105964
|
-
const
|
|
105965
|
-
|
|
105966
|
-
` : "";
|
|
105967
|
-
const output = notice + neutralized;
|
|
106706
|
+
const { content: scoped2 } = translateLegacyFrontmatter(content);
|
|
106707
|
+
const { content: neutralized, replacements } = neutralizeContent(scoped2);
|
|
106708
|
+
const output = withReviewNotice(neutralized, replacements);
|
|
105968
106709
|
if (options.dryRun) {
|
|
105969
106710
|
console.log(`[dry-run] Would write ${targetFileName} from ${sourcePath} (${replacements} replacements)`);
|
|
105970
106711
|
} else {
|
|
@@ -106040,6 +106781,7 @@ async function resolveRunProfileOverride(opts) {
|
|
|
106040
106781
|
}
|
|
106041
106782
|
// src/cli/features-resolve.ts
|
|
106042
106783
|
init_config();
|
|
106784
|
+
init_test_runners();
|
|
106043
106785
|
import { existsSync as existsSync28, readdirSync as readdirSync6 } from "fs";
|
|
106044
106786
|
import { join as join74, relative as relative17 } from "path";
|
|
106045
106787
|
|
|
@@ -106098,6 +106840,15 @@ async function resolveGroupCommand(repoRoot, packageDir, rootCommand) {
|
|
|
106098
106840
|
}
|
|
106099
106841
|
|
|
106100
106842
|
// src/cli/features-resolve.ts
|
|
106843
|
+
async function resolveTestPatterns(workdir) {
|
|
106844
|
+
try {
|
|
106845
|
+
const config2 = await loadConfig(workdir);
|
|
106846
|
+
const resolved = await resolveTestFilePatterns(config2, workdir);
|
|
106847
|
+
return { regex: resolved.regex.map((r) => r.source), resolution: resolved.resolution };
|
|
106848
|
+
} catch {
|
|
106849
|
+
return;
|
|
106850
|
+
}
|
|
106851
|
+
}
|
|
106101
106852
|
async function isNonEmptyFile(absolutePath) {
|
|
106102
106853
|
if (!existsSync28(absolutePath))
|
|
106103
106854
|
return false;
|
|
@@ -106202,6 +106953,7 @@ async function resolveFeatureSpec(name, workdir) {
|
|
|
106202
106953
|
featureName: name,
|
|
106203
106954
|
specSource: source2,
|
|
106204
106955
|
acceptance: await resolveFeatureAcceptance(name, workdir),
|
|
106956
|
+
testPatterns: await resolveTestPatterns(workdir),
|
|
106205
106957
|
message: `resolved spec: ${source2.path}`
|
|
106206
106958
|
};
|
|
106207
106959
|
}
|
|
@@ -106248,6 +107000,7 @@ async function resolveFeatureSpec(name, workdir) {
|
|
|
106248
107000
|
featureName: onlyName,
|
|
106249
107001
|
specSource: source,
|
|
106250
107002
|
acceptance: await resolveFeatureAcceptance(onlyName, workdir),
|
|
107003
|
+
testPatterns: await resolveTestPatterns(workdir),
|
|
106251
107004
|
message: `resolved spec: ${source.path}`
|
|
106252
107005
|
};
|
|
106253
107006
|
}
|
|
@@ -106267,7 +107020,7 @@ init_runtime();
|
|
|
106267
107020
|
init_json_file();
|
|
106268
107021
|
init_routing();
|
|
106269
107022
|
import { mkdirSync as mkdirSync6 } from "fs";
|
|
106270
|
-
import { basename as
|
|
107023
|
+
import { basename as basename15, join as join75 } from "path";
|
|
106271
107024
|
var _routingCalibrateDeps = {
|
|
106272
107025
|
loadRunMetrics: (outputDir) => loadRunMetrics(outputDir),
|
|
106273
107026
|
readConfig: (workdir) => loadConfig(workdir),
|
|
@@ -106325,7 +107078,7 @@ async function runRoutingCalibrateCli(options, deps = _routingCalibrateDeps) {
|
|
|
106325
107078
|
function resolveOutputDir2(workdir, override, prior) {
|
|
106326
107079
|
if (override)
|
|
106327
107080
|
return override;
|
|
106328
|
-
const key = prior?.name?.trim() ||
|
|
107081
|
+
const key = prior?.name?.trim() || basename15(workdir);
|
|
106329
107082
|
return projectOutputDir(key, prior?.outputDir);
|
|
106330
107083
|
}
|
|
106331
107084
|
function mergeComplexityRouting(prior, adjustments) {
|
|
@@ -107311,7 +108064,7 @@ init_errors();
|
|
|
107311
108064
|
init_checkpoint();
|
|
107312
108065
|
init_runtime();
|
|
107313
108066
|
import { existsSync as existsSync37 } from "fs";
|
|
107314
|
-
import { basename as
|
|
108067
|
+
import { basename as basename18, join as join95 } from "path";
|
|
107315
108068
|
async function defaultCheckpointExists(featureDir) {
|
|
107316
108069
|
if (!featureDir || !existsSync37(featureDir))
|
|
107317
108070
|
return false;
|
|
@@ -107390,7 +108143,7 @@ function registerResumeCommand(program2) {
|
|
|
107390
108143
|
const globalNaxDir = globalConfigDir();
|
|
107391
108144
|
const hooks = await loadHooksConfig2(naxDir, globalNaxDir);
|
|
107392
108145
|
applyResumeModeDeps2(opts.featureDir ?? "", "auto");
|
|
107393
|
-
const projectKey = config2.name?.trim() ||
|
|
108146
|
+
const projectKey = config2.name?.trim() || basename18(cmdOpts.dir);
|
|
107394
108147
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
107395
108148
|
const statusFilePath = join95(outputDir, "status.json");
|
|
107396
108149
|
const result = await run2({
|
|
@@ -115841,7 +116594,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
115841
116594
|
process.exit(1);
|
|
115842
116595
|
}
|
|
115843
116596
|
resetLogger();
|
|
115844
|
-
const projectKey = config2.name?.trim() ||
|
|
116597
|
+
const projectKey = config2.name?.trim() || basename20(workdir);
|
|
115845
116598
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
115846
116599
|
const runsDir = join101(outputDir, "features", options.feature, "runs");
|
|
115847
116600
|
mkdirSync8(runsDir, { recursive: true });
|
|
@@ -116462,12 +117215,13 @@ curator.command("dryrun").description("Re-run heuristics on an existing observat
|
|
|
116462
117215
|
process.exit(1);
|
|
116463
117216
|
}
|
|
116464
117217
|
});
|
|
116465
|
-
curator.command("gc").description("Prune old rows from the curator rollup JSONL").option("-p, --project <path>", "Project directory (default: CWD)").option("--keep <N>", "Number of most recent runIds to keep (default: 50)", "50").action(async (options) => {
|
|
117218
|
+
curator.command("gc").description("Prune old rows from the curator rollup JSONL").option("-p, --project <path>", "Project directory (default: CWD)").option("--keep <N>", "Number of most recent runIds to keep (default: 50)", "50").option("--sweep-unattributed", "Also drop rows with no projectKey (pre-#1429 history, unreadable by any project). Machine-wide \u2014 affects every project sharing the rollup.").action(async (options) => {
|
|
116466
117219
|
const { curatorGc: curatorGc2 } = await Promise.resolve().then(() => (init_curator2(), exports_curator));
|
|
116467
117220
|
try {
|
|
116468
117221
|
await curatorGc2({
|
|
116469
117222
|
project: options.project,
|
|
116470
|
-
keep: options.keep !== undefined ? Number.parseInt(options.keep, 10) : undefined
|
|
117223
|
+
keep: options.keep !== undefined ? Number.parseInt(options.keep, 10) : undefined,
|
|
117224
|
+
sweepUnattributed: options.sweepUnattributed === true
|
|
116471
117225
|
});
|
|
116472
117226
|
} catch (err) {
|
|
116473
117227
|
console.error(source_default.red(`Error: ${err.message}`));
|