@brainervirus/workit-codex 6.0.1 → 7.1.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/.codex-plugin/plugin.json +1 -1
- package/README.md +3 -1
- package/dist/launch-mcp.js +2099 -993
- package/dist/workit-hook.js +4158 -2715
- package/package.json +3 -3
- package/skills/workit-deslop/SKILL.md +1 -1
- package/skills/workit-implement/SKILL.md +9 -4
- package/skills/workit-review/SKILL.md +2 -0
- package/skills/workit-shape/SKILL.md +1 -1
- package/skills/workit-ship/SKILL.md +1 -1
package/dist/launch-mcp.js
CHANGED
|
@@ -6621,13 +6621,13 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
6621
6621
|
});
|
|
6622
6622
|
|
|
6623
6623
|
// packages/workit-codex/scripts/launch-mcp.ts
|
|
6624
|
-
import { existsSync as
|
|
6625
|
-
import
|
|
6624
|
+
import { existsSync as existsSync9, realpathSync as realpathSync7, statSync as statSync3 } from "node:fs";
|
|
6625
|
+
import path21 from "node:path";
|
|
6626
6626
|
|
|
6627
6627
|
// packages/workit-core/src/core.ts
|
|
6628
|
-
import { spawnSync as
|
|
6629
|
-
import { existsSync as
|
|
6630
|
-
import
|
|
6628
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
6629
|
+
import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
|
|
6630
|
+
import path19 from "node:path";
|
|
6631
6631
|
|
|
6632
6632
|
// packages/workit-core/src/core/task-contract.ts
|
|
6633
6633
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -12854,12 +12854,6 @@ var numberProcessor = (schema, ctx, _json, params) => {
|
|
|
12854
12854
|
var booleanProcessor = (_schema, _ctx, json, _params) => {
|
|
12855
12855
|
json.type = "boolean";
|
|
12856
12856
|
};
|
|
12857
|
-
var bigintProcessor = (schema, ctx, json, params) => {
|
|
12858
|
-
handleUnrepresentable(schema, ctx, json, params, "BigInt cannot be represented in JSON Schema");
|
|
12859
|
-
};
|
|
12860
|
-
var symbolProcessor = (schema, ctx, json, params) => {
|
|
12861
|
-
handleUnrepresentable(schema, ctx, json, params, "Symbols cannot be represented in JSON Schema");
|
|
12862
|
-
};
|
|
12863
12857
|
var nullProcessor = (_schema, ctx, json, _params) => {
|
|
12864
12858
|
if (ctx.target === "openapi-3.0") {
|
|
12865
12859
|
json.type = "string";
|
|
@@ -12869,20 +12863,10 @@ var nullProcessor = (_schema, ctx, json, _params) => {
|
|
|
12869
12863
|
json.type = "null";
|
|
12870
12864
|
}
|
|
12871
12865
|
};
|
|
12872
|
-
var undefinedProcessor = (schema, ctx, json, params) => {
|
|
12873
|
-
handleUnrepresentable(schema, ctx, json, params, "Undefined cannot be represented in JSON Schema");
|
|
12874
|
-
};
|
|
12875
|
-
var voidProcessor = (schema, ctx, json, params) => {
|
|
12876
|
-
handleUnrepresentable(schema, ctx, json, params, "Void cannot be represented in JSON Schema");
|
|
12877
|
-
};
|
|
12878
12866
|
var neverProcessor = (_schema, _ctx, json, _params) => {
|
|
12879
12867
|
json.not = {};
|
|
12880
12868
|
};
|
|
12881
|
-
var anyProcessor = (_schema, _ctx, _json, _params) => {};
|
|
12882
12869
|
var unknownProcessor = (_schema, _ctx, _json, _params) => {};
|
|
12883
|
-
var dateProcessor = (schema, ctx, json, params) => {
|
|
12884
|
-
handleUnrepresentable(schema, ctx, json, params, "Date cannot be represented in JSON Schema");
|
|
12885
|
-
};
|
|
12886
12870
|
var enumProcessor = (schema, _ctx, json, _params) => {
|
|
12887
12871
|
const def = schema._zod.def;
|
|
12888
12872
|
const values = getEnumValues(def.entries);
|
|
@@ -12935,54 +12919,12 @@ var literalProcessor = (schema, ctx, json, params) => {
|
|
|
12935
12919
|
json.enum = vals;
|
|
12936
12920
|
}
|
|
12937
12921
|
};
|
|
12938
|
-
var nanProcessor = (schema, ctx, json, params) => {
|
|
12939
|
-
handleUnrepresentable(schema, ctx, json, params, "NaN cannot be represented in JSON Schema");
|
|
12940
|
-
};
|
|
12941
|
-
var templateLiteralProcessor = (schema, _ctx, json, _params) => {
|
|
12942
|
-
const _json = json;
|
|
12943
|
-
const pattern = schema._zod.pattern;
|
|
12944
|
-
if (!pattern)
|
|
12945
|
-
throw new Error("Pattern not found in template literal");
|
|
12946
|
-
_json.type = "string";
|
|
12947
|
-
_json.pattern = pattern.source;
|
|
12948
|
-
};
|
|
12949
|
-
var fileProcessor = (schema, _ctx, json, _params) => {
|
|
12950
|
-
const _json = json;
|
|
12951
|
-
_json.type = "string";
|
|
12952
|
-
_json.format = "binary";
|
|
12953
|
-
_json.contentEncoding = "binary";
|
|
12954
|
-
const { minimum, maximum, mime } = aggregateChecks(schema);
|
|
12955
|
-
if (minimum !== undefined)
|
|
12956
|
-
_json.minLength = minimum;
|
|
12957
|
-
if (maximum !== undefined)
|
|
12958
|
-
_json.maxLength = maximum;
|
|
12959
|
-
if (!mime)
|
|
12960
|
-
return;
|
|
12961
|
-
if (mime.length === 0)
|
|
12962
|
-
_json.not = {};
|
|
12963
|
-
else if (mime.length === 1)
|
|
12964
|
-
_json.contentMediaType = mime[0];
|
|
12965
|
-
else
|
|
12966
|
-
_json.anyOf = mime.map((m) => ({ contentMediaType: m }));
|
|
12967
|
-
};
|
|
12968
|
-
var successProcessor = (_schema, _ctx, json, _params) => {
|
|
12969
|
-
json.type = "boolean";
|
|
12970
|
-
};
|
|
12971
12922
|
var customProcessor = (schema, ctx, json, params) => {
|
|
12972
12923
|
handleUnrepresentable(schema, ctx, json, params, "Custom types cannot be represented in JSON Schema");
|
|
12973
12924
|
};
|
|
12974
|
-
var functionProcessor = (schema, ctx, json, params) => {
|
|
12975
|
-
handleUnrepresentable(schema, ctx, json, params, "Function types cannot be represented in JSON Schema");
|
|
12976
|
-
};
|
|
12977
12925
|
var transformProcessor = (schema, ctx, json, params) => {
|
|
12978
12926
|
handleUnrepresentable(schema, ctx, json, params, "Transforms cannot be represented in JSON Schema");
|
|
12979
12927
|
};
|
|
12980
|
-
var mapProcessor = (schema, ctx, json, params) => {
|
|
12981
|
-
handleUnrepresentable(schema, ctx, json, params, "Map cannot be represented in JSON Schema");
|
|
12982
|
-
};
|
|
12983
|
-
var setProcessor = (schema, ctx, json, params) => {
|
|
12984
|
-
handleUnrepresentable(schema, ctx, json, params, "Set cannot be represented in JSON Schema");
|
|
12985
|
-
};
|
|
12986
12928
|
var arrayProcessor = (schema, ctx, _json, params) => {
|
|
12987
12929
|
const json = _json;
|
|
12988
12930
|
const def = schema._zod.def;
|
|
@@ -13076,70 +13018,6 @@ var intersectionProcessor = (schema, ctx, json, params) => {
|
|
|
13076
13018
|
json.allOf = allOf;
|
|
13077
13019
|
ctx.intersections.push(allOf);
|
|
13078
13020
|
};
|
|
13079
|
-
var tupleProcessor = (schema, ctx, _json, params) => {
|
|
13080
|
-
const json = _json;
|
|
13081
|
-
const def = schema._zod.def;
|
|
13082
|
-
json.type = "array";
|
|
13083
|
-
const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
13084
|
-
const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
13085
|
-
const prefixItems = def.items.map((x, i) => processSchema(x, ctx, {
|
|
13086
|
-
...params,
|
|
13087
|
-
path: [...params.path, prefixPath, i]
|
|
13088
|
-
}));
|
|
13089
|
-
const rest = def.rest ? processSchema(def.rest, ctx, {
|
|
13090
|
-
...params,
|
|
13091
|
-
path: [...params.path, restPath, ...ctx.target === "openapi-3.0" ? [def.items.length] : []]
|
|
13092
|
-
}) : null;
|
|
13093
|
-
let minItems = def.items.length;
|
|
13094
|
-
while (minItems > 0) {
|
|
13095
|
-
const item = def.items[minItems - 1];
|
|
13096
|
-
const optional = ctx.io === "input" ? inputOptin(item) !== undefined : item._zod.optout === "optional";
|
|
13097
|
-
if (!optional)
|
|
13098
|
-
break;
|
|
13099
|
-
minItems--;
|
|
13100
|
-
}
|
|
13101
|
-
const maxItems = def.items.length;
|
|
13102
|
-
const isClosed = !def.rest;
|
|
13103
|
-
if (ctx.target === "draft-2020-12") {
|
|
13104
|
-
json.prefixItems = prefixItems;
|
|
13105
|
-
if (isClosed) {
|
|
13106
|
-
json.items = false;
|
|
13107
|
-
} else if (rest) {
|
|
13108
|
-
json.items = rest;
|
|
13109
|
-
}
|
|
13110
|
-
if (minItems > 0)
|
|
13111
|
-
json.minItems = minItems;
|
|
13112
|
-
if (isClosed)
|
|
13113
|
-
json.maxItems = maxItems;
|
|
13114
|
-
} else if (ctx.target === "openapi-3.0") {
|
|
13115
|
-
json.items = {
|
|
13116
|
-
anyOf: prefixItems
|
|
13117
|
-
};
|
|
13118
|
-
if (rest) {
|
|
13119
|
-
json.items.anyOf.push(rest);
|
|
13120
|
-
}
|
|
13121
|
-
if (minItems > 0)
|
|
13122
|
-
json.minItems = minItems;
|
|
13123
|
-
if (isClosed)
|
|
13124
|
-
json.maxItems = maxItems;
|
|
13125
|
-
} else {
|
|
13126
|
-
json.items = prefixItems;
|
|
13127
|
-
if (isClosed) {
|
|
13128
|
-
json.additionalItems = false;
|
|
13129
|
-
} else if (rest) {
|
|
13130
|
-
json.additionalItems = rest;
|
|
13131
|
-
}
|
|
13132
|
-
if (minItems > 0)
|
|
13133
|
-
json.minItems = minItems;
|
|
13134
|
-
if (isClosed)
|
|
13135
|
-
json.maxItems = maxItems;
|
|
13136
|
-
}
|
|
13137
|
-
const { minimum, maximum } = aggregateChecks(schema);
|
|
13138
|
-
if (typeof minimum === "number")
|
|
13139
|
-
json.minItems = minimum;
|
|
13140
|
-
if (typeof maximum === "number")
|
|
13141
|
-
json.maxItems = maximum;
|
|
13142
|
-
};
|
|
13143
13021
|
function stringifyKeyNames(bySchema, json, visited) {
|
|
13144
13022
|
if (json.$ref) {
|
|
13145
13023
|
if (visited.has(json))
|
|
@@ -13326,99 +13204,12 @@ var readonlyProcessor = (schema, ctx, json, params) => {
|
|
|
13326
13204
|
seen.ref = def.innerType;
|
|
13327
13205
|
json.readOnly = true;
|
|
13328
13206
|
};
|
|
13329
|
-
var promiseProcessor = (schema, ctx, _json, params) => {
|
|
13330
|
-
const def = schema._zod.def;
|
|
13331
|
-
processSchema(def.innerType, ctx, params);
|
|
13332
|
-
const seen = ctx.seen.get(schema);
|
|
13333
|
-
seen.ref = def.innerType;
|
|
13334
|
-
};
|
|
13335
13207
|
var optionalProcessor = (schema, ctx, _json, params) => {
|
|
13336
13208
|
const def = schema._zod.def;
|
|
13337
13209
|
processSchema(def.innerType, ctx, params);
|
|
13338
13210
|
const seen = ctx.seen.get(schema);
|
|
13339
13211
|
seen.ref = def.innerType;
|
|
13340
13212
|
};
|
|
13341
|
-
var lazyProcessor = (schema, ctx, _json, params) => {
|
|
13342
|
-
const innerType = schema._zod.innerType;
|
|
13343
|
-
processSchema(innerType, ctx, params);
|
|
13344
|
-
const seen = ctx.seen.get(schema);
|
|
13345
|
-
seen.ref = innerType;
|
|
13346
|
-
};
|
|
13347
|
-
var allProcessors = {
|
|
13348
|
-
string: stringProcessor,
|
|
13349
|
-
number: numberProcessor,
|
|
13350
|
-
boolean: booleanProcessor,
|
|
13351
|
-
bigint: bigintProcessor,
|
|
13352
|
-
symbol: symbolProcessor,
|
|
13353
|
-
null: nullProcessor,
|
|
13354
|
-
undefined: undefinedProcessor,
|
|
13355
|
-
void: voidProcessor,
|
|
13356
|
-
never: neverProcessor,
|
|
13357
|
-
any: anyProcessor,
|
|
13358
|
-
unknown: unknownProcessor,
|
|
13359
|
-
date: dateProcessor,
|
|
13360
|
-
enum: enumProcessor,
|
|
13361
|
-
literal: literalProcessor,
|
|
13362
|
-
nan: nanProcessor,
|
|
13363
|
-
template_literal: templateLiteralProcessor,
|
|
13364
|
-
file: fileProcessor,
|
|
13365
|
-
success: successProcessor,
|
|
13366
|
-
custom: customProcessor,
|
|
13367
|
-
function: functionProcessor,
|
|
13368
|
-
transform: transformProcessor,
|
|
13369
|
-
map: mapProcessor,
|
|
13370
|
-
set: setProcessor,
|
|
13371
|
-
array: arrayProcessor,
|
|
13372
|
-
object: objectProcessor,
|
|
13373
|
-
union: unionProcessor,
|
|
13374
|
-
intersection: intersectionProcessor,
|
|
13375
|
-
tuple: tupleProcessor,
|
|
13376
|
-
record: recordProcessor,
|
|
13377
|
-
nullable: nullableProcessor,
|
|
13378
|
-
nonoptional: nonoptionalProcessor,
|
|
13379
|
-
default: defaultProcessor,
|
|
13380
|
-
prefault: prefaultProcessor,
|
|
13381
|
-
catch: catchProcessor,
|
|
13382
|
-
pipe: pipeProcessor,
|
|
13383
|
-
readonly: readonlyProcessor,
|
|
13384
|
-
promise: promiseProcessor,
|
|
13385
|
-
optional: optionalProcessor,
|
|
13386
|
-
lazy: lazyProcessor
|
|
13387
|
-
};
|
|
13388
|
-
function toJSONSchema(input, params) {
|
|
13389
|
-
if ("_idmap" in input) {
|
|
13390
|
-
const registry = input;
|
|
13391
|
-
const ctx = initializeContext({ ...params, processors: allProcessors });
|
|
13392
|
-
const defs = {};
|
|
13393
|
-
for (const entry of registry._idmap.entries()) {
|
|
13394
|
-
const [_, schema] = entry;
|
|
13395
|
-
processSchema(schema, ctx);
|
|
13396
|
-
}
|
|
13397
|
-
const schemas = {};
|
|
13398
|
-
const external = {
|
|
13399
|
-
registry,
|
|
13400
|
-
uri: params?.uri,
|
|
13401
|
-
defs
|
|
13402
|
-
};
|
|
13403
|
-
ctx.external = external;
|
|
13404
|
-
for (const entry of registry._idmap.entries()) {
|
|
13405
|
-
const [key, schema] = entry;
|
|
13406
|
-
extractDefs(ctx, schema);
|
|
13407
|
-
assignProp(schemas, key, finalize(ctx, schema));
|
|
13408
|
-
}
|
|
13409
|
-
if (Object.keys(defs).length > 0) {
|
|
13410
|
-
const defsSegment = ctx.target === "draft-2020-12" ? "$defs" : "definitions";
|
|
13411
|
-
schemas.__shared = {
|
|
13412
|
-
[defsSegment]: defs
|
|
13413
|
-
};
|
|
13414
|
-
}
|
|
13415
|
-
return { schemas };
|
|
13416
|
-
}
|
|
13417
|
-
const ctx = initializeContext({ ...params, processors: allProcessors });
|
|
13418
|
-
processSchema(input, ctx);
|
|
13419
|
-
extractDefs(ctx, input);
|
|
13420
|
-
return finalize(ctx, input);
|
|
13421
|
-
}
|
|
13422
13213
|
// node_modules/zod/v4/classic/errors.js
|
|
13423
13214
|
var _installedErrorProtos = /* @__PURE__ */ new WeakSet([Object.prototype, Error.prototype]);
|
|
13424
13215
|
function _lazyMethod(proto, key, make) {
|
|
@@ -14610,76 +14401,16 @@ var provenanceSchema = object({
|
|
|
14610
14401
|
workerId: nullableId
|
|
14611
14402
|
}).strict();
|
|
14612
14403
|
var entrySchema = (data) => object({ id, recordedAt: utc, provenance: provenanceSchema, data }).strict();
|
|
14613
|
-
var
|
|
14614
|
-
|
|
14615
|
-
|
|
14616
|
-
|
|
14617
|
-
|
|
14618
|
-
|
|
14619
|
-
|
|
14620
|
-
code: "custom",
|
|
14621
|
-
input: ctx.value,
|
|
14622
|
-
message: "observed facts require supporting references",
|
|
14623
|
-
path: ["refs"]
|
|
14624
|
-
});
|
|
14625
|
-
});
|
|
14626
|
-
var signalSchema = object({
|
|
14627
|
-
value: union([boolean2(), literal("unknown")]),
|
|
14628
|
-
basis: _enum(["observed", "inferred", "unknown"]),
|
|
14629
|
-
reason: text,
|
|
14404
|
+
var RISK_TIERS = ["trivial", "normal", "high"];
|
|
14405
|
+
var judgmentSchema = object({
|
|
14406
|
+
riskTier: _enum(RISK_TIERS),
|
|
14407
|
+
behaviorChange: boolean2(),
|
|
14408
|
+
productChoiceOpen: boolean2(),
|
|
14409
|
+
needsPlan: boolean2(),
|
|
14410
|
+
note: text.nullable(),
|
|
14630
14411
|
refs: array(refSchema)
|
|
14631
|
-
}).strict().check((ctx) => {
|
|
14632
|
-
const signal = ctx.value;
|
|
14633
|
-
const valid = signal.value === "unknown" ? signal.basis === "unknown" : signal.basis === "observed" || signal.basis === "inferred";
|
|
14634
|
-
if (!valid)
|
|
14635
|
-
ctx.issues.push({
|
|
14636
|
-
code: "custom",
|
|
14637
|
-
input: signal,
|
|
14638
|
-
message: "signal value and basis disagree",
|
|
14639
|
-
path: []
|
|
14640
|
-
});
|
|
14641
|
-
if (signal.basis === "observed" && signal.refs.length === 0)
|
|
14642
|
-
ctx.issues.push({
|
|
14643
|
-
code: "custom",
|
|
14644
|
-
input: signal,
|
|
14645
|
-
message: "observed signals require supporting references",
|
|
14646
|
-
path: ["refs"]
|
|
14647
|
-
});
|
|
14648
|
-
});
|
|
14649
|
-
var signalSet = object({
|
|
14650
|
-
approachUnknown: signalSchema,
|
|
14651
|
-
productChoiceOpen: signalSchema,
|
|
14652
|
-
behaviorChange: signalSchema,
|
|
14653
|
-
mechanicalLowRisk: signalSchema,
|
|
14654
|
-
durableAgreementNeeded: signalSchema,
|
|
14655
|
-
coordinationPlanNeeded: signalSchema,
|
|
14656
|
-
helperUseful: signalSchema,
|
|
14657
|
-
testFirstPractical: signalSchema
|
|
14658
|
-
}).strict();
|
|
14659
|
-
var consequenceSchema = object({
|
|
14660
|
-
area: _enum([
|
|
14661
|
-
"behavior",
|
|
14662
|
-
"public_contract",
|
|
14663
|
-
"data",
|
|
14664
|
-
"security",
|
|
14665
|
-
"operations",
|
|
14666
|
-
"reversibility",
|
|
14667
|
-
"consumers"
|
|
14668
|
-
]),
|
|
14669
|
-
fact: factSchema
|
|
14670
|
-
}).strict();
|
|
14671
|
-
var verificationSchema = object({
|
|
14672
|
-
claim: text,
|
|
14673
|
-
scope: scopeSchema,
|
|
14674
|
-
availableChecks: array(refSchema),
|
|
14675
|
-
gaps: array(text)
|
|
14676
|
-
}).strict();
|
|
14677
|
-
var assessmentSchema = object({
|
|
14678
|
-
facts: array(factSchema),
|
|
14679
|
-
signals: signalSet,
|
|
14680
|
-
consequences: array(consequenceSchema),
|
|
14681
|
-
verification: array(verificationSchema)
|
|
14682
14412
|
}).strict();
|
|
14413
|
+
var legacyAssessmentSchema = looseObject({});
|
|
14683
14414
|
var constraintSchema = object({
|
|
14684
14415
|
id: nonEmpty,
|
|
14685
14416
|
kind: _enum(["host", "project", "user"]),
|
|
@@ -14916,7 +14647,8 @@ var taskRecordSchema = object({
|
|
|
14916
14647
|
progress: progressSchema,
|
|
14917
14648
|
pauseReason: text.nullable().optional(),
|
|
14918
14649
|
runtime: runtimeSchema.optional(),
|
|
14919
|
-
assessments: array(entrySchema(
|
|
14650
|
+
assessments: array(entrySchema(legacyAssessmentSchema)),
|
|
14651
|
+
judgments: array(entrySchema(judgmentSchema)).optional(),
|
|
14920
14652
|
policy: policySchema.nullable(),
|
|
14921
14653
|
policyChanges: array(policyChangeSchema),
|
|
14922
14654
|
candidates: array(candidateSchema),
|
|
@@ -15092,14 +14824,15 @@ var taskOperations = {
|
|
|
15092
14824
|
decisionIds: array(id).optional()
|
|
15093
14825
|
})
|
|
15094
14826
|
};
|
|
14827
|
+
var rawJudgment = record(string2(), unknown());
|
|
15095
14828
|
var policyOperations = {
|
|
15096
14829
|
assess: operation({
|
|
15097
14830
|
action: literal("assess"),
|
|
15098
14831
|
...taskId,
|
|
15099
14832
|
expectedRevision: revision.optional(),
|
|
15100
|
-
|
|
14833
|
+
judgment: rawJudgment
|
|
15101
14834
|
}),
|
|
15102
|
-
preview: operation({ action: literal("preview"), ...taskId,
|
|
14835
|
+
preview: operation({ action: literal("preview"), ...taskId, judgment: rawJudgment }),
|
|
15103
14836
|
explain: operation({ action: literal("explain"), ...taskId })
|
|
15104
14837
|
};
|
|
15105
14838
|
var evidenceOperations = {
|
|
@@ -15138,7 +14871,10 @@ var decisionOperations = {
|
|
|
15138
14871
|
...taskId,
|
|
15139
14872
|
expectedRevision: revision.optional(),
|
|
15140
14873
|
purpose: decisionSchema.shape.purpose,
|
|
15141
|
-
binding: decisionSchema.shape.binding
|
|
14874
|
+
binding: decisionSchema.shape.binding.extend({
|
|
14875
|
+
taskId: id.optional(),
|
|
14876
|
+
workspaceId: id.optional()
|
|
14877
|
+
}),
|
|
15142
14878
|
response: decisionSchema.shape.response,
|
|
15143
14879
|
requirementIds: array(digest)
|
|
15144
14880
|
}),
|
|
@@ -15192,6 +14928,15 @@ var operationSchemas = {
|
|
|
15192
14928
|
worker: discriminatedUnion("action", Object.values(workerOperations)),
|
|
15193
14929
|
state: discriminatedUnion("action", Object.values(stateOperations))
|
|
15194
14930
|
};
|
|
14931
|
+
var OPERATION_ACTIONS = {
|
|
14932
|
+
task: Object.keys(taskOperations),
|
|
14933
|
+
policy: Object.keys(policyOperations),
|
|
14934
|
+
evidence: Object.keys(evidenceOperations),
|
|
14935
|
+
finding: Object.keys(findingOperations),
|
|
14936
|
+
decision: Object.keys(decisionOperations),
|
|
14937
|
+
worker: Object.keys(workerOperations),
|
|
14938
|
+
state: Object.keys(stateOperations)
|
|
14939
|
+
};
|
|
15195
14940
|
var withImplicitTask = (options) => Object.values(options).map((option) => ("taskId" in option.shape) ? option.extend({ taskId: id.optional() }) : option);
|
|
15196
14941
|
var advertised = (options) => discriminatedUnion("action", withImplicitTask(options));
|
|
15197
14942
|
var advertisedOperationSchemas = {
|
|
@@ -15225,103 +14970,6 @@ function parseOperation(family, input) {
|
|
|
15225
14970
|
}));
|
|
15226
14971
|
return failure2("invalid_input", "operation input is invalid", { fields });
|
|
15227
14972
|
}
|
|
15228
|
-
function operationJsonSchema(family) {
|
|
15229
|
-
return toJSONSchema(advertisedOperationSchemas[family], { target: "draft-2020-12" });
|
|
15230
|
-
}
|
|
15231
|
-
var OPERATION_SCHEMA_DEPTH = 1;
|
|
15232
|
-
var schemaShapeDescription = (value, depth = 0) => {
|
|
15233
|
-
if (!value || typeof value !== "object")
|
|
15234
|
-
return "value";
|
|
15235
|
-
const schema = value;
|
|
15236
|
-
if (Array.isArray(schema.oneOf) || Array.isArray(schema.anyOf)) {
|
|
15237
|
-
const options = Array.isArray(schema.oneOf) ? schema.oneOf : schema.anyOf;
|
|
15238
|
-
return `(${options.map((option) => schemaShapeDescription(option, depth)).join("|")})`;
|
|
15239
|
-
}
|
|
15240
|
-
if (schema.const !== undefined)
|
|
15241
|
-
return `=${JSON.stringify(schema.const)}`;
|
|
15242
|
-
if (Array.isArray(schema.enum))
|
|
15243
|
-
return `[${schema.enum.map(String).join("|")}]`;
|
|
15244
|
-
const type = Array.isArray(schema.type) ? schema.type.join("/") : schema.type;
|
|
15245
|
-
if (type === "array")
|
|
15246
|
-
return `arr<${schemaShapeDescription(schema.items, depth)}>`;
|
|
15247
|
-
if (type === "object" && schema.properties && typeof schema.properties === "object") {
|
|
15248
|
-
if (depth >= 2)
|
|
15249
|
-
return "obj";
|
|
15250
|
-
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
15251
|
-
const properties = schema.properties;
|
|
15252
|
-
const fields = Object.entries(properties).map(([name, child]) => `${name}${required.has(name) ? "!" : ""}:${schemaShapeDescription(child, depth + 1)}`);
|
|
15253
|
-
return `obj{${fields.join(",")}}`;
|
|
15254
|
-
}
|
|
15255
|
-
if (typeof type !== "string")
|
|
15256
|
-
return "?";
|
|
15257
|
-
return type.split("/").map((part) => ({ string: "str", boolean: "bool", number: "num", null: "null" })[part] ?? part).join("|");
|
|
15258
|
-
};
|
|
15259
|
-
var canonicalFieldsDescription = (value) => {
|
|
15260
|
-
if (Array.isArray(value))
|
|
15261
|
-
return `Canonical object fields: ${value.join(", ")}. Workit validates the complete nested value.`;
|
|
15262
|
-
const isZodType = (schema) => ("_zod" in schema) && typeof schema._zod === "object";
|
|
15263
|
-
const schema = isZodType(value) ? toJSONSchema(value, { target: "draft-2020-12" }) : value;
|
|
15264
|
-
if (!schema.properties || typeof schema.properties !== "object") {
|
|
15265
|
-
const variants = Array.isArray(schema.oneOf) ? schema.oneOf : Array.isArray(schema.anyOf) ? schema.anyOf : null;
|
|
15266
|
-
return variants ? `Canonical shapes: ${variants.map((variant) => schemaShapeDescription(variant)).join(" | ")}. Full nested value is validated.` : "Canonical nested value; Workit validates the complete value.";
|
|
15267
|
-
}
|
|
15268
|
-
const properties = schema.properties;
|
|
15269
|
-
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
15270
|
-
const fields = Object.entries(properties).map(([name, child]) => `${name}${required.has(name) ? "!" : ""}:${schemaShapeDescription(child)}`);
|
|
15271
|
-
return `Fields (! required): ${fields.join(", ")}. Full nested value is validated.`;
|
|
15272
|
-
};
|
|
15273
|
-
function boundedOperationJsonSchema(family, maxDepth = OPERATION_SCHEMA_DEPTH, stringifiedObjects = false) {
|
|
15274
|
-
const tolerate = (projected) => {
|
|
15275
|
-
if (!stringifiedObjects)
|
|
15276
|
-
return projected;
|
|
15277
|
-
if (projected.properties && typeof projected.properties === "object")
|
|
15278
|
-
return { ...projected, type: ["object", "string"] };
|
|
15279
|
-
if (projected.items)
|
|
15280
|
-
return { ...projected, type: ["array", "string"] };
|
|
15281
|
-
if ("const" in projected && (typeof projected.const === "number" || typeof projected.const === "boolean"))
|
|
15282
|
-
return { anyOf: [projected, { const: String(projected.const) }] };
|
|
15283
|
-
if (projected.type === "object")
|
|
15284
|
-
return { ...projected, type: ["object", "string"] };
|
|
15285
|
-
if (projected.type === "array")
|
|
15286
|
-
return { ...projected, type: ["array", "string"] };
|
|
15287
|
-
return projected;
|
|
15288
|
-
};
|
|
15289
|
-
const collapse = (node, currentDepth) => {
|
|
15290
|
-
if (Array.isArray(node))
|
|
15291
|
-
return node.map((item) => collapse(item, currentDepth));
|
|
15292
|
-
if (typeof node !== "object" || node === null)
|
|
15293
|
-
return node;
|
|
15294
|
-
const record = node;
|
|
15295
|
-
if (record.properties && typeof record.properties === "object") {
|
|
15296
|
-
const properties = record.properties;
|
|
15297
|
-
const fields = Object.keys(properties);
|
|
15298
|
-
if (currentDepth >= maxDepth || fields.length === 0) {
|
|
15299
|
-
const description = stringifiedObjects ? `${canonicalFieldsDescription(record)} A JSON-encoded string is also accepted.` : canonicalFieldsDescription(record);
|
|
15300
|
-
return tolerate({ type: "object", description });
|
|
15301
|
-
}
|
|
15302
|
-
return tolerate({
|
|
15303
|
-
...record,
|
|
15304
|
-
properties: Object.fromEntries(Object.entries(record.properties).map(([key, child]) => [
|
|
15305
|
-
key,
|
|
15306
|
-
collapse(child, currentDepth + 1)
|
|
15307
|
-
]))
|
|
15308
|
-
});
|
|
15309
|
-
}
|
|
15310
|
-
if (record.items)
|
|
15311
|
-
return tolerate({ ...record, items: collapse(record.items, currentDepth) });
|
|
15312
|
-
const composed = ["anyOf", "oneOf", "allOf"].filter((key) => Array.isArray(record[key]));
|
|
15313
|
-
if (composed.length > 0) {
|
|
15314
|
-
const projected = { ...record };
|
|
15315
|
-
for (const key of composed)
|
|
15316
|
-
projected[key] = record[key].map((item) => collapse(item, currentDepth));
|
|
15317
|
-
return projected;
|
|
15318
|
-
}
|
|
15319
|
-
if ("const" in record)
|
|
15320
|
-
return tolerate({ ...record });
|
|
15321
|
-
return node;
|
|
15322
|
-
};
|
|
15323
|
-
return collapse(operationJsonSchema(family), 0);
|
|
15324
|
-
}
|
|
15325
14973
|
function canonical(value, seen) {
|
|
15326
14974
|
if (value === null || typeof value === "boolean" || typeof value === "string") {
|
|
15327
14975
|
if (typeof value === "string" && invalidUnicode(value))
|
|
@@ -15410,6 +15058,402 @@ function requirementId(input) {
|
|
|
15410
15058
|
}
|
|
15411
15059
|
});
|
|
15412
15060
|
}
|
|
15061
|
+
// packages/workit-core/src/core/operation-input.ts
|
|
15062
|
+
var str = (d, values) => ({
|
|
15063
|
+
type: "string",
|
|
15064
|
+
...values ? { enum: values } : {},
|
|
15065
|
+
...d ? { d } : {}
|
|
15066
|
+
});
|
|
15067
|
+
var list = (d) => ({ type: "array", ...d ? { d } : {} });
|
|
15068
|
+
var bool = (d) => ({ type: "boolean", ...d ? { d } : {} });
|
|
15069
|
+
var int2 = (d) => ({ type: "integer", ...d ? { d } : {} });
|
|
15070
|
+
var scopeFields = {
|
|
15071
|
+
paths: list("Scope paths (default: whole checkout)."),
|
|
15072
|
+
exclusions: list(),
|
|
15073
|
+
scope: str("Scope description.")
|
|
15074
|
+
};
|
|
15075
|
+
var refs = list("Paths or URLs.");
|
|
15076
|
+
var FLAT = {
|
|
15077
|
+
task: {
|
|
15078
|
+
fields: {
|
|
15079
|
+
objective: str(),
|
|
15080
|
+
...scopeFields,
|
|
15081
|
+
refs,
|
|
15082
|
+
reason: str(),
|
|
15083
|
+
summary: str(),
|
|
15084
|
+
nextAction: str(),
|
|
15085
|
+
blockers: list(),
|
|
15086
|
+
outcome: str(undefined, ["verified", "accepted_limitations", "stopped"]),
|
|
15087
|
+
decisionIds: list(),
|
|
15088
|
+
status: str(undefined, ["open", "closed", "all"]),
|
|
15089
|
+
query: str(),
|
|
15090
|
+
limit: int2(),
|
|
15091
|
+
view: str(undefined, ["summary", "full"])
|
|
15092
|
+
}
|
|
15093
|
+
},
|
|
15094
|
+
policy: {
|
|
15095
|
+
fields: {
|
|
15096
|
+
riskTier: str("low=trivial, medium=normal.", ["trivial", "normal", "high", "low", "medium"]),
|
|
15097
|
+
behaviorChange: bool("Observable behavior changes."),
|
|
15098
|
+
productChoiceOpen: bool("A product/preference choice is still open."),
|
|
15099
|
+
needsPlan: bool("Needs a written plan or spec first."),
|
|
15100
|
+
note: str(),
|
|
15101
|
+
refs: list("Plan/spec paths or URLs.")
|
|
15102
|
+
}
|
|
15103
|
+
},
|
|
15104
|
+
evidence: {
|
|
15105
|
+
fields: {
|
|
15106
|
+
kind: str(undefined, ["check", "review", "investigation", "artifact"]),
|
|
15107
|
+
claim: str(),
|
|
15108
|
+
result: str(undefined, ["passed", "failed", "missing", "skipped"]),
|
|
15109
|
+
summary: str(),
|
|
15110
|
+
exitCode: int2(),
|
|
15111
|
+
refs,
|
|
15112
|
+
requirementIds: list(),
|
|
15113
|
+
candidateId: str(),
|
|
15114
|
+
beforeCandidateId: str(),
|
|
15115
|
+
reviewSession: str("Reviewer session handle (review evidence).")
|
|
15116
|
+
}
|
|
15117
|
+
},
|
|
15118
|
+
finding: {
|
|
15119
|
+
fields: {
|
|
15120
|
+
claim: str(),
|
|
15121
|
+
consequence: str(),
|
|
15122
|
+
...scopeFields,
|
|
15123
|
+
candidateId: str(),
|
|
15124
|
+
refs,
|
|
15125
|
+
findingId: str(),
|
|
15126
|
+
disposition: str(undefined, ["open", "fixed", "dismissed", "deferred"]),
|
|
15127
|
+
reason: str(),
|
|
15128
|
+
evidenceIds: list(),
|
|
15129
|
+
decisionIds: list()
|
|
15130
|
+
}
|
|
15131
|
+
},
|
|
15132
|
+
decision: {
|
|
15133
|
+
fields: {
|
|
15134
|
+
purpose: str(undefined, ["design", "action", "limitation", "preference"]),
|
|
15135
|
+
response: str(undefined, ["approved", "rejected", "stated"]),
|
|
15136
|
+
presented: str("What the user was asked."),
|
|
15137
|
+
choice: str("The user's stated choice (response=stated)."),
|
|
15138
|
+
...scopeFields,
|
|
15139
|
+
refs,
|
|
15140
|
+
requirementIds: list(),
|
|
15141
|
+
decisionId: str(),
|
|
15142
|
+
reason: str()
|
|
15143
|
+
}
|
|
15144
|
+
},
|
|
15145
|
+
worker: {
|
|
15146
|
+
fields: {
|
|
15147
|
+
role: str(undefined, ["investigator", "reviewer", "implementer"]),
|
|
15148
|
+
objective: str(),
|
|
15149
|
+
...scopeFields,
|
|
15150
|
+
decisionIds: list(),
|
|
15151
|
+
requirementIds: list(),
|
|
15152
|
+
candidateId: str(),
|
|
15153
|
+
stoppingCondition: str(),
|
|
15154
|
+
workerId: str(),
|
|
15155
|
+
outcome: str(undefined, ["completed", "failed", "cancelled"]),
|
|
15156
|
+
summary: str(),
|
|
15157
|
+
evidenceIds: list(),
|
|
15158
|
+
findingIds: list(),
|
|
15159
|
+
reason: str()
|
|
15160
|
+
}
|
|
15161
|
+
},
|
|
15162
|
+
state: {
|
|
15163
|
+
fields: { bundle: { type: "object", d: "An export bundle (object or JSON string)." } }
|
|
15164
|
+
}
|
|
15165
|
+
};
|
|
15166
|
+
var COMMON = {
|
|
15167
|
+
taskId: str("Omit for this branch's task."),
|
|
15168
|
+
expectedRevision: str(),
|
|
15169
|
+
expectedWorkspaceRevision: str()
|
|
15170
|
+
};
|
|
15171
|
+
var DESCRIPTIONS = {
|
|
15172
|
+
task: "Workit task lifecycle for this branch's task (optional continuity).",
|
|
15173
|
+
policy: "Judge the work in four flat calls (riskTier, behaviorChange, productChoiceOpen, needsPlan); Workit derives the checks, verdicts, decisions and plan it needs.",
|
|
15174
|
+
evidence: "Record a note-level evidence entry; only `workit check` runs satisfy check gates.",
|
|
15175
|
+
finding: "Record or resolve a review finding.",
|
|
15176
|
+
decision: "Record or revoke a user decision (never an authorization).",
|
|
15177
|
+
worker: "Assign, report or cancel a bounded helper.",
|
|
15178
|
+
state: "Export or import a task handoff bundle."
|
|
15179
|
+
};
|
|
15180
|
+
var operationDescription = (family) => `${DESCRIPTIONS[family]} Flat fields; omit taskId and revisions.`;
|
|
15181
|
+
var READ_TASK_FIELDS = new Set(["status", "query", "limit", "view"]);
|
|
15182
|
+
var READ_ONLY_ACTIONS = new Set(["list", "inspect", "preview", "explain", "export"]);
|
|
15183
|
+
function flatOperationJsonSchema(family, options = {}) {
|
|
15184
|
+
const spec = FLAT[family];
|
|
15185
|
+
const actions = options.readOnly ? OPERATION_ACTIONS[family].filter((action) => READ_ONLY_ACTIONS.has(action)) : [...OPERATION_ACTIONS[family]];
|
|
15186
|
+
if (!actions.length)
|
|
15187
|
+
return null;
|
|
15188
|
+
const property = (field) => {
|
|
15189
|
+
const base = field.type === "array" ? { type: "array", items: { type: "string" } } : field.type === "object" ? { type: "object" } : { type: field.type };
|
|
15190
|
+
const tolerant = options.stringTolerant && field.type !== "string" ? { type: [field.type, "string"] } : {};
|
|
15191
|
+
return {
|
|
15192
|
+
...base,
|
|
15193
|
+
...tolerant,
|
|
15194
|
+
...field.enum ? { enum: field.enum } : {},
|
|
15195
|
+
...field.d ? { description: field.d } : {}
|
|
15196
|
+
};
|
|
15197
|
+
};
|
|
15198
|
+
const properties = { action: { type: "string", enum: actions } };
|
|
15199
|
+
const readField = (name) => name === "taskId" || (family === "task" ? READ_TASK_FIELDS.has(name) : family === "policy");
|
|
15200
|
+
for (const [name, field] of Object.entries({ ...COMMON, ...spec.fields }))
|
|
15201
|
+
if (options.readOnly ? readField(name) : !(family === "state" && name === "expectedRevision"))
|
|
15202
|
+
properties[name] = property(field);
|
|
15203
|
+
return {
|
|
15204
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
15205
|
+
type: "object",
|
|
15206
|
+
properties,
|
|
15207
|
+
required: ["action"]
|
|
15208
|
+
};
|
|
15209
|
+
}
|
|
15210
|
+
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
15211
|
+
var decoded = (value) => {
|
|
15212
|
+
if (typeof value !== "string")
|
|
15213
|
+
return value;
|
|
15214
|
+
const trimmed = value.trim();
|
|
15215
|
+
if (!/^[[{]/.test(trimmed) && !/^(true|false|null|-?\d+)$/.test(trimmed))
|
|
15216
|
+
return value;
|
|
15217
|
+
try {
|
|
15218
|
+
return JSON.parse(trimmed);
|
|
15219
|
+
} catch {
|
|
15220
|
+
return value;
|
|
15221
|
+
}
|
|
15222
|
+
};
|
|
15223
|
+
var strings = (value) => {
|
|
15224
|
+
const plain = decoded(value);
|
|
15225
|
+
if (typeof plain === "string")
|
|
15226
|
+
return plain.trim() ? [plain] : [];
|
|
15227
|
+
return plain;
|
|
15228
|
+
};
|
|
15229
|
+
var refOf = (value) => {
|
|
15230
|
+
if (typeof value !== "string")
|
|
15231
|
+
return value;
|
|
15232
|
+
return /^https?:\/\//i.test(value) ? { kind: "external", url: value } : { kind: "file", path: value.replace(/^\.\//, ""), digest: null };
|
|
15233
|
+
};
|
|
15234
|
+
var refList = (value) => {
|
|
15235
|
+
const items = strings(value);
|
|
15236
|
+
return Array.isArray(items) ? items.map(refOf) : items;
|
|
15237
|
+
};
|
|
15238
|
+
var take = (flat, keys, map = decoded) => {
|
|
15239
|
+
const out = {};
|
|
15240
|
+
for (const key of keys)
|
|
15241
|
+
if (key in flat) {
|
|
15242
|
+
out[key] = map(flat[key]);
|
|
15243
|
+
delete flat[key];
|
|
15244
|
+
}
|
|
15245
|
+
return out;
|
|
15246
|
+
};
|
|
15247
|
+
var nest = (request, key, flat, defaults = {}) => {
|
|
15248
|
+
const nested = isObject2(decoded(request[key])) ? decoded(request[key]) : null;
|
|
15249
|
+
if (!nested && !Object.keys(flat).length)
|
|
15250
|
+
return;
|
|
15251
|
+
request[key] = { ...defaults, ...flat, ...nested };
|
|
15252
|
+
};
|
|
15253
|
+
var scopeOf = (flat, description) => {
|
|
15254
|
+
const scope = decoded(flat.scope);
|
|
15255
|
+
const has = "paths" in flat || "exclusions" in flat || typeof scope === "string";
|
|
15256
|
+
const out = isObject2(scope) ? scope : has ? {
|
|
15257
|
+
description: typeof scope === "string" ? scope : String(description ?? ""),
|
|
15258
|
+
paths: strings(flat.paths ?? ["."]),
|
|
15259
|
+
exclusions: strings(flat.exclusions ?? [])
|
|
15260
|
+
} : null;
|
|
15261
|
+
delete flat.scope;
|
|
15262
|
+
delete flat.paths;
|
|
15263
|
+
delete flat.exclusions;
|
|
15264
|
+
return out;
|
|
15265
|
+
};
|
|
15266
|
+
var defaultScope = (description) => ({
|
|
15267
|
+
description: typeof description === "string" ? description : "",
|
|
15268
|
+
paths: ["."],
|
|
15269
|
+
exclusions: []
|
|
15270
|
+
});
|
|
15271
|
+
var LISTS = [
|
|
15272
|
+
"decisionIds",
|
|
15273
|
+
"evidenceIds",
|
|
15274
|
+
"findingIds",
|
|
15275
|
+
"requirementIds",
|
|
15276
|
+
"refs",
|
|
15277
|
+
"blockers"
|
|
15278
|
+
];
|
|
15279
|
+
var ENVELOPE = new Set([
|
|
15280
|
+
"schemaVersion",
|
|
15281
|
+
"action",
|
|
15282
|
+
"taskId",
|
|
15283
|
+
"expectedRevision",
|
|
15284
|
+
"expectedWorkspaceRevision",
|
|
15285
|
+
"judgment"
|
|
15286
|
+
]);
|
|
15287
|
+
function normalizeOperationInput(family, input, host) {
|
|
15288
|
+
if (!isObject2(input))
|
|
15289
|
+
return input;
|
|
15290
|
+
const request = { ...input };
|
|
15291
|
+
if (!("schemaVersion" in request))
|
|
15292
|
+
request.schemaVersion = 1;
|
|
15293
|
+
else
|
|
15294
|
+
request.schemaVersion = decoded(request.schemaVersion);
|
|
15295
|
+
for (const key of ["limit", "exitCode"])
|
|
15296
|
+
if (key in request)
|
|
15297
|
+
request[key] = decoded(request[key]);
|
|
15298
|
+
const action = request.action;
|
|
15299
|
+
const flat = request;
|
|
15300
|
+
switch (family) {
|
|
15301
|
+
case "task": {
|
|
15302
|
+
if (action === "start" || action === "revise") {
|
|
15303
|
+
const intent = isObject2(decoded(request.intent)) ? decoded(request.intent) : {};
|
|
15304
|
+
const objective = flat.objective ?? intent.objective;
|
|
15305
|
+
const scope = scopeOf(flat, objective);
|
|
15306
|
+
const fields = take(flat, ["objective"]);
|
|
15307
|
+
const authorityRefs = "refs" in flat ? refList(flat.refs) : undefined;
|
|
15308
|
+
delete flat.refs;
|
|
15309
|
+
nest(request, "intent", {
|
|
15310
|
+
...fields,
|
|
15311
|
+
...scope ? { scope } : {},
|
|
15312
|
+
...authorityRefs ? { authorityRefs } : {}
|
|
15313
|
+
}, { scope: defaultScope(objective), authorityRefs: [] });
|
|
15314
|
+
} else if (action === "progress") {
|
|
15315
|
+
const fields = take(flat, ["summary", "nextAction"]);
|
|
15316
|
+
const blockers = "blockers" in flat ? strings(flat.blockers) : undefined;
|
|
15317
|
+
delete flat.blockers;
|
|
15318
|
+
nest(request, "progress", {
|
|
15319
|
+
...fields,
|
|
15320
|
+
...Array.isArray(blockers) ? {
|
|
15321
|
+
blockers: blockers.map((item) => typeof item === "string" ? { reason: item, dependentAction: "", refs: [] } : item)
|
|
15322
|
+
} : {}
|
|
15323
|
+
}, { nextAction: null, blockers: [] });
|
|
15324
|
+
} else if (action === "close" && "decisionIds" in flat)
|
|
15325
|
+
flat.decisionIds = strings(flat.decisionIds);
|
|
15326
|
+
break;
|
|
15327
|
+
}
|
|
15328
|
+
case "policy": {
|
|
15329
|
+
if (action === "assess" || action === "preview") {
|
|
15330
|
+
const judgment = isObject2(decoded(request.judgment)) ? { ...decoded(request.judgment) } : {};
|
|
15331
|
+
for (const key of Object.keys(flat))
|
|
15332
|
+
if (!ENVELOPE.has(key)) {
|
|
15333
|
+
if (!(key in judgment))
|
|
15334
|
+
judgment[key] = decoded(flat[key]);
|
|
15335
|
+
delete flat[key];
|
|
15336
|
+
}
|
|
15337
|
+
request.judgment = judgment;
|
|
15338
|
+
}
|
|
15339
|
+
break;
|
|
15340
|
+
}
|
|
15341
|
+
case "evidence": {
|
|
15342
|
+
if (action === "record") {
|
|
15343
|
+
const review = "reviewSession" in flat ? decoded(flat.reviewSession) : undefined;
|
|
15344
|
+
delete flat.reviewSession;
|
|
15345
|
+
const fields = take(flat, [
|
|
15346
|
+
"kind",
|
|
15347
|
+
"claim",
|
|
15348
|
+
"result",
|
|
15349
|
+
"summary",
|
|
15350
|
+
"exitCode",
|
|
15351
|
+
"candidateId",
|
|
15352
|
+
"beforeCandidateId",
|
|
15353
|
+
"reviewContext"
|
|
15354
|
+
]);
|
|
15355
|
+
if ("refs" in flat)
|
|
15356
|
+
fields.refs = refList(flat.refs);
|
|
15357
|
+
if ("requirementIds" in flat)
|
|
15358
|
+
fields.requirementIds = strings(flat.requirementIds);
|
|
15359
|
+
delete flat.refs;
|
|
15360
|
+
delete flat.requirementIds;
|
|
15361
|
+
if (typeof review === "string" && review)
|
|
15362
|
+
fields.reviewContext = { kind: "host", host, handle: review };
|
|
15363
|
+
nest(request, "evidence", fields, {
|
|
15364
|
+
summary: "",
|
|
15365
|
+
refs: [],
|
|
15366
|
+
requirementIds: [],
|
|
15367
|
+
exitCode: null,
|
|
15368
|
+
reviewContext: null
|
|
15369
|
+
});
|
|
15370
|
+
}
|
|
15371
|
+
break;
|
|
15372
|
+
}
|
|
15373
|
+
case "finding": {
|
|
15374
|
+
if (action === "record") {
|
|
15375
|
+
const scope = scopeOf(flat, flat.claim);
|
|
15376
|
+
if (scope)
|
|
15377
|
+
request.scope = scope;
|
|
15378
|
+
else if (!("scope" in request))
|
|
15379
|
+
request.scope = defaultScope(request.claim);
|
|
15380
|
+
request.refs = "refs" in flat ? refList(flat.refs) : [];
|
|
15381
|
+
}
|
|
15382
|
+
break;
|
|
15383
|
+
}
|
|
15384
|
+
case "decision": {
|
|
15385
|
+
if (action === "record") {
|
|
15386
|
+
const binding = isObject2(decoded(request.binding)) ? decoded(request.binding) : {};
|
|
15387
|
+
const presented = flat.presented ?? binding.presented;
|
|
15388
|
+
const scope = scopeOf(flat, presented);
|
|
15389
|
+
const fields = take(flat, ["presented"]);
|
|
15390
|
+
const choice = flat.choice;
|
|
15391
|
+
delete flat.choice;
|
|
15392
|
+
const contentRefs = "refs" in flat ? refList(flat.refs) : undefined;
|
|
15393
|
+
delete flat.refs;
|
|
15394
|
+
nest(request, "binding", {
|
|
15395
|
+
...fields,
|
|
15396
|
+
...scope ? { scope } : {},
|
|
15397
|
+
...contentRefs ? { contentRefs } : {},
|
|
15398
|
+
...typeof choice === "string" ? { statedChoice: { ref: "choice", text: choice } } : {}
|
|
15399
|
+
}, { scope: defaultScope(presented), contentRefs: [] });
|
|
15400
|
+
}
|
|
15401
|
+
break;
|
|
15402
|
+
}
|
|
15403
|
+
case "worker": {
|
|
15404
|
+
if (action === "assign") {
|
|
15405
|
+
const objective = flat.objective;
|
|
15406
|
+
const scope = scopeOf(flat, objective);
|
|
15407
|
+
const fields = take(flat, ["role", "objective", "candidateId", "stoppingCondition"]);
|
|
15408
|
+
for (const key of ["decisionIds", "requirementIds"])
|
|
15409
|
+
if (key in flat) {
|
|
15410
|
+
fields[key] = strings(flat[key]);
|
|
15411
|
+
delete flat[key];
|
|
15412
|
+
}
|
|
15413
|
+
nest(request, "assignment", { ...fields, ...scope ? { scope } : {} }, { scope: defaultScope(objective), decisionIds: [], requirementIds: [] });
|
|
15414
|
+
} else if (action === "report") {
|
|
15415
|
+
const fields = take(flat, ["outcome", "summary"]);
|
|
15416
|
+
for (const key of ["evidenceIds", "findingIds"])
|
|
15417
|
+
if (key in flat) {
|
|
15418
|
+
fields[key] = strings(flat[key]);
|
|
15419
|
+
delete flat[key];
|
|
15420
|
+
}
|
|
15421
|
+
nest(request, "report", fields, { evidenceIds: [], findingIds: [] });
|
|
15422
|
+
}
|
|
15423
|
+
break;
|
|
15424
|
+
}
|
|
15425
|
+
case "state": {
|
|
15426
|
+
if ("bundle" in request)
|
|
15427
|
+
request.bundle = decoded(request.bundle);
|
|
15428
|
+
break;
|
|
15429
|
+
}
|
|
15430
|
+
}
|
|
15431
|
+
const defaults = {
|
|
15432
|
+
finding: { resolve: ["evidenceIds", "decisionIds"] },
|
|
15433
|
+
decision: { record: ["requirementIds"] }
|
|
15434
|
+
};
|
|
15435
|
+
for (const key of defaults[family]?.[String(action)] ?? [])
|
|
15436
|
+
if (!(key in request))
|
|
15437
|
+
request[key] = [];
|
|
15438
|
+
for (const key of LISTS)
|
|
15439
|
+
if (key in request && typeof request[key] === "string")
|
|
15440
|
+
request[key] = strings(request[key]);
|
|
15441
|
+
return request;
|
|
15442
|
+
}
|
|
15443
|
+
function parseAdvertisedOperation(family, input, host) {
|
|
15444
|
+
const request = normalizeOperationInput(family, input, host);
|
|
15445
|
+
if (isObject2(request) && request.schemaVersion !== SCHEMA_VERSION)
|
|
15446
|
+
return failure2("unsupported_version", "unsupported schema version", { operation: family });
|
|
15447
|
+
const parsed = advertisedOperationSchemas[family].safeParse(request);
|
|
15448
|
+
if (parsed.success)
|
|
15449
|
+
return success(null, null, request);
|
|
15450
|
+
return failure2("invalid_input", "operation input is invalid", {
|
|
15451
|
+
fields: parsed.error.issues.map((issue) => ({
|
|
15452
|
+
path: issue.code === "unrecognized_keys" ? issue.keys.join(".") : issue.path.join("."),
|
|
15453
|
+
reason: issue.message
|
|
15454
|
+
}))
|
|
15455
|
+
});
|
|
15456
|
+
}
|
|
15413
15457
|
// packages/workit-core/src/core/task-store.ts
|
|
15414
15458
|
import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
|
|
15415
15459
|
import * as fs7 from "node:fs";
|
|
@@ -16464,7 +16508,7 @@ class LogDamage extends Error {
|
|
|
16464
16508
|
this.upgrade = upgrade;
|
|
16465
16509
|
}
|
|
16466
16510
|
}
|
|
16467
|
-
var
|
|
16511
|
+
var isObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16468
16512
|
var parseEvent = (line, file, offset) => {
|
|
16469
16513
|
let value;
|
|
16470
16514
|
try {
|
|
@@ -16472,11 +16516,11 @@ var parseEvent = (line, file, offset) => {
|
|
|
16472
16516
|
} catch {
|
|
16473
16517
|
throw new LogDamage(`${file}: unreadable event at byte ${offset}`);
|
|
16474
16518
|
}
|
|
16475
|
-
if (!
|
|
16519
|
+
if (!isObject3(value) || typeof value.v !== "number")
|
|
16476
16520
|
throw new LogDamage(`${file}: malformed event at byte ${offset}`);
|
|
16477
16521
|
if (value.v > EVENT_VERSION)
|
|
16478
16522
|
throw new LogDamage(`${file}: event format v${value.v} was written by a newer Workit; upgrade Workit to read this task`, true);
|
|
16479
|
-
if (typeof value.seq !== "number" || !Number.isInteger(value.seq) || typeof value.type !== "string" || typeof value.id !== "string" || typeof value.task !== "string" || !
|
|
16523
|
+
if (typeof value.seq !== "number" || !Number.isInteger(value.seq) || typeof value.type !== "string" || typeof value.id !== "string" || typeof value.task !== "string" || !isObject3(value.data))
|
|
16480
16524
|
throw new LogDamage(`${file}: malformed event at byte ${offset}`);
|
|
16481
16525
|
return value;
|
|
16482
16526
|
};
|
|
@@ -16747,13 +16791,13 @@ var KNOWN_TYPES = new Set([
|
|
|
16747
16791
|
"decision.updated",
|
|
16748
16792
|
"worker.updated"
|
|
16749
16793
|
]);
|
|
16750
|
-
var
|
|
16751
|
-
var isKey = (value) =>
|
|
16794
|
+
var isObject4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16795
|
+
var isKey = (value) => isObject4(value) && typeof value.key === "string" && (value.kind === "branch" || value.kind === "detached" || value.kind === "dir") && (value.branch === null || typeof value.branch === "string");
|
|
16752
16796
|
var BLOB_REF = "$blob";
|
|
16753
|
-
var blobRef = (value) =>
|
|
16797
|
+
var blobRef = (value) => isObject4(value) && Object.keys(value).length === 1 && typeof value[BLOB_REF] === "string" && /^[0-9a-f]{64}$/.test(value[BLOB_REF]) ? value[BLOB_REF] : null;
|
|
16754
16798
|
var blobDigest = (value) => createHash3("sha256").update(JSON.stringify(value)).digest("hex");
|
|
16755
16799
|
var toBlob = (value, blobs) => {
|
|
16756
|
-
if (!
|
|
16800
|
+
if (!isObject4(value))
|
|
16757
16801
|
return value;
|
|
16758
16802
|
const digest = blobDigest(value);
|
|
16759
16803
|
blobs.write(digest, value);
|
|
@@ -16777,7 +16821,7 @@ var decode2 = (value, blobs) => {
|
|
|
16777
16821
|
return blobs.read(digest);
|
|
16778
16822
|
if (Array.isArray(value))
|
|
16779
16823
|
return value.map((item) => decode2(item, blobs));
|
|
16780
|
-
if (
|
|
16824
|
+
if (isObject4(value)) {
|
|
16781
16825
|
const out = {};
|
|
16782
16826
|
for (const [key, item] of Object.entries(value))
|
|
16783
16827
|
out[key] = decode2(item, blobs);
|
|
@@ -16796,7 +16840,7 @@ function reduce(state, event, blobs) {
|
|
|
16796
16840
|
}
|
|
16797
16841
|
let next = state;
|
|
16798
16842
|
if ("record" in data) {
|
|
16799
|
-
if (!
|
|
16843
|
+
if (!isObject4(data.record))
|
|
16800
16844
|
throw new LogDamage(`event ${event.seq} carries a malformed record`);
|
|
16801
16845
|
next = {
|
|
16802
16846
|
record: decode2(data.record, blobs),
|
|
@@ -16818,7 +16862,7 @@ function reduce(state, event, blobs) {
|
|
|
16818
16862
|
}
|
|
16819
16863
|
if ("key" in data)
|
|
16820
16864
|
next.key = isKey(data.key) ? data.key : null;
|
|
16821
|
-
if ("legacy" in data &&
|
|
16865
|
+
if ("legacy" in data && isObject4(data.legacy))
|
|
16822
16866
|
next.legacy = {
|
|
16823
16867
|
path: String(data.legacy.path ?? ""),
|
|
16824
16868
|
digest: String(data.legacy.digest ?? "")
|
|
@@ -16874,8 +16918,8 @@ var AUTO_COMPACT_BYTES = 2 * 1024 * 1024;
|
|
|
16874
16918
|
var TOMBSTONE = Symbol("tombstone");
|
|
16875
16919
|
var STALE_TEMPORARY_MS = 60 * 60000;
|
|
16876
16920
|
var INDEX_VERSION = 3;
|
|
16877
|
-
var
|
|
16878
|
-
var hostSession = (value) =>
|
|
16921
|
+
var isObject5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16922
|
+
var hostSession = (value) => isObject5(value) && value.kind === "host" && typeof value.host === "string" && typeof value.handle === "string" ? { host: value.host, handle: value.handle } : null;
|
|
16879
16923
|
var indexEntry = (task, key, legacy, file) => {
|
|
16880
16924
|
const sessions = [];
|
|
16881
16925
|
const intent = hostSession(task.intent.provenance.session);
|
|
@@ -17688,7 +17732,7 @@ class TaskStore {
|
|
|
17688
17732
|
readSnapshot(taskId) {
|
|
17689
17733
|
try {
|
|
17690
17734
|
const value = JSON.parse(fs7.readFileSync(this.snapshotPath(taskId), "utf8"));
|
|
17691
|
-
if (!
|
|
17735
|
+
if (!isObject5(value) || value.v !== 1 || value.task !== taskId || typeof value.seq !== "number" || typeof value.lastId !== "string" || typeof value.lastStart !== "number" || typeof value.end !== "number" || !isObject5(value.record))
|
|
17692
17736
|
return null;
|
|
17693
17737
|
return value;
|
|
17694
17738
|
} catch {
|
|
@@ -18034,11 +18078,11 @@ class TaskStore {
|
|
|
18034
18078
|
readIndex() {
|
|
18035
18079
|
try {
|
|
18036
18080
|
const value = JSON.parse(fs7.readFileSync(this.paths().index, "utf8"));
|
|
18037
|
-
if (!
|
|
18081
|
+
if (!isObject5(value) || value.version !== INDEX_VERSION || !isObject5(value.tasks))
|
|
18038
18082
|
return {};
|
|
18039
18083
|
const tasks = {};
|
|
18040
18084
|
for (const [id, entry] of Object.entries(value.tasks))
|
|
18041
|
-
if (
|
|
18085
|
+
if (isObject5(entry) && entry.id === id && typeof entry.file === "string" && typeof entry.status === "string" && typeof entry.updatedAt === "string" && typeof entry.workspaceId === "string" && Array.isArray(entry.sessions) && isObject5(entry.progress) && isObject5(entry.source))
|
|
18042
18086
|
tasks[id] = entry;
|
|
18043
18087
|
return tasks;
|
|
18044
18088
|
} catch {
|
|
@@ -18082,12 +18126,12 @@ class TaskStore {
|
|
|
18082
18126
|
return this.parseValue(value, schema, label);
|
|
18083
18127
|
}
|
|
18084
18128
|
parseValue(value, schema, label) {
|
|
18085
|
-
if (
|
|
18129
|
+
if (isObject5(value) && "schemaVersion" in value && value.schemaVersion !== SCHEMA_VERSION)
|
|
18086
18130
|
return failure2("unsupported_version", `unsupported ${label} schema version`);
|
|
18087
18131
|
const parsed = parseStoredRecord(schema, value);
|
|
18088
18132
|
if (parsed.success)
|
|
18089
18133
|
return success(null, null, parsed.data);
|
|
18090
|
-
const writerVersion =
|
|
18134
|
+
const writerVersion = isObject5(value) && isObject5(value.runtime) ? value.runtime.updatedWith : null;
|
|
18091
18135
|
if (parsed.critical.length > 0)
|
|
18092
18136
|
return failure2("recovery_required", `${label} record requires fields this Workit cannot read (${parsed.critical.join(", ")}); upgrade Workit before mutating this checkout`);
|
|
18093
18137
|
if (typeof writerVersion === "string" && isNewerVersion(writerVersion, runtimeVersion()))
|
|
@@ -18289,7 +18333,7 @@ class TaskStore {
|
|
|
18289
18333
|
}
|
|
18290
18334
|
try {
|
|
18291
18335
|
const value = JSON.parse(bytes);
|
|
18292
|
-
if (
|
|
18336
|
+
if (isObject5(value) && isObject5(value.store) && value.store.format === "workit-store")
|
|
18293
18337
|
return TOMBSTONE;
|
|
18294
18338
|
} catch {}
|
|
18295
18339
|
return { bytes };
|
|
@@ -18334,7 +18378,7 @@ class TaskStore {
|
|
|
18334
18378
|
isStub(file) {
|
|
18335
18379
|
try {
|
|
18336
18380
|
const value = JSON.parse(fs7.readFileSync(file, "utf8"));
|
|
18337
|
-
return
|
|
18381
|
+
return isObject5(value) && isObject5(value.store) && value.store.format === "workit-store";
|
|
18338
18382
|
} catch {
|
|
18339
18383
|
return false;
|
|
18340
18384
|
}
|
|
@@ -18505,10 +18549,10 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
|
18505
18549
|
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
18506
18550
|
|
|
18507
18551
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
18508
|
-
import { createHash as
|
|
18509
|
-
import * as
|
|
18510
|
-
import { spawnSync as
|
|
18511
|
-
import
|
|
18552
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
18553
|
+
import * as fs11 from "node:fs";
|
|
18554
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
18555
|
+
import path11 from "node:path";
|
|
18512
18556
|
|
|
18513
18557
|
// packages/workit-core/src/check-config.ts
|
|
18514
18558
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
@@ -18748,6 +18792,7 @@ var line = (run) => {
|
|
|
18748
18792
|
const value = run.stdout.trim();
|
|
18749
18793
|
return value ? value : null;
|
|
18750
18794
|
};
|
|
18795
|
+
var safeArg = (value) => value.length > 0 && !value.startsWith("-");
|
|
18751
18796
|
function headSha(cwd) {
|
|
18752
18797
|
return line(git2(cwd, ["rev-parse", "--verify", "-q", "HEAD^{commit}"]));
|
|
18753
18798
|
}
|
|
@@ -18839,79 +18884,677 @@ function worktreeSignal(cwd, options = {}) {
|
|
|
18839
18884
|
}
|
|
18840
18885
|
return `sig:${hash.digest("hex")}`;
|
|
18841
18886
|
}
|
|
18842
|
-
|
|
18843
|
-
|
|
18844
|
-
var digestBytes = (value) => createHash6("sha256").update(value).digest("hex");
|
|
18845
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path9.sep}`);
|
|
18846
|
-
var relative = (root, candidate) => path9.relative(root, candidate).split(path9.sep).join("/") || ".";
|
|
18847
|
-
var canonicalPath = (value) => {
|
|
18848
|
-
if (typeof value !== "string" || !value)
|
|
18887
|
+
function mergeBase(cwd, a, b) {
|
|
18888
|
+
if (!safeArg(a) || !safeArg(b))
|
|
18849
18889
|
return null;
|
|
18850
|
-
|
|
18890
|
+
return line(git2(cwd, ["merge-base", a, b]));
|
|
18891
|
+
}
|
|
18892
|
+
function patchId(cwd, base, head) {
|
|
18893
|
+
if (!safeArg(base) || !safeArg(head))
|
|
18851
18894
|
return null;
|
|
18852
|
-
const
|
|
18853
|
-
|
|
18895
|
+
const diff = git2(cwd, [
|
|
18896
|
+
"diff",
|
|
18897
|
+
"-U3",
|
|
18898
|
+
"--diff-algorithm=myers",
|
|
18899
|
+
"--indent-heuristic",
|
|
18900
|
+
"--no-color",
|
|
18901
|
+
"--no-ext-diff",
|
|
18902
|
+
"--no-textconv",
|
|
18903
|
+
"--no-renames",
|
|
18904
|
+
"--full-index",
|
|
18905
|
+
"--src-prefix=a/",
|
|
18906
|
+
"--dst-prefix=b/",
|
|
18907
|
+
`${base}...${head}`,
|
|
18908
|
+
"--"
|
|
18909
|
+
]);
|
|
18910
|
+
if (!diff.ok || !diff.stdout.trim())
|
|
18854
18911
|
return null;
|
|
18855
|
-
|
|
18856
|
-
|
|
18857
|
-
|
|
18858
|
-
|
|
18859
|
-
|
|
18860
|
-
|
|
18861
|
-
|
|
18912
|
+
const id = git2(cwd, ["patch-id", "--stable"], { input: diff.stdout });
|
|
18913
|
+
return line(id)?.split(/\s+/u)[0] ?? null;
|
|
18914
|
+
}
|
|
18915
|
+
function remoteNames(cwd) {
|
|
18916
|
+
return (git2(cwd, ["remote"]).stdout ?? "").split(/\r?\n/u).map((value) => value.trim()).filter(Boolean);
|
|
18917
|
+
}
|
|
18918
|
+
function resolveRef(cwd, ref) {
|
|
18919
|
+
if (!safeArg(ref))
|
|
18862
18920
|
return null;
|
|
18921
|
+
return line(git2(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
|
|
18922
|
+
}
|
|
18923
|
+
var config2 = (cwd, key) => line(git2(cwd, ["config", "--get", key]));
|
|
18924
|
+
function currentBranch(cwd) {
|
|
18925
|
+
return line(git2(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
|
|
18926
|
+
}
|
|
18927
|
+
function pushRemoteName(cwd, branch) {
|
|
18928
|
+
const name = branch ?? currentBranch(cwd);
|
|
18929
|
+
const remotes = remoteNames(cwd);
|
|
18930
|
+
const candidates = [
|
|
18931
|
+
name ? config2(cwd, `branch.${name}.pushRemote`) : null,
|
|
18932
|
+
config2(cwd, "remote.pushDefault"),
|
|
18933
|
+
name ? config2(cwd, `branch.${name}.remote`) : null
|
|
18934
|
+
];
|
|
18935
|
+
for (const candidate of candidates)
|
|
18936
|
+
if (candidate && candidate !== "." && remotes.includes(candidate))
|
|
18937
|
+
return candidate;
|
|
18938
|
+
if (remotes.includes("origin"))
|
|
18939
|
+
return "origin";
|
|
18940
|
+
return remotes.length === 1 ? remotes[0] : null;
|
|
18941
|
+
}
|
|
18942
|
+
|
|
18943
|
+
// packages/workit-core/src/core/policy/derive.ts
|
|
18944
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
18945
|
+
import path10 from "node:path";
|
|
18946
|
+
|
|
18947
|
+
// packages/workit-core/src/ledger.ts
|
|
18948
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
18949
|
+
import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
|
|
18950
|
+
import fs10 from "node:fs";
|
|
18951
|
+
import path9 from "node:path";
|
|
18952
|
+
var LEDGER_VERSION = 1;
|
|
18953
|
+
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
18954
|
+
var FAILING = new Set(["failed", "blocked"]);
|
|
18955
|
+
var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
|
|
18956
|
+
var PR_ROW_TYPES = new Set(["pr.created", "pr.status", "pr.merged"]);
|
|
18957
|
+
var err = (code, error, unblock) => ({
|
|
18958
|
+
ok: false,
|
|
18959
|
+
code,
|
|
18960
|
+
error,
|
|
18961
|
+
...unblock ? { unblock } : {}
|
|
18962
|
+
});
|
|
18963
|
+
var gitRun2 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
|
|
18964
|
+
const run = spawnSync5("git", args, {
|
|
18965
|
+
cwd,
|
|
18966
|
+
encoding: "utf8",
|
|
18967
|
+
timeout: timeoutMs,
|
|
18968
|
+
killSignal: "SIGKILL",
|
|
18969
|
+
windowsHide: true,
|
|
18970
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
18971
|
+
env: {
|
|
18972
|
+
...process.env,
|
|
18973
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
18974
|
+
GIT_OPTIONAL_LOCKS: "0",
|
|
18975
|
+
LC_ALL: "C",
|
|
18976
|
+
LANGUAGE: "C"
|
|
18977
|
+
}
|
|
18978
|
+
});
|
|
18863
18979
|
return {
|
|
18864
|
-
|
|
18865
|
-
|
|
18866
|
-
|
|
18980
|
+
ok: run.status === 0,
|
|
18981
|
+
stdout: run.stdout ?? "",
|
|
18982
|
+
stderr: run.error ? String(run.error.message) : run.stderr ?? ""
|
|
18867
18983
|
};
|
|
18868
18984
|
};
|
|
18869
|
-
var
|
|
18870
|
-
|
|
18871
|
-
const
|
|
18872
|
-
|
|
18873
|
-
return false;
|
|
18874
|
-
const paths = normalized.paths;
|
|
18875
|
-
return paths.some((item) => item === "." || value === item || value.startsWith(`${item}/`)) && !excluded(value, normalized.exclusions);
|
|
18985
|
+
var git3 = (cwd, args) => {
|
|
18986
|
+
const run = gitRun2(cwd, args);
|
|
18987
|
+
const value = run.ok ? run.stdout.trim() : "";
|
|
18988
|
+
return value ? value : null;
|
|
18876
18989
|
};
|
|
18877
|
-
var
|
|
18878
|
-
|
|
18879
|
-
|
|
18880
|
-
|
|
18881
|
-
|
|
18882
|
-
|
|
18883
|
-
|
|
18990
|
+
var safeRef = (value) => value.length > 0 && !value.startsWith("-");
|
|
18991
|
+
function resolveCommit(cwd, ref) {
|
|
18992
|
+
if (!safeRef(ref))
|
|
18993
|
+
return null;
|
|
18994
|
+
return git3(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
|
|
18995
|
+
}
|
|
18996
|
+
function branchHead(cwd, branch) {
|
|
18997
|
+
if (!safeRef(branch))
|
|
18998
|
+
return null;
|
|
18999
|
+
return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
|
|
19000
|
+
}
|
|
19001
|
+
function defaultBase(cwd) {
|
|
19002
|
+
const originHead = git3(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]);
|
|
19003
|
+
if (originHead && resolveCommit(cwd, originHead))
|
|
19004
|
+
return originHead;
|
|
19005
|
+
for (const candidate of ["origin/main", "origin/master", "main", "master"])
|
|
19006
|
+
if (resolveCommit(cwd, candidate))
|
|
19007
|
+
return candidate;
|
|
19008
|
+
return null;
|
|
19009
|
+
}
|
|
19010
|
+
function diffHash(cwd, base, head) {
|
|
19011
|
+
if (!safeRef(base) || !safeRef(head))
|
|
19012
|
+
return null;
|
|
19013
|
+
const diff = gitRun2(cwd, [
|
|
19014
|
+
"diff",
|
|
19015
|
+
"-U3",
|
|
19016
|
+
"--diff-algorithm=myers",
|
|
19017
|
+
"--indent-heuristic",
|
|
19018
|
+
"--no-color",
|
|
19019
|
+
"--no-ext-diff",
|
|
19020
|
+
"--no-textconv",
|
|
19021
|
+
"--no-renames",
|
|
19022
|
+
"--full-index",
|
|
19023
|
+
"--binary",
|
|
19024
|
+
"--src-prefix=a/",
|
|
19025
|
+
"--dst-prefix=b/",
|
|
19026
|
+
`${base}...${head}`,
|
|
19027
|
+
"--"
|
|
19028
|
+
], GIT_TIMEOUTS.worktree);
|
|
19029
|
+
if (!diff.ok || !diff.stdout)
|
|
19030
|
+
return null;
|
|
19031
|
+
const hash = createHash6("sha256");
|
|
19032
|
+
for (const line of diff.stdout.split(`
|
|
19033
|
+
`)) {
|
|
19034
|
+
if (line.startsWith("index "))
|
|
19035
|
+
continue;
|
|
19036
|
+
hash.update(line.startsWith("@@ ") ? "@@" : line);
|
|
19037
|
+
hash.update(`
|
|
19038
|
+
`);
|
|
18884
19039
|
}
|
|
18885
|
-
return
|
|
19040
|
+
return `sha256:${hash.digest("hex")}`;
|
|
19041
|
+
}
|
|
19042
|
+
function storeRoot(cwd) {
|
|
19043
|
+
const location = resolveStore(path9.resolve(cwd));
|
|
19044
|
+
if (location instanceof Error)
|
|
19045
|
+
return err("unavailable", location.message.replace(/; fix the repository.*$/u, ""), "fix the repository (git status should work here) or run from a non-git directory");
|
|
19046
|
+
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
19047
|
+
}
|
|
19048
|
+
function ledgerPath(cwd) {
|
|
19049
|
+
const root = storeRoot(cwd);
|
|
19050
|
+
return root.ok ? { ok: true, value: path9.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
19051
|
+
}
|
|
19052
|
+
var NETWORK_FS_TYPES = new Set([
|
|
19053
|
+
26985,
|
|
19054
|
+
20859,
|
|
19055
|
+
4266872130,
|
|
19056
|
+
4283649346,
|
|
19057
|
+
1702057286,
|
|
19058
|
+
1397113167,
|
|
19059
|
+
12805120,
|
|
19060
|
+
16914839,
|
|
19061
|
+
1196443219,
|
|
19062
|
+
198183888
|
|
19063
|
+
]);
|
|
19064
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19065
|
+
var str2 = (value) => typeof value === "string" ? value : null;
|
|
19066
|
+
var normalizeActor = (value) => {
|
|
19067
|
+
const actor = isRecord2(value) ? value : {};
|
|
19068
|
+
return {
|
|
19069
|
+
host: str2(actor.host) ?? "unknown",
|
|
19070
|
+
session: str2(actor.session),
|
|
19071
|
+
agentId: str2(actor.agentId)
|
|
19072
|
+
};
|
|
18886
19073
|
};
|
|
18887
|
-
|
|
18888
|
-
|
|
18889
|
-
|
|
18890
|
-
|
|
18891
|
-
|
|
18892
|
-
if (
|
|
18893
|
-
|
|
18894
|
-
|
|
18895
|
-
|
|
18896
|
-
|
|
18897
|
-
|
|
18898
|
-
|
|
18899
|
-
|
|
18900
|
-
|
|
18901
|
-
|
|
19074
|
+
function supersedeAllowed(by, target) {
|
|
19075
|
+
if (by.type !== target.type)
|
|
19076
|
+
return `a ${by.type} row cannot supersede a ${target.type} row`;
|
|
19077
|
+
if (!by.actor.session || by.actor.session !== target.actor.session)
|
|
19078
|
+
return "only the session that wrote a row can supersede it";
|
|
19079
|
+
if (by.type === "verdict" && by.self === true && target.self !== true)
|
|
19080
|
+
return "a self verdict cannot replace an independent verdict";
|
|
19081
|
+
return null;
|
|
19082
|
+
}
|
|
19083
|
+
function readLedger(cwd) {
|
|
19084
|
+
const file = ledgerPath(cwd);
|
|
19085
|
+
if (!file.ok)
|
|
19086
|
+
return file;
|
|
19087
|
+
let text = "";
|
|
19088
|
+
try {
|
|
19089
|
+
text = fs10.readFileSync(file.value, "utf8");
|
|
19090
|
+
} catch (error) {
|
|
19091
|
+
if (error.code === "ENOENT")
|
|
19092
|
+
return { ok: true, value: { path: file.value, rows: [], skipped: 0 } };
|
|
19093
|
+
return err("unavailable", `cannot read ${file.value}: ${error.message}`);
|
|
19094
|
+
}
|
|
19095
|
+
const rows = [];
|
|
19096
|
+
let skipped = 0;
|
|
19097
|
+
for (const raw of text.split(`
|
|
19098
|
+
`)) {
|
|
19099
|
+
const trimmed = raw.trim();
|
|
19100
|
+
if (!trimmed)
|
|
19101
|
+
continue;
|
|
19102
|
+
let parsed;
|
|
19103
|
+
try {
|
|
19104
|
+
parsed = JSON.parse(trimmed);
|
|
19105
|
+
} catch {
|
|
19106
|
+
skipped += 1;
|
|
19107
|
+
continue;
|
|
19108
|
+
}
|
|
19109
|
+
if (!isRecord2(parsed) || !str2(parsed.id) || !str2(parsed.type) || !str2(parsed.at)) {
|
|
19110
|
+
skipped += 1;
|
|
19111
|
+
continue;
|
|
19112
|
+
}
|
|
19113
|
+
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
19114
|
+
rows.push({
|
|
19115
|
+
...parsed,
|
|
19116
|
+
v: typeof parsed.v === "number" ? parsed.v : LEDGER_VERSION,
|
|
19117
|
+
id: parsed.id,
|
|
19118
|
+
at: parsed.at,
|
|
19119
|
+
type: parsed.type,
|
|
19120
|
+
actor: normalizeActor(parsed.actor),
|
|
19121
|
+
branch: str2(parsed.branch),
|
|
19122
|
+
head: str2(parsed.head),
|
|
19123
|
+
base: str2(parsed.base),
|
|
19124
|
+
baseSha: str2(parsed.baseSha),
|
|
19125
|
+
patchId: str2(parsed.patchId),
|
|
19126
|
+
diffHash: str2(parsed.diffHash),
|
|
19127
|
+
tree: str2(parsed.tree),
|
|
19128
|
+
dirty: typeof parsed.dirty === "boolean" ? parsed.dirty : null,
|
|
19129
|
+
...pr === undefined ? {} : { pr },
|
|
19130
|
+
...str2(parsed.supersedes) ? { supersedes: parsed.supersedes } : {},
|
|
19131
|
+
seq: rows.length + 1,
|
|
19132
|
+
superseded: false,
|
|
19133
|
+
supersedeIgnored: false
|
|
19134
|
+
});
|
|
19135
|
+
}
|
|
19136
|
+
const byId = new Map;
|
|
19137
|
+
for (const row of rows) {
|
|
19138
|
+
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
19139
|
+
if (target && supersedeAllowed(row, target) === null)
|
|
19140
|
+
target.superseded = true;
|
|
19141
|
+
else if (row.supersedes)
|
|
19142
|
+
row.supersedeIgnored = true;
|
|
19143
|
+
if (!byId.has(row.id))
|
|
19144
|
+
byId.set(row.id, row);
|
|
19145
|
+
}
|
|
19146
|
+
return { ok: true, value: { path: file.value, rows, skipped } };
|
|
19147
|
+
}
|
|
19148
|
+
function verdictBasis(verdict, current) {
|
|
19149
|
+
if (verdict.head && verdict.head === current.head)
|
|
19150
|
+
return "fresh";
|
|
19151
|
+
if (verdict.patchId && verdict.patchId === current.patchId && verdict.diffHash && verdict.diffHash === current.diffHash)
|
|
19152
|
+
return "carried";
|
|
19153
|
+
return "stale";
|
|
19154
|
+
}
|
|
19155
|
+
var isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.superseded;
|
|
19156
|
+
function trailerSessions(cwd, base, head) {
|
|
19157
|
+
if (!base || !head || !safeRef(base) || !safeRef(head))
|
|
19158
|
+
return [];
|
|
19159
|
+
const out = git3(cwd, [
|
|
19160
|
+
"log",
|
|
19161
|
+
"--format=%(trailers:key=Workit-Session,valueonly,separator=%x00)",
|
|
19162
|
+
`${base}..${head}`
|
|
19163
|
+
]);
|
|
19164
|
+
return (out ?? "").split(/[\0\n]/u).map((value) => value.trim()).filter(Boolean);
|
|
19165
|
+
}
|
|
19166
|
+
function authorSessions(cwd, rows, branch, range = { base: null, head: null }) {
|
|
19167
|
+
const sessions = new Set;
|
|
19168
|
+
for (const row of rows) {
|
|
19169
|
+
if (row.branch !== branch || row.type !== "commit.recorded" && row.type !== "task.opened")
|
|
19170
|
+
continue;
|
|
19171
|
+
const session = str2(row.session) ?? row.actor.session;
|
|
19172
|
+
if (session)
|
|
19173
|
+
sessions.add(session);
|
|
19174
|
+
}
|
|
19175
|
+
for (const session of trailerSessions(cwd, range.base, range.head))
|
|
19176
|
+
sessions.add(session);
|
|
19177
|
+
return sessions;
|
|
19178
|
+
}
|
|
19179
|
+
var independenceReasons = (row, authors) => {
|
|
19180
|
+
const reasons = [];
|
|
19181
|
+
if (!row.actor.session)
|
|
19182
|
+
reasons.push("no_session");
|
|
19183
|
+
else if (authors.has(row.actor.session))
|
|
19184
|
+
reasons.push("author_session");
|
|
19185
|
+
if (row.self === true && !reasons.includes("no_session"))
|
|
19186
|
+
reasons.push("self");
|
|
19187
|
+
return reasons;
|
|
19188
|
+
};
|
|
19189
|
+
function effectiveOf(rows, authors) {
|
|
19190
|
+
let effective = null;
|
|
19191
|
+
for (const row of rows) {
|
|
19192
|
+
if (!effective) {
|
|
19193
|
+
effective = row;
|
|
19194
|
+
continue;
|
|
19195
|
+
}
|
|
19196
|
+
const rowIndependent = independenceReasons(row, authors).length === 0;
|
|
19197
|
+
const effIndependent = independenceReasons(effective, authors).length === 0;
|
|
19198
|
+
if (effIndependent && !rowIndependent)
|
|
19199
|
+
continue;
|
|
19200
|
+
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session)
|
|
19201
|
+
continue;
|
|
19202
|
+
effective = row;
|
|
19203
|
+
}
|
|
19204
|
+
return effective;
|
|
19205
|
+
}
|
|
19206
|
+
function restackCarries(rows, branch, from, to) {
|
|
19207
|
+
if (!from || !to)
|
|
19208
|
+
return false;
|
|
19209
|
+
const edges = new Map;
|
|
19210
|
+
for (const row of rows)
|
|
19211
|
+
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
19212
|
+
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
19213
|
+
const seen = new Set([from]);
|
|
19214
|
+
const queue = [from];
|
|
19215
|
+
while (queue.length) {
|
|
19216
|
+
const next = queue.shift();
|
|
19217
|
+
if (next === to)
|
|
19218
|
+
return true;
|
|
19219
|
+
for (const target of edges.get(next) ?? [])
|
|
19220
|
+
if (!seen.has(target)) {
|
|
19221
|
+
seen.add(target);
|
|
19222
|
+
queue.push(target);
|
|
19223
|
+
}
|
|
19224
|
+
}
|
|
19225
|
+
return false;
|
|
19226
|
+
}
|
|
19227
|
+
function checkVerdicts(cwd, branch, rows) {
|
|
19228
|
+
const head = branchHead(cwd, branch);
|
|
19229
|
+
const fallbackBase = defaultBase(cwd);
|
|
19230
|
+
const keyCache = new Map;
|
|
19231
|
+
const keyFor = (base) => {
|
|
19232
|
+
if (!base || !head)
|
|
19233
|
+
return { patchId: null, diffHash: null };
|
|
19234
|
+
let key = keyCache.get(base);
|
|
19235
|
+
if (!key) {
|
|
19236
|
+
key = { patchId: patchId(cwd, base, head), diffHash: diffHash(cwd, base, head) };
|
|
19237
|
+
keyCache.set(base, key);
|
|
19238
|
+
}
|
|
19239
|
+
return key;
|
|
19240
|
+
};
|
|
19241
|
+
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head });
|
|
19242
|
+
const byKind = new Map;
|
|
19243
|
+
for (const row of rows)
|
|
19244
|
+
if (row.branch === branch && isLiveVerdict(row)) {
|
|
19245
|
+
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
19246
|
+
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
19247
|
+
}
|
|
19248
|
+
const verdicts = [];
|
|
19249
|
+
for (const [kind, list] of byKind) {
|
|
19250
|
+
const row = effectiveOf(list, authors);
|
|
19251
|
+
if (!row)
|
|
19252
|
+
continue;
|
|
19253
|
+
let basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
|
|
19254
|
+
if (basis === "stale" && restackCarries(rows, branch, row.head, head))
|
|
19255
|
+
basis = "carried";
|
|
19256
|
+
const current = basis !== "stale";
|
|
19257
|
+
const independence = independenceReasons(row, authors);
|
|
19258
|
+
const reasons = [
|
|
19259
|
+
...current ? [] : ["stale"],
|
|
19260
|
+
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
19261
|
+
...independence
|
|
19262
|
+
];
|
|
19263
|
+
verdicts.push({
|
|
19264
|
+
kind,
|
|
19265
|
+
basis,
|
|
19266
|
+
current,
|
|
19267
|
+
independent: independence.length === 0,
|
|
19268
|
+
accepted: reasons.length === 0,
|
|
19269
|
+
reasons,
|
|
19270
|
+
verdict: row
|
|
19271
|
+
});
|
|
19272
|
+
}
|
|
19273
|
+
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
19274
|
+
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
19275
|
+
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
19276
|
+
const newestAccepted = verdicts.findLast((entry) => entry.accepted) ?? null;
|
|
19277
|
+
const accepted = newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
19278
|
+
accepted: false,
|
|
19279
|
+
verdict: failing?.verdict ?? null,
|
|
19280
|
+
reasons: failing ? ["failing_verdict"] : newestCurrent?.reasons ?? ["no_verdict"]
|
|
19281
|
+
};
|
|
19282
|
+
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
19283
|
+
return {
|
|
19284
|
+
branch,
|
|
19285
|
+
head,
|
|
19286
|
+
review: accepted.accepted ? "verified" : selfEntry ? "self-reviewed" : "unreviewed",
|
|
19287
|
+
selfVerdict: accepted.accepted ? null : selfEntry?.verdict ?? null,
|
|
19288
|
+
base: newestCurrent?.verdict.base ?? fallbackBase,
|
|
19289
|
+
patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
|
|
19290
|
+
current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
|
|
19291
|
+
accepted,
|
|
19292
|
+
verdicts,
|
|
19293
|
+
authors: [...authors].toSorted()
|
|
19294
|
+
};
|
|
19295
|
+
}
|
|
19296
|
+
var OBSERVED_ONLY = new Set(["check", ...PR_ROW_TYPES]);
|
|
19297
|
+
|
|
19298
|
+
// packages/workit-core/src/core/policy/derive.ts
|
|
19299
|
+
var RULES = {
|
|
19300
|
+
test: "check:test",
|
|
19301
|
+
self: "verdict:self",
|
|
19302
|
+
nonAuthor: "verdict:non-author",
|
|
19303
|
+
verified: "verdict:verified",
|
|
19304
|
+
product: "decision:product",
|
|
19305
|
+
plan: "plan"
|
|
19306
|
+
};
|
|
19307
|
+
var derived2 = (spec) => ({
|
|
19308
|
+
...spec,
|
|
19309
|
+
id: requirementId({
|
|
19310
|
+
ruleId: spec.ruleId,
|
|
19311
|
+
scope: spec.scope,
|
|
19312
|
+
satisfaction: spec.satisfaction,
|
|
19313
|
+
before: spec.before,
|
|
19314
|
+
dependentAction: spec.dependentAction
|
|
19315
|
+
})
|
|
19316
|
+
});
|
|
19317
|
+
var sorted = (values) => [...new Set(values)].toSorted();
|
|
19318
|
+
var normalizedScope = (scope) => ({
|
|
19319
|
+
description: scope.description,
|
|
19320
|
+
paths: sorted(scope.paths),
|
|
19321
|
+
exclusions: sorted(scope.exclusions)
|
|
19322
|
+
});
|
|
19323
|
+
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
19324
|
+
const out = [];
|
|
19325
|
+
const base = { scope, dependentAction: null };
|
|
19326
|
+
if (judgment.behaviorChange)
|
|
19327
|
+
out.push(derived2({
|
|
19328
|
+
...base,
|
|
19329
|
+
ruleId: RULES.test,
|
|
19330
|
+
dimension: "testing",
|
|
19331
|
+
reason: "Behavior changes: the configured test check must pass on the final tree.",
|
|
19332
|
+
satisfaction: "Run `workit check test` on the final tree.",
|
|
19333
|
+
before: "close",
|
|
19334
|
+
acceptanceAllowed: false
|
|
19335
|
+
}));
|
|
19336
|
+
if (judgment.behaviorChange && judgment.riskTier === "normal")
|
|
19337
|
+
out.push(verification === "independent" ? derived2({
|
|
19338
|
+
...base,
|
|
19339
|
+
ruleId: RULES.nonAuthor,
|
|
19340
|
+
dimension: "review",
|
|
19341
|
+
reason: "This workspace requires independent verification: a session that did not author the change records the verdict.",
|
|
19342
|
+
satisfaction: "An independent session records a passing verdict: `workit ledger verdict verified --how <what it exercised>`.",
|
|
19343
|
+
before: "close",
|
|
19344
|
+
acceptanceAllowed: false
|
|
19345
|
+
}) : derived2({
|
|
19346
|
+
...base,
|
|
19347
|
+
ruleId: RULES.self,
|
|
19348
|
+
dimension: "review",
|
|
19349
|
+
reason: "A behavior change needs a recorded verdict; the author's own is allowed and is shown as self-reviewed, never verified.",
|
|
19350
|
+
satisfaction: "After an observed passing `workit check test`, record `workit ledger verdict tests-verified --self --how <what you ran>` (or have a verifier record one).",
|
|
19351
|
+
before: "close",
|
|
19352
|
+
acceptanceAllowed: false
|
|
19353
|
+
}));
|
|
19354
|
+
if (judgment.riskTier === "high")
|
|
19355
|
+
out.push(derived2({
|
|
19356
|
+
...base,
|
|
19357
|
+
ruleId: RULES.verified,
|
|
19358
|
+
dimension: "review",
|
|
19359
|
+
reason: "High risk: an independent verifier must exercise the live surface.",
|
|
19360
|
+
satisfaction: "An independent verifier runs the real surface and records `workit ledger verdict verified --kind live --how <what it exercised>`.",
|
|
19361
|
+
before: "close",
|
|
19362
|
+
acceptanceAllowed: false
|
|
19363
|
+
}));
|
|
19364
|
+
if (judgment.productChoiceOpen)
|
|
19365
|
+
out.push(derived2({
|
|
19366
|
+
...base,
|
|
19367
|
+
ruleId: RULES.product,
|
|
19368
|
+
dimension: "decisions",
|
|
19369
|
+
reason: "A product or preference choice is open; the user decides it before code is written.",
|
|
19370
|
+
satisfaction: 'Ask the user (with your recommendation), then record the answer: `workit ledger decision "<choice>" --why "<reason>"`.',
|
|
19371
|
+
before: "write",
|
|
19372
|
+
acceptanceAllowed: false
|
|
19373
|
+
}));
|
|
19374
|
+
if (judgment.needsPlan || judgment.riskTier === "high")
|
|
19375
|
+
out.push(derived2({
|
|
19376
|
+
...base,
|
|
19377
|
+
ruleId: RULES.plan,
|
|
19378
|
+
dimension: "artifacts",
|
|
19379
|
+
reason: judgment.needsPlan ? "The work needs a written plan or spec before implementation." : "High-risk work needs a written plan before implementation.",
|
|
19380
|
+
satisfaction: "Write the plan or spec (e.g. docs/plans/<slug>.md; docs and Markdown writes stay allowed), then record it: `workit policy assess --ref <path>`.",
|
|
19381
|
+
before: "write",
|
|
19382
|
+
acceptanceAllowed: true
|
|
19383
|
+
}));
|
|
19384
|
+
for (const constraint of constraints)
|
|
19385
|
+
for (const obligation of constraint.requires)
|
|
19386
|
+
out.push(derived2({
|
|
19387
|
+
ruleId: `project-constraint:${constraint.id}:${obligation.kind}:${sha256({
|
|
19388
|
+
kind: obligation.kind,
|
|
19389
|
+
scope: normalizedScope(obligation.scope),
|
|
19390
|
+
refs: obligation.refs.map((ref) => JSON.stringify(ref)).toSorted(),
|
|
19391
|
+
method: obligation.method,
|
|
19392
|
+
before: obligation.before,
|
|
19393
|
+
dependentAction: obligation.dependentAction
|
|
19394
|
+
})}`,
|
|
19395
|
+
dimension: obligation.method ?? (obligation.kind === "check" ? "verification" : obligation.kind === "decision" ? "decisions" : "challenge"),
|
|
19396
|
+
scope: obligation.scope,
|
|
19397
|
+
reason: `${constraint.kind} constraint requires ${obligation.kind}: ${constraint.statement}`,
|
|
19398
|
+
satisfaction: `Satisfy the constraint obligation using its referenced ${obligation.kind}.`,
|
|
19399
|
+
before: obligation.before,
|
|
19400
|
+
dependentAction: obligation.dependentAction,
|
|
19401
|
+
acceptanceAllowed: constraint.acceptanceAllowed && obligation.kind !== "decision"
|
|
19402
|
+
}));
|
|
19403
|
+
const seen = new Set;
|
|
19404
|
+
return out.filter((item) => seen.has(item.id) ? false : (seen.add(item.id), true));
|
|
19405
|
+
}
|
|
19406
|
+
var latestJudgment = (task) => task.judgments?.at(-1)?.data ?? null;
|
|
19407
|
+
var ledgerView = (root) => {
|
|
19408
|
+
let branch;
|
|
19409
|
+
let rows;
|
|
19410
|
+
return {
|
|
19411
|
+
branch: () => branch === undefined ? branch = currentBranch(root) : branch,
|
|
19412
|
+
rows: () => {
|
|
19413
|
+
if (rows === undefined) {
|
|
19414
|
+
const read = readLedger(root);
|
|
19415
|
+
rows = read.ok ? read.value.rows : [];
|
|
19416
|
+
}
|
|
19417
|
+
return rows;
|
|
19418
|
+
}
|
|
19419
|
+
};
|
|
19420
|
+
};
|
|
19421
|
+
var productDecision = (task, ledger) => {
|
|
19422
|
+
const decisions = task.decisions.filter((entry) => entry.data.revoked === null && (entry.data.response === "approved" || entry.data.response === "stated") && entry.data.purpose !== "limitation");
|
|
19423
|
+
if (decisions.length)
|
|
19424
|
+
return {
|
|
19425
|
+
met: true,
|
|
19426
|
+
reason: "the user's decision is recorded on the task",
|
|
19427
|
+
decisionIds: decisions.map((entry) => entry.id)
|
|
19428
|
+
};
|
|
19429
|
+
const since = Date.parse(task.createdAt);
|
|
19430
|
+
const branch = ledger.branch();
|
|
19431
|
+
const row = ledger.rows().findLast((item) => item.type === "decision" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
|
|
19432
|
+
return row ? { met: true, reason: `ledger decision ${row.id} records the choice`, decisionIds: [] } : {
|
|
19433
|
+
met: false,
|
|
19434
|
+
reason: 'open product choice: ask the user, then `workit ledger decision "<choice>" --why "<reason>"` (or re-judge productChoiceOpen=false if it is not open)',
|
|
19435
|
+
decisionIds: []
|
|
19436
|
+
};
|
|
19437
|
+
};
|
|
19438
|
+
var planRecorded = (task, root) => {
|
|
19439
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync3(path10.resolve(root, ref.path)));
|
|
19440
|
+
if (cited)
|
|
19441
|
+
return {
|
|
19442
|
+
met: true,
|
|
19443
|
+
reason: `plan recorded: ${cited.kind === "file" ? cited.path : cited.kind === "external" ? cited.url : ""}`,
|
|
19444
|
+
decisionIds: []
|
|
19445
|
+
};
|
|
19446
|
+
const artifact = task.evidence.some((entry) => entry.data.kind === "artifact" && entry.data.result === "passed");
|
|
19447
|
+
return artifact ? { met: true, reason: "a passing artifact records the plan", decisionIds: [] } : {
|
|
19448
|
+
met: false,
|
|
19449
|
+
reason: "no plan recorded: write it (docs/ and Markdown writes stay allowed), then `workit policy assess --ref <path>`",
|
|
19450
|
+
decisionIds: []
|
|
19451
|
+
};
|
|
19452
|
+
};
|
|
19453
|
+
var verdict = (requirement, root, ledger) => {
|
|
19454
|
+
const branch = ledger.branch();
|
|
19455
|
+
if (!branch)
|
|
19456
|
+
return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
|
|
19457
|
+
const check = checkVerdicts(root, branch, ledger.rows());
|
|
19458
|
+
const failing = check.accepted.reasons.includes("failing_verdict");
|
|
19459
|
+
const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
|
|
19460
|
+
const met = !failing && (requirement.ruleId === RULES.verified ? check.verdicts.find((entry) => entry.accepted && entry.kind === "live" && String(entry.verdict.result) === "verified") : requirement.ruleId === RULES.nonAuthor ? check.verdicts.find((entry) => entry.accepted && strong(entry)) : check.verdicts.find((entry) => entry.current && strong(entry)));
|
|
19461
|
+
if (met)
|
|
19462
|
+
return {
|
|
19463
|
+
met: true,
|
|
19464
|
+
reason: `${met.accepted ? "accepted independent" : "self-reviewed (author's own)"} ${String(met.verdict.result)} verdict ${met.verdict.id}`,
|
|
19465
|
+
decisionIds: []
|
|
19466
|
+
};
|
|
19467
|
+
const why = failing ? "a current independent verdict fails" : check.verdicts.some((entry) => entry.current && entry.verdict.result === "type-check-only") ? "type-check-only does not prove a behavior change" : (check.accepted.reasons.join(", ") || "no_verdict").replace(/_/g, " ");
|
|
19468
|
+
const unblock = requirement.ruleId === RULES.self ? "record your own verdict after an observed `workit check test`: `workit ledger verdict tests-verified --self --how <what you ran>` (shown as self-reviewed), or have a verifier record one" : `an independent session records \`workit ledger verdict verified${requirement.ruleId === RULES.verified ? " --kind live" : ""} --how <what it exercised>\``;
|
|
19469
|
+
return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
|
|
19470
|
+
};
|
|
19471
|
+
function derivedRuleStatus(task, requirement, root, ledger) {
|
|
19472
|
+
switch (requirement.ruleId) {
|
|
19473
|
+
case RULES.product:
|
|
19474
|
+
return productDecision(task, ledger);
|
|
19475
|
+
case RULES.plan:
|
|
19476
|
+
return planRecorded(task, root);
|
|
19477
|
+
case RULES.self:
|
|
19478
|
+
case RULES.nonAuthor:
|
|
19479
|
+
case RULES.verified:
|
|
19480
|
+
return verdict(requirement, root, ledger);
|
|
19481
|
+
default:
|
|
19482
|
+
return null;
|
|
19483
|
+
}
|
|
19484
|
+
}
|
|
19485
|
+
|
|
19486
|
+
// packages/workit-core/src/core/task-evaluation.ts
|
|
19487
|
+
var digestBytes = (value) => createHash7("sha256").update(value).digest("hex");
|
|
19488
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path11.sep}`);
|
|
19489
|
+
var relative = (root, candidate) => path11.relative(root, candidate).split(path11.sep).join("/") || ".";
|
|
19490
|
+
var canonicalPath = (value) => {
|
|
19491
|
+
if (typeof value !== "string" || !value)
|
|
19492
|
+
return null;
|
|
19493
|
+
if (value.includes("\\") || value.split("/").includes(".."))
|
|
19494
|
+
return null;
|
|
19495
|
+
const normalized = path11.posix.normalize(value);
|
|
19496
|
+
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
19497
|
+
return null;
|
|
19498
|
+
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
19499
|
+
};
|
|
19500
|
+
var canonicalScope = (scope) => {
|
|
19501
|
+
const paths = scope.paths.length ? scope.paths : ["."];
|
|
19502
|
+
const normalizedPaths = paths.map(canonicalPath);
|
|
19503
|
+
const normalizedExclusions = scope.exclusions.map(canonicalPath);
|
|
19504
|
+
if (normalizedPaths.some((value) => value === null) || normalizedExclusions.some((value) => value === null))
|
|
19505
|
+
return null;
|
|
19506
|
+
return {
|
|
19507
|
+
description: scope.description,
|
|
19508
|
+
paths: [...new Set(normalizedPaths)],
|
|
19509
|
+
exclusions: [...new Set(normalizedExclusions)]
|
|
19510
|
+
};
|
|
19511
|
+
};
|
|
19512
|
+
var excluded = (value, exclusions) => exclusions.some((item) => item === value || item !== "." && value.startsWith(`${item}/`));
|
|
19513
|
+
var scopeMatches = (value, scope) => {
|
|
19514
|
+
const normalized = canonicalScope(scope);
|
|
19515
|
+
if (!normalized)
|
|
19516
|
+
return false;
|
|
19517
|
+
const paths = normalized.paths;
|
|
19518
|
+
return paths.some((item) => item === "." || value === item || value.startsWith(`${item}/`)) && !excluded(value, normalized.exclusions);
|
|
19519
|
+
};
|
|
19520
|
+
var pathRelevant = (value, scope) => scopeMatches(value, scope);
|
|
19521
|
+
var compareCodeUnits2 = (left, right) => {
|
|
19522
|
+
const length = Math.min(left.length, right.length);
|
|
19523
|
+
for (let index = 0;index < length; index += 1) {
|
|
19524
|
+
const difference = left.charCodeAt(index) - right.charCodeAt(index);
|
|
19525
|
+
if (difference !== 0)
|
|
19526
|
+
return difference;
|
|
19527
|
+
}
|
|
19528
|
+
return left.length - right.length;
|
|
19529
|
+
};
|
|
19530
|
+
var gitPaths = (root) => {
|
|
19531
|
+
const result = spawnSync6("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
19532
|
+
cwd: root,
|
|
19533
|
+
encoding: "buffer"
|
|
19534
|
+
});
|
|
19535
|
+
if (result.status !== 0) {
|
|
19536
|
+
const stderr = result.stderr?.toString("utf8") ?? "";
|
|
19537
|
+
return {
|
|
19538
|
+
paths: [],
|
|
19539
|
+
uncertain: !/not a git repository/i.test(stderr),
|
|
19540
|
+
git: !/not a git repository/i.test(stderr)
|
|
19541
|
+
};
|
|
19542
|
+
}
|
|
19543
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path11.sep).join("/")) : [];
|
|
19544
|
+
const stagedDeleted = spawnSync6("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
18902
19545
|
if (stagedDeleted.status !== 0) {
|
|
18903
19546
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
18904
19547
|
if (!/not a git repository/i.test(stderr))
|
|
18905
19548
|
return { paths, uncertain: true, git: true };
|
|
18906
19549
|
} else if (stagedDeleted.stdout) {
|
|
18907
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
19550
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path11.sep).join("/")));
|
|
18908
19551
|
}
|
|
18909
19552
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
18910
19553
|
};
|
|
18911
19554
|
var walk = (root, directory, output) => {
|
|
18912
19555
|
let names;
|
|
18913
19556
|
try {
|
|
18914
|
-
names =
|
|
19557
|
+
names = fs11.readdirSync(directory);
|
|
18915
19558
|
} catch {
|
|
18916
19559
|
return true;
|
|
18917
19560
|
}
|
|
@@ -18919,10 +19562,10 @@ var walk = (root, directory, output) => {
|
|
|
18919
19562
|
for (const name of names) {
|
|
18920
19563
|
if (name === ".git" || name === ".workit")
|
|
18921
19564
|
continue;
|
|
18922
|
-
const target =
|
|
19565
|
+
const target = path11.join(directory, name);
|
|
18923
19566
|
let stat;
|
|
18924
19567
|
try {
|
|
18925
|
-
stat =
|
|
19568
|
+
stat = fs11.lstatSync(target);
|
|
18926
19569
|
} catch {
|
|
18927
19570
|
uncertain = true;
|
|
18928
19571
|
continue;
|
|
@@ -18937,14 +19580,14 @@ var walk = (root, directory, output) => {
|
|
|
18937
19580
|
var scopeRoots = (root, scope) => {
|
|
18938
19581
|
const roots = [];
|
|
18939
19582
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
18940
|
-
const target =
|
|
19583
|
+
const target = path11.resolve(root, item);
|
|
18941
19584
|
if (!inside(root, target))
|
|
18942
19585
|
return failure2("invalid_input", `candidate scope escapes checkout: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} is outside the checkout` }] });
|
|
18943
19586
|
let ancestor = target;
|
|
18944
|
-
while (!
|
|
18945
|
-
ancestor =
|
|
19587
|
+
while (!fs11.existsSync(ancestor) && ancestor !== root)
|
|
19588
|
+
ancestor = path11.dirname(ancestor);
|
|
18946
19589
|
try {
|
|
18947
|
-
if (!inside(root,
|
|
19590
|
+
if (!inside(root, fs11.realpathSync(ancestor)))
|
|
18948
19591
|
return failure2("invalid_input", `candidate scope escapes checkout through a symlink: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} resolves outside the checkout` }] });
|
|
18949
19592
|
} catch {
|
|
18950
19593
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -18956,7 +19599,7 @@ var scopeRoots = (root, scope) => {
|
|
|
18956
19599
|
function captureCandidate(root, scope, environment = []) {
|
|
18957
19600
|
let checkout;
|
|
18958
19601
|
try {
|
|
18959
|
-
checkout =
|
|
19602
|
+
checkout = fs11.realpathSync(root);
|
|
18960
19603
|
} catch {
|
|
18961
19604
|
return failure2("invalid_input", "candidate root does not exist");
|
|
18962
19605
|
}
|
|
@@ -18974,11 +19617,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
18974
19617
|
if (item === ".git" || item === ".workit")
|
|
18975
19618
|
continue;
|
|
18976
19619
|
names.add(item);
|
|
18977
|
-
if (!
|
|
19620
|
+
if (!fs11.existsSync(target))
|
|
18978
19621
|
continue;
|
|
18979
19622
|
let stat;
|
|
18980
19623
|
try {
|
|
18981
|
-
stat =
|
|
19624
|
+
stat = fs11.lstatSync(target);
|
|
18982
19625
|
} catch {
|
|
18983
19626
|
uncertain = true;
|
|
18984
19627
|
continue;
|
|
@@ -18994,17 +19637,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
18994
19637
|
for (const item of [...names].toSorted()) {
|
|
18995
19638
|
if (!scopeMatches(item, normalizedScope))
|
|
18996
19639
|
continue;
|
|
18997
|
-
const target =
|
|
19640
|
+
const target = path11.join(checkout, item);
|
|
18998
19641
|
try {
|
|
18999
|
-
const stat =
|
|
19642
|
+
const stat = fs11.lstatSync(target);
|
|
19000
19643
|
if (stat.isSymbolicLink()) {
|
|
19001
|
-
const link =
|
|
19644
|
+
const link = fs11.readlinkSync(target);
|
|
19002
19645
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
19003
19646
|
} else if (stat.isFile()) {
|
|
19004
19647
|
files.push({
|
|
19005
19648
|
path: item,
|
|
19006
19649
|
kind: "file",
|
|
19007
|
-
digest: digestBytes(
|
|
19650
|
+
digest: digestBytes(fs11.readFileSync(target)),
|
|
19008
19651
|
executable: (stat.mode & 73) !== 0
|
|
19009
19652
|
});
|
|
19010
19653
|
} else if (!stat.isDirectory()) {
|
|
@@ -19029,7 +19672,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
19029
19672
|
const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
|
|
19030
19673
|
if (values.some(({ value }) => value === null))
|
|
19031
19674
|
completeness = "uncertain";
|
|
19032
|
-
const headResult =
|
|
19675
|
+
const headResult = spawnSync6("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
|
|
19033
19676
|
if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
|
|
19034
19677
|
completeness = "uncertain";
|
|
19035
19678
|
const head = headResult.status === 0 ? headResult.stdout.trim() : null;
|
|
@@ -19169,7 +19812,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
19169
19812
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
19170
19813
|
let root;
|
|
19171
19814
|
try {
|
|
19172
|
-
root =
|
|
19815
|
+
root = fs11.realpathSync(checkoutRoot);
|
|
19173
19816
|
} catch {
|
|
19174
19817
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
19175
19818
|
}
|
|
@@ -19178,23 +19821,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
19178
19821
|
continue;
|
|
19179
19822
|
if (!reference.digest)
|
|
19180
19823
|
return failure2("invalid_input", "document references require a byte digest");
|
|
19181
|
-
const target =
|
|
19182
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
19824
|
+
const target = path11.resolve(root, reference.path);
|
|
19825
|
+
if (target !== root && !target.startsWith(`${root}${path11.sep}`))
|
|
19183
19826
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
19184
19827
|
fields: [
|
|
19185
19828
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
19186
19829
|
]
|
|
19187
19830
|
});
|
|
19188
19831
|
try {
|
|
19189
|
-
const below =
|
|
19832
|
+
const below = path11.relative(root, target);
|
|
19190
19833
|
let current = root;
|
|
19191
|
-
for (const segment of below.split(
|
|
19834
|
+
for (const segment of below.split(path11.sep)) {
|
|
19192
19835
|
if (!segment)
|
|
19193
19836
|
continue;
|
|
19194
|
-
current =
|
|
19195
|
-
const stat =
|
|
19196
|
-
const real =
|
|
19197
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
19837
|
+
current = path11.join(current, segment);
|
|
19838
|
+
const stat = fs11.lstatSync(current);
|
|
19839
|
+
const real = fs11.realpathSync(current);
|
|
19840
|
+
if (real !== root && !real.startsWith(`${root}${path11.sep}`))
|
|
19198
19841
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
19199
19842
|
fields: [
|
|
19200
19843
|
{
|
|
@@ -19210,7 +19853,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
19210
19853
|
if (current !== target && !stat.isDirectory())
|
|
19211
19854
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
19212
19855
|
}
|
|
19213
|
-
if (digestBytes(
|
|
19856
|
+
if (digestBytes(fs11.readFileSync(target)) !== reference.digest)
|
|
19214
19857
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
19215
19858
|
} catch {
|
|
19216
19859
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -19295,7 +19938,35 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
19295
19938
|
decisionIds: [],
|
|
19296
19939
|
reason: "stored policy version is unsupported; reassessment is required"
|
|
19297
19940
|
}));
|
|
19941
|
+
const ledger = ledgerView(checkoutRoot);
|
|
19298
19942
|
return (task.policy?.requirements ?? []).map((requirement) => {
|
|
19943
|
+
const derived = derivedRuleStatus(task, requirement, checkoutRoot, ledger);
|
|
19944
|
+
if (derived) {
|
|
19945
|
+
if (derived.met)
|
|
19946
|
+
return {
|
|
19947
|
+
requirementId: requirement.id,
|
|
19948
|
+
status: "satisfied",
|
|
19949
|
+
evidenceIds: [],
|
|
19950
|
+
decisionIds: derived.decisionIds,
|
|
19951
|
+
reason: derived.reason
|
|
19952
|
+
};
|
|
19953
|
+
const waived = requirement.acceptanceAllowed ? applicableDecision(task, workspace, requirement, checkoutRoot) : [];
|
|
19954
|
+
if (waived.length)
|
|
19955
|
+
return {
|
|
19956
|
+
requirementId: requirement.id,
|
|
19957
|
+
status: "accepted_limitation",
|
|
19958
|
+
evidenceIds: [],
|
|
19959
|
+
decisionIds: waived.map((decision) => task.decisions.find((entry) => entry.data === decision).id),
|
|
19960
|
+
reason: "an applicable approved limitation permits the missing evidence"
|
|
19961
|
+
};
|
|
19962
|
+
return {
|
|
19963
|
+
requirementId: requirement.id,
|
|
19964
|
+
status: "unsatisfied",
|
|
19965
|
+
evidenceIds: [],
|
|
19966
|
+
decisionIds: [],
|
|
19967
|
+
reason: derived.reason
|
|
19968
|
+
};
|
|
19969
|
+
}
|
|
19299
19970
|
const gate = observedGate(requirement) ? evaluateObservedGate(task, requirement, statuses, configFor()) : null;
|
|
19300
19971
|
if (gate?.satisfied.length)
|
|
19301
19972
|
return {
|
|
@@ -19710,14 +20381,14 @@ var gitContext = (workspaceRoot, paths = []) => {
|
|
|
19710
20381
|
import {
|
|
19711
20382
|
copyFileSync as copyFileSync2,
|
|
19712
20383
|
cpSync as cpSync2,
|
|
19713
|
-
existsSync as
|
|
20384
|
+
existsSync as existsSync5,
|
|
19714
20385
|
mkdirSync as mkdirSync2,
|
|
19715
20386
|
readFileSync as readFileSync3,
|
|
19716
20387
|
readdirSync as readdirSync3,
|
|
19717
20388
|
writeFileSync as writeFileSync2
|
|
19718
20389
|
} from "node:fs";
|
|
19719
20390
|
import os2 from "node:os";
|
|
19720
|
-
import
|
|
20391
|
+
import path12 from "node:path";
|
|
19721
20392
|
|
|
19722
20393
|
// packages/workit-core/src/core/boundary.ts
|
|
19723
20394
|
import { statSync as statSync2 } from "node:fs";
|
|
@@ -19776,7 +20447,7 @@ var mergePreset = (preset, input = {}, current = {
|
|
|
19776
20447
|
protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
|
|
19777
20448
|
};
|
|
19778
20449
|
};
|
|
19779
|
-
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
20450
|
+
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path12.join(process.env.XDG_CONFIG_HOME || path12.join(os2.homedir(), ".config"), "workit");
|
|
19780
20451
|
var migratedDir = null;
|
|
19781
20452
|
var migrationFailed = false;
|
|
19782
20453
|
var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
@@ -19786,12 +20457,12 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
19786
20457
|
migratedDir = dir;
|
|
19787
20458
|
return dir;
|
|
19788
20459
|
}
|
|
19789
|
-
const legacy =
|
|
19790
|
-
if (!
|
|
20460
|
+
const legacy = path12.join(process.env.XDG_CONFIG_HOME || path12.join(os2.homedir(), ".config"), "workflow-toolkit");
|
|
20461
|
+
if (!existsSync5(legacy)) {
|
|
19791
20462
|
migratedDir = dir;
|
|
19792
20463
|
return dir;
|
|
19793
20464
|
}
|
|
19794
|
-
if (!migrationFailed &&
|
|
20465
|
+
if (!migrationFailed && existsSync5(dir)) {
|
|
19795
20466
|
migratedDir = dir;
|
|
19796
20467
|
return dir;
|
|
19797
20468
|
}
|
|
@@ -19799,9 +20470,9 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
19799
20470
|
mkdirSync2(dir, { recursive: true });
|
|
19800
20471
|
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
19801
20472
|
for (const entry of readdirSync3(legacy, { withFileTypes: true })) {
|
|
19802
|
-
const src =
|
|
19803
|
-
const dest =
|
|
19804
|
-
if (
|
|
20473
|
+
const src = path12.join(legacy, entry.name);
|
|
20474
|
+
const dest = path12.join(dir, entry.name);
|
|
20475
|
+
if (existsSync5(dest))
|
|
19805
20476
|
continue;
|
|
19806
20477
|
try {
|
|
19807
20478
|
if (entry.isDirectory())
|
|
@@ -19872,7 +20543,7 @@ var parseConfigResult = (raw, file) => {
|
|
|
19872
20543
|
};
|
|
19873
20544
|
};
|
|
19874
20545
|
var readConfigTyped = (dir) => {
|
|
19875
|
-
const file =
|
|
20546
|
+
const file = path12.join(dir ?? configDir(), "config.json");
|
|
19876
20547
|
return parseConfigResult(readSafe(file), file);
|
|
19877
20548
|
};
|
|
19878
20549
|
var readConfig = () => {
|
|
@@ -19881,6 +20552,8 @@ var readConfig = () => {
|
|
|
19881
20552
|
throw new Error(result.error);
|
|
19882
20553
|
return result.config ?? DEFAULTS;
|
|
19883
20554
|
};
|
|
20555
|
+
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path12.join(configDir(), "vcs.json");
|
|
20556
|
+
var branchGlobPattern = (glob) => new RegExp(`^${glob.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`, "i");
|
|
19884
20557
|
var resolveBranchPolicy = (config, workspace) => {
|
|
19885
20558
|
const wp = workspace?.branchPolicy ?? {};
|
|
19886
20559
|
const preset = Object.hasOwn(PRESETS, wp.preset) ? wp.preset : config.branchPolicy?.preset ?? "gitflow";
|
|
@@ -19888,7 +20561,7 @@ var resolveBranchPolicy = (config, workspace) => {
|
|
|
19888
20561
|
allowed: wp.allowed,
|
|
19889
20562
|
protectedNames: wp.protected
|
|
19890
20563
|
}, config);
|
|
19891
|
-
const allowed = merged.allowed.map(
|
|
20564
|
+
const allowed = merged.allowed.map(branchGlobPattern);
|
|
19892
20565
|
return {
|
|
19893
20566
|
preset,
|
|
19894
20567
|
allowed,
|
|
@@ -19905,20 +20578,11 @@ var COMMIT_PRESETS = [
|
|
|
19905
20578
|
"custom",
|
|
19906
20579
|
"auto"
|
|
19907
20580
|
];
|
|
19908
|
-
var resolveCommitPolicy = (config, workspace) => {
|
|
19909
|
-
const wp = workspace?.commitPolicy ?? {};
|
|
19910
|
-
if (COMMIT_PRESETS.includes(String(wp.preset ?? ""))) {
|
|
19911
|
-
return {
|
|
19912
|
-
preset: wp.preset,
|
|
19913
|
-
...typeof wp.pattern === "string" ? { pattern: wp.pattern } : {}
|
|
19914
|
-
};
|
|
19915
|
-
}
|
|
19916
|
-
return config.commitPolicy ?? { preset: "conventional" };
|
|
19917
|
-
};
|
|
19918
20581
|
|
|
19919
20582
|
// packages/workit-core/src/core/workspaces.ts
|
|
20583
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
19920
20584
|
import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
|
|
19921
|
-
import
|
|
20585
|
+
import path13 from "node:path";
|
|
19922
20586
|
var nonBlank = string2().refine((value) => value.trim().length > 0, "must not be blank");
|
|
19923
20587
|
var branchPreset = custom((value) => typeof value === "string" && Object.hasOwn(PRESETS, value), "unsupported branch preset");
|
|
19924
20588
|
var commitPreset = custom((value) => typeof value === "string" && COMMIT_PRESETS.includes(value), "unsupported commit preset");
|
|
@@ -19952,26 +20616,31 @@ var versionSourceSchema = discriminatedUnion("kind", [
|
|
|
19952
20616
|
object({ kind: literal("manual") }).strict()
|
|
19953
20617
|
]);
|
|
19954
20618
|
var releaseTrackSchema = object({
|
|
19955
|
-
strategy: branchPreset,
|
|
20619
|
+
strategy: branchPreset.optional(),
|
|
19956
20620
|
productionBranch: nonBlank,
|
|
19957
20621
|
integrationBranch: nonBlank,
|
|
19958
|
-
naming: object({
|
|
19959
|
-
|
|
19960
|
-
|
|
19961
|
-
|
|
19962
|
-
|
|
19963
|
-
|
|
19964
|
-
|
|
19965
|
-
|
|
20622
|
+
naming: object({
|
|
20623
|
+
feature: nonBlank.optional(),
|
|
20624
|
+
release: nonBlank.optional(),
|
|
20625
|
+
hotfix: nonBlank.optional()
|
|
20626
|
+
}).passthrough().optional(),
|
|
20627
|
+
baseBranch: nonBlank.optional(),
|
|
20628
|
+
mergeBackBranches: array(nonBlank).optional(),
|
|
20629
|
+
pullRequestTarget: nonBlank.optional(),
|
|
20630
|
+
tagNamespace: string2().optional(),
|
|
20631
|
+
versionSource: versionSourceSchema.optional(),
|
|
20632
|
+
requiredChecks: array(nonBlank).optional(),
|
|
20633
|
+
critical: array(nonBlank).optional()
|
|
20634
|
+
}).passthrough().superRefine((track, context) => {
|
|
19966
20635
|
for (const [field, values] of [
|
|
19967
|
-
["mergeBackBranches", track.mergeBackBranches],
|
|
19968
|
-
["requiredChecks", track.requiredChecks]
|
|
20636
|
+
["mergeBackBranches", track.mergeBackBranches ?? []],
|
|
20637
|
+
["requiredChecks", track.requiredChecks ?? []]
|
|
19969
20638
|
]) {
|
|
19970
20639
|
if (new Set(values).size !== values.length) {
|
|
19971
20640
|
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
19972
20641
|
}
|
|
19973
20642
|
}
|
|
19974
|
-
if (track.versionSource
|
|
20643
|
+
if (track.versionSource?.kind === "package-json" && (path13.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
|
|
19975
20644
|
context.addIssue({
|
|
19976
20645
|
code: "custom",
|
|
19977
20646
|
path: ["versionSource", "path"],
|
|
@@ -19990,6 +20659,7 @@ var workspaceConfigSchema = object({
|
|
|
19990
20659
|
}).passthrough().optional(),
|
|
19991
20660
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
19992
20661
|
defaultEndpoint: _enum(["commit", "pr"]).optional(),
|
|
20662
|
+
verification: _enum(["self", "independent"]).optional(),
|
|
19993
20663
|
autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
19994
20664
|
kind,
|
|
19995
20665
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -20030,6 +20700,72 @@ var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).opt
|
|
|
20030
20700
|
names.add(workspace.name);
|
|
20031
20701
|
}
|
|
20032
20702
|
});
|
|
20703
|
+
var workspacesRevision = (contents) => createHash8("sha256").update(contents ?? "<missing>").digest("hex");
|
|
20704
|
+
var validateWorkspacesDocument = (parsed, file) => {
|
|
20705
|
+
if (!isConfigObject(parsed)) {
|
|
20706
|
+
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
20707
|
+
}
|
|
20708
|
+
const validated = workspacesFileSchema.safeParse(parsed, { reportInput: true });
|
|
20709
|
+
if (!validated.success) {
|
|
20710
|
+
const detail = validated.error.issues.map((issue) => {
|
|
20711
|
+
const field = String(issue.path[issue.path.length - 1] ?? "");
|
|
20712
|
+
const attempted = ["provider", "preset", "strategy"].includes(field) && typeof issue.input === "string" ? ` (${JSON.stringify(issue.input)})` : "";
|
|
20713
|
+
return `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}${attempted}`;
|
|
20714
|
+
}).join("; ");
|
|
20715
|
+
return {
|
|
20716
|
+
status: "invalid",
|
|
20717
|
+
path: file,
|
|
20718
|
+
entries: [],
|
|
20719
|
+
error: `${file} has invalid workspace configuration: ${detail}`
|
|
20720
|
+
};
|
|
20721
|
+
}
|
|
20722
|
+
for (const [index, workspace] of (validated.data.workspaces ?? []).entries()) {
|
|
20723
|
+
const glob = validateWorkspaceGlob(workspace.glob);
|
|
20724
|
+
if (!glob.ok) {
|
|
20725
|
+
return {
|
|
20726
|
+
status: "invalid",
|
|
20727
|
+
path: file,
|
|
20728
|
+
entries: [],
|
|
20729
|
+
error: `${file} workspaces.${index}.glob: ${glob.error}`
|
|
20730
|
+
};
|
|
20731
|
+
}
|
|
20732
|
+
}
|
|
20733
|
+
return {
|
|
20734
|
+
status: "valid",
|
|
20735
|
+
path: file,
|
|
20736
|
+
entries: validated.data.workspaces ?? [],
|
|
20737
|
+
document: parsed
|
|
20738
|
+
};
|
|
20739
|
+
};
|
|
20740
|
+
var readWorkspacesResult = (dir = configDir()) => {
|
|
20741
|
+
const file = path13.join(dir, "workspaces.json");
|
|
20742
|
+
let raw;
|
|
20743
|
+
try {
|
|
20744
|
+
raw = readFileSync4(file, "utf8");
|
|
20745
|
+
} catch (error) {
|
|
20746
|
+
if (error.code === "ENOENT")
|
|
20747
|
+
return { status: "missing", path: file, entries: [], revision: workspacesRevision(null) };
|
|
20748
|
+
return {
|
|
20749
|
+
status: "malformed",
|
|
20750
|
+
path: file,
|
|
20751
|
+
entries: [],
|
|
20752
|
+
error: `${file} could not be read: ${error instanceof Error ? error.message : String(error)}`
|
|
20753
|
+
};
|
|
20754
|
+
}
|
|
20755
|
+
let parsed;
|
|
20756
|
+
try {
|
|
20757
|
+
parsed = JSON.parse(raw);
|
|
20758
|
+
} catch {
|
|
20759
|
+
return {
|
|
20760
|
+
status: "malformed",
|
|
20761
|
+
path: file,
|
|
20762
|
+
entries: [],
|
|
20763
|
+
revision: workspacesRevision(raw),
|
|
20764
|
+
error: `${file} is not valid JSON`
|
|
20765
|
+
};
|
|
20766
|
+
}
|
|
20767
|
+
return { ...validateWorkspacesDocument(parsed, file), revision: workspacesRevision(raw) };
|
|
20768
|
+
};
|
|
20033
20769
|
var globToRegExp = (glob) => {
|
|
20034
20770
|
let out = "";
|
|
20035
20771
|
for (let i = 0;i < glob.length; i++) {
|
|
@@ -20127,6 +20863,30 @@ var selectWorkspaceMatch = (matches, cwd, workspaceName) => {
|
|
|
20127
20863
|
}
|
|
20128
20864
|
return mostSpecific[0];
|
|
20129
20865
|
};
|
|
20866
|
+
var resolveWorkspaceFromEntries = (cwd, entries, workspaceName) => {
|
|
20867
|
+
const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
|
|
20868
|
+
const matches = [];
|
|
20869
|
+
for (const ws of entries) {
|
|
20870
|
+
const glob = ws.glob.replaceAll("\\", "/");
|
|
20871
|
+
const canonical = canonicalGlob(glob);
|
|
20872
|
+
let matched = false;
|
|
20873
|
+
for (const target of targets) {
|
|
20874
|
+
if (matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target)) {
|
|
20875
|
+
matched = true;
|
|
20876
|
+
break;
|
|
20877
|
+
}
|
|
20878
|
+
}
|
|
20879
|
+
if (matched)
|
|
20880
|
+
matches.push(ws);
|
|
20881
|
+
}
|
|
20882
|
+
return selectWorkspaceMatch(matches, cwd, workspaceName) ?? null;
|
|
20883
|
+
};
|
|
20884
|
+
var resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
|
|
20885
|
+
const result = readWorkspacesResult(dir);
|
|
20886
|
+
if (result.status === "malformed" || result.status === "invalid")
|
|
20887
|
+
throw new Error(result.error);
|
|
20888
|
+
return resolveWorkspaceFromEntries(cwd, result.entries, workspaceName);
|
|
20889
|
+
};
|
|
20130
20890
|
var runtimeWorkspacePolicySchema = (kind) => {
|
|
20131
20891
|
const key = kind === "branch" ? "branchPolicy" : kind === "commit" ? "commitPolicy" : "vcs";
|
|
20132
20892
|
const policySchema = kind === "branch" ? workspaceBranchPolicySchema : kind === "commit" ? workspaceCommitPolicySchema : object({
|
|
@@ -20148,8 +20908,8 @@ var resolveRuntimeWorkspaceVcs = (cwd) => resolveRuntimeWorkspaceCandidate(cwd,
|
|
|
20148
20908
|
var runtimeWorkspaceIndexSchema = object({
|
|
20149
20909
|
workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
20150
20910
|
}).passthrough();
|
|
20151
|
-
var
|
|
20152
|
-
const file =
|
|
20911
|
+
var matchRuntimeWorkspace = (cwd) => {
|
|
20912
|
+
const file = path13.join(configDir(), "workspaces.json");
|
|
20153
20913
|
let raw;
|
|
20154
20914
|
try {
|
|
20155
20915
|
raw = readFileSync4(file, "utf8");
|
|
@@ -20183,8 +20943,17 @@ var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
|
20183
20943
|
});
|
|
20184
20944
|
const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
|
|
20185
20945
|
const selected = selectWorkspaceMatch(matches, cwd, workspaceName);
|
|
20186
|
-
|
|
20946
|
+
return selected ? { file, selected } : null;
|
|
20947
|
+
};
|
|
20948
|
+
var resolveRuntimeReleaseTracks = (cwd) => {
|
|
20949
|
+
const matched = matchRuntimeWorkspace(cwd);
|
|
20950
|
+
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
20951
|
+
};
|
|
20952
|
+
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
20953
|
+
const matched = matchRuntimeWorkspace(cwd);
|
|
20954
|
+
if (!matched)
|
|
20187
20955
|
return null;
|
|
20956
|
+
const { file, selected } = matched;
|
|
20188
20957
|
const policy = runtimeWorkspacePolicySchema(kind).safeParse(selected);
|
|
20189
20958
|
if (!policy.success) {
|
|
20190
20959
|
const detail = policy.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspace"}: ${issue.message}`).join("; ");
|
|
@@ -20210,15 +20979,410 @@ var resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
|
|
|
20210
20979
|
source: profilePolicy ? `profile:${profileName}` : workspacePolicy ? `workspace:${candidate.name}` : "user-default"
|
|
20211
20980
|
};
|
|
20212
20981
|
};
|
|
20982
|
+
function workspaceDefaultTarget(cwd, globalDefault) {
|
|
20983
|
+
const ws = resolveRuntimeWorkspaceVcs(cwd);
|
|
20984
|
+
const wp = ws?.branchPolicy ?? {};
|
|
20985
|
+
const hasWorkspacePolicy = typeof wp.preset === "string" && Object.hasOwn(PRESETS, wp.preset);
|
|
20986
|
+
const selected = resolveRuntimeWorkspacePolicy(cwd, "branch", process.env.WORKFLOW_PROFILE?.trim() || undefined);
|
|
20987
|
+
const policyDefault = resolveBranchPolicy(readConfig(), selected.policy ? { branchPolicy: selected.policy } : null).defaultTargetBranch;
|
|
20988
|
+
return String(ws?.vcs?.defaultTargetBranch ?? (hasWorkspacePolicy ? policyDefault : globalDefault ?? policyDefault) ?? "develop");
|
|
20989
|
+
}
|
|
20213
20990
|
|
|
20214
20991
|
// packages/workit-core/src/core/vcs-config.ts
|
|
20215
|
-
import
|
|
20216
|
-
import
|
|
20217
|
-
import { spawnSync as
|
|
20218
|
-
|
|
20992
|
+
import fs12 from "node:fs";
|
|
20993
|
+
import path14 from "node:path";
|
|
20994
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
20995
|
+
|
|
20996
|
+
// packages/workit-core/src/core/release-tracks.ts
|
|
20997
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
20998
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
20999
|
+
var KNOWN_FIELDS = new Set([
|
|
21000
|
+
"strategy",
|
|
21001
|
+
"productionBranch",
|
|
21002
|
+
"integrationBranch",
|
|
21003
|
+
"naming",
|
|
21004
|
+
"baseBranch",
|
|
21005
|
+
"mergeBackBranches",
|
|
21006
|
+
"pullRequestTarget",
|
|
21007
|
+
"tagNamespace",
|
|
21008
|
+
"versionSource",
|
|
21009
|
+
"requiredChecks",
|
|
21010
|
+
"critical"
|
|
21011
|
+
]);
|
|
21012
|
+
var BRANCH_FIELDS = ["productionBranch", "integrationBranch", "baseBranch", "pullRequestTarget"];
|
|
21013
|
+
var PLACEHOLDER = /\{[^}]*\}/gu;
|
|
21014
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21015
|
+
var branchName = (value) => typeof value === "string" && value.trim() && !value.trim().startsWith("-") ? value.trim() : null;
|
|
21016
|
+
var namedBranches = (entry) => [
|
|
21017
|
+
...BRANCH_FIELDS.map((field) => branchName(entry[field])),
|
|
21018
|
+
...Array.isArray(entry.mergeBackBranches) ? entry.mergeBackBranches.map(branchName) : []
|
|
21019
|
+
].filter((name) => name !== null);
|
|
21020
|
+
var literalOf = (template) => template.replace(PLACEHOLDER, "").replace(/[*/]/gu, "").trim();
|
|
21021
|
+
function readReleaseTracks(raw) {
|
|
21022
|
+
const out = { tracks: [], issues: [], error: null, protectedBranches: [] };
|
|
21023
|
+
if (raw === undefined || raw === null)
|
|
21024
|
+
return out;
|
|
21025
|
+
if (!isRecord3(raw)) {
|
|
21026
|
+
out.issues.push("releaseTracks is not an object; ignored");
|
|
21027
|
+
return out;
|
|
21028
|
+
}
|
|
21029
|
+
const fatal = [];
|
|
21030
|
+
const protectedNames = new Set;
|
|
21031
|
+
for (const [name, entry] of Object.entries(raw).toSorted(([a], [b]) => a < b ? -1 : 1)) {
|
|
21032
|
+
const label = `release track ${JSON.stringify(name)}`;
|
|
21033
|
+
if (!isRecord3(entry)) {
|
|
21034
|
+
out.issues.push(`${label} is not an object; ignored`);
|
|
21035
|
+
continue;
|
|
21036
|
+
}
|
|
21037
|
+
for (const branch of namedBranches(entry))
|
|
21038
|
+
protectedNames.add(branch);
|
|
21039
|
+
if (!name.trim()) {
|
|
21040
|
+
out.issues.push("a release track with a blank name was ignored");
|
|
21041
|
+
continue;
|
|
21042
|
+
}
|
|
21043
|
+
let critical = [];
|
|
21044
|
+
if (Array.isArray(entry.critical))
|
|
21045
|
+
critical = entry.critical.filter((item) => typeof item === "string");
|
|
21046
|
+
else if (entry.critical !== undefined)
|
|
21047
|
+
out.issues.push(`${label} critical: must be a list of field names; ignored`);
|
|
21048
|
+
const isCritical = (field) => critical.some((item) => field === item || field.startsWith(`${item}.`));
|
|
21049
|
+
const problem = (field, message) => {
|
|
21050
|
+
if (isCritical(field))
|
|
21051
|
+
fatal.push(`${label} ${field}: ${message}`);
|
|
21052
|
+
else
|
|
21053
|
+
out.issues.push(`${label} ${field}: ${message}`);
|
|
21054
|
+
};
|
|
21055
|
+
for (const key of Object.keys(entry))
|
|
21056
|
+
if (!KNOWN_FIELDS.has(key))
|
|
21057
|
+
problem(key, "unknown field, ignored");
|
|
21058
|
+
const productionBranch = branchName(entry.productionBranch);
|
|
21059
|
+
const integrationBranch = branchName(entry.integrationBranch);
|
|
21060
|
+
if (!productionBranch)
|
|
21061
|
+
problem("productionBranch", "missing or not a branch name");
|
|
21062
|
+
if (!integrationBranch)
|
|
21063
|
+
problem("integrationBranch", "missing or not a branch name");
|
|
21064
|
+
const optionalBranch = (field) => {
|
|
21065
|
+
if (entry[field] === undefined)
|
|
21066
|
+
return null;
|
|
21067
|
+
const value = branchName(entry[field]);
|
|
21068
|
+
if (!value)
|
|
21069
|
+
problem(field, "not a branch name; using the integration branch");
|
|
21070
|
+
return value;
|
|
21071
|
+
};
|
|
21072
|
+
const baseBranch = optionalBranch("baseBranch");
|
|
21073
|
+
const pullRequestTarget = optionalBranch("pullRequestTarget");
|
|
21074
|
+
let mergeBackBranches = [];
|
|
21075
|
+
if (entry.mergeBackBranches !== undefined) {
|
|
21076
|
+
const list = Array.isArray(entry.mergeBackBranches) ? entry.mergeBackBranches.map(branchName) : [null];
|
|
21077
|
+
if (list.some((item) => item === null))
|
|
21078
|
+
problem("mergeBackBranches", "must be a list of branch names; ignored");
|
|
21079
|
+
else
|
|
21080
|
+
mergeBackBranches = [...new Set(list)];
|
|
21081
|
+
}
|
|
21082
|
+
const naming = {};
|
|
21083
|
+
if (entry.naming !== undefined) {
|
|
21084
|
+
if (!isRecord3(entry.naming))
|
|
21085
|
+
problem("naming", "not an object; ignored");
|
|
21086
|
+
else
|
|
21087
|
+
for (const kind of ["feature", "release", "hotfix"]) {
|
|
21088
|
+
const template = entry.naming[kind];
|
|
21089
|
+
if (template === undefined)
|
|
21090
|
+
continue;
|
|
21091
|
+
if (typeof template !== "string" || !template.trim())
|
|
21092
|
+
problem(`naming.${kind}`, "not a template string; ignored");
|
|
21093
|
+
else if (!literalOf(template))
|
|
21094
|
+
problem(`naming.${kind}`, "needs literal text besides placeholders; ignored");
|
|
21095
|
+
else
|
|
21096
|
+
naming[kind] = template.trim();
|
|
21097
|
+
}
|
|
21098
|
+
}
|
|
21099
|
+
if (!productionBranch || !integrationBranch) {
|
|
21100
|
+
out.issues.push(`${label} was skipped: it needs a productionBranch and integrationBranch`);
|
|
21101
|
+
continue;
|
|
21102
|
+
}
|
|
21103
|
+
out.tracks.push({
|
|
21104
|
+
name,
|
|
21105
|
+
productionBranch,
|
|
21106
|
+
integrationBranch,
|
|
21107
|
+
baseBranch: baseBranch ?? integrationBranch,
|
|
21108
|
+
pullRequestTarget: pullRequestTarget ?? integrationBranch,
|
|
21109
|
+
mergeBackBranches,
|
|
21110
|
+
naming
|
|
21111
|
+
});
|
|
21112
|
+
}
|
|
21113
|
+
out.protectedBranches = [...protectedNames];
|
|
21114
|
+
if (fatal.length)
|
|
21115
|
+
out.error = `workspace releaseTracks cannot be read by this Workit (${fatal.join("; ")}); upgrade Workit or fix the track`;
|
|
21116
|
+
return out;
|
|
21117
|
+
}
|
|
21118
|
+
var IMPLICIT = "";
|
|
21119
|
+
var ownedBy = (tracks, branch) => tracks.filter((track) => [track.integrationBranch, track.productionBranch, track.baseBranch].includes(branch));
|
|
21120
|
+
var shortRef = (ref, remotes) => {
|
|
21121
|
+
const bare = ref.replace(/^refs\/heads\//u, "").replace(/^refs\/remotes\/[^/]+\//u, "");
|
|
21122
|
+
const remote = remotes.find((name) => bare.startsWith(`${name}/`));
|
|
21123
|
+
return remote ? bare.slice(remote.length + 1) : bare;
|
|
21124
|
+
};
|
|
21125
|
+
var MAX_RECORDED_HOPS = 10;
|
|
21126
|
+
function resolveReleaseTrack(input) {
|
|
21127
|
+
const tracks = [...input.tracks];
|
|
21128
|
+
const issues = [...input.issues ?? []];
|
|
21129
|
+
const requested = input.requested?.trim();
|
|
21130
|
+
if (tracks.length === 0) {
|
|
21131
|
+
if (requested)
|
|
21132
|
+
return {
|
|
21133
|
+
status: "invalid",
|
|
21134
|
+
error: `no release tracks configured for this workspace; --track ${requested} cannot apply`,
|
|
21135
|
+
issues
|
|
21136
|
+
};
|
|
21137
|
+
return { status: "none", issues };
|
|
21138
|
+
}
|
|
21139
|
+
const names = tracks.map((track) => track.name).join(", ");
|
|
21140
|
+
let envProblem = null;
|
|
21141
|
+
const done = (track, source, detail, warnings = [], ambiguous = null) => ({
|
|
21142
|
+
status: "resolved",
|
|
21143
|
+
track: track && track.name !== IMPLICIT ? track : null,
|
|
21144
|
+
source,
|
|
21145
|
+
detail,
|
|
21146
|
+
warnings: [...issues, ...envProblem ? [envProblem] : [], ...warnings],
|
|
21147
|
+
tracks,
|
|
21148
|
+
blocking: envProblem ?? ambiguous
|
|
21149
|
+
});
|
|
21150
|
+
if (requested) {
|
|
21151
|
+
const named = tracks.find((track) => track.name === requested);
|
|
21152
|
+
if (!named)
|
|
21153
|
+
return {
|
|
21154
|
+
status: "invalid",
|
|
21155
|
+
error: `no release track ${JSON.stringify(requested)}; configured: ${names}`,
|
|
21156
|
+
issues
|
|
21157
|
+
};
|
|
21158
|
+
return done(named, "flag", `track ${named.name} was requested explicitly`);
|
|
21159
|
+
}
|
|
21160
|
+
const owned = (branch) => branch !== null && tracks.some((track) => [
|
|
21161
|
+
track.integrationBranch,
|
|
21162
|
+
track.productionBranch,
|
|
21163
|
+
track.baseBranch,
|
|
21164
|
+
track.pullRequestTarget
|
|
21165
|
+
].includes(branch));
|
|
21166
|
+
const implicit = input.defaultBranch && !owned(input.defaultBranch) ? {
|
|
21167
|
+
name: IMPLICIT,
|
|
21168
|
+
productionBranch: input.defaultBranch,
|
|
21169
|
+
integrationBranch: input.defaultBranch,
|
|
21170
|
+
baseBranch: input.defaultBranch,
|
|
21171
|
+
pullRequestTarget: input.defaultBranch,
|
|
21172
|
+
mergeBackBranches: [],
|
|
21173
|
+
naming: {}
|
|
21174
|
+
} : null;
|
|
21175
|
+
const candidates = implicit ? [...tracks, implicit] : tracks;
|
|
21176
|
+
const label = (track) => track.name === IMPLICIT ? `the workspace default line (${track.integrationBranch})` : `track ${track.name}`;
|
|
21177
|
+
const fallback = (why) => {
|
|
21178
|
+
const byDefault = input.defaultBranch ? tracks.filter((track) => track.integrationBranch === input.defaultBranch || track.pullRequestTarget === input.defaultBranch) : [];
|
|
21179
|
+
const track = byDefault.length === 1 ? byDefault[0] : null;
|
|
21180
|
+
const target = track ? `track ${track.name}` : `default target ${input.defaultBranch ?? "(none)"}`;
|
|
21181
|
+
const ambiguous = candidates.length >= 2 ? `can't tell which release line ${input.branch ?? "HEAD"} is on (${why}); pass --track <name> (${names}) or --base <branch>` : null;
|
|
21182
|
+
return done(track, "default", `${why}; using the workspace ${target}`, [
|
|
21183
|
+
`release track not determined: ${why}; using the workspace ${target} (pass --track <name> to choose)`
|
|
21184
|
+
], ambiguous);
|
|
21185
|
+
};
|
|
21186
|
+
const branch = input.branch;
|
|
21187
|
+
if (branch) {
|
|
21188
|
+
const owners = ownedBy(candidates, branch);
|
|
21189
|
+
if (owners.length === 1)
|
|
21190
|
+
return done(owners[0], "branch", `${branch} is a branch of ${label(owners[0])}`);
|
|
21191
|
+
if (owners.length > 1)
|
|
21192
|
+
return fallback(`${branch} belongs to more than one track (${owners.map((track) => track.name).join(", ")})`);
|
|
21193
|
+
}
|
|
21194
|
+
const env = input.envRequested?.trim();
|
|
21195
|
+
if (env) {
|
|
21196
|
+
const named = tracks.find((track) => track.name === env);
|
|
21197
|
+
if (named)
|
|
21198
|
+
return done(named, "env", `WORKFLOW_RELEASE_TRACK selects track ${named.name}`);
|
|
21199
|
+
envProblem = `WORKFLOW_RELEASE_TRACK=${env} names no release track of this workspace (${names}); ignored`;
|
|
21200
|
+
}
|
|
21201
|
+
if (!branch)
|
|
21202
|
+
return fallback("HEAD is detached");
|
|
21203
|
+
if (tracks.length === 1 && !implicit)
|
|
21204
|
+
return done(tracks[0], "only-track", `${tracks[0].name} is the only track`);
|
|
21205
|
+
let remotes = null;
|
|
21206
|
+
const short = (ref) => shortRef(ref, remotes ??= input.probe.remotes());
|
|
21207
|
+
const ownerOf = (ref) => {
|
|
21208
|
+
const owner = ownedBy(candidates, short(ref));
|
|
21209
|
+
return owner.length === 1 ? owner[0] : null;
|
|
21210
|
+
};
|
|
21211
|
+
const followRecorded = (start) => {
|
|
21212
|
+
let cursor = start;
|
|
21213
|
+
const seen = new Set([start]);
|
|
21214
|
+
for (let hop = 0;hop < MAX_RECORDED_HOPS; hop += 1) {
|
|
21215
|
+
const recorded = input.probe.recordedBase(cursor);
|
|
21216
|
+
if (!recorded)
|
|
21217
|
+
return null;
|
|
21218
|
+
const base = short(recorded);
|
|
21219
|
+
const owner = ownerOf(base);
|
|
21220
|
+
if (owner)
|
|
21221
|
+
return owner;
|
|
21222
|
+
if (ownedBy(candidates, base).length > 1 || seen.has(base))
|
|
21223
|
+
return null;
|
|
21224
|
+
seen.add(base);
|
|
21225
|
+
cursor = base;
|
|
21226
|
+
}
|
|
21227
|
+
return null;
|
|
21228
|
+
};
|
|
21229
|
+
const recordedOwner = followRecorded(branch);
|
|
21230
|
+
if (recordedOwner)
|
|
21231
|
+
return done(recordedOwner, "recorded-base", `${branch} was created on ${label(recordedOwner)}'s line (recorded base)`);
|
|
21232
|
+
for (const source of ["reflog", "checkout"]) {
|
|
21233
|
+
const ref = source === "reflog" ? input.probe.createdFrom(branch) : input.probe.checkedOutFrom(branch);
|
|
21234
|
+
if (!ref)
|
|
21235
|
+
continue;
|
|
21236
|
+
const owner = ownerOf(ref) ?? followRecorded(short(ref));
|
|
21237
|
+
if (owner)
|
|
21238
|
+
return done(owner, source, `${branch} was created from ${short(ref)} (${source === "reflog" ? "branch reflog" : "HEAD reflog"})`);
|
|
21239
|
+
}
|
|
21240
|
+
const forks = [];
|
|
21241
|
+
for (const track of candidates)
|
|
21242
|
+
for (const ref of new Set([track.integrationBranch, track.baseBranch])) {
|
|
21243
|
+
const sha = input.probe.mergeBase(branch, ref);
|
|
21244
|
+
if (sha)
|
|
21245
|
+
forks.push({ track, ref, sha });
|
|
21246
|
+
}
|
|
21247
|
+
if (forks.length === 0)
|
|
21248
|
+
return fallback(`no track branch could be compared with ${branch} (fetch the track branches)`);
|
|
21249
|
+
const ancestry = new Map;
|
|
21250
|
+
const descends = (older, newer) => {
|
|
21251
|
+
if (older === newer)
|
|
21252
|
+
return false;
|
|
21253
|
+
const key = `${older}>${newer}`;
|
|
21254
|
+
if (!ancestry.has(key))
|
|
21255
|
+
ancestry.set(key, input.probe.isAncestor(older, newer));
|
|
21256
|
+
return ancestry.get(key);
|
|
21257
|
+
};
|
|
21258
|
+
const winners = forks.filter((fork) => forks.every((other) => other.track === fork.track || descends(other.sha, fork.sha)));
|
|
21259
|
+
const winningTracks = [...new Set(winners.map((fork) => fork.track))];
|
|
21260
|
+
if (winningTracks.length === 1 && forks.some((fork) => fork.track !== winningTracks[0])) {
|
|
21261
|
+
const win = winners[0];
|
|
21262
|
+
return done(win.track, "ancestry", `${branch} forked from ${win.ref} at ${win.sha.slice(0, 12)}, after every other line's fork point`);
|
|
21263
|
+
}
|
|
21264
|
+
if (winningTracks.length === 1)
|
|
21265
|
+
return fallback(`only ${label(winningTracks[0])} could be compared with ${branch}`);
|
|
21266
|
+
const newest = forks.filter((fork) => !forks.some((other) => descends(fork.sha, other.sha)));
|
|
21267
|
+
return fallback(`${branch} has the same or unrelated fork points with ${[...new Set(newest.map((fork) => `${fork.ref} (${fork.track.name === IMPLICIT ? "workspace default" : fork.track.name})`))].join(" and ")}`);
|
|
21268
|
+
}
|
|
21269
|
+
var git4 = (cwd, args) => {
|
|
21270
|
+
const run = spawnSync7("git", args, {
|
|
21271
|
+
cwd,
|
|
21272
|
+
encoding: "utf8",
|
|
21273
|
+
timeout: 20000,
|
|
21274
|
+
windowsHide: true,
|
|
21275
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
21276
|
+
});
|
|
21277
|
+
return run.status === 0 ? (run.stdout ?? "").trim() : null;
|
|
21278
|
+
};
|
|
21279
|
+
var recordedBaseKey = (branch) => `branch.${branch}.workitBase`;
|
|
21280
|
+
var escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
21281
|
+
var reflog = (cwd, ref) => (git4(cwd, ["log", "-g", "--date=unix", "--format=%H%x09%gd%x09%gs", ref, "--"]) ?? "").split(`
|
|
21282
|
+
`).map((line) => {
|
|
21283
|
+
const [sha = "", selector = "", ...subject] = line.split("\t");
|
|
21284
|
+
return {
|
|
21285
|
+
sha,
|
|
21286
|
+
time: Number(/@\{(\d+)\}$/u.exec(selector)?.[1] ?? Number.NaN),
|
|
21287
|
+
subject: subject.join("\t")
|
|
21288
|
+
};
|
|
21289
|
+
}).filter((entry) => entry.sha && Number.isFinite(entry.time));
|
|
21290
|
+
function gitTrackProbe(cwd) {
|
|
21291
|
+
let remote;
|
|
21292
|
+
let remotes;
|
|
21293
|
+
const tip = (branch) => {
|
|
21294
|
+
remote ??= pushRemoteName(cwd) ?? "origin";
|
|
21295
|
+
return resolveRef(cwd, `refs/remotes/${remote}/${branch}`) ?? resolveRef(cwd, `refs/heads/${branch}`);
|
|
21296
|
+
};
|
|
21297
|
+
return {
|
|
21298
|
+
remotes: () => remotes ??= remoteNames(cwd),
|
|
21299
|
+
recordedBase: (branch) => git4(cwd, ["config", "--get", recordedBaseKey(branch)]) || null,
|
|
21300
|
+
createdFrom: (branch) => {
|
|
21301
|
+
const log = git4(cwd, ["reflog", "show", "--format=%gs", `refs/heads/${branch}`, "--"]);
|
|
21302
|
+
const oldest = log?.split(`
|
|
21303
|
+
`).findLast(Boolean) ?? "";
|
|
21304
|
+
const match = /^branch: Created from (\S+)$/u.exec(oldest);
|
|
21305
|
+
return match && match[1] !== "HEAD" ? match[1] : null;
|
|
21306
|
+
},
|
|
21307
|
+
checkedOutFrom: (branch) => {
|
|
21308
|
+
const own = reflog(cwd, `refs/heads/${branch}`);
|
|
21309
|
+
const created = own.at(-1);
|
|
21310
|
+
if (!created || created.subject !== "branch: Created from HEAD")
|
|
21311
|
+
return null;
|
|
21312
|
+
const pattern = new RegExp(`^checkout: moving from (\\S+) to ${escapeRegExp(branch)}$`, "u");
|
|
21313
|
+
const entry = reflog(cwd, "HEAD").findLast((item) => item.time >= created.time && item.sha === created.sha && pattern.test(item.subject));
|
|
21314
|
+
return entry ? pattern.exec(entry.subject)?.[1] ?? null : null;
|
|
21315
|
+
},
|
|
21316
|
+
mergeBase: (branch, trackBranch) => {
|
|
21317
|
+
const base = tip(trackBranch);
|
|
21318
|
+
return base && resolveRef(cwd, `refs/heads/${branch}`) ? mergeBase(cwd, base, `refs/heads/${branch}`) : null;
|
|
21319
|
+
},
|
|
21320
|
+
isAncestor: (ancestor, descendant) => git4(cwd, ["merge-base", "--is-ancestor", ancestor, descendant]) !== null
|
|
21321
|
+
};
|
|
21322
|
+
}
|
|
21323
|
+
function workspaceReleaseTracks(cwd) {
|
|
21324
|
+
const raw = resolveRuntimeReleaseTracks(cwd);
|
|
21325
|
+
return { workspace: raw?.workspace ?? null, ...readReleaseTracks(raw?.releaseTracks) };
|
|
21326
|
+
}
|
|
21327
|
+
var memo3 = new Map;
|
|
21328
|
+
var MEMO_LIMIT = 64;
|
|
21329
|
+
function resolveTrackFor(cwd, options) {
|
|
21330
|
+
const read = workspaceReleaseTracks(cwd);
|
|
21331
|
+
if (read.error)
|
|
21332
|
+
return { status: "invalid", error: read.error, issues: read.issues };
|
|
21333
|
+
if (read.tracks.length === 0 && !options.track?.trim())
|
|
21334
|
+
return { status: "none", issues: read.issues };
|
|
21335
|
+
const branch = options.branch === undefined ? currentBranch(cwd) : options.branch;
|
|
21336
|
+
const envRequested = process.env.WORKFLOW_RELEASE_TRACK?.trim() || null;
|
|
21337
|
+
const input = {
|
|
21338
|
+
tracks: read.tracks,
|
|
21339
|
+
requested: options.track ?? null,
|
|
21340
|
+
envRequested,
|
|
21341
|
+
branch,
|
|
21342
|
+
defaultBranch: options.defaultBranch,
|
|
21343
|
+
issues: read.issues
|
|
21344
|
+
};
|
|
21345
|
+
let key = null;
|
|
21346
|
+
if (!options.probe) {
|
|
21347
|
+
const refs = git4(cwd, [
|
|
21348
|
+
"for-each-ref",
|
|
21349
|
+
"--format=%(refname) %(objectname)",
|
|
21350
|
+
"refs/heads",
|
|
21351
|
+
"refs/remotes"
|
|
21352
|
+
]) ?? "";
|
|
21353
|
+
const head = git4(cwd, ["reflog", "show", "-n", "1", "--format=%H %gs", "HEAD", "--"]) ?? "";
|
|
21354
|
+
const own = branch ? [
|
|
21355
|
+
git4(cwd, [
|
|
21356
|
+
"log",
|
|
21357
|
+
"-g",
|
|
21358
|
+
"-n",
|
|
21359
|
+
"1",
|
|
21360
|
+
"--date=unix",
|
|
21361
|
+
"--format=%H %gd %gs",
|
|
21362
|
+
`refs/heads/${branch}`,
|
|
21363
|
+
"--"
|
|
21364
|
+
]) ?? "",
|
|
21365
|
+
git4(cwd, ["config", "--get", recordedBaseKey(branch)]) ?? ""
|
|
21366
|
+
] : [];
|
|
21367
|
+
key = createHash9("sha256").update(JSON.stringify({ cwd, input, refs, head, own })).digest("hex");
|
|
21368
|
+
const hit = memo3.get(key);
|
|
21369
|
+
if (hit)
|
|
21370
|
+
return hit;
|
|
21371
|
+
}
|
|
21372
|
+
const resolution = resolveReleaseTrack({ ...input, probe: options.probe ?? gitTrackProbe(cwd) });
|
|
21373
|
+
if (key) {
|
|
21374
|
+
if (memo3.size >= MEMO_LIMIT)
|
|
21375
|
+
memo3.delete(memo3.keys().next().value);
|
|
21376
|
+
memo3.set(key, resolution);
|
|
21377
|
+
}
|
|
21378
|
+
return resolution;
|
|
21379
|
+
}
|
|
21380
|
+
|
|
21381
|
+
// packages/workit-core/src/core/vcs-config.ts
|
|
21382
|
+
var workspacesPath = () => path14.join(configDir(), "workspaces.json");
|
|
20219
21383
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
20220
21384
|
var remoteProvider = (cwd) => {
|
|
20221
|
-
const r =
|
|
21385
|
+
const r = spawnSync8("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
20222
21386
|
if (r.status !== 0)
|
|
20223
21387
|
return null;
|
|
20224
21388
|
const url = (r.stdout ?? "").trim();
|
|
@@ -20255,7 +21419,7 @@ var remoteEndpoint = (remote) => {
|
|
|
20255
21419
|
var configuredSshEndpoint = (host) => {
|
|
20256
21420
|
if (host.startsWith("-"))
|
|
20257
21421
|
return null;
|
|
20258
|
-
const resolved =
|
|
21422
|
+
const resolved = spawnSync8("ssh", ["-G", host], {
|
|
20259
21423
|
encoding: "utf8",
|
|
20260
21424
|
timeout: 5000,
|
|
20261
21425
|
maxBuffer: 1024 * 1024,
|
|
@@ -20305,7 +21469,7 @@ var readVcsConfig = () => {
|
|
|
20305
21469
|
const file = vcsConfigPath();
|
|
20306
21470
|
let raw;
|
|
20307
21471
|
try {
|
|
20308
|
-
raw =
|
|
21472
|
+
raw = fs12.readFileSync(file, "utf8");
|
|
20309
21473
|
} catch {
|
|
20310
21474
|
return { status: "missing", path: file, config: {} };
|
|
20311
21475
|
}
|
|
@@ -20320,7 +21484,16 @@ var readVcsConfig = () => {
|
|
|
20320
21484
|
}
|
|
20321
21485
|
return { status: "malformed", path: file, config: {}, error: `${file} is not a JSON object` };
|
|
20322
21486
|
};
|
|
20323
|
-
|
|
21487
|
+
var trackSummary = (resolution) => resolution.status === "resolved" ? {
|
|
21488
|
+
name: resolution.track?.name ?? null,
|
|
21489
|
+
source: resolution.source,
|
|
21490
|
+
detail: resolution.detail,
|
|
21491
|
+
warnings: resolution.warnings,
|
|
21492
|
+
track: resolution.track,
|
|
21493
|
+
tracks: resolution.tracks,
|
|
21494
|
+
blocking: resolution.blocking
|
|
21495
|
+
} : null;
|
|
21496
|
+
function vcsConfig(mode, cwd, options = {}) {
|
|
20324
21497
|
const ws = resolveRuntimeWorkspaceVcs(vcsCwd(cwd));
|
|
20325
21498
|
const wsVcs = ws?.vcs ?? {};
|
|
20326
21499
|
const wsYt = ws?.youtrack ?? {};
|
|
@@ -20330,10 +21503,7 @@ function vcsConfig(mode, cwd) {
|
|
|
20330
21503
|
const determinateRoot = cwd !== undefined || process.env.WORKFLOW_WORKSPACE_ROOT !== undefined;
|
|
20331
21504
|
const rawProvider = wsVcs.provider ?? (determinateRoot ? remoteProvider(root) : null) ?? cfg.provider ?? null;
|
|
20332
21505
|
const provider = rawProvider === null || rawProvider === undefined ? null : String(rawProvider).toLowerCase() || null;
|
|
20333
|
-
const
|
|
20334
|
-
const hasWorkspacePolicy = typeof wp.preset === "string" && Object.hasOwn(PRESETS, wp.preset);
|
|
20335
|
-
const policyDefault = resolveBranchPolicyFor(root).defaultTargetBranch;
|
|
20336
|
-
const defaultTarget = String(wsVcs.defaultTargetBranch ?? (hasWorkspacePolicy ? policyDefault : cfg.defaultTargetBranch ?? policyDefault) ?? "develop");
|
|
21506
|
+
const workspaceDefault = workspaceDefaultTarget(root, cfg.defaultTargetBranch);
|
|
20337
21507
|
const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
|
|
20338
21508
|
const youtrackBaseUrl = typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
|
|
20339
21509
|
let issuesProvider = null;
|
|
@@ -20345,11 +21515,24 @@ function vcsConfig(mode, cwd) {
|
|
|
20345
21515
|
if (mode === "resolve") {
|
|
20346
21516
|
if (cfgStatus === "malformed")
|
|
20347
21517
|
return { ok: false, error: cfgError, configPath: cfgPath };
|
|
21518
|
+
const trackResolution = resolveTrackFor(root, {
|
|
21519
|
+
track: options.track,
|
|
21520
|
+
...options.branch !== undefined ? { branch: options.branch } : {},
|
|
21521
|
+
defaultBranch: workspaceDefault
|
|
21522
|
+
});
|
|
21523
|
+
if (trackResolution.status === "invalid")
|
|
21524
|
+
return { ok: false, error: trackResolution.error, configPath: workspacesPath() };
|
|
21525
|
+
const releaseTrack = trackSummary(trackResolution);
|
|
21526
|
+
const defaultTarget = releaseTrack?.track?.pullRequestTarget ?? workspaceDefault;
|
|
21527
|
+
const baseBranch = releaseTrack?.track?.baseBranch ?? defaultTarget;
|
|
20348
21528
|
return {
|
|
20349
21529
|
ok: true,
|
|
20350
21530
|
workspace_name: ws?.name ?? null,
|
|
20351
21531
|
provider,
|
|
20352
21532
|
defaultTargetBranch: defaultTarget,
|
|
21533
|
+
workspaceDefaultTargetBranch: workspaceDefault,
|
|
21534
|
+
baseBranch,
|
|
21535
|
+
releaseTrack,
|
|
20353
21536
|
link_issues: linkIssues,
|
|
20354
21537
|
youtrack_base_url: youtrackBaseUrl,
|
|
20355
21538
|
issues_provider: issuesProvider,
|
|
@@ -20371,9 +21554,9 @@ function vcsConfig(mode, cwd) {
|
|
|
20371
21554
|
const prov = cfg[provider] ?? {};
|
|
20372
21555
|
const out = {
|
|
20373
21556
|
ok: true,
|
|
20374
|
-
configPath:
|
|
21557
|
+
configPath: path14.resolve(cfgPath),
|
|
20375
21558
|
provider,
|
|
20376
|
-
defaultTargetBranch:
|
|
21559
|
+
defaultTargetBranch: workspaceDefault,
|
|
20377
21560
|
pr: cfg.pr ?? {},
|
|
20378
21561
|
workspace_name: ws?.name ?? null,
|
|
20379
21562
|
link_issues: linkIssues,
|
|
@@ -20409,7 +21592,7 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
20409
21592
|
`).filter((l) => l.startsWith("## ")).map((l) => l.trim()),
|
|
20410
21593
|
descriptionPreview: desc.slice(0, 600)
|
|
20411
21594
|
});
|
|
20412
|
-
const remoteResult =
|
|
21595
|
+
const remoteResult = spawnSync8("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
20413
21596
|
cwd,
|
|
20414
21597
|
encoding: "utf8"
|
|
20415
21598
|
});
|
|
@@ -20425,7 +21608,7 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
20425
21608
|
if (!hostingApiHostMatches(remote, host))
|
|
20426
21609
|
return { ok: false, error: "PR style target does not match the provider host" };
|
|
20427
21610
|
if (provider === "gitlab") {
|
|
20428
|
-
const run = (args) =>
|
|
21611
|
+
const run = (args) => spawnSync8("glab", ["api", ...args], {
|
|
20429
21612
|
cwd,
|
|
20430
21613
|
encoding: "utf8",
|
|
20431
21614
|
env
|
|
@@ -20445,7 +21628,7 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
20445
21628
|
});
|
|
20446
21629
|
}
|
|
20447
21630
|
} else if (provider === "github") {
|
|
20448
|
-
const r =
|
|
21631
|
+
const r = spawnSync8("gh", [
|
|
20449
21632
|
"pr",
|
|
20450
21633
|
"list",
|
|
20451
21634
|
"--repo",
|
|
@@ -20476,323 +21659,38 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
20476
21659
|
examples
|
|
20477
21660
|
};
|
|
20478
21661
|
}
|
|
20479
|
-
|
|
20480
|
-
// packages/workit-core/src/core/branch.ts
|
|
20481
|
-
function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
|
|
20482
|
-
const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
|
|
20483
|
-
const config = readConfig();
|
|
20484
|
-
return kind === "branch" ? {
|
|
20485
|
-
branchPolicy: resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null),
|
|
20486
|
-
provenance: { branchPolicy: selected.source }
|
|
20487
|
-
} : {
|
|
20488
|
-
commitPolicy: resolveCommitPolicy(config, selected.policy ? { commitPolicy: selected.policy } : null),
|
|
20489
|
-
provenance: { commitPolicy: selected.source }
|
|
20490
|
-
};
|
|
20491
|
-
}
|
|
20492
|
-
var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
|
|
20493
21662
|
// packages/workit-core/src/core/task-engine.ts
|
|
20494
21663
|
import { realpathSync as realpathSync4 } from "node:fs";
|
|
20495
21664
|
|
|
20496
21665
|
// packages/workit-core/src/core/policy-resolver.ts
|
|
20497
|
-
|
|
20498
|
-
|
|
20499
|
-
assessment: assessmentSchema,
|
|
20500
|
-
constraints: array(constraintSchema),
|
|
20501
|
-
prior: object({
|
|
20502
|
-
decisions: array(decisionSchema),
|
|
20503
|
-
findings: array(findingSchema),
|
|
20504
|
-
requirements: array(requirementSchema)
|
|
20505
|
-
}).strict(),
|
|
20506
|
-
preferences: object({ fast: boolean2().optional(), thorough: boolean2().optional() }).strict().optional()
|
|
20507
|
-
}).strict();
|
|
20508
|
-
var compareCodeUnits3 = (left, right) => {
|
|
20509
|
-
const length = Math.min(left.length, right.length);
|
|
20510
|
-
for (let index = 0;index < length; index += 1) {
|
|
20511
|
-
const difference = left.charCodeAt(index) - right.charCodeAt(index);
|
|
20512
|
-
if (difference !== 0)
|
|
20513
|
-
return difference;
|
|
20514
|
-
}
|
|
20515
|
-
return left.length - right.length;
|
|
20516
|
-
};
|
|
20517
|
-
var stableUnique = (values) => [...new Set(values)].toSorted(compareCodeUnits3);
|
|
20518
|
-
var stableList = (values) => [...values].toSorted((left, right) => compareCodeUnits3(canonicalJson(left), canonicalJson(right)));
|
|
20519
|
-
var normalizedScope = (scope) => ({
|
|
20520
|
-
description: scope.description,
|
|
20521
|
-
paths: stableUnique(scope.paths),
|
|
20522
|
-
exclusions: stableUnique(scope.exclusions)
|
|
20523
|
-
});
|
|
20524
|
-
var normalizedRefList = (values) => stableList(values);
|
|
20525
|
-
var normalizeAssessment = (assessment) => ({
|
|
20526
|
-
facts: stableList(assessment.facts.map((fact) => ({ ...fact, refs: normalizedRefList(fact.refs) }))),
|
|
20527
|
-
signals: Object.fromEntries(Object.entries(assessment.signals).map(([name, signal]) => [
|
|
20528
|
-
name,
|
|
20529
|
-
{ ...signal, refs: normalizedRefList(signal.refs) }
|
|
20530
|
-
])),
|
|
20531
|
-
consequences: stableList(assessment.consequences.map((consequence) => ({
|
|
20532
|
-
...consequence,
|
|
20533
|
-
fact: { ...consequence.fact, refs: normalizedRefList(consequence.fact.refs) }
|
|
20534
|
-
}))),
|
|
20535
|
-
verification: stableList(assessment.verification.map((verification) => ({
|
|
20536
|
-
...verification,
|
|
20537
|
-
scope: normalizedScope(verification.scope),
|
|
20538
|
-
availableChecks: normalizedRefList(verification.availableChecks),
|
|
20539
|
-
gaps: stableUnique(verification.gaps)
|
|
20540
|
-
})))
|
|
20541
|
-
});
|
|
20542
|
-
var normalizeConstraint = (constraint) => ({
|
|
20543
|
-
...constraint,
|
|
20544
|
-
source: constraint.source,
|
|
20545
|
-
requires: stableList(constraint.requires.map((requirement) => ({
|
|
20546
|
-
...requirement,
|
|
20547
|
-
scope: normalizedScope(requirement.scope),
|
|
20548
|
-
refs: normalizedRefList(requirement.refs)
|
|
20549
|
-
})))
|
|
20550
|
-
});
|
|
20551
|
-
var normalizeDecision = (decision) => ({
|
|
20552
|
-
...decision,
|
|
20553
|
-
binding: {
|
|
20554
|
-
...decision.binding,
|
|
20555
|
-
scope: normalizedScope(decision.binding.scope),
|
|
20556
|
-
contentRefs: normalizedRefList(decision.binding.contentRefs)
|
|
20557
|
-
},
|
|
20558
|
-
requirementIds: stableUnique(decision.requirementIds)
|
|
20559
|
-
});
|
|
20560
|
-
var normalizeIntent = (intent) => ({
|
|
20561
|
-
...intent,
|
|
20562
|
-
scope: normalizedScope(intent.scope),
|
|
20563
|
-
authorityRefs: normalizedRefList(intent.authorityRefs)
|
|
20564
|
-
});
|
|
20565
|
-
var normalizePrior = (prior) => ({
|
|
20566
|
-
decisions: stableList(prior.decisions.map(normalizeDecision)),
|
|
20567
|
-
findings: stableList(prior.findings.map((finding) => ({
|
|
20568
|
-
...finding,
|
|
20569
|
-
scope: normalizedScope(finding.scope),
|
|
20570
|
-
refs: normalizedRefList(finding.refs),
|
|
20571
|
-
resolution: finding.resolution ? {
|
|
20572
|
-
...finding.resolution,
|
|
20573
|
-
evidenceIds: stableUnique(finding.resolution.evidenceIds),
|
|
20574
|
-
decisionIds: stableUnique(finding.resolution.decisionIds)
|
|
20575
|
-
} : null
|
|
20576
|
-
}))),
|
|
20577
|
-
requirements: stableList(prior.requirements.map((requirement) => ({
|
|
20578
|
-
...requirement,
|
|
20579
|
-
scope: normalizedScope(requirement.scope)
|
|
20580
|
-
})))
|
|
20581
|
-
});
|
|
20582
|
-
var invalidFields = (error) => ({
|
|
20583
|
-
fields: error.issues.map((issue) => ({
|
|
20584
|
-
path: issue.code === "unrecognized_keys" ? issue.keys.join(".") : issue.path.join("."),
|
|
20585
|
-
reason: issue.message
|
|
20586
|
-
}))
|
|
20587
|
-
});
|
|
20588
|
-
function normalizeResolverInput(input) {
|
|
20589
|
-
const parsed = resolverInputSchema.safeParse(input);
|
|
20590
|
-
if (!parsed.success)
|
|
20591
|
-
return failure2("invalid_input", "resolver input is invalid", invalidFields(parsed.error));
|
|
20592
|
-
const value = parsed.data;
|
|
20593
|
-
const signals = value.assessment.signals;
|
|
20594
|
-
if (signals.behaviorChange.value === true && signals.mechanicalLowRisk.value === true)
|
|
20595
|
-
return failure2("invalid_input", "behavioral change cannot be wholly mechanical", {
|
|
20596
|
-
fields: [
|
|
20597
|
-
{
|
|
20598
|
-
path: "assessment.signals",
|
|
20599
|
-
reason: "contradictory behaviorChange and mechanicalLowRisk"
|
|
20600
|
-
}
|
|
20601
|
-
]
|
|
20602
|
-
});
|
|
20603
|
-
const ids = value.constraints.map((constraint) => constraint.id);
|
|
21666
|
+
function resolvePolicy(input) {
|
|
21667
|
+
const ids = input.constraints.map((constraint) => constraint.id);
|
|
20604
21668
|
if (new Set(ids).size !== ids.length)
|
|
20605
21669
|
return failure2("invalid_input", "constraint IDs must be unique", {
|
|
20606
21670
|
fields: [{ path: "constraints", reason: "duplicate constraint id" }]
|
|
20607
21671
|
});
|
|
20608
|
-
if (value.preferences?.fast && value.preferences.thorough)
|
|
20609
|
-
return failure2("invalid_input", "fast and thorough preferences contradict", {
|
|
20610
|
-
fields: [{ path: "preferences", reason: "fast and thorough cannot both be enabled" }]
|
|
20611
|
-
});
|
|
20612
|
-
const normalized = {
|
|
20613
|
-
intent: normalizeIntent(value.intent),
|
|
20614
|
-
assessment: normalizeAssessment(value.assessment),
|
|
20615
|
-
constraints: stableList(value.constraints.map(normalizeConstraint)),
|
|
20616
|
-
prior: normalizePrior(value.prior),
|
|
20617
|
-
...value.preferences ? { preferences: { ...value.preferences } } : {},
|
|
20618
|
-
policyVersion: POLICY_VERSION
|
|
20619
|
-
};
|
|
20620
|
-
return success(null, null, normalized);
|
|
20621
|
-
}
|
|
20622
|
-
var requirement = (spec) => ({
|
|
20623
|
-
...spec,
|
|
20624
|
-
id: requirementId({
|
|
20625
|
-
ruleId: spec.ruleId,
|
|
20626
|
-
scope: spec.scope,
|
|
20627
|
-
satisfaction: spec.satisfaction,
|
|
20628
|
-
before: spec.before,
|
|
20629
|
-
dependentAction: spec.dependentAction
|
|
20630
|
-
})
|
|
20631
|
-
});
|
|
20632
|
-
var signalReason = (input, name) => input.assessment.signals[name].reason;
|
|
20633
|
-
function resolveRequirements(input) {
|
|
20634
|
-
const { signals } = input.assessment;
|
|
20635
|
-
const scope = input.intent.scope;
|
|
20636
|
-
const requirements = [];
|
|
20637
|
-
const hasUnknownSignal = Object.values(signals).some((signal) => signal.value === "unknown");
|
|
20638
|
-
const hasUnknownFact = [
|
|
20639
|
-
...input.assessment.facts,
|
|
20640
|
-
...input.assessment.consequences.map(({ fact }) => fact)
|
|
20641
|
-
].some((fact) => fact.basis === "unknown");
|
|
20642
|
-
if (hasUnknownSignal || hasUnknownFact)
|
|
20643
|
-
requirements.push(requirement({
|
|
20644
|
-
ruleId: "consequential-unknown",
|
|
20645
|
-
dimension: "investigation",
|
|
20646
|
-
scope,
|
|
20647
|
-
reason: `Resolve consequential uncertainty before the dependent action: ${[
|
|
20648
|
-
...Object.entries(signals).filter(([, signal]) => signal.value === "unknown").map(([name]) => name),
|
|
20649
|
-
...hasUnknownFact ? ["unknown fact"] : []
|
|
20650
|
-
].join(", ")}`,
|
|
20651
|
-
satisfaction: "An observed or inferred fact resolves the uncertainty and records its supporting references.",
|
|
20652
|
-
before: "dependent_action",
|
|
20653
|
-
dependentAction: null,
|
|
20654
|
-
acceptanceAllowed: false
|
|
20655
|
-
}));
|
|
20656
|
-
if (signals.productChoiceOpen.value === true)
|
|
20657
|
-
requirements.push(requirement({
|
|
20658
|
-
ruleId: "product-decision",
|
|
20659
|
-
dimension: "decisions",
|
|
20660
|
-
scope,
|
|
20661
|
-
reason: `A consequential product choice remains open: ${signalReason(input, "productChoiceOpen")}`,
|
|
20662
|
-
satisfaction: "Present the material alternatives and record the user's decision before the dependent action.",
|
|
20663
|
-
before: "dependent_action",
|
|
20664
|
-
dependentAction: null,
|
|
20665
|
-
acceptanceAllowed: false
|
|
20666
|
-
}));
|
|
20667
|
-
const independentBehaviorReview = input.preferences?.thorough === true || input.assessment.consequences.some((consequence) => ["security", "data", "public_contract", "operations"].includes(consequence.area));
|
|
20668
|
-
if (signals.behaviorChange.value === true)
|
|
20669
|
-
requirements.push(requirement({
|
|
20670
|
-
ruleId: "behavioral-verification",
|
|
20671
|
-
dimension: "testing",
|
|
20672
|
-
scope,
|
|
20673
|
-
reason: `Behavior, side effects, permissions, or data handling may change: ${signalReason(input, "behaviorChange")}`,
|
|
20674
|
-
satisfaction: "A useful behavioral check passes against the affected behavior and current candidate.",
|
|
20675
|
-
before: "close",
|
|
20676
|
-
dependentAction: null,
|
|
20677
|
-
acceptanceAllowed: false
|
|
20678
|
-
}), requirement({
|
|
20679
|
-
ruleId: independentBehaviorReview ? "fresh-context-review" : "self-review",
|
|
20680
|
-
dimension: "review",
|
|
20681
|
-
scope,
|
|
20682
|
-
reason: independentBehaviorReview ? "Consequential behavior or the thorough preference requires fresh-context review." : "Bounded behavior changes use self-review unless consequential risks or trusted rules require more.",
|
|
20683
|
-
satisfaction: independentBehaviorReview ? "A separate review context examines the current candidate and records its result." : "The lead reviews the resulting diff and records the relevant scope and checks.",
|
|
20684
|
-
before: "close",
|
|
20685
|
-
dependentAction: null,
|
|
20686
|
-
acceptanceAllowed: !independentBehaviorReview
|
|
20687
|
-
}));
|
|
20688
|
-
else if (signals.mechanicalLowRisk.value === true)
|
|
20689
|
-
requirements.push(requirement({
|
|
20690
|
-
ruleId: "mechanical-existing-checks",
|
|
20691
|
-
dimension: "verification",
|
|
20692
|
-
scope,
|
|
20693
|
-
reason: `The change is demonstrably mechanical and low-risk: ${signalReason(input, "mechanicalLowRisk")}`,
|
|
20694
|
-
satisfaction: "Run the relevant existing checks for affected consumers; do not add tautological tests.",
|
|
20695
|
-
before: "close",
|
|
20696
|
-
dependentAction: null,
|
|
20697
|
-
acceptanceAllowed: true
|
|
20698
|
-
}), requirement({
|
|
20699
|
-
ruleId: input.preferences?.thorough ? "fresh-context-review" : "self-review",
|
|
20700
|
-
dimension: "review",
|
|
20701
|
-
scope,
|
|
20702
|
-
reason: input.preferences?.thorough ? "The thorough preference upgrades mechanical self-review to fresh-context review." : "Mechanical low-risk work uses self-review rather than mandatory fresh-context review.",
|
|
20703
|
-
satisfaction: input.preferences?.thorough ? "A separate review context examines the current candidate and records its result." : "The lead reviews the resulting diff and records the relevant scope and checks.",
|
|
20704
|
-
before: "close",
|
|
20705
|
-
dependentAction: null,
|
|
20706
|
-
acceptanceAllowed: !input.preferences?.thorough
|
|
20707
|
-
}));
|
|
20708
|
-
if (signals.behaviorChange.value === true || signals.mechanicalLowRisk.value === true)
|
|
20709
|
-
requirements.push(requirement({
|
|
20710
|
-
ruleId: "pre-pr-cleanup",
|
|
20711
|
-
dimension: "verification",
|
|
20712
|
-
scope,
|
|
20713
|
-
reason: "Implementation output must be deslopped before it is delivered.",
|
|
20714
|
-
satisfaction: "Run the workit-deslop pass and record passing check evidence, or record an approved limitation decision waiving cleanup.",
|
|
20715
|
-
before: "dependent_action",
|
|
20716
|
-
dependentAction: "hosting.pull_request",
|
|
20717
|
-
acceptanceAllowed: true
|
|
20718
|
-
}));
|
|
20719
|
-
if (signals.durableAgreementNeeded.value === true)
|
|
20720
|
-
requirements.push(requirement({
|
|
20721
|
-
ruleId: "durable-spec",
|
|
20722
|
-
dimension: "artifacts",
|
|
20723
|
-
scope,
|
|
20724
|
-
reason: `The work needs a durable agreement about intended behavior: ${signalReason(input, "durableAgreementNeeded")}`,
|
|
20725
|
-
satisfaction: "Record the agreed behavior in a durable specification before implementation writes.",
|
|
20726
|
-
before: "write",
|
|
20727
|
-
dependentAction: null,
|
|
20728
|
-
acceptanceAllowed: true
|
|
20729
|
-
}));
|
|
20730
|
-
if (signals.coordinationPlanNeeded.value === true)
|
|
20731
|
-
requirements.push(requirement({
|
|
20732
|
-
ruleId: "coordination-plan",
|
|
20733
|
-
dimension: "continuity",
|
|
20734
|
-
scope,
|
|
20735
|
-
reason: `Dependencies, sequencing, or resumption need durable coordination: ${signalReason(input, "coordinationPlanNeeded")}`,
|
|
20736
|
-
satisfaction: "Record useful sequencing, dependencies, and the next action in a compact plan.",
|
|
20737
|
-
before: "dependent_action",
|
|
20738
|
-
dependentAction: null,
|
|
20739
|
-
acceptanceAllowed: true
|
|
20740
|
-
}));
|
|
20741
|
-
if (signals.helperUseful.value === true && !input.preferences?.fast)
|
|
20742
|
-
requirements.push(requirement({
|
|
20743
|
-
ruleId: "helper-usefulness",
|
|
20744
|
-
dimension: "delegation",
|
|
20745
|
-
scope,
|
|
20746
|
-
reason: `Independent judgment or investigation justifies a scoped helper: ${signalReason(input, "helperUseful")}`,
|
|
20747
|
-
satisfaction: "Use a bounded helper only for the independent objective and reconcile its result.",
|
|
20748
|
-
before: "dependent_action",
|
|
20749
|
-
dependentAction: null,
|
|
20750
|
-
acceptanceAllowed: true
|
|
20751
|
-
}));
|
|
20752
|
-
for (const constraint of input.constraints)
|
|
20753
|
-
for (const obligation of constraint.requires) {
|
|
20754
|
-
const dimension = obligation.method ?? (obligation.kind === "check" ? "verification" : obligation.kind === "decision" ? "decisions" : "challenge");
|
|
20755
|
-
requirements.push(requirement({
|
|
20756
|
-
ruleId: `project-constraint:${constraint.id}:${obligation.kind}:${sha256({
|
|
20757
|
-
kind: obligation.kind,
|
|
20758
|
-
scope: normalizedScope(obligation.scope),
|
|
20759
|
-
refs: normalizedRefList(obligation.refs),
|
|
20760
|
-
method: obligation.method,
|
|
20761
|
-
before: obligation.before,
|
|
20762
|
-
dependentAction: obligation.dependentAction
|
|
20763
|
-
})}`,
|
|
20764
|
-
dimension,
|
|
20765
|
-
scope: obligation.scope,
|
|
20766
|
-
reason: `${constraint.kind} constraint requires ${obligation.kind}: ${constraint.statement}`,
|
|
20767
|
-
satisfaction: `Satisfy the constraint obligation using its referenced ${obligation.kind}.`,
|
|
20768
|
-
before: obligation.before,
|
|
20769
|
-
dependentAction: obligation.dependentAction,
|
|
20770
|
-
acceptanceAllowed: constraint.acceptanceAllowed && obligation.kind !== "decision"
|
|
20771
|
-
}));
|
|
20772
|
-
}
|
|
20773
|
-
const seen = new Set;
|
|
20774
|
-
return requirements.filter((item) => {
|
|
20775
|
-
if (seen.has(item.id))
|
|
20776
|
-
return false;
|
|
20777
|
-
seen.add(item.id);
|
|
20778
|
-
return true;
|
|
20779
|
-
});
|
|
20780
|
-
}
|
|
20781
|
-
function resolvePolicy(input) {
|
|
20782
|
-
const normalized = normalizeResolverInput(input);
|
|
20783
|
-
if (!normalized.ok)
|
|
20784
|
-
return normalized;
|
|
20785
|
-
const requirements = resolveRequirements(normalized.data);
|
|
20786
21672
|
const policy = {
|
|
20787
21673
|
policyVersion: POLICY_VERSION,
|
|
20788
|
-
inputDigest: sha256(canonicalJson(
|
|
20789
|
-
|
|
21674
|
+
inputDigest: sha256(canonicalJson({
|
|
21675
|
+
judgment: input.judgment,
|
|
21676
|
+
scope: input.intent.scope,
|
|
21677
|
+
constraints: input.constraints,
|
|
21678
|
+
verification: input.verification ?? "self",
|
|
21679
|
+
policyVersion: POLICY_VERSION
|
|
21680
|
+
})),
|
|
21681
|
+
requirements: deriveRequirements(input.judgment, input.intent.scope, input.constraints, input.verification)
|
|
20790
21682
|
};
|
|
20791
21683
|
const parsed = policySchema.safeParse(policy);
|
|
20792
21684
|
if (!parsed.success)
|
|
20793
|
-
return failure2("invalid_input", "resolved policy is invalid",
|
|
21685
|
+
return failure2("invalid_input", "resolved policy is invalid", {
|
|
21686
|
+
fields: parsed.error.issues.map((issue) => ({
|
|
21687
|
+
path: issue.path.join("."),
|
|
21688
|
+
reason: issue.message
|
|
21689
|
+
}))
|
|
21690
|
+
});
|
|
20794
21691
|
return success(null, null, parsed.data);
|
|
20795
21692
|
}
|
|
21693
|
+
var compareCodeUnits3 = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
20796
21694
|
function diffPolicy(previous, next, reason, now) {
|
|
20797
21695
|
if (!policySchema.safeParse(previous).success && previous !== null)
|
|
20798
21696
|
throw new TypeError("previous policy is invalid");
|
|
@@ -20820,7 +21718,222 @@ function diffPolicy(previous, next, reason, now) {
|
|
|
20820
21718
|
return parsed.data;
|
|
20821
21719
|
}
|
|
20822
21720
|
|
|
21721
|
+
// packages/workit-core/src/core/policy/judgment.ts
|
|
21722
|
+
var isObject6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21723
|
+
var squash = (key) => key.toLowerCase().replace(/[_-]/g, "");
|
|
21724
|
+
var ALIASES = {
|
|
21725
|
+
riskTier: ["risktier", "risk", "risklevel", "tier"],
|
|
21726
|
+
behaviorChange: ["behaviorchange", "behavior", "behaviour", "behaviourchange"],
|
|
21727
|
+
productChoiceOpen: ["productchoiceopen", "openproductchoice", "productchoice", "openchoice"],
|
|
21728
|
+
needsPlan: ["needsplan", "plan", "needsspec", "spec"],
|
|
21729
|
+
note: ["note", "notes", "reason", "why"],
|
|
21730
|
+
refs: ["refs", "ref", "references"]
|
|
21731
|
+
};
|
|
21732
|
+
var LEGACY_KEYS = new Set(["assessment", "facts", "signals", "consequences", "verification"]);
|
|
21733
|
+
var RISK = {
|
|
21734
|
+
trivial: "trivial",
|
|
21735
|
+
low: "trivial",
|
|
21736
|
+
minor: "trivial",
|
|
21737
|
+
mechanical: "trivial",
|
|
21738
|
+
none: "trivial",
|
|
21739
|
+
normal: "normal",
|
|
21740
|
+
medium: "normal",
|
|
21741
|
+
med: "normal",
|
|
21742
|
+
moderate: "normal",
|
|
21743
|
+
high: "high",
|
|
21744
|
+
critical: "high",
|
|
21745
|
+
major: "high"
|
|
21746
|
+
};
|
|
21747
|
+
var YES = new Set(["true", "yes", "y", "1", "on"]);
|
|
21748
|
+
var NO = new Set(["false", "no", "n", "0", "off", "none"]);
|
|
21749
|
+
var flag = (value, unknownAs) => {
|
|
21750
|
+
if (typeof value === "boolean")
|
|
21751
|
+
return { ok: true, value };
|
|
21752
|
+
if (typeof value === "number" && (value === 0 || value === 1))
|
|
21753
|
+
return { ok: true, value: value === 1 };
|
|
21754
|
+
if (typeof value !== "string")
|
|
21755
|
+
return { ok: false };
|
|
21756
|
+
const text = value.trim().toLowerCase();
|
|
21757
|
+
if (YES.has(text))
|
|
21758
|
+
return { ok: true, value: true };
|
|
21759
|
+
if (NO.has(text))
|
|
21760
|
+
return { ok: true, value: false };
|
|
21761
|
+
if (text === "unknown" || text === "maybe")
|
|
21762
|
+
return { ok: true, value: unknownAs };
|
|
21763
|
+
return { ok: false };
|
|
21764
|
+
};
|
|
21765
|
+
var fromLegacy = (raw, previous) => {
|
|
21766
|
+
const source = isObject6(raw.assessment) ? raw.assessment : raw;
|
|
21767
|
+
const signals = isObject6(source.signals) ? source.signals : {};
|
|
21768
|
+
const value = (name) => isObject6(signals[name]) ? signals[name].value : undefined;
|
|
21769
|
+
const known = (name, fallback) => typeof value(name) === "boolean" ? value(name) : fallback;
|
|
21770
|
+
const behaviorChange = known("behaviorChange", previous?.behaviorChange ?? false);
|
|
21771
|
+
const consequences = Array.isArray(source.consequences) ? source.consequences : [];
|
|
21772
|
+
const freshReview = value("behaviorChange") === true && consequences.some((item) => isObject6(item) && ["security", "data", "public_contract", "operations"].includes(String(item.area)));
|
|
21773
|
+
return {
|
|
21774
|
+
riskTier: freshReview ? "high" : value("mechanicalLowRisk") === true && !behaviorChange ? "trivial" : "normal",
|
|
21775
|
+
behaviorChange,
|
|
21776
|
+
productChoiceOpen: known("productChoiceOpen", previous?.productChoiceOpen ?? false),
|
|
21777
|
+
needsPlan: known("durableAgreementNeeded", previous?.needsPlan ?? false) || value("coordinationPlanNeeded") === true
|
|
21778
|
+
};
|
|
21779
|
+
};
|
|
21780
|
+
var DEFAULT = {
|
|
21781
|
+
riskTier: "normal",
|
|
21782
|
+
behaviorChange: false,
|
|
21783
|
+
productChoiceOpen: false,
|
|
21784
|
+
needsPlan: false,
|
|
21785
|
+
note: null,
|
|
21786
|
+
refs: []
|
|
21787
|
+
};
|
|
21788
|
+
function normalizeJudgment(raw, previous) {
|
|
21789
|
+
const input = isObject6(raw) ? raw : {};
|
|
21790
|
+
const legacy = Object.keys(input).some((key) => LEGACY_KEYS.has(key));
|
|
21791
|
+
const base = { ...DEFAULT, ...previous, refs: [...previous?.refs ?? []] };
|
|
21792
|
+
const judgment = legacy ? { ...base, ...fromLegacy(input, previous) } : { ...base };
|
|
21793
|
+
const ignored = [];
|
|
21794
|
+
const fields = [];
|
|
21795
|
+
for (const [key, value] of Object.entries(input)) {
|
|
21796
|
+
if (LEGACY_KEYS.has(key))
|
|
21797
|
+
continue;
|
|
21798
|
+
const name = Object.keys(ALIASES).find((field) => ALIASES[field].includes(squash(key)));
|
|
21799
|
+
if (!name) {
|
|
21800
|
+
ignored.push(key);
|
|
21801
|
+
continue;
|
|
21802
|
+
}
|
|
21803
|
+
if (name === "riskTier") {
|
|
21804
|
+
const tier = typeof value === "string" ? RISK[value.trim().toLowerCase()] : undefined;
|
|
21805
|
+
if (tier)
|
|
21806
|
+
judgment.riskTier = tier;
|
|
21807
|
+
else
|
|
21808
|
+
fields.push({ path: key, reason: "expected trivial|normal|high (or low|medium)" });
|
|
21809
|
+
} else if (name === "note") {
|
|
21810
|
+
judgment.note = typeof value === "string" && value.trim() ? value.trim() : null;
|
|
21811
|
+
} else if (name === "refs") {
|
|
21812
|
+
const items = Array.isArray(value) ? value : [value];
|
|
21813
|
+
judgment.refs.push(...items.filter((item) => item !== null && item !== "").map(refOf));
|
|
21814
|
+
} else {
|
|
21815
|
+
if (name === "needsPlan" && typeof value === "string" && !flag(value, false).ok && value.trim()) {
|
|
21816
|
+
judgment.needsPlan = true;
|
|
21817
|
+
judgment.refs.push(refOf(value.trim()));
|
|
21818
|
+
continue;
|
|
21819
|
+
}
|
|
21820
|
+
const parsed = flag(value, name === "behaviorChange");
|
|
21821
|
+
if (parsed.ok)
|
|
21822
|
+
judgment[name] = parsed.value;
|
|
21823
|
+
else
|
|
21824
|
+
fields.push({ path: key, reason: "expected true|false (yes|no accepted)" });
|
|
21825
|
+
}
|
|
21826
|
+
}
|
|
21827
|
+
if (fields.length)
|
|
21828
|
+
return failure2("invalid_input", "judgment values are invalid", { fields });
|
|
21829
|
+
const seen = new Set;
|
|
21830
|
+
judgment.refs = judgment.refs.filter((ref) => {
|
|
21831
|
+
const key = JSON.stringify(ref);
|
|
21832
|
+
return seen.has(key) ? false : (seen.add(key), true);
|
|
21833
|
+
});
|
|
21834
|
+
const parsed = judgmentSchema.safeParse(judgment);
|
|
21835
|
+
if (!parsed.success)
|
|
21836
|
+
return failure2("invalid_input", "judgment is invalid", {
|
|
21837
|
+
fields: parsed.error.issues.map((issue) => ({
|
|
21838
|
+
path: issue.path.join("."),
|
|
21839
|
+
reason: issue.message
|
|
21840
|
+
}))
|
|
21841
|
+
});
|
|
21842
|
+
return success(null, null, { judgment: parsed.data, ignored, legacy });
|
|
21843
|
+
}
|
|
21844
|
+
|
|
21845
|
+
// packages/workit-core/src/autonomy.ts
|
|
21846
|
+
import os3 from "node:os";
|
|
21847
|
+
import path15 from "node:path";
|
|
21848
|
+
var accountHome = () => {
|
|
21849
|
+
try {
|
|
21850
|
+
return os3.userInfo().homedir;
|
|
21851
|
+
} catch {
|
|
21852
|
+
return os3.homedir();
|
|
21853
|
+
}
|
|
21854
|
+
};
|
|
21855
|
+
var grantsHome = { resolve: accountHome };
|
|
21856
|
+
var grantsDir = () => path15.join(grantsHome.resolve(), ".config", "workit");
|
|
21857
|
+
var grantsOverride = () => {
|
|
21858
|
+
const configured = path15.resolve(resolveConfigDir());
|
|
21859
|
+
return configured === path15.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path15.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
21860
|
+
};
|
|
21861
|
+
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
21862
|
+
var DEFAULT_GRANTS = Object.freeze({
|
|
21863
|
+
push: true,
|
|
21864
|
+
pr: true,
|
|
21865
|
+
merge: false,
|
|
21866
|
+
release: false,
|
|
21867
|
+
rerun: true
|
|
21868
|
+
});
|
|
21869
|
+
var DEFAULT_ENDPOINT = "commit";
|
|
21870
|
+
var DEFAULT_VERIFICATION = "self";
|
|
21871
|
+
var verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self";
|
|
21872
|
+
var endpointOf = (entry) => entry?.defaultEndpoint === "pr" ? "pr" : "commit";
|
|
21873
|
+
var isGrantValue = (value) => value === true || value === false || value === "verified";
|
|
21874
|
+
var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
|
|
21875
|
+
var fromAutoApprove = (value) => {
|
|
21876
|
+
if (value === true)
|
|
21877
|
+
return { push: true, pr: true, merge: "verified" };
|
|
21878
|
+
if (!Array.isArray(value))
|
|
21879
|
+
return null;
|
|
21880
|
+
const grants = {};
|
|
21881
|
+
for (const item of value)
|
|
21882
|
+
if (item === "push" || item === "pr")
|
|
21883
|
+
grants[item] = true;
|
|
21884
|
+
else if (item === "merge")
|
|
21885
|
+
grants.merge = "verified";
|
|
21886
|
+
return Object.keys(grants).length > 0 ? grants : null;
|
|
21887
|
+
};
|
|
21888
|
+
function configuredGrants(entry) {
|
|
21889
|
+
const record = entry ?? {};
|
|
21890
|
+
const raw = record.autonomy;
|
|
21891
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
21892
|
+
const grants = {};
|
|
21893
|
+
for (const kind of GRANT_KINDS) {
|
|
21894
|
+
const value = raw[kind];
|
|
21895
|
+
if (isGrantValue(value))
|
|
21896
|
+
grants[kind] = normalized(kind, value);
|
|
21897
|
+
}
|
|
21898
|
+
return { grants, source: "autonomy" };
|
|
21899
|
+
}
|
|
21900
|
+
const legacy = fromAutoApprove(record.autoApprove);
|
|
21901
|
+
return legacy ? { grants: legacy, source: "autoApprove" } : { grants: {}, source: "default" };
|
|
21902
|
+
}
|
|
21903
|
+
function resolveAutonomy(cwd) {
|
|
21904
|
+
const override = grantsOverride();
|
|
21905
|
+
if (override)
|
|
21906
|
+
return {
|
|
21907
|
+
workspace: null,
|
|
21908
|
+
grants: { ...DEFAULT_GRANTS },
|
|
21909
|
+
configured: [],
|
|
21910
|
+
source: "default",
|
|
21911
|
+
accountConfigured: false,
|
|
21912
|
+
defaultEndpoint: DEFAULT_ENDPOINT,
|
|
21913
|
+
verification: DEFAULT_VERIFICATION,
|
|
21914
|
+
note: override
|
|
21915
|
+
};
|
|
21916
|
+
const workspace = resolveWorkspaceFrom(cwd, grantsDir(), process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined);
|
|
21917
|
+
const { grants, source } = configuredGrants(workspace);
|
|
21918
|
+
return {
|
|
21919
|
+
workspace: workspace?.name ?? null,
|
|
21920
|
+
grants: { ...DEFAULT_GRANTS, ...grants },
|
|
21921
|
+
configured: GRANT_KINDS.filter((kind) => grants[kind] !== undefined),
|
|
21922
|
+
source,
|
|
21923
|
+
accountConfigured: Boolean(workspace?.vcs?.account),
|
|
21924
|
+
defaultEndpoint: endpointOf(workspace),
|
|
21925
|
+
verification: verificationOf(workspace)
|
|
21926
|
+
};
|
|
21927
|
+
}
|
|
21928
|
+
|
|
20823
21929
|
// packages/workit-core/src/core/task-engine.ts
|
|
21930
|
+
var workspaceVerification = (root) => {
|
|
21931
|
+
try {
|
|
21932
|
+
return resolveAutonomy(root).verification;
|
|
21933
|
+
} catch {
|
|
21934
|
+
return "self";
|
|
21935
|
+
}
|
|
21936
|
+
};
|
|
20824
21937
|
var provenance = (context, kind = context.provenanceKind ?? "host_observed") => ({
|
|
20825
21938
|
kind,
|
|
20826
21939
|
host: context.caller.host,
|
|
@@ -21013,22 +22126,7 @@ var mapRef = (ref, ids) => {
|
|
|
21013
22126
|
return ref;
|
|
21014
22127
|
return { ...ref, id: ids.get(ref.id) ?? ref.id };
|
|
21015
22128
|
};
|
|
21016
|
-
var portableRefs = (refs) => refs.filter((ref) => ref.kind === "file" || ref.kind === "record");
|
|
21017
22129
|
var portableRef = (ref) => ref && (ref.kind === "file" || ref.kind === "record") ? ref : null;
|
|
21018
|
-
var portableFact = (fact) => {
|
|
21019
|
-
const refs = portableRefs(fact.refs);
|
|
21020
|
-
return fact.basis === "observed" && refs.length === 0 ? { ...fact, basis: "unknown", refs } : { ...fact, refs };
|
|
21021
|
-
};
|
|
21022
|
-
var portableSignal = (signal) => {
|
|
21023
|
-
const refs = portableRefs(signal.refs);
|
|
21024
|
-
return signal.basis === "observed" && refs.length === 0 ? {
|
|
21025
|
-
...signal,
|
|
21026
|
-
value: "unknown",
|
|
21027
|
-
basis: "unknown",
|
|
21028
|
-
reason: "portable supporting reference was removed",
|
|
21029
|
-
refs
|
|
21030
|
-
} : { ...signal, refs };
|
|
21031
|
-
};
|
|
21032
22130
|
var importedProvenance = (context) => ({
|
|
21033
22131
|
kind: "imported",
|
|
21034
22132
|
host: context.caller.host,
|
|
@@ -21040,18 +22138,6 @@ var portableTask = (task) => {
|
|
|
21040
22138
|
if (typeof stripped !== "object" || stripped === null || Array.isArray(stripped))
|
|
21041
22139
|
throw new TypeError("portable task rewrite produced a non-record");
|
|
21042
22140
|
const clone = structuredClone(stripped);
|
|
21043
|
-
const demote = (facts, signals, consequences) => {
|
|
21044
|
-
for (const fact of facts)
|
|
21045
|
-
Object.assign(fact, portableFact(fact));
|
|
21046
|
-
for (const name of Object.keys(signals)) {
|
|
21047
|
-
const signal = signals[name];
|
|
21048
|
-
Object.assign(signal, portableSignal(signal));
|
|
21049
|
-
}
|
|
21050
|
-
for (const consequence of consequences)
|
|
21051
|
-
Object.assign(consequence.fact, portableFact(consequence.fact));
|
|
21052
|
-
};
|
|
21053
|
-
for (const entry of clone.assessments)
|
|
21054
|
-
demote(entry.data.facts, entry.data.signals, entry.data.consequences);
|
|
21055
22141
|
clone.candidates = [];
|
|
21056
22142
|
return clone;
|
|
21057
22143
|
};
|
|
@@ -21068,6 +22154,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
21068
22154
|
for (const entry of [
|
|
21069
22155
|
source.intent,
|
|
21070
22156
|
...source.assessments,
|
|
22157
|
+
...source.judgments ?? [],
|
|
21071
22158
|
...source.evidence,
|
|
21072
22159
|
...source.decisions,
|
|
21073
22160
|
...source.findings,
|
|
@@ -21091,9 +22178,16 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
21091
22178
|
id: fresh(entry.id),
|
|
21092
22179
|
recordedAt: timestamp,
|
|
21093
22180
|
provenance: imported,
|
|
21094
|
-
data: rewriteRecordRefs(data,
|
|
22181
|
+
data: rewriteRecordRefs(data, legacyAssessmentSchema, remap)
|
|
21095
22182
|
};
|
|
21096
22183
|
});
|
|
22184
|
+
const judgments = source.judgments?.map((entry) => ({
|
|
22185
|
+
...entry,
|
|
22186
|
+
id: fresh(entry.id),
|
|
22187
|
+
recordedAt: timestamp,
|
|
22188
|
+
provenance: imported,
|
|
22189
|
+
data: rewriteRecordRefs(entry.data, judgmentSchema, remap)
|
|
22190
|
+
}));
|
|
21097
22191
|
const evidence = source.evidence.map((entry) => ({
|
|
21098
22192
|
...entry,
|
|
21099
22193
|
id: fresh(entry.id),
|
|
@@ -21161,6 +22255,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
21161
22255
|
status: "paused",
|
|
21162
22256
|
closure: null,
|
|
21163
22257
|
assessments,
|
|
22258
|
+
...judgments ? { judgments } : {},
|
|
21164
22259
|
evidence,
|
|
21165
22260
|
decisions: decisions.map((entry) => {
|
|
21166
22261
|
const data = rewriteRecordRefs(entry.data, decisionSchema, remap);
|
|
@@ -21230,7 +22325,8 @@ class WorkitCore {
|
|
|
21230
22325
|
retryOmittedRevisions(request, run) {
|
|
21231
22326
|
return retryFilledRevisions(filledRevisions(request), run);
|
|
21232
22327
|
}
|
|
21233
|
-
implicitTask(family,
|
|
22328
|
+
implicitTask(family, raw) {
|
|
22329
|
+
const request = normalizeOperationInput(family, raw, this.context.caller.host);
|
|
21234
22330
|
if (typeof request !== "object" || request === null || Array.isArray(request))
|
|
21235
22331
|
return success(null, null, request);
|
|
21236
22332
|
const value = request;
|
|
@@ -21484,7 +22580,11 @@ class WorkitCore {
|
|
|
21484
22580
|
if ((this.context.workerId ?? null) !== null)
|
|
21485
22581
|
return failure2("permission_denied", "helpers cannot change task requirements");
|
|
21486
22582
|
this.fillRevisions(input, task.data);
|
|
21487
|
-
const
|
|
22583
|
+
const judged = normalizeJudgment(input.judgment, latestJudgment(task.data));
|
|
22584
|
+
if (!judged.ok)
|
|
22585
|
+
return judged;
|
|
22586
|
+
const judgment = judged.data.judgment;
|
|
22587
|
+
const resolved = this.resolve(task.data, judgment);
|
|
21488
22588
|
if (!resolved.ok)
|
|
21489
22589
|
return resolved;
|
|
21490
22590
|
if (input.action === "preview")
|
|
@@ -21495,16 +22595,17 @@ class WorkitCore {
|
|
|
21495
22595
|
if (current.status === "closed")
|
|
21496
22596
|
return failure2("invalid_transition", "closed task cannot be assessed");
|
|
21497
22597
|
const nextChange = diffPolicy(current.policy, resolved.data, "policy reassessed", mutation.now);
|
|
21498
|
-
const
|
|
22598
|
+
const entry = {
|
|
21499
22599
|
id: newId(),
|
|
21500
22600
|
recordedAt: mutation.now,
|
|
21501
22601
|
provenance: provenance(this.context),
|
|
21502
|
-
data:
|
|
22602
|
+
data: judgment
|
|
21503
22603
|
};
|
|
21504
22604
|
const next = {
|
|
21505
22605
|
...current,
|
|
21506
22606
|
constraints: this.context.constraints,
|
|
21507
|
-
|
|
22607
|
+
judgments: [...current.judgments ?? [], entry],
|
|
22608
|
+
critical: [...new Set([...current.critical ?? [], "judgments"])],
|
|
21508
22609
|
policy: resolved.data,
|
|
21509
22610
|
policyChanges: nextChange ? [...current.policyChanges, nextChange] : current.policyChanges
|
|
21510
22611
|
};
|
|
@@ -21706,9 +22807,14 @@ class WorkitCore {
|
|
|
21706
22807
|
return workspace;
|
|
21707
22808
|
if (!workspace.data)
|
|
21708
22809
|
return failure2("not_found", "workspace not found");
|
|
21709
|
-
|
|
22810
|
+
const binding = {
|
|
22811
|
+
...input.binding,
|
|
22812
|
+
taskId: input.binding.taskId ?? task.data.id,
|
|
22813
|
+
workspaceId: input.binding.workspaceId ?? workspace.data.id
|
|
22814
|
+
};
|
|
22815
|
+
if (binding.taskId !== task.data.id || binding.workspaceId !== workspace.data.id)
|
|
21710
22816
|
return failure2("invalid_input", "decision task or workspace binding is invalid");
|
|
21711
|
-
const content = verifyDecisionContentAtRoot(this.store.root,
|
|
22817
|
+
const content = verifyDecisionContentAtRoot(this.store.root, binding);
|
|
21712
22818
|
if (!content.ok)
|
|
21713
22819
|
return content;
|
|
21714
22820
|
const knownRequirements = new Set(task.data.policy?.requirements.map((item) => item.id) ?? []);
|
|
@@ -21716,7 +22822,7 @@ class WorkitCore {
|
|
|
21716
22822
|
return failure2("invalid_input", "decision references an unknown requirement");
|
|
21717
22823
|
const base = {
|
|
21718
22824
|
purpose: input.purpose,
|
|
21719
|
-
binding
|
|
22825
|
+
binding,
|
|
21720
22826
|
response: input.response,
|
|
21721
22827
|
requirementIds: input.requirementIds,
|
|
21722
22828
|
revoked: null
|
|
@@ -22075,16 +23181,12 @@ class WorkitCore {
|
|
|
22075
23181
|
now: trustedNow(this.context)
|
|
22076
23182
|
});
|
|
22077
23183
|
}
|
|
22078
|
-
resolve(task,
|
|
23184
|
+
resolve(task, judgment) {
|
|
22079
23185
|
return resolvePolicy({
|
|
22080
23186
|
intent: task.intent.data,
|
|
22081
|
-
|
|
23187
|
+
judgment,
|
|
22082
23188
|
constraints: this.context.constraints,
|
|
22083
|
-
|
|
22084
|
-
decisions: task.decisions.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data),
|
|
22085
|
-
findings: task.findings.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data),
|
|
22086
|
-
requirements: task.origin || task.policy === null ? [] : task.policy.requirements
|
|
22087
|
-
}
|
|
23189
|
+
verification: workspaceVerification(this.context.root)
|
|
22088
23190
|
});
|
|
22089
23191
|
}
|
|
22090
23192
|
summary(task) {
|
|
@@ -22410,9 +23512,9 @@ class WorkitCore {
|
|
|
22410
23512
|
}
|
|
22411
23513
|
}
|
|
22412
23514
|
// packages/workit-core/src/core/repo-context.ts
|
|
22413
|
-
import { spawnSync as
|
|
22414
|
-
import { existsSync as
|
|
22415
|
-
import
|
|
23515
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
23516
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
23517
|
+
import path16 from "node:path";
|
|
22416
23518
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
22417
23519
|
try {
|
|
22418
23520
|
gitRevisionParts(value);
|
|
@@ -22439,7 +23541,7 @@ var invalidRange = (cwd) => ({
|
|
|
22439
23541
|
cwd
|
|
22440
23542
|
});
|
|
22441
23543
|
var runGit = (cwd, args) => {
|
|
22442
|
-
const result =
|
|
23544
|
+
const result = spawnSync9("git", args, { cwd, encoding: "utf8" });
|
|
22443
23545
|
return {
|
|
22444
23546
|
stdout: (result.stdout ?? "").trimEnd(),
|
|
22445
23547
|
stderr: (result.stderr ?? "").trimEnd(),
|
|
@@ -22450,11 +23552,11 @@ var repoRoot = (cwd) => {
|
|
|
22450
23552
|
const r = runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
22451
23553
|
return r.exitCode === 0 && r.stdout ? r.stdout : cwd;
|
|
22452
23554
|
};
|
|
22453
|
-
var
|
|
23555
|
+
var currentBranch2 = (cwd) => {
|
|
22454
23556
|
const r = runGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
22455
23557
|
return r.exitCode === 0 && r.stdout ? r.stdout : "unknown";
|
|
22456
23558
|
};
|
|
22457
|
-
var
|
|
23559
|
+
var defaultBase2 = (cwd) => {
|
|
22458
23560
|
for (const ref of [
|
|
22459
23561
|
"origin/main",
|
|
22460
23562
|
"main",
|
|
@@ -22468,11 +23570,11 @@ var defaultBase = (cwd) => {
|
|
|
22468
23570
|
}
|
|
22469
23571
|
return "HEAD~1";
|
|
22470
23572
|
};
|
|
22471
|
-
var rangeArgOrDefault = (arg, cwd) => arg ? arg : `${
|
|
23573
|
+
var rangeArgOrDefault = (arg, cwd) => arg ? arg : `${defaultBase2(cwd)}...HEAD`;
|
|
22472
23574
|
var isProtectedBranch = (branch) => ["main", "master", "develop", "prod", "production"].includes(branch);
|
|
22473
23575
|
var isPrBranch = (branch) => branch.startsWith("feature/") || branch.startsWith("bugfix/");
|
|
22474
23576
|
function resolvePrBranchContext(cwd) {
|
|
22475
|
-
const branch =
|
|
23577
|
+
const branch = currentBranch2(cwd);
|
|
22476
23578
|
if (isProtectedBranch(branch)) {
|
|
22477
23579
|
return {
|
|
22478
23580
|
ok: false,
|
|
@@ -22546,7 +23648,7 @@ var findPrTemplate = (cwd) => {
|
|
|
22546
23648
|
]) {
|
|
22547
23649
|
const parts = rel.split("/");
|
|
22548
23650
|
const base = parts[parts.length - 1];
|
|
22549
|
-
const dir = parts.length > 1 ?
|
|
23651
|
+
const dir = parts.length > 1 ? path16.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
22550
23652
|
const actual = probeCaseInsensitive(dir, base);
|
|
22551
23653
|
if (actual)
|
|
22552
23654
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -22577,8 +23679,8 @@ var diffStatForRange = (cwd, range) => {
|
|
|
22577
23679
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
22578
23680
|
};
|
|
22579
23681
|
var packageScripts = (cwd, keys) => {
|
|
22580
|
-
const pkgPath =
|
|
22581
|
-
if (!
|
|
23682
|
+
const pkgPath = path16.join(cwd, "package.json");
|
|
23683
|
+
if (!existsSync6(pkgPath))
|
|
22582
23684
|
return [];
|
|
22583
23685
|
try {
|
|
22584
23686
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
@@ -22603,9 +23705,9 @@ var documentationFiles = (cwd) => {
|
|
|
22603
23705
|
if (entry.isDirectory()) {
|
|
22604
23706
|
if (excluded.has(entry.name))
|
|
22605
23707
|
continue;
|
|
22606
|
-
walk(
|
|
23708
|
+
walk(path16.join(dir, entry.name), depth + 1);
|
|
22607
23709
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
22608
|
-
matches.push("./" +
|
|
23710
|
+
matches.push("./" + path16.relative(cwd, path16.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
22609
23711
|
}
|
|
22610
23712
|
}
|
|
22611
23713
|
};
|
|
@@ -22613,7 +23715,7 @@ var documentationFiles = (cwd) => {
|
|
|
22613
23715
|
return matches.toSorted().slice(0, 200);
|
|
22614
23716
|
};
|
|
22615
23717
|
var readTrimmed2 = (file, maxLines) => {
|
|
22616
|
-
if (!
|
|
23718
|
+
if (!existsSync6(file))
|
|
22617
23719
|
return "";
|
|
22618
23720
|
const lines = readFileSync5(file, "utf8").split(`
|
|
22619
23721
|
`);
|
|
@@ -22621,7 +23723,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
22621
23723
|
`);
|
|
22622
23724
|
};
|
|
22623
23725
|
function prReadyContext(root, range) {
|
|
22624
|
-
const cwd =
|
|
23726
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
22625
23727
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
22626
23728
|
return invalidRange(cwd);
|
|
22627
23729
|
let stdout = "";
|
|
@@ -22637,7 +23739,7 @@ function prReadyContext(root, range) {
|
|
|
22637
23739
|
stdout += printSection("Repository");
|
|
22638
23740
|
stdout += `root: ${cwd}
|
|
22639
23741
|
`;
|
|
22640
|
-
stdout += `branch: ${
|
|
23742
|
+
stdout += `branch: ${currentBranch2(cwd)}
|
|
22641
23743
|
`;
|
|
22642
23744
|
stdout += `range: ${range}
|
|
22643
23745
|
`;
|
|
@@ -22679,7 +23781,7 @@ ${status.stderr}` : "");
|
|
|
22679
23781
|
stdout += `template_path: ${template}
|
|
22680
23782
|
|
|
22681
23783
|
`;
|
|
22682
|
-
stdout += readTrimmed2(
|
|
23784
|
+
stdout += readTrimmed2(path16.join(cwd, template), 220);
|
|
22683
23785
|
} else {
|
|
22684
23786
|
stdout += `template_path: none
|
|
22685
23787
|
|
|
@@ -22689,7 +23791,7 @@ ${status.stderr}` : "");
|
|
|
22689
23791
|
stdout += `
|
|
22690
23792
|
`;
|
|
22691
23793
|
stdout += printSection("Recent Validation Signals");
|
|
22692
|
-
if (
|
|
23794
|
+
if (existsSync6(path16.join(cwd, "package.json"))) {
|
|
22693
23795
|
stdout += `package.json detected. Common scripts:
|
|
22694
23796
|
`;
|
|
22695
23797
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -22698,7 +23800,7 @@ ${status.stderr}` : "");
|
|
|
22698
23800
|
`) + `
|
|
22699
23801
|
`;
|
|
22700
23802
|
}
|
|
22701
|
-
if (
|
|
23803
|
+
if (existsSync6(path16.join(cwd, "Cargo.toml")) || existsSync6(path16.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
22702
23804
|
stdout += `Rust project detected.
|
|
22703
23805
|
`;
|
|
22704
23806
|
}
|
|
@@ -22731,7 +23833,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
22731
23833
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
22732
23834
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
22733
23835
|
function changelogContext(root, range) {
|
|
22734
|
-
const cwd =
|
|
23836
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
22735
23837
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
22736
23838
|
return invalidRange(cwd);
|
|
22737
23839
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -22741,7 +23843,7 @@ function changelogContext(root, range) {
|
|
|
22741
23843
|
stdout += printSection("Repository");
|
|
22742
23844
|
stdout += `root: ${cwd}
|
|
22743
23845
|
`;
|
|
22744
|
-
stdout += `branch: ${
|
|
23846
|
+
stdout += `branch: ${currentBranch2(cwd)}
|
|
22745
23847
|
`;
|
|
22746
23848
|
stdout += `range: ${resolvedRange}
|
|
22747
23849
|
`;
|
|
@@ -22749,8 +23851,8 @@ function changelogContext(root, range) {
|
|
|
22749
23851
|
stdout += CHANGELOG_RULES + `
|
|
22750
23852
|
`;
|
|
22751
23853
|
stdout += printSection("Existing CHANGELOG.md");
|
|
22752
|
-
const changelogPath =
|
|
22753
|
-
if (
|
|
23854
|
+
const changelogPath = path16.join(cwd, "CHANGELOG.md");
|
|
23855
|
+
if (existsSync6(changelogPath)) {
|
|
22754
23856
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
22755
23857
|
`;
|
|
22756
23858
|
} else {
|
|
@@ -22769,7 +23871,7 @@ function changelogContext(root, range) {
|
|
|
22769
23871
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
22770
23872
|
}
|
|
22771
23873
|
function docsRefreshContext(root, range) {
|
|
22772
|
-
const cwd =
|
|
23874
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
22773
23875
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
22774
23876
|
return invalidRange(cwd);
|
|
22775
23877
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -22779,7 +23881,7 @@ function docsRefreshContext(root, range) {
|
|
|
22779
23881
|
stdout += printSection("Repository");
|
|
22780
23882
|
stdout += `root: ${cwd}
|
|
22781
23883
|
`;
|
|
22782
|
-
stdout += `branch: ${
|
|
23884
|
+
stdout += `branch: ${currentBranch2(cwd)}
|
|
22783
23885
|
`;
|
|
22784
23886
|
stdout += `range: ${resolvedRange}
|
|
22785
23887
|
`;
|
|
@@ -22791,8 +23893,8 @@ function docsRefreshContext(root, range) {
|
|
|
22791
23893
|
`) + `
|
|
22792
23894
|
`;
|
|
22793
23895
|
stdout += printSection("README Preview");
|
|
22794
|
-
const readme =
|
|
22795
|
-
if (
|
|
23896
|
+
const readme = path16.join(cwd, "README.md");
|
|
23897
|
+
if (existsSync6(readme)) {
|
|
22796
23898
|
stdout += readTrimmed2(readme, 220) + `
|
|
22797
23899
|
`;
|
|
22798
23900
|
} else {
|
|
@@ -22800,8 +23902,8 @@ function docsRefreshContext(root, range) {
|
|
|
22800
23902
|
`;
|
|
22801
23903
|
}
|
|
22802
23904
|
stdout += printSection("Package Scripts");
|
|
22803
|
-
const pkgPath =
|
|
22804
|
-
if (
|
|
23905
|
+
const pkgPath = path16.join(cwd, "package.json");
|
|
23906
|
+
if (existsSync6(pkgPath)) {
|
|
22805
23907
|
try {
|
|
22806
23908
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
22807
23909
|
stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
|
|
@@ -22814,7 +23916,7 @@ function docsRefreshContext(root, range) {
|
|
|
22814
23916
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
22815
23917
|
}
|
|
22816
23918
|
function releaseNotesContext(root, rangeOrTag) {
|
|
22817
|
-
const cwd =
|
|
23919
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
22818
23920
|
if (!rangeOrTag) {
|
|
22819
23921
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
22820
23922
|
`, exitCode: 1, cwd };
|
|
@@ -22828,7 +23930,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
22828
23930
|
stdout += printSection("Repository");
|
|
22829
23931
|
stdout += `root: ${cwd}
|
|
22830
23932
|
`;
|
|
22831
|
-
stdout += `branch: ${
|
|
23933
|
+
stdout += `branch: ${currentBranch2(cwd)}
|
|
22832
23934
|
`;
|
|
22833
23935
|
stdout += `requested: ${rangeOrTag}
|
|
22834
23936
|
`;
|
|
@@ -22852,7 +23954,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
22852
23954
|
`;
|
|
22853
23955
|
stdout += printSection("Existing Release Files");
|
|
22854
23956
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
22855
|
-
if (
|
|
23957
|
+
if (existsSync6(path16.join(cwd, rel)))
|
|
22856
23958
|
stdout += rel + `
|
|
22857
23959
|
`;
|
|
22858
23960
|
}
|
|
@@ -22880,12 +23982,12 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
22880
23982
|
}
|
|
22881
23983
|
|
|
22882
23984
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
22883
|
-
import { spawnSync as
|
|
23985
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
22884
23986
|
|
|
22885
23987
|
// packages/workit-core/src/core/pr-create.ts
|
|
22886
|
-
import
|
|
22887
|
-
import
|
|
22888
|
-
import { spawnSync as
|
|
23988
|
+
import fs13 from "node:fs";
|
|
23989
|
+
import path17 from "node:path";
|
|
23990
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
22889
23991
|
// packages/workit-core/src/forge/pr-body.ts
|
|
22890
23992
|
function parseGhIssue(value) {
|
|
22891
23993
|
const m = /issues\/(\d+)/.exec(value);
|
|
@@ -22895,13 +23997,13 @@ function parseGhIssue(value) {
|
|
|
22895
23997
|
// packages/workit-core/src/core/pr-create.ts
|
|
22896
23998
|
function whichOnPath(tool) {
|
|
22897
23999
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
22898
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
24000
|
+
for (const dir of (process.env.PATH ?? "").split(path17.delimiter)) {
|
|
22899
24001
|
if (!dir)
|
|
22900
24002
|
continue;
|
|
22901
24003
|
for (const name of names) {
|
|
22902
|
-
const candidate =
|
|
24004
|
+
const candidate = path17.join(dir, name);
|
|
22903
24005
|
try {
|
|
22904
|
-
|
|
24006
|
+
fs13.accessSync(candidate, fs13.constants.X_OK);
|
|
22905
24007
|
return candidate;
|
|
22906
24008
|
} catch {}
|
|
22907
24009
|
}
|
|
@@ -22910,7 +24012,7 @@ function whichOnPath(tool) {
|
|
|
22910
24012
|
}
|
|
22911
24013
|
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
22912
24014
|
var pushRemote = (cwd) => {
|
|
22913
|
-
const result =
|
|
24015
|
+
const result = spawnSync10("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
22914
24016
|
cwd,
|
|
22915
24017
|
encoding: "utf8"
|
|
22916
24018
|
});
|
|
@@ -22941,7 +24043,7 @@ var defaultCli = (bin, available) => {
|
|
|
22941
24043
|
if (!available)
|
|
22942
24044
|
return null;
|
|
22943
24045
|
return (args, root) => {
|
|
22944
|
-
const out =
|
|
24046
|
+
const out = spawnSync11(bin, args, {
|
|
22945
24047
|
cwd: root,
|
|
22946
24048
|
encoding: "utf8",
|
|
22947
24049
|
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
@@ -23019,8 +24121,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
23019
24121
|
};
|
|
23020
24122
|
|
|
23021
24123
|
// packages/workit-core/src/core/youtrack.ts
|
|
23022
|
-
import
|
|
23023
|
-
import
|
|
24124
|
+
import fs14 from "node:fs";
|
|
24125
|
+
import path18 from "node:path";
|
|
23024
24126
|
|
|
23025
24127
|
// packages/workit-core/src/core/templates.ts
|
|
23026
24128
|
var repoRoot2 = assetRoot();
|
|
@@ -23033,22 +24135,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
23033
24135
|
// packages/workit-core/src/core/youtrack.ts
|
|
23034
24136
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
23035
24137
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
23036
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
23037
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
24138
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(configDir(), "youtrack.json");
|
|
24139
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(resolveConfigDir(), "youtrack.json");
|
|
23038
24140
|
var youTrackTokenModeOk = (p) => {
|
|
23039
24141
|
if (process.platform === "win32")
|
|
23040
24142
|
return true;
|
|
23041
|
-
const mode =
|
|
24143
|
+
const mode = fs14.statSync(p).mode & 511;
|
|
23042
24144
|
return mode === 384;
|
|
23043
24145
|
};
|
|
23044
24146
|
function readYouTrackConfig(required) {
|
|
23045
24147
|
const cfgPath = youTrackConfigPath();
|
|
23046
|
-
if (!
|
|
24148
|
+
if (!fs14.existsSync(cfgPath)) {
|
|
23047
24149
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
23048
24150
|
}
|
|
23049
24151
|
let parsed;
|
|
23050
24152
|
try {
|
|
23051
|
-
parsed = JSON.parse(
|
|
24153
|
+
parsed = JSON.parse(fs14.readFileSync(cfgPath, "utf8"));
|
|
23052
24154
|
} catch {
|
|
23053
24155
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
23054
24156
|
}
|
|
@@ -23059,11 +24161,11 @@ function readYouTrackConfig(required) {
|
|
|
23059
24161
|
}
|
|
23060
24162
|
var readYouTrackContextConfig = () => {
|
|
23061
24163
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
23062
|
-
if (!
|
|
24164
|
+
if (!fs14.existsSync(cfgPath))
|
|
23063
24165
|
return { data: {} };
|
|
23064
24166
|
let parsed;
|
|
23065
24167
|
try {
|
|
23066
|
-
parsed = JSON.parse(
|
|
24168
|
+
parsed = JSON.parse(fs14.readFileSync(cfgPath, "utf8"));
|
|
23067
24169
|
} catch {
|
|
23068
24170
|
return { error: "YouTrack configuration is unavailable" };
|
|
23069
24171
|
}
|
|
@@ -23102,12 +24204,12 @@ var youTrackToken = () => {
|
|
|
23102
24204
|
if ("error" in loaded)
|
|
23103
24205
|
return loaded;
|
|
23104
24206
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
23105
|
-
const tokenPath = tokenFile ?
|
|
23106
|
-
if (!tokenPath || !
|
|
24207
|
+
const tokenPath = tokenFile ? path18.isAbsolute(tokenFile) ? path18.resolve(tokenFile) : path18.resolve(process.cwd(), tokenFile) : "";
|
|
24208
|
+
if (!tokenPath || !fs14.existsSync(tokenPath))
|
|
23107
24209
|
return { error: "missing youtrack.token" };
|
|
23108
24210
|
if (!youTrackTokenModeOk(tokenPath))
|
|
23109
24211
|
return { error: "youtrack.token mode must be 0600" };
|
|
23110
|
-
const token =
|
|
24212
|
+
const token = fs14.readFileSync(tokenPath, "utf8").trim();
|
|
23111
24213
|
if (!token)
|
|
23112
24214
|
return { error: "empty token file" };
|
|
23113
24215
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -23172,9 +24274,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
23172
24274
|
} catch {
|
|
23173
24275
|
continue;
|
|
23174
24276
|
}
|
|
23175
|
-
if (!
|
|
24277
|
+
if (!fs14.existsSync(full))
|
|
23176
24278
|
continue;
|
|
23177
|
-
const text =
|
|
24279
|
+
const text = fs14.readFileSync(full, "utf8");
|
|
23178
24280
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
23179
24281
|
if (m)
|
|
23180
24282
|
return m[1];
|
|
@@ -23444,22 +24546,22 @@ function resolveGitRevision(root, value) {
|
|
|
23444
24546
|
}
|
|
23445
24547
|
function resolveInside(root, candidate) {
|
|
23446
24548
|
const base = realpathSync5(root);
|
|
23447
|
-
const target =
|
|
23448
|
-
if (target !== base && !target.startsWith(base +
|
|
24549
|
+
const target = path19.resolve(base, candidate);
|
|
24550
|
+
if (target !== base && !target.startsWith(base + path19.sep)) {
|
|
23449
24551
|
throw new Error("path must stay inside repository root");
|
|
23450
24552
|
}
|
|
23451
24553
|
let ancestor = target;
|
|
23452
|
-
while (!
|
|
23453
|
-
ancestor =
|
|
24554
|
+
while (!existsSync7(ancestor))
|
|
24555
|
+
ancestor = path19.dirname(ancestor);
|
|
23454
24556
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
23455
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
24557
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path19.sep)) {
|
|
23456
24558
|
throw new Error("path must stay inside repository root");
|
|
23457
24559
|
}
|
|
23458
24560
|
return target;
|
|
23459
24561
|
}
|
|
23460
24562
|
function run2(root, executable, args, env = {}) {
|
|
23461
24563
|
const cwd = realpathSync5(root);
|
|
23462
|
-
const result =
|
|
24564
|
+
const result = spawnSync12(executable, args, {
|
|
23463
24565
|
cwd,
|
|
23464
24566
|
encoding: "utf8",
|
|
23465
24567
|
env: { ...process.env, ...env }
|
|
@@ -23483,8 +24585,8 @@ var SUPPORT_MATRIX = {
|
|
|
23483
24585
|
};
|
|
23484
24586
|
|
|
23485
24587
|
// packages/workit-mcp/src/server.ts
|
|
23486
|
-
import { existsSync as
|
|
23487
|
-
import
|
|
24588
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, realpathSync as realpathSync6 } from "node:fs";
|
|
24589
|
+
import path20 from "node:path";
|
|
23488
24590
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
23489
24591
|
function isZ4Schema(s) {
|
|
23490
24592
|
const schema = s;
|
|
@@ -25863,7 +26965,7 @@ class StdioServerTransport {
|
|
|
25863
26965
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
25864
26966
|
|
|
25865
26967
|
// packages/workit-core/src/core/logger.ts
|
|
25866
|
-
import
|
|
26968
|
+
import os4 from "node:os";
|
|
25867
26969
|
var REDACTED = "[REDACTED]";
|
|
25868
26970
|
var SECRET_VALUE = /\b(?:Bearer|Basic|Digest|Token)\s+\S+/gi;
|
|
25869
26971
|
var KEY_EQ_VALUE = /\b([A-Za-z0-9_-]*(?:token|secret|password|passwd|apikey|api[_-]?key|authorization|credential|bearer)[A-Za-z0-9_-]*)([:=]\s*).+/gi;
|
|
@@ -25905,8 +27007,8 @@ var SENSITIVE_WORDS = new Set([
|
|
|
25905
27007
|
"query"
|
|
25906
27008
|
]);
|
|
25907
27009
|
var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
|
|
25908
|
-
var
|
|
25909
|
-
var homePattern = new RegExp(`${
|
|
27010
|
+
var escapeRegExp2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
27011
|
+
var homePattern = new RegExp(`${escapeRegExp2(os4.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
|
|
25910
27012
|
var applyPatterns = (value) => {
|
|
25911
27013
|
let out = value;
|
|
25912
27014
|
out = out.replace(SECRET_VALUE, REDACTED);
|
|
@@ -25957,13 +27059,13 @@ var rootVariants = (workspaceRoot) => {
|
|
|
25957
27059
|
if (!workspaceRoot)
|
|
25958
27060
|
return [];
|
|
25959
27061
|
const roots = new Set;
|
|
25960
|
-
for (const candidate of [workspaceRoot,
|
|
25961
|
-
if (
|
|
27062
|
+
for (const candidate of [workspaceRoot, path20.resolve(workspaceRoot)]) {
|
|
27063
|
+
if (path20.isAbsolute(candidate) && candidate !== path20.parse(candidate).root)
|
|
25962
27064
|
roots.add(candidate);
|
|
25963
27065
|
}
|
|
25964
27066
|
try {
|
|
25965
27067
|
const canonical = realpathSync6(workspaceRoot);
|
|
25966
|
-
if (canonical !==
|
|
27068
|
+
if (canonical !== path20.parse(canonical).root)
|
|
25967
27069
|
roots.add(canonical);
|
|
25968
27070
|
} catch {}
|
|
25969
27071
|
return [...roots].toSorted((left, right) => right.length - left.length);
|
|
@@ -25985,39 +27087,20 @@ function assertMcpHost(host) {
|
|
|
25985
27087
|
throw new Error("MCP host must be cursor, codex_cli, or codex_desktop");
|
|
25986
27088
|
}
|
|
25987
27089
|
}
|
|
25988
|
-
var
|
|
25989
|
-
var
|
|
25990
|
-
var
|
|
25991
|
-
|
|
25992
|
-
|
|
25993
|
-
|
|
25994
|
-
|
|
25995
|
-
|
|
25996
|
-
var readOnlyBranches = (family) => schemaBranches(boundedOperationJsonSchema(family)).filter((branch) => {
|
|
25997
|
-
const action = branch.properties?.action?.const;
|
|
25998
|
-
return typeof action === "string" && READ_ONLY_ACTIONS.has(action);
|
|
25999
|
-
});
|
|
26000
|
-
var advertisedToolSchemas = (attested) => {
|
|
26001
|
-
if (attested)
|
|
26002
|
-
return OPERATION_FAMILIES.map((family) => ({
|
|
27090
|
+
var READ_ONLY_ACTIONS2 = new Set(["list", "inspect", "preview", "explain", "export"]);
|
|
27091
|
+
var requiresCallerIdentity = (input) => typeof input !== "object" || input === null || !READ_ONLY_ACTIONS2.has(String(input.action));
|
|
27092
|
+
var advertisedToolSchemas = (attested) => OPERATION_FAMILIES.flatMap((family) => {
|
|
27093
|
+
const schema = flatOperationJsonSchema(family, { readOnly: !attested });
|
|
27094
|
+
if (!schema)
|
|
27095
|
+
return [];
|
|
27096
|
+
return [
|
|
27097
|
+
{
|
|
26003
27098
|
name: `workit_${family}`,
|
|
26004
|
-
description: operationDescription(family)
|
|
26005
|
-
inputSchema:
|
|
26006
|
-
}
|
|
26007
|
-
|
|
26008
|
-
|
|
26009
|
-
if (!branches.length)
|
|
26010
|
-
return [];
|
|
26011
|
-
const schema = boundedOperationJsonSchema(family);
|
|
26012
|
-
return [
|
|
26013
|
-
{
|
|
26014
|
-
name: `workit_${family}`,
|
|
26015
|
-
description: `${operationDescription(family)} Unattested MCP callers can only run read-only actions; mutating operations require an attested host session.`,
|
|
26016
|
-
inputSchema: { type: "object", ...schema, oneOf: branches }
|
|
26017
|
-
}
|
|
26018
|
-
];
|
|
26019
|
-
});
|
|
26020
|
-
};
|
|
27099
|
+
description: attested ? operationDescription(family) : `${operationDescription(family)} Unattested MCP callers can only run read-only actions; mutating operations require an attested host session.`,
|
|
27100
|
+
inputSchema: schema
|
|
27101
|
+
}
|
|
27102
|
+
];
|
|
27103
|
+
});
|
|
26021
27104
|
var sanitizeFailure = (result, workspaceRoot) => {
|
|
26022
27105
|
if (result.ok)
|
|
26023
27106
|
return result;
|
|
@@ -26064,12 +27147,12 @@ var thrownResult = (tool, error, workspaceRoot) => {
|
|
|
26064
27147
|
};
|
|
26065
27148
|
function createMcpServer(host, contextProvider) {
|
|
26066
27149
|
assertMcpHost(host);
|
|
26067
|
-
const here =
|
|
27150
|
+
const here = path20.dirname(fileURLToPath2(import.meta.url));
|
|
26068
27151
|
const sourceMarker = markSourcesLoaded([
|
|
26069
|
-
|
|
26070
|
-
|
|
26071
|
-
|
|
26072
|
-
].filter((file) =>
|
|
27152
|
+
path20.join(here, "server.ts"),
|
|
27153
|
+
path20.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
|
|
27154
|
+
path20.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
27155
|
+
].filter((file) => existsSync8(file)));
|
|
26073
27156
|
let staleWarned = false;
|
|
26074
27157
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
26075
27158
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -26193,7 +27276,7 @@ function createMcpServer(host, contextProvider) {
|
|
|
26193
27276
|
details: { operation: family }
|
|
26194
27277
|
}, workspaceRoot);
|
|
26195
27278
|
}
|
|
26196
|
-
const parsed =
|
|
27279
|
+
const parsed = parseAdvertisedOperation(family, request.params.arguments, host);
|
|
26197
27280
|
if (!parsed.ok)
|
|
26198
27281
|
return resultForClient(parsed, workspaceRoot);
|
|
26199
27282
|
if (context.callerAttested !== true && requiresCallerIdentity(parsed.data))
|
|
@@ -26260,12 +27343,34 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
26260
27343
|
// packages/workit-core/src/hooks/context.ts
|
|
26261
27344
|
var signals = new Map;
|
|
26262
27345
|
var cache = new Map;
|
|
27346
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
27347
|
+
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
27348
|
+
var PREFIXES = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
27349
|
+
var cache2 = new Map;
|
|
27350
|
+
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
27351
|
+
var SHELLS = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
|
|
27352
|
+
var WRITE_TOOLS = new Set([
|
|
27353
|
+
"write",
|
|
27354
|
+
"edit",
|
|
27355
|
+
"multiedit",
|
|
27356
|
+
"notebookedit",
|
|
27357
|
+
"applypatch",
|
|
27358
|
+
"patch",
|
|
27359
|
+
"delete",
|
|
27360
|
+
"remove",
|
|
27361
|
+
"rename",
|
|
27362
|
+
"mkdir",
|
|
27363
|
+
"mv",
|
|
27364
|
+
"cp",
|
|
27365
|
+
"touch",
|
|
27366
|
+
"strreplace",
|
|
27367
|
+
"searchreplace",
|
|
27368
|
+
"editfile",
|
|
27369
|
+
"writefile"
|
|
27370
|
+
]);
|
|
26263
27371
|
// packages/workit-core/src/hooks/handle.ts
|
|
26264
27372
|
var offered = new Set;
|
|
26265
27373
|
var CLAUDE_JUDGES = new Set(["workit:verifier", "workit:reviewer"]);
|
|
26266
|
-
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
26267
|
-
var SHELLS = new Set(["sh", "bash", "zsh", "dash", "pwsh", "powershell", "cmd"]);
|
|
26268
|
-
|
|
26269
27374
|
// packages/workit-core/src/hooks/hosts/claude-code.ts
|
|
26270
27375
|
var SOURCES = new Set(["startup", "resume", "clear", "compact", "fork"]);
|
|
26271
27376
|
var SHELL_TOOLS = new Set(["Bash", "PowerShell"]);
|
|
@@ -26287,6 +27392,7 @@ var CODEX_DESCRIPTOR = {
|
|
|
26287
27392
|
"context.turn": undocumented,
|
|
26288
27393
|
"shell.pre": { support: "native", native: "PreToolUse" },
|
|
26289
27394
|
"tool.pre": { support: "native", native: "PreToolUse" },
|
|
27395
|
+
"write.pre": { support: "undocumented", native: null },
|
|
26290
27396
|
"shell.post": undocumented,
|
|
26291
27397
|
"subagent.start": { support: "native", native: "SubagentStart" },
|
|
26292
27398
|
"subagent.stop": { support: "native", native: "SubagentStop" },
|
|
@@ -26399,8 +27505,8 @@ var codexQualification = (host) => host === "codex_cli" ? { surface: host, cli:
|
|
|
26399
27505
|
var resolveCodexWorkspaceRoot = (pluginRoot, env) => {
|
|
26400
27506
|
const explicit = env.WORKFLOW_WORKSPACE_ROOT;
|
|
26401
27507
|
const inherited = env.PWD;
|
|
26402
|
-
const candidate = explicit ?? (inherited &&
|
|
26403
|
-
if (!candidate || !
|
|
27508
|
+
const candidate = explicit ?? (inherited && path21.resolve(inherited) !== path21.resolve(pluginRoot) ? inherited : undefined);
|
|
27509
|
+
if (!candidate || !path21.isAbsolute(candidate) || !existsSync9(candidate))
|
|
26404
27510
|
return null;
|
|
26405
27511
|
try {
|
|
26406
27512
|
if (!statSync3(candidate).isDirectory())
|