@brainervirus/workit-mcp 6.0.1 → 7.0.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/README.md +2 -1
- package/dist/index.js +1590 -942
- package/package.json +2 -2
- package/src/server.ts +15 -44
package/dist/index.js
CHANGED
|
@@ -6620,11 +6620,11 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
6620
6620
|
|
|
6621
6621
|
// packages/workit-mcp/src/index.ts
|
|
6622
6622
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6623
|
-
import
|
|
6623
|
+
import path21 from "node:path";
|
|
6624
6624
|
|
|
6625
6625
|
// packages/workit-mcp/src/server.ts
|
|
6626
|
-
import { existsSync as
|
|
6627
|
-
import
|
|
6626
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, realpathSync as realpathSync6 } from "node:fs";
|
|
6627
|
+
import path20 from "node:path";
|
|
6628
6628
|
|
|
6629
6629
|
// node_modules/zod/v4/core/util.js
|
|
6630
6630
|
function getEnumValues(entries) {
|
|
@@ -12848,12 +12848,6 @@ var numberProcessor = (schema, ctx, _json, params) => {
|
|
|
12848
12848
|
var booleanProcessor = (_schema, _ctx, json, _params) => {
|
|
12849
12849
|
json.type = "boolean";
|
|
12850
12850
|
};
|
|
12851
|
-
var bigintProcessor = (schema, ctx, json, params) => {
|
|
12852
|
-
handleUnrepresentable(schema, ctx, json, params, "BigInt cannot be represented in JSON Schema");
|
|
12853
|
-
};
|
|
12854
|
-
var symbolProcessor = (schema, ctx, json, params) => {
|
|
12855
|
-
handleUnrepresentable(schema, ctx, json, params, "Symbols cannot be represented in JSON Schema");
|
|
12856
|
-
};
|
|
12857
12851
|
var nullProcessor = (_schema, ctx, json, _params) => {
|
|
12858
12852
|
if (ctx.target === "openapi-3.0") {
|
|
12859
12853
|
json.type = "string";
|
|
@@ -12863,20 +12857,10 @@ var nullProcessor = (_schema, ctx, json, _params) => {
|
|
|
12863
12857
|
json.type = "null";
|
|
12864
12858
|
}
|
|
12865
12859
|
};
|
|
12866
|
-
var undefinedProcessor = (schema, ctx, json, params) => {
|
|
12867
|
-
handleUnrepresentable(schema, ctx, json, params, "Undefined cannot be represented in JSON Schema");
|
|
12868
|
-
};
|
|
12869
|
-
var voidProcessor = (schema, ctx, json, params) => {
|
|
12870
|
-
handleUnrepresentable(schema, ctx, json, params, "Void cannot be represented in JSON Schema");
|
|
12871
|
-
};
|
|
12872
12860
|
var neverProcessor = (_schema, _ctx, json, _params) => {
|
|
12873
12861
|
json.not = {};
|
|
12874
12862
|
};
|
|
12875
|
-
var anyProcessor = (_schema, _ctx, _json, _params) => {};
|
|
12876
12863
|
var unknownProcessor = (_schema, _ctx, _json, _params) => {};
|
|
12877
|
-
var dateProcessor = (schema, ctx, json, params) => {
|
|
12878
|
-
handleUnrepresentable(schema, ctx, json, params, "Date cannot be represented in JSON Schema");
|
|
12879
|
-
};
|
|
12880
12864
|
var enumProcessor = (schema, _ctx, json, _params) => {
|
|
12881
12865
|
const def = schema._zod.def;
|
|
12882
12866
|
const values = getEnumValues(def.entries);
|
|
@@ -12929,54 +12913,12 @@ var literalProcessor = (schema, ctx, json, params) => {
|
|
|
12929
12913
|
json.enum = vals;
|
|
12930
12914
|
}
|
|
12931
12915
|
};
|
|
12932
|
-
var nanProcessor = (schema, ctx, json, params) => {
|
|
12933
|
-
handleUnrepresentable(schema, ctx, json, params, "NaN cannot be represented in JSON Schema");
|
|
12934
|
-
};
|
|
12935
|
-
var templateLiteralProcessor = (schema, _ctx, json, _params) => {
|
|
12936
|
-
const _json = json;
|
|
12937
|
-
const pattern = schema._zod.pattern;
|
|
12938
|
-
if (!pattern)
|
|
12939
|
-
throw new Error("Pattern not found in template literal");
|
|
12940
|
-
_json.type = "string";
|
|
12941
|
-
_json.pattern = pattern.source;
|
|
12942
|
-
};
|
|
12943
|
-
var fileProcessor = (schema, _ctx, json, _params) => {
|
|
12944
|
-
const _json = json;
|
|
12945
|
-
_json.type = "string";
|
|
12946
|
-
_json.format = "binary";
|
|
12947
|
-
_json.contentEncoding = "binary";
|
|
12948
|
-
const { minimum, maximum, mime } = aggregateChecks(schema);
|
|
12949
|
-
if (minimum !== undefined)
|
|
12950
|
-
_json.minLength = minimum;
|
|
12951
|
-
if (maximum !== undefined)
|
|
12952
|
-
_json.maxLength = maximum;
|
|
12953
|
-
if (!mime)
|
|
12954
|
-
return;
|
|
12955
|
-
if (mime.length === 0)
|
|
12956
|
-
_json.not = {};
|
|
12957
|
-
else if (mime.length === 1)
|
|
12958
|
-
_json.contentMediaType = mime[0];
|
|
12959
|
-
else
|
|
12960
|
-
_json.anyOf = mime.map((m) => ({ contentMediaType: m }));
|
|
12961
|
-
};
|
|
12962
|
-
var successProcessor = (_schema, _ctx, json, _params) => {
|
|
12963
|
-
json.type = "boolean";
|
|
12964
|
-
};
|
|
12965
12916
|
var customProcessor = (schema, ctx, json, params) => {
|
|
12966
12917
|
handleUnrepresentable(schema, ctx, json, params, "Custom types cannot be represented in JSON Schema");
|
|
12967
12918
|
};
|
|
12968
|
-
var functionProcessor = (schema, ctx, json, params) => {
|
|
12969
|
-
handleUnrepresentable(schema, ctx, json, params, "Function types cannot be represented in JSON Schema");
|
|
12970
|
-
};
|
|
12971
12919
|
var transformProcessor = (schema, ctx, json, params) => {
|
|
12972
12920
|
handleUnrepresentable(schema, ctx, json, params, "Transforms cannot be represented in JSON Schema");
|
|
12973
12921
|
};
|
|
12974
|
-
var mapProcessor = (schema, ctx, json, params) => {
|
|
12975
|
-
handleUnrepresentable(schema, ctx, json, params, "Map cannot be represented in JSON Schema");
|
|
12976
|
-
};
|
|
12977
|
-
var setProcessor = (schema, ctx, json, params) => {
|
|
12978
|
-
handleUnrepresentable(schema, ctx, json, params, "Set cannot be represented in JSON Schema");
|
|
12979
|
-
};
|
|
12980
12922
|
var arrayProcessor = (schema, ctx, _json, params) => {
|
|
12981
12923
|
const json = _json;
|
|
12982
12924
|
const def = schema._zod.def;
|
|
@@ -13070,70 +13012,6 @@ var intersectionProcessor = (schema, ctx, json, params) => {
|
|
|
13070
13012
|
json.allOf = allOf;
|
|
13071
13013
|
ctx.intersections.push(allOf);
|
|
13072
13014
|
};
|
|
13073
|
-
var tupleProcessor = (schema, ctx, _json, params) => {
|
|
13074
|
-
const json = _json;
|
|
13075
|
-
const def = schema._zod.def;
|
|
13076
|
-
json.type = "array";
|
|
13077
|
-
const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
13078
|
-
const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
13079
|
-
const prefixItems = def.items.map((x, i) => processSchema(x, ctx, {
|
|
13080
|
-
...params,
|
|
13081
|
-
path: [...params.path, prefixPath, i]
|
|
13082
|
-
}));
|
|
13083
|
-
const rest = def.rest ? processSchema(def.rest, ctx, {
|
|
13084
|
-
...params,
|
|
13085
|
-
path: [...params.path, restPath, ...ctx.target === "openapi-3.0" ? [def.items.length] : []]
|
|
13086
|
-
}) : null;
|
|
13087
|
-
let minItems = def.items.length;
|
|
13088
|
-
while (minItems > 0) {
|
|
13089
|
-
const item = def.items[minItems - 1];
|
|
13090
|
-
const optional = ctx.io === "input" ? inputOptin(item) !== undefined : item._zod.optout === "optional";
|
|
13091
|
-
if (!optional)
|
|
13092
|
-
break;
|
|
13093
|
-
minItems--;
|
|
13094
|
-
}
|
|
13095
|
-
const maxItems = def.items.length;
|
|
13096
|
-
const isClosed = !def.rest;
|
|
13097
|
-
if (ctx.target === "draft-2020-12") {
|
|
13098
|
-
json.prefixItems = prefixItems;
|
|
13099
|
-
if (isClosed) {
|
|
13100
|
-
json.items = false;
|
|
13101
|
-
} else if (rest) {
|
|
13102
|
-
json.items = rest;
|
|
13103
|
-
}
|
|
13104
|
-
if (minItems > 0)
|
|
13105
|
-
json.minItems = minItems;
|
|
13106
|
-
if (isClosed)
|
|
13107
|
-
json.maxItems = maxItems;
|
|
13108
|
-
} else if (ctx.target === "openapi-3.0") {
|
|
13109
|
-
json.items = {
|
|
13110
|
-
anyOf: prefixItems
|
|
13111
|
-
};
|
|
13112
|
-
if (rest) {
|
|
13113
|
-
json.items.anyOf.push(rest);
|
|
13114
|
-
}
|
|
13115
|
-
if (minItems > 0)
|
|
13116
|
-
json.minItems = minItems;
|
|
13117
|
-
if (isClosed)
|
|
13118
|
-
json.maxItems = maxItems;
|
|
13119
|
-
} else {
|
|
13120
|
-
json.items = prefixItems;
|
|
13121
|
-
if (isClosed) {
|
|
13122
|
-
json.additionalItems = false;
|
|
13123
|
-
} else if (rest) {
|
|
13124
|
-
json.additionalItems = rest;
|
|
13125
|
-
}
|
|
13126
|
-
if (minItems > 0)
|
|
13127
|
-
json.minItems = minItems;
|
|
13128
|
-
if (isClosed)
|
|
13129
|
-
json.maxItems = maxItems;
|
|
13130
|
-
}
|
|
13131
|
-
const { minimum, maximum } = aggregateChecks(schema);
|
|
13132
|
-
if (typeof minimum === "number")
|
|
13133
|
-
json.minItems = minimum;
|
|
13134
|
-
if (typeof maximum === "number")
|
|
13135
|
-
json.maxItems = maximum;
|
|
13136
|
-
};
|
|
13137
13015
|
function stringifyKeyNames(bySchema, json, visited) {
|
|
13138
13016
|
if (json.$ref) {
|
|
13139
13017
|
if (visited.has(json))
|
|
@@ -13320,99 +13198,12 @@ var readonlyProcessor = (schema, ctx, json, params) => {
|
|
|
13320
13198
|
seen.ref = def.innerType;
|
|
13321
13199
|
json.readOnly = true;
|
|
13322
13200
|
};
|
|
13323
|
-
var promiseProcessor = (schema, ctx, _json, params) => {
|
|
13324
|
-
const def = schema._zod.def;
|
|
13325
|
-
processSchema(def.innerType, ctx, params);
|
|
13326
|
-
const seen = ctx.seen.get(schema);
|
|
13327
|
-
seen.ref = def.innerType;
|
|
13328
|
-
};
|
|
13329
13201
|
var optionalProcessor = (schema, ctx, _json, params) => {
|
|
13330
13202
|
const def = schema._zod.def;
|
|
13331
13203
|
processSchema(def.innerType, ctx, params);
|
|
13332
13204
|
const seen = ctx.seen.get(schema);
|
|
13333
13205
|
seen.ref = def.innerType;
|
|
13334
13206
|
};
|
|
13335
|
-
var lazyProcessor = (schema, ctx, _json, params) => {
|
|
13336
|
-
const innerType = schema._zod.innerType;
|
|
13337
|
-
processSchema(innerType, ctx, params);
|
|
13338
|
-
const seen = ctx.seen.get(schema);
|
|
13339
|
-
seen.ref = innerType;
|
|
13340
|
-
};
|
|
13341
|
-
var allProcessors = {
|
|
13342
|
-
string: stringProcessor,
|
|
13343
|
-
number: numberProcessor,
|
|
13344
|
-
boolean: booleanProcessor,
|
|
13345
|
-
bigint: bigintProcessor,
|
|
13346
|
-
symbol: symbolProcessor,
|
|
13347
|
-
null: nullProcessor,
|
|
13348
|
-
undefined: undefinedProcessor,
|
|
13349
|
-
void: voidProcessor,
|
|
13350
|
-
never: neverProcessor,
|
|
13351
|
-
any: anyProcessor,
|
|
13352
|
-
unknown: unknownProcessor,
|
|
13353
|
-
date: dateProcessor,
|
|
13354
|
-
enum: enumProcessor,
|
|
13355
|
-
literal: literalProcessor,
|
|
13356
|
-
nan: nanProcessor,
|
|
13357
|
-
template_literal: templateLiteralProcessor,
|
|
13358
|
-
file: fileProcessor,
|
|
13359
|
-
success: successProcessor,
|
|
13360
|
-
custom: customProcessor,
|
|
13361
|
-
function: functionProcessor,
|
|
13362
|
-
transform: transformProcessor,
|
|
13363
|
-
map: mapProcessor,
|
|
13364
|
-
set: setProcessor,
|
|
13365
|
-
array: arrayProcessor,
|
|
13366
|
-
object: objectProcessor,
|
|
13367
|
-
union: unionProcessor,
|
|
13368
|
-
intersection: intersectionProcessor,
|
|
13369
|
-
tuple: tupleProcessor,
|
|
13370
|
-
record: recordProcessor,
|
|
13371
|
-
nullable: nullableProcessor,
|
|
13372
|
-
nonoptional: nonoptionalProcessor,
|
|
13373
|
-
default: defaultProcessor,
|
|
13374
|
-
prefault: prefaultProcessor,
|
|
13375
|
-
catch: catchProcessor,
|
|
13376
|
-
pipe: pipeProcessor,
|
|
13377
|
-
readonly: readonlyProcessor,
|
|
13378
|
-
promise: promiseProcessor,
|
|
13379
|
-
optional: optionalProcessor,
|
|
13380
|
-
lazy: lazyProcessor
|
|
13381
|
-
};
|
|
13382
|
-
function toJSONSchema(input, params) {
|
|
13383
|
-
if ("_idmap" in input) {
|
|
13384
|
-
const registry = input;
|
|
13385
|
-
const ctx = initializeContext({ ...params, processors: allProcessors });
|
|
13386
|
-
const defs = {};
|
|
13387
|
-
for (const entry of registry._idmap.entries()) {
|
|
13388
|
-
const [_, schema] = entry;
|
|
13389
|
-
processSchema(schema, ctx);
|
|
13390
|
-
}
|
|
13391
|
-
const schemas = {};
|
|
13392
|
-
const external = {
|
|
13393
|
-
registry,
|
|
13394
|
-
uri: params?.uri,
|
|
13395
|
-
defs
|
|
13396
|
-
};
|
|
13397
|
-
ctx.external = external;
|
|
13398
|
-
for (const entry of registry._idmap.entries()) {
|
|
13399
|
-
const [key, schema] = entry;
|
|
13400
|
-
extractDefs(ctx, schema);
|
|
13401
|
-
assignProp(schemas, key, finalize(ctx, schema));
|
|
13402
|
-
}
|
|
13403
|
-
if (Object.keys(defs).length > 0) {
|
|
13404
|
-
const defsSegment = ctx.target === "draft-2020-12" ? "$defs" : "definitions";
|
|
13405
|
-
schemas.__shared = {
|
|
13406
|
-
[defsSegment]: defs
|
|
13407
|
-
};
|
|
13408
|
-
}
|
|
13409
|
-
return { schemas };
|
|
13410
|
-
}
|
|
13411
|
-
const ctx = initializeContext({ ...params, processors: allProcessors });
|
|
13412
|
-
processSchema(input, ctx);
|
|
13413
|
-
extractDefs(ctx, input);
|
|
13414
|
-
return finalize(ctx, input);
|
|
13415
|
-
}
|
|
13416
13207
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
13417
13208
|
function isZ4Schema(s) {
|
|
13418
13209
|
const schema = s;
|
|
@@ -16783,9 +16574,9 @@ class StdioServerTransport {
|
|
|
16783
16574
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
16784
16575
|
|
|
16785
16576
|
// packages/workit-core/src/core.ts
|
|
16786
|
-
import { spawnSync as
|
|
16787
|
-
import { existsSync as
|
|
16788
|
-
import
|
|
16577
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
16578
|
+
import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
|
|
16579
|
+
import path19 from "node:path";
|
|
16789
16580
|
|
|
16790
16581
|
// packages/workit-core/src/core/task-contract.ts
|
|
16791
16582
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -16986,76 +16777,16 @@ var provenanceSchema = object2({
|
|
|
16986
16777
|
workerId: nullableId
|
|
16987
16778
|
}).strict();
|
|
16988
16779
|
var entrySchema = (data) => object2({ id, recordedAt: utc, provenance: provenanceSchema, data }).strict();
|
|
16989
|
-
var
|
|
16990
|
-
|
|
16991
|
-
|
|
16992
|
-
|
|
16993
|
-
|
|
16994
|
-
|
|
16995
|
-
|
|
16996
|
-
code: "custom",
|
|
16997
|
-
input: ctx.value,
|
|
16998
|
-
message: "observed facts require supporting references",
|
|
16999
|
-
path: ["refs"]
|
|
17000
|
-
});
|
|
17001
|
-
});
|
|
17002
|
-
var signalSchema = object2({
|
|
17003
|
-
value: union([boolean2(), literal("unknown")]),
|
|
17004
|
-
basis: _enum(["observed", "inferred", "unknown"]),
|
|
17005
|
-
reason: text,
|
|
16780
|
+
var RISK_TIERS = ["trivial", "normal", "high"];
|
|
16781
|
+
var judgmentSchema = object2({
|
|
16782
|
+
riskTier: _enum(RISK_TIERS),
|
|
16783
|
+
behaviorChange: boolean2(),
|
|
16784
|
+
productChoiceOpen: boolean2(),
|
|
16785
|
+
needsPlan: boolean2(),
|
|
16786
|
+
note: text.nullable(),
|
|
17006
16787
|
refs: array(refSchema)
|
|
17007
|
-
}).strict().check((ctx) => {
|
|
17008
|
-
const signal = ctx.value;
|
|
17009
|
-
const valid = signal.value === "unknown" ? signal.basis === "unknown" : signal.basis === "observed" || signal.basis === "inferred";
|
|
17010
|
-
if (!valid)
|
|
17011
|
-
ctx.issues.push({
|
|
17012
|
-
code: "custom",
|
|
17013
|
-
input: signal,
|
|
17014
|
-
message: "signal value and basis disagree",
|
|
17015
|
-
path: []
|
|
17016
|
-
});
|
|
17017
|
-
if (signal.basis === "observed" && signal.refs.length === 0)
|
|
17018
|
-
ctx.issues.push({
|
|
17019
|
-
code: "custom",
|
|
17020
|
-
input: signal,
|
|
17021
|
-
message: "observed signals require supporting references",
|
|
17022
|
-
path: ["refs"]
|
|
17023
|
-
});
|
|
17024
|
-
});
|
|
17025
|
-
var signalSet = object2({
|
|
17026
|
-
approachUnknown: signalSchema,
|
|
17027
|
-
productChoiceOpen: signalSchema,
|
|
17028
|
-
behaviorChange: signalSchema,
|
|
17029
|
-
mechanicalLowRisk: signalSchema,
|
|
17030
|
-
durableAgreementNeeded: signalSchema,
|
|
17031
|
-
coordinationPlanNeeded: signalSchema,
|
|
17032
|
-
helperUseful: signalSchema,
|
|
17033
|
-
testFirstPractical: signalSchema
|
|
17034
|
-
}).strict();
|
|
17035
|
-
var consequenceSchema = object2({
|
|
17036
|
-
area: _enum([
|
|
17037
|
-
"behavior",
|
|
17038
|
-
"public_contract",
|
|
17039
|
-
"data",
|
|
17040
|
-
"security",
|
|
17041
|
-
"operations",
|
|
17042
|
-
"reversibility",
|
|
17043
|
-
"consumers"
|
|
17044
|
-
]),
|
|
17045
|
-
fact: factSchema
|
|
17046
|
-
}).strict();
|
|
17047
|
-
var verificationSchema = object2({
|
|
17048
|
-
claim: text,
|
|
17049
|
-
scope: scopeSchema,
|
|
17050
|
-
availableChecks: array(refSchema),
|
|
17051
|
-
gaps: array(text)
|
|
17052
|
-
}).strict();
|
|
17053
|
-
var assessmentSchema = object2({
|
|
17054
|
-
facts: array(factSchema),
|
|
17055
|
-
signals: signalSet,
|
|
17056
|
-
consequences: array(consequenceSchema),
|
|
17057
|
-
verification: array(verificationSchema)
|
|
17058
16788
|
}).strict();
|
|
16789
|
+
var legacyAssessmentSchema = looseObject({});
|
|
17059
16790
|
var constraintSchema = object2({
|
|
17060
16791
|
id: nonEmpty,
|
|
17061
16792
|
kind: _enum(["host", "project", "user"]),
|
|
@@ -17292,7 +17023,8 @@ var taskRecordSchema = object2({
|
|
|
17292
17023
|
progress: progressSchema,
|
|
17293
17024
|
pauseReason: text.nullable().optional(),
|
|
17294
17025
|
runtime: runtimeSchema.optional(),
|
|
17295
|
-
assessments: array(entrySchema(
|
|
17026
|
+
assessments: array(entrySchema(legacyAssessmentSchema)),
|
|
17027
|
+
judgments: array(entrySchema(judgmentSchema)).optional(),
|
|
17296
17028
|
policy: policySchema.nullable(),
|
|
17297
17029
|
policyChanges: array(policyChangeSchema),
|
|
17298
17030
|
candidates: array(candidateSchema),
|
|
@@ -17468,14 +17200,15 @@ var taskOperations = {
|
|
|
17468
17200
|
decisionIds: array(id).optional()
|
|
17469
17201
|
})
|
|
17470
17202
|
};
|
|
17203
|
+
var rawJudgment = record(string2(), unknown());
|
|
17471
17204
|
var policyOperations = {
|
|
17472
17205
|
assess: operation({
|
|
17473
17206
|
action: literal("assess"),
|
|
17474
17207
|
...taskId,
|
|
17475
17208
|
expectedRevision: revision.optional(),
|
|
17476
|
-
|
|
17209
|
+
judgment: rawJudgment
|
|
17477
17210
|
}),
|
|
17478
|
-
preview: operation({ action: literal("preview"), ...taskId,
|
|
17211
|
+
preview: operation({ action: literal("preview"), ...taskId, judgment: rawJudgment }),
|
|
17479
17212
|
explain: operation({ action: literal("explain"), ...taskId })
|
|
17480
17213
|
};
|
|
17481
17214
|
var evidenceOperations = {
|
|
@@ -17514,7 +17247,10 @@ var decisionOperations = {
|
|
|
17514
17247
|
...taskId,
|
|
17515
17248
|
expectedRevision: revision.optional(),
|
|
17516
17249
|
purpose: decisionSchema.shape.purpose,
|
|
17517
|
-
binding: decisionSchema.shape.binding
|
|
17250
|
+
binding: decisionSchema.shape.binding.extend({
|
|
17251
|
+
taskId: id.optional(),
|
|
17252
|
+
workspaceId: id.optional()
|
|
17253
|
+
}),
|
|
17518
17254
|
response: decisionSchema.shape.response,
|
|
17519
17255
|
requirementIds: array(digest)
|
|
17520
17256
|
}),
|
|
@@ -17568,6 +17304,15 @@ var operationSchemas = {
|
|
|
17568
17304
|
worker: discriminatedUnion("action", Object.values(workerOperations)),
|
|
17569
17305
|
state: discriminatedUnion("action", Object.values(stateOperations))
|
|
17570
17306
|
};
|
|
17307
|
+
var OPERATION_ACTIONS = {
|
|
17308
|
+
task: Object.keys(taskOperations),
|
|
17309
|
+
policy: Object.keys(policyOperations),
|
|
17310
|
+
evidence: Object.keys(evidenceOperations),
|
|
17311
|
+
finding: Object.keys(findingOperations),
|
|
17312
|
+
decision: Object.keys(decisionOperations),
|
|
17313
|
+
worker: Object.keys(workerOperations),
|
|
17314
|
+
state: Object.keys(stateOperations)
|
|
17315
|
+
};
|
|
17571
17316
|
var withImplicitTask = (options) => Object.values(options).map((option) => ("taskId" in option.shape) ? option.extend({ taskId: id.optional() }) : option);
|
|
17572
17317
|
var advertised = (options) => discriminatedUnion("action", withImplicitTask(options));
|
|
17573
17318
|
var advertisedOperationSchemas = {
|
|
@@ -17601,103 +17346,6 @@ function parseOperation(family, input) {
|
|
|
17601
17346
|
}));
|
|
17602
17347
|
return failure2("invalid_input", "operation input is invalid", { fields });
|
|
17603
17348
|
}
|
|
17604
|
-
function operationJsonSchema(family) {
|
|
17605
|
-
return toJSONSchema(advertisedOperationSchemas[family], { target: "draft-2020-12" });
|
|
17606
|
-
}
|
|
17607
|
-
var OPERATION_SCHEMA_DEPTH = 1;
|
|
17608
|
-
var schemaShapeDescription = (value, depth = 0) => {
|
|
17609
|
-
if (!value || typeof value !== "object")
|
|
17610
|
-
return "value";
|
|
17611
|
-
const schema = value;
|
|
17612
|
-
if (Array.isArray(schema.oneOf) || Array.isArray(schema.anyOf)) {
|
|
17613
|
-
const options = Array.isArray(schema.oneOf) ? schema.oneOf : schema.anyOf;
|
|
17614
|
-
return `(${options.map((option) => schemaShapeDescription(option, depth)).join("|")})`;
|
|
17615
|
-
}
|
|
17616
|
-
if (schema.const !== undefined)
|
|
17617
|
-
return `=${JSON.stringify(schema.const)}`;
|
|
17618
|
-
if (Array.isArray(schema.enum))
|
|
17619
|
-
return `[${schema.enum.map(String).join("|")}]`;
|
|
17620
|
-
const type = Array.isArray(schema.type) ? schema.type.join("/") : schema.type;
|
|
17621
|
-
if (type === "array")
|
|
17622
|
-
return `arr<${schemaShapeDescription(schema.items, depth)}>`;
|
|
17623
|
-
if (type === "object" && schema.properties && typeof schema.properties === "object") {
|
|
17624
|
-
if (depth >= 2)
|
|
17625
|
-
return "obj";
|
|
17626
|
-
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
17627
|
-
const properties = schema.properties;
|
|
17628
|
-
const fields = Object.entries(properties).map(([name, child]) => `${name}${required.has(name) ? "!" : ""}:${schemaShapeDescription(child, depth + 1)}`);
|
|
17629
|
-
return `obj{${fields.join(",")}}`;
|
|
17630
|
-
}
|
|
17631
|
-
if (typeof type !== "string")
|
|
17632
|
-
return "?";
|
|
17633
|
-
return type.split("/").map((part) => ({ string: "str", boolean: "bool", number: "num", null: "null" })[part] ?? part).join("|");
|
|
17634
|
-
};
|
|
17635
|
-
var canonicalFieldsDescription = (value) => {
|
|
17636
|
-
if (Array.isArray(value))
|
|
17637
|
-
return `Canonical object fields: ${value.join(", ")}. Workit validates the complete nested value.`;
|
|
17638
|
-
const isZodType = (schema) => ("_zod" in schema) && typeof schema._zod === "object";
|
|
17639
|
-
const schema = isZodType(value) ? toJSONSchema(value, { target: "draft-2020-12" }) : value;
|
|
17640
|
-
if (!schema.properties || typeof schema.properties !== "object") {
|
|
17641
|
-
const variants = Array.isArray(schema.oneOf) ? schema.oneOf : Array.isArray(schema.anyOf) ? schema.anyOf : null;
|
|
17642
|
-
return variants ? `Canonical shapes: ${variants.map((variant) => schemaShapeDescription(variant)).join(" | ")}. Full nested value is validated.` : "Canonical nested value; Workit validates the complete value.";
|
|
17643
|
-
}
|
|
17644
|
-
const properties = schema.properties;
|
|
17645
|
-
const required = new Set(Array.isArray(schema.required) ? schema.required : []);
|
|
17646
|
-
const fields = Object.entries(properties).map(([name, child]) => `${name}${required.has(name) ? "!" : ""}:${schemaShapeDescription(child)}`);
|
|
17647
|
-
return `Fields (! required): ${fields.join(", ")}. Full nested value is validated.`;
|
|
17648
|
-
};
|
|
17649
|
-
function boundedOperationJsonSchema(family, maxDepth = OPERATION_SCHEMA_DEPTH, stringifiedObjects = false) {
|
|
17650
|
-
const tolerate = (projected) => {
|
|
17651
|
-
if (!stringifiedObjects)
|
|
17652
|
-
return projected;
|
|
17653
|
-
if (projected.properties && typeof projected.properties === "object")
|
|
17654
|
-
return { ...projected, type: ["object", "string"] };
|
|
17655
|
-
if (projected.items)
|
|
17656
|
-
return { ...projected, type: ["array", "string"] };
|
|
17657
|
-
if ("const" in projected && (typeof projected.const === "number" || typeof projected.const === "boolean"))
|
|
17658
|
-
return { anyOf: [projected, { const: String(projected.const) }] };
|
|
17659
|
-
if (projected.type === "object")
|
|
17660
|
-
return { ...projected, type: ["object", "string"] };
|
|
17661
|
-
if (projected.type === "array")
|
|
17662
|
-
return { ...projected, type: ["array", "string"] };
|
|
17663
|
-
return projected;
|
|
17664
|
-
};
|
|
17665
|
-
const collapse = (node, currentDepth) => {
|
|
17666
|
-
if (Array.isArray(node))
|
|
17667
|
-
return node.map((item) => collapse(item, currentDepth));
|
|
17668
|
-
if (typeof node !== "object" || node === null)
|
|
17669
|
-
return node;
|
|
17670
|
-
const record = node;
|
|
17671
|
-
if (record.properties && typeof record.properties === "object") {
|
|
17672
|
-
const properties = record.properties;
|
|
17673
|
-
const fields = Object.keys(properties);
|
|
17674
|
-
if (currentDepth >= maxDepth || fields.length === 0) {
|
|
17675
|
-
const description = stringifiedObjects ? `${canonicalFieldsDescription(record)} A JSON-encoded string is also accepted.` : canonicalFieldsDescription(record);
|
|
17676
|
-
return tolerate({ type: "object", description });
|
|
17677
|
-
}
|
|
17678
|
-
return tolerate({
|
|
17679
|
-
...record,
|
|
17680
|
-
properties: Object.fromEntries(Object.entries(record.properties).map(([key, child]) => [
|
|
17681
|
-
key,
|
|
17682
|
-
collapse(child, currentDepth + 1)
|
|
17683
|
-
]))
|
|
17684
|
-
});
|
|
17685
|
-
}
|
|
17686
|
-
if (record.items)
|
|
17687
|
-
return tolerate({ ...record, items: collapse(record.items, currentDepth) });
|
|
17688
|
-
const composed = ["anyOf", "oneOf", "allOf"].filter((key) => Array.isArray(record[key]));
|
|
17689
|
-
if (composed.length > 0) {
|
|
17690
|
-
const projected = { ...record };
|
|
17691
|
-
for (const key of composed)
|
|
17692
|
-
projected[key] = record[key].map((item) => collapse(item, currentDepth));
|
|
17693
|
-
return projected;
|
|
17694
|
-
}
|
|
17695
|
-
if ("const" in record)
|
|
17696
|
-
return tolerate({ ...record });
|
|
17697
|
-
return node;
|
|
17698
|
-
};
|
|
17699
|
-
return collapse(operationJsonSchema(family), 0);
|
|
17700
|
-
}
|
|
17701
17349
|
function canonical(value, seen) {
|
|
17702
17350
|
if (value === null || typeof value === "boolean" || typeof value === "string") {
|
|
17703
17351
|
if (typeof value === "string" && invalidUnicode(value))
|
|
@@ -17786,6 +17434,402 @@ function requirementId(input) {
|
|
|
17786
17434
|
}
|
|
17787
17435
|
});
|
|
17788
17436
|
}
|
|
17437
|
+
// packages/workit-core/src/core/operation-input.ts
|
|
17438
|
+
var str = (d, values) => ({
|
|
17439
|
+
type: "string",
|
|
17440
|
+
...values ? { enum: values } : {},
|
|
17441
|
+
...d ? { d } : {}
|
|
17442
|
+
});
|
|
17443
|
+
var list = (d) => ({ type: "array", ...d ? { d } : {} });
|
|
17444
|
+
var bool = (d) => ({ type: "boolean", ...d ? { d } : {} });
|
|
17445
|
+
var int2 = (d) => ({ type: "integer", ...d ? { d } : {} });
|
|
17446
|
+
var scopeFields = {
|
|
17447
|
+
paths: list("Scope paths (default: whole checkout)."),
|
|
17448
|
+
exclusions: list(),
|
|
17449
|
+
scope: str("Scope description.")
|
|
17450
|
+
};
|
|
17451
|
+
var refs = list("Paths or URLs.");
|
|
17452
|
+
var FLAT = {
|
|
17453
|
+
task: {
|
|
17454
|
+
fields: {
|
|
17455
|
+
objective: str(),
|
|
17456
|
+
...scopeFields,
|
|
17457
|
+
refs,
|
|
17458
|
+
reason: str(),
|
|
17459
|
+
summary: str(),
|
|
17460
|
+
nextAction: str(),
|
|
17461
|
+
blockers: list(),
|
|
17462
|
+
outcome: str(undefined, ["verified", "accepted_limitations", "stopped"]),
|
|
17463
|
+
decisionIds: list(),
|
|
17464
|
+
status: str(undefined, ["open", "closed", "all"]),
|
|
17465
|
+
query: str(),
|
|
17466
|
+
limit: int2(),
|
|
17467
|
+
view: str(undefined, ["summary", "full"])
|
|
17468
|
+
}
|
|
17469
|
+
},
|
|
17470
|
+
policy: {
|
|
17471
|
+
fields: {
|
|
17472
|
+
riskTier: str("low=trivial, medium=normal.", ["trivial", "normal", "high", "low", "medium"]),
|
|
17473
|
+
behaviorChange: bool("Observable behavior changes."),
|
|
17474
|
+
productChoiceOpen: bool("A product/preference choice is still open."),
|
|
17475
|
+
needsPlan: bool("Needs a written plan or spec first."),
|
|
17476
|
+
note: str(),
|
|
17477
|
+
refs: list("Plan/spec paths or URLs.")
|
|
17478
|
+
}
|
|
17479
|
+
},
|
|
17480
|
+
evidence: {
|
|
17481
|
+
fields: {
|
|
17482
|
+
kind: str(undefined, ["check", "review", "investigation", "artifact"]),
|
|
17483
|
+
claim: str(),
|
|
17484
|
+
result: str(undefined, ["passed", "failed", "missing", "skipped"]),
|
|
17485
|
+
summary: str(),
|
|
17486
|
+
exitCode: int2(),
|
|
17487
|
+
refs,
|
|
17488
|
+
requirementIds: list(),
|
|
17489
|
+
candidateId: str(),
|
|
17490
|
+
beforeCandidateId: str(),
|
|
17491
|
+
reviewSession: str("Reviewer session handle (review evidence).")
|
|
17492
|
+
}
|
|
17493
|
+
},
|
|
17494
|
+
finding: {
|
|
17495
|
+
fields: {
|
|
17496
|
+
claim: str(),
|
|
17497
|
+
consequence: str(),
|
|
17498
|
+
...scopeFields,
|
|
17499
|
+
candidateId: str(),
|
|
17500
|
+
refs,
|
|
17501
|
+
findingId: str(),
|
|
17502
|
+
disposition: str(undefined, ["open", "fixed", "dismissed", "deferred"]),
|
|
17503
|
+
reason: str(),
|
|
17504
|
+
evidenceIds: list(),
|
|
17505
|
+
decisionIds: list()
|
|
17506
|
+
}
|
|
17507
|
+
},
|
|
17508
|
+
decision: {
|
|
17509
|
+
fields: {
|
|
17510
|
+
purpose: str(undefined, ["design", "action", "limitation", "preference"]),
|
|
17511
|
+
response: str(undefined, ["approved", "rejected", "stated"]),
|
|
17512
|
+
presented: str("What the user was asked."),
|
|
17513
|
+
choice: str("The user's stated choice (response=stated)."),
|
|
17514
|
+
...scopeFields,
|
|
17515
|
+
refs,
|
|
17516
|
+
requirementIds: list(),
|
|
17517
|
+
decisionId: str(),
|
|
17518
|
+
reason: str()
|
|
17519
|
+
}
|
|
17520
|
+
},
|
|
17521
|
+
worker: {
|
|
17522
|
+
fields: {
|
|
17523
|
+
role: str(undefined, ["investigator", "reviewer", "implementer"]),
|
|
17524
|
+
objective: str(),
|
|
17525
|
+
...scopeFields,
|
|
17526
|
+
decisionIds: list(),
|
|
17527
|
+
requirementIds: list(),
|
|
17528
|
+
candidateId: str(),
|
|
17529
|
+
stoppingCondition: str(),
|
|
17530
|
+
workerId: str(),
|
|
17531
|
+
outcome: str(undefined, ["completed", "failed", "cancelled"]),
|
|
17532
|
+
summary: str(),
|
|
17533
|
+
evidenceIds: list(),
|
|
17534
|
+
findingIds: list(),
|
|
17535
|
+
reason: str()
|
|
17536
|
+
}
|
|
17537
|
+
},
|
|
17538
|
+
state: {
|
|
17539
|
+
fields: { bundle: { type: "object", d: "An export bundle (object or JSON string)." } }
|
|
17540
|
+
}
|
|
17541
|
+
};
|
|
17542
|
+
var COMMON = {
|
|
17543
|
+
taskId: str("Omit for this branch's task."),
|
|
17544
|
+
expectedRevision: str(),
|
|
17545
|
+
expectedWorkspaceRevision: str()
|
|
17546
|
+
};
|
|
17547
|
+
var DESCRIPTIONS = {
|
|
17548
|
+
task: "Workit task lifecycle for this branch's task (optional continuity).",
|
|
17549
|
+
policy: "Judge the work in four flat calls (riskTier, behaviorChange, productChoiceOpen, needsPlan); Workit derives the checks, verdicts, decisions and plan it needs.",
|
|
17550
|
+
evidence: "Record a note-level evidence entry; only `workit check` runs satisfy check gates.",
|
|
17551
|
+
finding: "Record or resolve a review finding.",
|
|
17552
|
+
decision: "Record or revoke a user decision (never an authorization).",
|
|
17553
|
+
worker: "Assign, report or cancel a bounded helper.",
|
|
17554
|
+
state: "Export or import a task handoff bundle."
|
|
17555
|
+
};
|
|
17556
|
+
var operationDescription = (family) => `${DESCRIPTIONS[family]} Flat fields; omit taskId and revisions.`;
|
|
17557
|
+
var READ_TASK_FIELDS = new Set(["status", "query", "limit", "view"]);
|
|
17558
|
+
var READ_ONLY_ACTIONS = new Set(["list", "inspect", "preview", "explain", "export"]);
|
|
17559
|
+
function flatOperationJsonSchema(family, options = {}) {
|
|
17560
|
+
const spec = FLAT[family];
|
|
17561
|
+
const actions = options.readOnly ? OPERATION_ACTIONS[family].filter((action) => READ_ONLY_ACTIONS.has(action)) : [...OPERATION_ACTIONS[family]];
|
|
17562
|
+
if (!actions.length)
|
|
17563
|
+
return null;
|
|
17564
|
+
const property = (field) => {
|
|
17565
|
+
const base = field.type === "array" ? { type: "array", items: { type: "string" } } : field.type === "object" ? { type: "object" } : { type: field.type };
|
|
17566
|
+
const tolerant = options.stringTolerant && field.type !== "string" ? { type: [field.type, "string"] } : {};
|
|
17567
|
+
return {
|
|
17568
|
+
...base,
|
|
17569
|
+
...tolerant,
|
|
17570
|
+
...field.enum ? { enum: field.enum } : {},
|
|
17571
|
+
...field.d ? { description: field.d } : {}
|
|
17572
|
+
};
|
|
17573
|
+
};
|
|
17574
|
+
const properties = { action: { type: "string", enum: actions } };
|
|
17575
|
+
const readField = (name) => name === "taskId" || (family === "task" ? READ_TASK_FIELDS.has(name) : family === "policy");
|
|
17576
|
+
for (const [name, field] of Object.entries({ ...COMMON, ...spec.fields }))
|
|
17577
|
+
if (options.readOnly ? readField(name) : !(family === "state" && name === "expectedRevision"))
|
|
17578
|
+
properties[name] = property(field);
|
|
17579
|
+
return {
|
|
17580
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
17581
|
+
type: "object",
|
|
17582
|
+
properties,
|
|
17583
|
+
required: ["action"]
|
|
17584
|
+
};
|
|
17585
|
+
}
|
|
17586
|
+
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17587
|
+
var decoded = (value) => {
|
|
17588
|
+
if (typeof value !== "string")
|
|
17589
|
+
return value;
|
|
17590
|
+
const trimmed = value.trim();
|
|
17591
|
+
if (!/^[[{]/.test(trimmed) && !/^(true|false|null|-?\d+)$/.test(trimmed))
|
|
17592
|
+
return value;
|
|
17593
|
+
try {
|
|
17594
|
+
return JSON.parse(trimmed);
|
|
17595
|
+
} catch {
|
|
17596
|
+
return value;
|
|
17597
|
+
}
|
|
17598
|
+
};
|
|
17599
|
+
var strings = (value) => {
|
|
17600
|
+
const plain = decoded(value);
|
|
17601
|
+
if (typeof plain === "string")
|
|
17602
|
+
return plain.trim() ? [plain] : [];
|
|
17603
|
+
return plain;
|
|
17604
|
+
};
|
|
17605
|
+
var refOf = (value) => {
|
|
17606
|
+
if (typeof value !== "string")
|
|
17607
|
+
return value;
|
|
17608
|
+
return /^https?:\/\//i.test(value) ? { kind: "external", url: value } : { kind: "file", path: value.replace(/^\.\//, ""), digest: null };
|
|
17609
|
+
};
|
|
17610
|
+
var refList = (value) => {
|
|
17611
|
+
const items = strings(value);
|
|
17612
|
+
return Array.isArray(items) ? items.map(refOf) : items;
|
|
17613
|
+
};
|
|
17614
|
+
var take = (flat, keys, map = decoded) => {
|
|
17615
|
+
const out = {};
|
|
17616
|
+
for (const key of keys)
|
|
17617
|
+
if (key in flat) {
|
|
17618
|
+
out[key] = map(flat[key]);
|
|
17619
|
+
delete flat[key];
|
|
17620
|
+
}
|
|
17621
|
+
return out;
|
|
17622
|
+
};
|
|
17623
|
+
var nest = (request, key, flat, defaults = {}) => {
|
|
17624
|
+
const nested = isObject2(decoded(request[key])) ? decoded(request[key]) : null;
|
|
17625
|
+
if (!nested && !Object.keys(flat).length)
|
|
17626
|
+
return;
|
|
17627
|
+
request[key] = { ...defaults, ...flat, ...nested };
|
|
17628
|
+
};
|
|
17629
|
+
var scopeOf = (flat, description) => {
|
|
17630
|
+
const scope = decoded(flat.scope);
|
|
17631
|
+
const has = "paths" in flat || "exclusions" in flat || typeof scope === "string";
|
|
17632
|
+
const out = isObject2(scope) ? scope : has ? {
|
|
17633
|
+
description: typeof scope === "string" ? scope : String(description ?? ""),
|
|
17634
|
+
paths: strings(flat.paths ?? ["."]),
|
|
17635
|
+
exclusions: strings(flat.exclusions ?? [])
|
|
17636
|
+
} : null;
|
|
17637
|
+
delete flat.scope;
|
|
17638
|
+
delete flat.paths;
|
|
17639
|
+
delete flat.exclusions;
|
|
17640
|
+
return out;
|
|
17641
|
+
};
|
|
17642
|
+
var defaultScope = (description) => ({
|
|
17643
|
+
description: typeof description === "string" ? description : "",
|
|
17644
|
+
paths: ["."],
|
|
17645
|
+
exclusions: []
|
|
17646
|
+
});
|
|
17647
|
+
var LISTS = [
|
|
17648
|
+
"decisionIds",
|
|
17649
|
+
"evidenceIds",
|
|
17650
|
+
"findingIds",
|
|
17651
|
+
"requirementIds",
|
|
17652
|
+
"refs",
|
|
17653
|
+
"blockers"
|
|
17654
|
+
];
|
|
17655
|
+
var ENVELOPE = new Set([
|
|
17656
|
+
"schemaVersion",
|
|
17657
|
+
"action",
|
|
17658
|
+
"taskId",
|
|
17659
|
+
"expectedRevision",
|
|
17660
|
+
"expectedWorkspaceRevision",
|
|
17661
|
+
"judgment"
|
|
17662
|
+
]);
|
|
17663
|
+
function normalizeOperationInput(family, input, host) {
|
|
17664
|
+
if (!isObject2(input))
|
|
17665
|
+
return input;
|
|
17666
|
+
const request = { ...input };
|
|
17667
|
+
if (!("schemaVersion" in request))
|
|
17668
|
+
request.schemaVersion = 1;
|
|
17669
|
+
else
|
|
17670
|
+
request.schemaVersion = decoded(request.schemaVersion);
|
|
17671
|
+
for (const key of ["limit", "exitCode"])
|
|
17672
|
+
if (key in request)
|
|
17673
|
+
request[key] = decoded(request[key]);
|
|
17674
|
+
const action = request.action;
|
|
17675
|
+
const flat = request;
|
|
17676
|
+
switch (family) {
|
|
17677
|
+
case "task": {
|
|
17678
|
+
if (action === "start" || action === "revise") {
|
|
17679
|
+
const intent = isObject2(decoded(request.intent)) ? decoded(request.intent) : {};
|
|
17680
|
+
const objective = flat.objective ?? intent.objective;
|
|
17681
|
+
const scope = scopeOf(flat, objective);
|
|
17682
|
+
const fields = take(flat, ["objective"]);
|
|
17683
|
+
const authorityRefs = "refs" in flat ? refList(flat.refs) : undefined;
|
|
17684
|
+
delete flat.refs;
|
|
17685
|
+
nest(request, "intent", {
|
|
17686
|
+
...fields,
|
|
17687
|
+
...scope ? { scope } : {},
|
|
17688
|
+
...authorityRefs ? { authorityRefs } : {}
|
|
17689
|
+
}, { scope: defaultScope(objective), authorityRefs: [] });
|
|
17690
|
+
} else if (action === "progress") {
|
|
17691
|
+
const fields = take(flat, ["summary", "nextAction"]);
|
|
17692
|
+
const blockers = "blockers" in flat ? strings(flat.blockers) : undefined;
|
|
17693
|
+
delete flat.blockers;
|
|
17694
|
+
nest(request, "progress", {
|
|
17695
|
+
...fields,
|
|
17696
|
+
...Array.isArray(blockers) ? {
|
|
17697
|
+
blockers: blockers.map((item) => typeof item === "string" ? { reason: item, dependentAction: "", refs: [] } : item)
|
|
17698
|
+
} : {}
|
|
17699
|
+
}, { nextAction: null, blockers: [] });
|
|
17700
|
+
} else if (action === "close" && "decisionIds" in flat)
|
|
17701
|
+
flat.decisionIds = strings(flat.decisionIds);
|
|
17702
|
+
break;
|
|
17703
|
+
}
|
|
17704
|
+
case "policy": {
|
|
17705
|
+
if (action === "assess" || action === "preview") {
|
|
17706
|
+
const judgment = isObject2(decoded(request.judgment)) ? { ...decoded(request.judgment) } : {};
|
|
17707
|
+
for (const key of Object.keys(flat))
|
|
17708
|
+
if (!ENVELOPE.has(key)) {
|
|
17709
|
+
if (!(key in judgment))
|
|
17710
|
+
judgment[key] = decoded(flat[key]);
|
|
17711
|
+
delete flat[key];
|
|
17712
|
+
}
|
|
17713
|
+
request.judgment = judgment;
|
|
17714
|
+
}
|
|
17715
|
+
break;
|
|
17716
|
+
}
|
|
17717
|
+
case "evidence": {
|
|
17718
|
+
if (action === "record") {
|
|
17719
|
+
const review = "reviewSession" in flat ? decoded(flat.reviewSession) : undefined;
|
|
17720
|
+
delete flat.reviewSession;
|
|
17721
|
+
const fields = take(flat, [
|
|
17722
|
+
"kind",
|
|
17723
|
+
"claim",
|
|
17724
|
+
"result",
|
|
17725
|
+
"summary",
|
|
17726
|
+
"exitCode",
|
|
17727
|
+
"candidateId",
|
|
17728
|
+
"beforeCandidateId",
|
|
17729
|
+
"reviewContext"
|
|
17730
|
+
]);
|
|
17731
|
+
if ("refs" in flat)
|
|
17732
|
+
fields.refs = refList(flat.refs);
|
|
17733
|
+
if ("requirementIds" in flat)
|
|
17734
|
+
fields.requirementIds = strings(flat.requirementIds);
|
|
17735
|
+
delete flat.refs;
|
|
17736
|
+
delete flat.requirementIds;
|
|
17737
|
+
if (typeof review === "string" && review)
|
|
17738
|
+
fields.reviewContext = { kind: "host", host, handle: review };
|
|
17739
|
+
nest(request, "evidence", fields, {
|
|
17740
|
+
summary: "",
|
|
17741
|
+
refs: [],
|
|
17742
|
+
requirementIds: [],
|
|
17743
|
+
exitCode: null,
|
|
17744
|
+
reviewContext: null
|
|
17745
|
+
});
|
|
17746
|
+
}
|
|
17747
|
+
break;
|
|
17748
|
+
}
|
|
17749
|
+
case "finding": {
|
|
17750
|
+
if (action === "record") {
|
|
17751
|
+
const scope = scopeOf(flat, flat.claim);
|
|
17752
|
+
if (scope)
|
|
17753
|
+
request.scope = scope;
|
|
17754
|
+
else if (!("scope" in request))
|
|
17755
|
+
request.scope = defaultScope(request.claim);
|
|
17756
|
+
request.refs = "refs" in flat ? refList(flat.refs) : [];
|
|
17757
|
+
}
|
|
17758
|
+
break;
|
|
17759
|
+
}
|
|
17760
|
+
case "decision": {
|
|
17761
|
+
if (action === "record") {
|
|
17762
|
+
const binding = isObject2(decoded(request.binding)) ? decoded(request.binding) : {};
|
|
17763
|
+
const presented = flat.presented ?? binding.presented;
|
|
17764
|
+
const scope = scopeOf(flat, presented);
|
|
17765
|
+
const fields = take(flat, ["presented"]);
|
|
17766
|
+
const choice = flat.choice;
|
|
17767
|
+
delete flat.choice;
|
|
17768
|
+
const contentRefs = "refs" in flat ? refList(flat.refs) : undefined;
|
|
17769
|
+
delete flat.refs;
|
|
17770
|
+
nest(request, "binding", {
|
|
17771
|
+
...fields,
|
|
17772
|
+
...scope ? { scope } : {},
|
|
17773
|
+
...contentRefs ? { contentRefs } : {},
|
|
17774
|
+
...typeof choice === "string" ? { statedChoice: { ref: "choice", text: choice } } : {}
|
|
17775
|
+
}, { scope: defaultScope(presented), contentRefs: [] });
|
|
17776
|
+
}
|
|
17777
|
+
break;
|
|
17778
|
+
}
|
|
17779
|
+
case "worker": {
|
|
17780
|
+
if (action === "assign") {
|
|
17781
|
+
const objective = flat.objective;
|
|
17782
|
+
const scope = scopeOf(flat, objective);
|
|
17783
|
+
const fields = take(flat, ["role", "objective", "candidateId", "stoppingCondition"]);
|
|
17784
|
+
for (const key of ["decisionIds", "requirementIds"])
|
|
17785
|
+
if (key in flat) {
|
|
17786
|
+
fields[key] = strings(flat[key]);
|
|
17787
|
+
delete flat[key];
|
|
17788
|
+
}
|
|
17789
|
+
nest(request, "assignment", { ...fields, ...scope ? { scope } : {} }, { scope: defaultScope(objective), decisionIds: [], requirementIds: [] });
|
|
17790
|
+
} else if (action === "report") {
|
|
17791
|
+
const fields = take(flat, ["outcome", "summary"]);
|
|
17792
|
+
for (const key of ["evidenceIds", "findingIds"])
|
|
17793
|
+
if (key in flat) {
|
|
17794
|
+
fields[key] = strings(flat[key]);
|
|
17795
|
+
delete flat[key];
|
|
17796
|
+
}
|
|
17797
|
+
nest(request, "report", fields, { evidenceIds: [], findingIds: [] });
|
|
17798
|
+
}
|
|
17799
|
+
break;
|
|
17800
|
+
}
|
|
17801
|
+
case "state": {
|
|
17802
|
+
if ("bundle" in request)
|
|
17803
|
+
request.bundle = decoded(request.bundle);
|
|
17804
|
+
break;
|
|
17805
|
+
}
|
|
17806
|
+
}
|
|
17807
|
+
const defaults = {
|
|
17808
|
+
finding: { resolve: ["evidenceIds", "decisionIds"] },
|
|
17809
|
+
decision: { record: ["requirementIds"] }
|
|
17810
|
+
};
|
|
17811
|
+
for (const key of defaults[family]?.[String(action)] ?? [])
|
|
17812
|
+
if (!(key in request))
|
|
17813
|
+
request[key] = [];
|
|
17814
|
+
for (const key of LISTS)
|
|
17815
|
+
if (key in request && typeof request[key] === "string")
|
|
17816
|
+
request[key] = strings(request[key]);
|
|
17817
|
+
return request;
|
|
17818
|
+
}
|
|
17819
|
+
function parseAdvertisedOperation(family, input, host) {
|
|
17820
|
+
const request = normalizeOperationInput(family, input, host);
|
|
17821
|
+
if (isObject2(request) && request.schemaVersion !== SCHEMA_VERSION)
|
|
17822
|
+
return failure2("unsupported_version", "unsupported schema version", { operation: family });
|
|
17823
|
+
const parsed = advertisedOperationSchemas[family].safeParse(request);
|
|
17824
|
+
if (parsed.success)
|
|
17825
|
+
return success(null, null, request);
|
|
17826
|
+
return failure2("invalid_input", "operation input is invalid", {
|
|
17827
|
+
fields: parsed.error.issues.map((issue) => ({
|
|
17828
|
+
path: issue.code === "unrecognized_keys" ? issue.keys.join(".") : issue.path.join("."),
|
|
17829
|
+
reason: issue.message
|
|
17830
|
+
}))
|
|
17831
|
+
});
|
|
17832
|
+
}
|
|
17789
17833
|
// packages/workit-core/src/core/task-store.ts
|
|
17790
17834
|
import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
|
|
17791
17835
|
import * as fs7 from "node:fs";
|
|
@@ -18840,7 +18884,7 @@ class LogDamage extends Error {
|
|
|
18840
18884
|
this.upgrade = upgrade;
|
|
18841
18885
|
}
|
|
18842
18886
|
}
|
|
18843
|
-
var
|
|
18887
|
+
var isObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18844
18888
|
var parseEvent = (line, file, offset) => {
|
|
18845
18889
|
let value;
|
|
18846
18890
|
try {
|
|
@@ -18848,11 +18892,11 @@ var parseEvent = (line, file, offset) => {
|
|
|
18848
18892
|
} catch {
|
|
18849
18893
|
throw new LogDamage(`${file}: unreadable event at byte ${offset}`);
|
|
18850
18894
|
}
|
|
18851
|
-
if (!
|
|
18895
|
+
if (!isObject3(value) || typeof value.v !== "number")
|
|
18852
18896
|
throw new LogDamage(`${file}: malformed event at byte ${offset}`);
|
|
18853
18897
|
if (value.v > EVENT_VERSION)
|
|
18854
18898
|
throw new LogDamage(`${file}: event format v${value.v} was written by a newer Workit; upgrade Workit to read this task`, true);
|
|
18855
|
-
if (typeof value.seq !== "number" || !Number.isInteger(value.seq) || typeof value.type !== "string" || typeof value.id !== "string" || typeof value.task !== "string" || !
|
|
18899
|
+
if (typeof value.seq !== "number" || !Number.isInteger(value.seq) || typeof value.type !== "string" || typeof value.id !== "string" || typeof value.task !== "string" || !isObject3(value.data))
|
|
18856
18900
|
throw new LogDamage(`${file}: malformed event at byte ${offset}`);
|
|
18857
18901
|
return value;
|
|
18858
18902
|
};
|
|
@@ -19123,13 +19167,13 @@ var KNOWN_TYPES = new Set([
|
|
|
19123
19167
|
"decision.updated",
|
|
19124
19168
|
"worker.updated"
|
|
19125
19169
|
]);
|
|
19126
|
-
var
|
|
19127
|
-
var isKey = (value) =>
|
|
19170
|
+
var isObject4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19171
|
+
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");
|
|
19128
19172
|
var BLOB_REF = "$blob";
|
|
19129
|
-
var blobRef = (value) =>
|
|
19173
|
+
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;
|
|
19130
19174
|
var blobDigest = (value) => createHash3("sha256").update(JSON.stringify(value)).digest("hex");
|
|
19131
19175
|
var toBlob = (value, blobs) => {
|
|
19132
|
-
if (!
|
|
19176
|
+
if (!isObject4(value))
|
|
19133
19177
|
return value;
|
|
19134
19178
|
const digest = blobDigest(value);
|
|
19135
19179
|
blobs.write(digest, value);
|
|
@@ -19153,7 +19197,7 @@ var decode3 = (value, blobs) => {
|
|
|
19153
19197
|
return blobs.read(digest);
|
|
19154
19198
|
if (Array.isArray(value))
|
|
19155
19199
|
return value.map((item) => decode3(item, blobs));
|
|
19156
|
-
if (
|
|
19200
|
+
if (isObject4(value)) {
|
|
19157
19201
|
const out = {};
|
|
19158
19202
|
for (const [key, item] of Object.entries(value))
|
|
19159
19203
|
out[key] = decode3(item, blobs);
|
|
@@ -19172,7 +19216,7 @@ function reduce(state, event, blobs) {
|
|
|
19172
19216
|
}
|
|
19173
19217
|
let next = state;
|
|
19174
19218
|
if ("record" in data) {
|
|
19175
|
-
if (!
|
|
19219
|
+
if (!isObject4(data.record))
|
|
19176
19220
|
throw new LogDamage(`event ${event.seq} carries a malformed record`);
|
|
19177
19221
|
next = {
|
|
19178
19222
|
record: decode3(data.record, blobs),
|
|
@@ -19194,7 +19238,7 @@ function reduce(state, event, blobs) {
|
|
|
19194
19238
|
}
|
|
19195
19239
|
if ("key" in data)
|
|
19196
19240
|
next.key = isKey(data.key) ? data.key : null;
|
|
19197
|
-
if ("legacy" in data &&
|
|
19241
|
+
if ("legacy" in data && isObject4(data.legacy))
|
|
19198
19242
|
next.legacy = {
|
|
19199
19243
|
path: String(data.legacy.path ?? ""),
|
|
19200
19244
|
digest: String(data.legacy.digest ?? "")
|
|
@@ -19250,8 +19294,8 @@ var AUTO_COMPACT_BYTES = 2 * 1024 * 1024;
|
|
|
19250
19294
|
var TOMBSTONE = Symbol("tombstone");
|
|
19251
19295
|
var STALE_TEMPORARY_MS = 60 * 60000;
|
|
19252
19296
|
var INDEX_VERSION = 3;
|
|
19253
|
-
var
|
|
19254
|
-
var hostSession = (value) =>
|
|
19297
|
+
var isObject5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19298
|
+
var hostSession = (value) => isObject5(value) && value.kind === "host" && typeof value.host === "string" && typeof value.handle === "string" ? { host: value.host, handle: value.handle } : null;
|
|
19255
19299
|
var indexEntry = (task, key, legacy, file) => {
|
|
19256
19300
|
const sessions = [];
|
|
19257
19301
|
const intent = hostSession(task.intent.provenance.session);
|
|
@@ -20064,7 +20108,7 @@ class TaskStore {
|
|
|
20064
20108
|
readSnapshot(taskId) {
|
|
20065
20109
|
try {
|
|
20066
20110
|
const value = JSON.parse(fs7.readFileSync(this.snapshotPath(taskId), "utf8"));
|
|
20067
|
-
if (!
|
|
20111
|
+
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))
|
|
20068
20112
|
return null;
|
|
20069
20113
|
return value;
|
|
20070
20114
|
} catch {
|
|
@@ -20410,11 +20454,11 @@ class TaskStore {
|
|
|
20410
20454
|
readIndex() {
|
|
20411
20455
|
try {
|
|
20412
20456
|
const value = JSON.parse(fs7.readFileSync(this.paths().index, "utf8"));
|
|
20413
|
-
if (!
|
|
20457
|
+
if (!isObject5(value) || value.version !== INDEX_VERSION || !isObject5(value.tasks))
|
|
20414
20458
|
return {};
|
|
20415
20459
|
const tasks = {};
|
|
20416
20460
|
for (const [id, entry] of Object.entries(value.tasks))
|
|
20417
|
-
if (
|
|
20461
|
+
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))
|
|
20418
20462
|
tasks[id] = entry;
|
|
20419
20463
|
return tasks;
|
|
20420
20464
|
} catch {
|
|
@@ -20458,12 +20502,12 @@ class TaskStore {
|
|
|
20458
20502
|
return this.parseValue(value, schema, label);
|
|
20459
20503
|
}
|
|
20460
20504
|
parseValue(value, schema, label) {
|
|
20461
|
-
if (
|
|
20505
|
+
if (isObject5(value) && "schemaVersion" in value && value.schemaVersion !== SCHEMA_VERSION)
|
|
20462
20506
|
return failure2("unsupported_version", `unsupported ${label} schema version`);
|
|
20463
20507
|
const parsed = parseStoredRecord(schema, value);
|
|
20464
20508
|
if (parsed.success)
|
|
20465
20509
|
return success(null, null, parsed.data);
|
|
20466
|
-
const writerVersion =
|
|
20510
|
+
const writerVersion = isObject5(value) && isObject5(value.runtime) ? value.runtime.updatedWith : null;
|
|
20467
20511
|
if (parsed.critical.length > 0)
|
|
20468
20512
|
return failure2("recovery_required", `${label} record requires fields this Workit cannot read (${parsed.critical.join(", ")}); upgrade Workit before mutating this checkout`);
|
|
20469
20513
|
if (typeof writerVersion === "string" && isNewerVersion(writerVersion, runtimeVersion()))
|
|
@@ -20665,7 +20709,7 @@ class TaskStore {
|
|
|
20665
20709
|
}
|
|
20666
20710
|
try {
|
|
20667
20711
|
const value = JSON.parse(bytes);
|
|
20668
|
-
if (
|
|
20712
|
+
if (isObject5(value) && isObject5(value.store) && value.store.format === "workit-store")
|
|
20669
20713
|
return TOMBSTONE;
|
|
20670
20714
|
} catch {}
|
|
20671
20715
|
return { bytes };
|
|
@@ -20710,7 +20754,7 @@ class TaskStore {
|
|
|
20710
20754
|
isStub(file) {
|
|
20711
20755
|
try {
|
|
20712
20756
|
const value = JSON.parse(fs7.readFileSync(file, "utf8"));
|
|
20713
|
-
return
|
|
20757
|
+
return isObject5(value) && isObject5(value.store) && value.store.format === "workit-store";
|
|
20714
20758
|
} catch {
|
|
20715
20759
|
return false;
|
|
20716
20760
|
}
|
|
@@ -20881,10 +20925,10 @@ var isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state);
|
|
|
20881
20925
|
var workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
20882
20926
|
|
|
20883
20927
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
20884
|
-
import { createHash as
|
|
20885
|
-
import * as
|
|
20886
|
-
import { spawnSync as
|
|
20887
|
-
import
|
|
20928
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
20929
|
+
import * as fs11 from "node:fs";
|
|
20930
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
20931
|
+
import path11 from "node:path";
|
|
20888
20932
|
|
|
20889
20933
|
// packages/workit-core/src/check-config.ts
|
|
20890
20934
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
@@ -21124,6 +21168,7 @@ var line = (run) => {
|
|
|
21124
21168
|
const value = run.stdout.trim();
|
|
21125
21169
|
return value ? value : null;
|
|
21126
21170
|
};
|
|
21171
|
+
var safeArg = (value) => value.length > 0 && !value.startsWith("-");
|
|
21127
21172
|
function headSha(cwd) {
|
|
21128
21173
|
return line(git2(cwd, ["rev-parse", "--verify", "-q", "HEAD^{commit}"]));
|
|
21129
21174
|
}
|
|
@@ -21215,79 +21260,648 @@ function worktreeSignal(cwd, options = {}) {
|
|
|
21215
21260
|
}
|
|
21216
21261
|
return `sig:${hash.digest("hex")}`;
|
|
21217
21262
|
}
|
|
21218
|
-
|
|
21219
|
-
|
|
21220
|
-
var digestBytes = (value) => createHash6("sha256").update(value).digest("hex");
|
|
21221
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path9.sep}`);
|
|
21222
|
-
var relative = (root, candidate) => path9.relative(root, candidate).split(path9.sep).join("/") || ".";
|
|
21223
|
-
var canonicalPath = (value) => {
|
|
21224
|
-
if (typeof value !== "string" || !value)
|
|
21225
|
-
return null;
|
|
21226
|
-
if (value.includes("\\") || value.split("/").includes(".."))
|
|
21263
|
+
function patchId(cwd, base, head) {
|
|
21264
|
+
if (!safeArg(base) || !safeArg(head))
|
|
21227
21265
|
return null;
|
|
21228
|
-
const
|
|
21229
|
-
|
|
21230
|
-
|
|
21231
|
-
|
|
21232
|
-
|
|
21233
|
-
|
|
21234
|
-
|
|
21235
|
-
|
|
21236
|
-
|
|
21237
|
-
|
|
21266
|
+
const diff = git2(cwd, [
|
|
21267
|
+
"diff",
|
|
21268
|
+
"-U3",
|
|
21269
|
+
"--diff-algorithm=myers",
|
|
21270
|
+
"--indent-heuristic",
|
|
21271
|
+
"--no-color",
|
|
21272
|
+
"--no-ext-diff",
|
|
21273
|
+
"--no-textconv",
|
|
21274
|
+
"--no-renames",
|
|
21275
|
+
"--full-index",
|
|
21276
|
+
"--src-prefix=a/",
|
|
21277
|
+
"--dst-prefix=b/",
|
|
21278
|
+
`${base}...${head}`,
|
|
21279
|
+
"--"
|
|
21280
|
+
]);
|
|
21281
|
+
if (!diff.ok || !diff.stdout.trim())
|
|
21238
21282
|
return null;
|
|
21283
|
+
const id = git2(cwd, ["patch-id", "--stable"], { input: diff.stdout });
|
|
21284
|
+
return line(id)?.split(/\s+/u)[0] ?? null;
|
|
21285
|
+
}
|
|
21286
|
+
function currentBranch(cwd) {
|
|
21287
|
+
return line(git2(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
|
|
21288
|
+
}
|
|
21289
|
+
|
|
21290
|
+
// packages/workit-core/src/core/policy/derive.ts
|
|
21291
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
21292
|
+
import path10 from "node:path";
|
|
21293
|
+
|
|
21294
|
+
// packages/workit-core/src/ledger.ts
|
|
21295
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
21296
|
+
import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
|
|
21297
|
+
import fs10 from "node:fs";
|
|
21298
|
+
import path9 from "node:path";
|
|
21299
|
+
var LEDGER_VERSION = 1;
|
|
21300
|
+
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
21301
|
+
var FAILING = new Set(["failed", "blocked"]);
|
|
21302
|
+
var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
|
|
21303
|
+
var PR_ROW_TYPES = new Set(["pr.created", "pr.status", "pr.merged"]);
|
|
21304
|
+
var err = (code, error, unblock) => ({
|
|
21305
|
+
ok: false,
|
|
21306
|
+
code,
|
|
21307
|
+
error,
|
|
21308
|
+
...unblock ? { unblock } : {}
|
|
21309
|
+
});
|
|
21310
|
+
var gitRun2 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
|
|
21311
|
+
const run = spawnSync5("git", args, {
|
|
21312
|
+
cwd,
|
|
21313
|
+
encoding: "utf8",
|
|
21314
|
+
timeout: timeoutMs,
|
|
21315
|
+
killSignal: "SIGKILL",
|
|
21316
|
+
windowsHide: true,
|
|
21317
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
21318
|
+
env: {
|
|
21319
|
+
...process.env,
|
|
21320
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
21321
|
+
GIT_OPTIONAL_LOCKS: "0",
|
|
21322
|
+
LC_ALL: "C",
|
|
21323
|
+
LANGUAGE: "C"
|
|
21324
|
+
}
|
|
21325
|
+
});
|
|
21239
21326
|
return {
|
|
21240
|
-
|
|
21241
|
-
|
|
21242
|
-
|
|
21327
|
+
ok: run.status === 0,
|
|
21328
|
+
stdout: run.stdout ?? "",
|
|
21329
|
+
stderr: run.error ? String(run.error.message) : run.stderr ?? ""
|
|
21243
21330
|
};
|
|
21244
21331
|
};
|
|
21245
|
-
var
|
|
21246
|
-
|
|
21247
|
-
const
|
|
21248
|
-
|
|
21249
|
-
return false;
|
|
21250
|
-
const paths = normalized.paths;
|
|
21251
|
-
return paths.some((item) => item === "." || value === item || value.startsWith(`${item}/`)) && !excluded(value, normalized.exclusions);
|
|
21332
|
+
var git3 = (cwd, args) => {
|
|
21333
|
+
const run = gitRun2(cwd, args);
|
|
21334
|
+
const value = run.ok ? run.stdout.trim() : "";
|
|
21335
|
+
return value ? value : null;
|
|
21252
21336
|
};
|
|
21253
|
-
var
|
|
21254
|
-
|
|
21255
|
-
|
|
21256
|
-
|
|
21257
|
-
|
|
21258
|
-
|
|
21259
|
-
|
|
21337
|
+
var safeRef = (value) => value.length > 0 && !value.startsWith("-");
|
|
21338
|
+
function resolveCommit(cwd, ref) {
|
|
21339
|
+
if (!safeRef(ref))
|
|
21340
|
+
return null;
|
|
21341
|
+
return git3(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]);
|
|
21342
|
+
}
|
|
21343
|
+
function branchHead(cwd, branch) {
|
|
21344
|
+
if (!safeRef(branch))
|
|
21345
|
+
return null;
|
|
21346
|
+
return resolveCommit(cwd, `refs/heads/${branch}`) ?? resolveCommit(cwd, `refs/remotes/origin/${branch}`);
|
|
21347
|
+
}
|
|
21348
|
+
function defaultBase(cwd) {
|
|
21349
|
+
const originHead = git3(cwd, ["symbolic-ref", "-q", "--short", "refs/remotes/origin/HEAD"]);
|
|
21350
|
+
if (originHead && resolveCommit(cwd, originHead))
|
|
21351
|
+
return originHead;
|
|
21352
|
+
for (const candidate of ["origin/main", "origin/master", "main", "master"])
|
|
21353
|
+
if (resolveCommit(cwd, candidate))
|
|
21354
|
+
return candidate;
|
|
21355
|
+
return null;
|
|
21356
|
+
}
|
|
21357
|
+
function diffHash(cwd, base, head) {
|
|
21358
|
+
if (!safeRef(base) || !safeRef(head))
|
|
21359
|
+
return null;
|
|
21360
|
+
const diff = gitRun2(cwd, [
|
|
21361
|
+
"diff",
|
|
21362
|
+
"-U3",
|
|
21363
|
+
"--diff-algorithm=myers",
|
|
21364
|
+
"--indent-heuristic",
|
|
21365
|
+
"--no-color",
|
|
21366
|
+
"--no-ext-diff",
|
|
21367
|
+
"--no-textconv",
|
|
21368
|
+
"--no-renames",
|
|
21369
|
+
"--full-index",
|
|
21370
|
+
"--binary",
|
|
21371
|
+
"--src-prefix=a/",
|
|
21372
|
+
"--dst-prefix=b/",
|
|
21373
|
+
`${base}...${head}`,
|
|
21374
|
+
"--"
|
|
21375
|
+
], GIT_TIMEOUTS.worktree);
|
|
21376
|
+
if (!diff.ok || !diff.stdout)
|
|
21377
|
+
return null;
|
|
21378
|
+
const hash = createHash6("sha256");
|
|
21379
|
+
for (const line of diff.stdout.split(`
|
|
21380
|
+
`)) {
|
|
21381
|
+
if (line.startsWith("index "))
|
|
21382
|
+
continue;
|
|
21383
|
+
hash.update(line.startsWith("@@ ") ? "@@" : line);
|
|
21384
|
+
hash.update(`
|
|
21385
|
+
`);
|
|
21260
21386
|
}
|
|
21261
|
-
return
|
|
21387
|
+
return `sha256:${hash.digest("hex")}`;
|
|
21388
|
+
}
|
|
21389
|
+
function storeRoot(cwd) {
|
|
21390
|
+
const location = resolveStore(path9.resolve(cwd));
|
|
21391
|
+
if (location instanceof Error)
|
|
21392
|
+
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");
|
|
21393
|
+
return { ok: true, value: { root: location.dir, shared: location.shared } };
|
|
21394
|
+
}
|
|
21395
|
+
function ledgerPath(cwd) {
|
|
21396
|
+
const root = storeRoot(cwd);
|
|
21397
|
+
return root.ok ? { ok: true, value: path9.join(root.value.root, "ledger", "ledger.jsonl") } : root;
|
|
21398
|
+
}
|
|
21399
|
+
var NETWORK_FS_TYPES = new Set([
|
|
21400
|
+
26985,
|
|
21401
|
+
20859,
|
|
21402
|
+
4266872130,
|
|
21403
|
+
4283649346,
|
|
21404
|
+
1702057286,
|
|
21405
|
+
1397113167,
|
|
21406
|
+
12805120,
|
|
21407
|
+
16914839,
|
|
21408
|
+
1196443219,
|
|
21409
|
+
198183888
|
|
21410
|
+
]);
|
|
21411
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21412
|
+
var str2 = (value) => typeof value === "string" ? value : null;
|
|
21413
|
+
var normalizeActor = (value) => {
|
|
21414
|
+
const actor = isRecord2(value) ? value : {};
|
|
21415
|
+
return {
|
|
21416
|
+
host: str2(actor.host) ?? "unknown",
|
|
21417
|
+
session: str2(actor.session),
|
|
21418
|
+
agentId: str2(actor.agentId)
|
|
21419
|
+
};
|
|
21262
21420
|
};
|
|
21263
|
-
|
|
21264
|
-
|
|
21265
|
-
|
|
21266
|
-
|
|
21267
|
-
|
|
21268
|
-
if (
|
|
21269
|
-
|
|
21270
|
-
|
|
21271
|
-
|
|
21421
|
+
function supersedeAllowed(by, target) {
|
|
21422
|
+
if (by.type !== target.type)
|
|
21423
|
+
return `a ${by.type} row cannot supersede a ${target.type} row`;
|
|
21424
|
+
if (!by.actor.session || by.actor.session !== target.actor.session)
|
|
21425
|
+
return "only the session that wrote a row can supersede it";
|
|
21426
|
+
if (by.type === "verdict" && by.self === true && target.self !== true)
|
|
21427
|
+
return "a self verdict cannot replace an independent verdict";
|
|
21428
|
+
return null;
|
|
21429
|
+
}
|
|
21430
|
+
function readLedger(cwd) {
|
|
21431
|
+
const file = ledgerPath(cwd);
|
|
21432
|
+
if (!file.ok)
|
|
21433
|
+
return file;
|
|
21434
|
+
let text = "";
|
|
21435
|
+
try {
|
|
21436
|
+
text = fs10.readFileSync(file.value, "utf8");
|
|
21437
|
+
} catch (error) {
|
|
21438
|
+
if (error.code === "ENOENT")
|
|
21439
|
+
return { ok: true, value: { path: file.value, rows: [], skipped: 0 } };
|
|
21440
|
+
return err("unavailable", `cannot read ${file.value}: ${error.message}`);
|
|
21441
|
+
}
|
|
21442
|
+
const rows = [];
|
|
21443
|
+
let skipped = 0;
|
|
21444
|
+
for (const raw of text.split(`
|
|
21445
|
+
`)) {
|
|
21446
|
+
const trimmed = raw.trim();
|
|
21447
|
+
if (!trimmed)
|
|
21448
|
+
continue;
|
|
21449
|
+
let parsed;
|
|
21450
|
+
try {
|
|
21451
|
+
parsed = JSON.parse(trimmed);
|
|
21452
|
+
} catch {
|
|
21453
|
+
skipped += 1;
|
|
21454
|
+
continue;
|
|
21455
|
+
}
|
|
21456
|
+
if (!isRecord2(parsed) || !str2(parsed.id) || !str2(parsed.type) || !str2(parsed.at)) {
|
|
21457
|
+
skipped += 1;
|
|
21458
|
+
continue;
|
|
21459
|
+
}
|
|
21460
|
+
const pr = typeof parsed.pr === "number" && Number.isInteger(parsed.pr) ? parsed.pr : undefined;
|
|
21461
|
+
rows.push({
|
|
21462
|
+
...parsed,
|
|
21463
|
+
v: typeof parsed.v === "number" ? parsed.v : LEDGER_VERSION,
|
|
21464
|
+
id: parsed.id,
|
|
21465
|
+
at: parsed.at,
|
|
21466
|
+
type: parsed.type,
|
|
21467
|
+
actor: normalizeActor(parsed.actor),
|
|
21468
|
+
branch: str2(parsed.branch),
|
|
21469
|
+
head: str2(parsed.head),
|
|
21470
|
+
base: str2(parsed.base),
|
|
21471
|
+
baseSha: str2(parsed.baseSha),
|
|
21472
|
+
patchId: str2(parsed.patchId),
|
|
21473
|
+
diffHash: str2(parsed.diffHash),
|
|
21474
|
+
tree: str2(parsed.tree),
|
|
21475
|
+
dirty: typeof parsed.dirty === "boolean" ? parsed.dirty : null,
|
|
21476
|
+
...pr === undefined ? {} : { pr },
|
|
21477
|
+
...str2(parsed.supersedes) ? { supersedes: parsed.supersedes } : {},
|
|
21478
|
+
seq: rows.length + 1,
|
|
21479
|
+
superseded: false,
|
|
21480
|
+
supersedeIgnored: false
|
|
21481
|
+
});
|
|
21482
|
+
}
|
|
21483
|
+
const byId = new Map;
|
|
21484
|
+
for (const row of rows) {
|
|
21485
|
+
const target = row.supersedes ? byId.get(row.supersedes) : undefined;
|
|
21486
|
+
if (target && supersedeAllowed(row, target) === null)
|
|
21487
|
+
target.superseded = true;
|
|
21488
|
+
else if (row.supersedes)
|
|
21489
|
+
row.supersedeIgnored = true;
|
|
21490
|
+
if (!byId.has(row.id))
|
|
21491
|
+
byId.set(row.id, row);
|
|
21492
|
+
}
|
|
21493
|
+
return { ok: true, value: { path: file.value, rows, skipped } };
|
|
21494
|
+
}
|
|
21495
|
+
function verdictBasis(verdict, current) {
|
|
21496
|
+
if (verdict.head && verdict.head === current.head)
|
|
21497
|
+
return "fresh";
|
|
21498
|
+
if (verdict.patchId && verdict.patchId === current.patchId && verdict.diffHash && verdict.diffHash === current.diffHash)
|
|
21499
|
+
return "carried";
|
|
21500
|
+
return "stale";
|
|
21501
|
+
}
|
|
21502
|
+
var isLiveVerdict = (row) => row.type === "verdict" && typeof row.result === "string" && !row.superseded;
|
|
21503
|
+
function trailerSessions(cwd, base, head) {
|
|
21504
|
+
if (!base || !head || !safeRef(base) || !safeRef(head))
|
|
21505
|
+
return [];
|
|
21506
|
+
const out = git3(cwd, [
|
|
21507
|
+
"log",
|
|
21508
|
+
"--format=%(trailers:key=Workit-Session,valueonly,separator=%x00)",
|
|
21509
|
+
`${base}..${head}`
|
|
21510
|
+
]);
|
|
21511
|
+
return (out ?? "").split(/[\0\n]/u).map((value) => value.trim()).filter(Boolean);
|
|
21512
|
+
}
|
|
21513
|
+
function authorSessions(cwd, rows, branch, range = { base: null, head: null }) {
|
|
21514
|
+
const sessions = new Set;
|
|
21515
|
+
for (const row of rows) {
|
|
21516
|
+
if (row.branch !== branch || row.type !== "commit.recorded" && row.type !== "task.opened")
|
|
21517
|
+
continue;
|
|
21518
|
+
const session = str2(row.session) ?? row.actor.session;
|
|
21519
|
+
if (session)
|
|
21520
|
+
sessions.add(session);
|
|
21521
|
+
}
|
|
21522
|
+
for (const session of trailerSessions(cwd, range.base, range.head))
|
|
21523
|
+
sessions.add(session);
|
|
21524
|
+
return sessions;
|
|
21525
|
+
}
|
|
21526
|
+
var independenceReasons = (row, authors) => {
|
|
21527
|
+
const reasons = [];
|
|
21528
|
+
if (!row.actor.session)
|
|
21529
|
+
reasons.push("no_session");
|
|
21530
|
+
else if (authors.has(row.actor.session))
|
|
21531
|
+
reasons.push("author_session");
|
|
21532
|
+
if (row.self === true && !reasons.includes("no_session"))
|
|
21533
|
+
reasons.push("self");
|
|
21534
|
+
return reasons;
|
|
21535
|
+
};
|
|
21536
|
+
function effectiveOf(rows, authors) {
|
|
21537
|
+
let effective = null;
|
|
21538
|
+
for (const row of rows) {
|
|
21539
|
+
if (!effective) {
|
|
21540
|
+
effective = row;
|
|
21541
|
+
continue;
|
|
21542
|
+
}
|
|
21543
|
+
const rowIndependent = independenceReasons(row, authors).length === 0;
|
|
21544
|
+
const effIndependent = independenceReasons(effective, authors).length === 0;
|
|
21545
|
+
if (effIndependent && !rowIndependent)
|
|
21546
|
+
continue;
|
|
21547
|
+
if (effIndependent && FAILING.has(String(effective.result)) && row.actor.session === effective.actor.session)
|
|
21548
|
+
continue;
|
|
21549
|
+
effective = row;
|
|
21550
|
+
}
|
|
21551
|
+
return effective;
|
|
21552
|
+
}
|
|
21553
|
+
function restackCarries(rows, branch, from, to) {
|
|
21554
|
+
if (!from || !to)
|
|
21555
|
+
return false;
|
|
21556
|
+
const edges = new Map;
|
|
21557
|
+
for (const row of rows)
|
|
21558
|
+
if (row.type === "stack.restacked" && row.observer === "workit_cli" && row.branch === branch && row.patchEqual === true && typeof row.fromHead === "string" && row.head)
|
|
21559
|
+
edges.set(row.fromHead, [...edges.get(row.fromHead) ?? [], row.head]);
|
|
21560
|
+
const seen = new Set([from]);
|
|
21561
|
+
const queue = [from];
|
|
21562
|
+
while (queue.length) {
|
|
21563
|
+
const next = queue.shift();
|
|
21564
|
+
if (next === to)
|
|
21565
|
+
return true;
|
|
21566
|
+
for (const target of edges.get(next) ?? [])
|
|
21567
|
+
if (!seen.has(target)) {
|
|
21568
|
+
seen.add(target);
|
|
21569
|
+
queue.push(target);
|
|
21570
|
+
}
|
|
21571
|
+
}
|
|
21572
|
+
return false;
|
|
21573
|
+
}
|
|
21574
|
+
function checkVerdicts(cwd, branch, rows) {
|
|
21575
|
+
const head = branchHead(cwd, branch);
|
|
21576
|
+
const fallbackBase = defaultBase(cwd);
|
|
21577
|
+
const keyCache = new Map;
|
|
21578
|
+
const keyFor = (base) => {
|
|
21579
|
+
if (!base || !head)
|
|
21580
|
+
return { patchId: null, diffHash: null };
|
|
21581
|
+
let key = keyCache.get(base);
|
|
21582
|
+
if (!key) {
|
|
21583
|
+
key = { patchId: patchId(cwd, base, head), diffHash: diffHash(cwd, base, head) };
|
|
21584
|
+
keyCache.set(base, key);
|
|
21585
|
+
}
|
|
21586
|
+
return key;
|
|
21587
|
+
};
|
|
21588
|
+
const authors = authorSessions(cwd, rows, branch, { base: fallbackBase, head });
|
|
21589
|
+
const byKind = new Map;
|
|
21590
|
+
for (const row of rows)
|
|
21591
|
+
if (row.branch === branch && isLiveVerdict(row)) {
|
|
21592
|
+
const kind = typeof row.kind === "string" ? row.kind : "review";
|
|
21593
|
+
byKind.set(kind, [...byKind.get(kind) ?? [], row]);
|
|
21594
|
+
}
|
|
21595
|
+
const verdicts = [];
|
|
21596
|
+
for (const [kind, list] of byKind) {
|
|
21597
|
+
const row = effectiveOf(list, authors);
|
|
21598
|
+
if (!row)
|
|
21599
|
+
continue;
|
|
21600
|
+
let basis = verdictBasis(row, { head, ...keyFor(row.base ?? fallbackBase) });
|
|
21601
|
+
if (basis === "stale" && restackCarries(rows, branch, row.head, head))
|
|
21602
|
+
basis = "carried";
|
|
21603
|
+
const current = basis !== "stale";
|
|
21604
|
+
const independence = independenceReasons(row, authors);
|
|
21605
|
+
const reasons = [
|
|
21606
|
+
...current ? [] : ["stale"],
|
|
21607
|
+
...PASSING.has(String(row.result)) ? [] : ["not_passing"],
|
|
21608
|
+
...independence
|
|
21609
|
+
];
|
|
21610
|
+
verdicts.push({
|
|
21611
|
+
kind,
|
|
21612
|
+
basis,
|
|
21613
|
+
current,
|
|
21614
|
+
independent: independence.length === 0,
|
|
21615
|
+
accepted: reasons.length === 0,
|
|
21616
|
+
reasons,
|
|
21617
|
+
verdict: row
|
|
21618
|
+
});
|
|
21619
|
+
}
|
|
21620
|
+
verdicts.sort((a, b) => a.verdict.seq - b.verdict.seq);
|
|
21621
|
+
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
21622
|
+
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
21623
|
+
const newestAccepted = verdicts.findLast((entry) => entry.accepted) ?? null;
|
|
21624
|
+
const accepted = newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
21625
|
+
accepted: false,
|
|
21626
|
+
verdict: failing?.verdict ?? null,
|
|
21627
|
+
reasons: failing ? ["failing_verdict"] : newestCurrent?.reasons ?? ["no_verdict"]
|
|
21628
|
+
};
|
|
21629
|
+
const selfEntry = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && STRONG_RESULTS.has(String(entry.verdict.result)));
|
|
21630
|
+
return {
|
|
21631
|
+
branch,
|
|
21632
|
+
head,
|
|
21633
|
+
review: accepted.accepted ? "verified" : selfEntry ? "self-reviewed" : "unreviewed",
|
|
21634
|
+
selfVerdict: accepted.accepted ? null : selfEntry?.verdict ?? null,
|
|
21635
|
+
base: newestCurrent?.verdict.base ?? fallbackBase,
|
|
21636
|
+
patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
|
|
21637
|
+
current: { basis: newestCurrent?.basis ?? "none", verdict: newestCurrent?.verdict ?? null },
|
|
21638
|
+
accepted,
|
|
21639
|
+
verdicts,
|
|
21640
|
+
authors: [...authors].toSorted()
|
|
21641
|
+
};
|
|
21642
|
+
}
|
|
21643
|
+
var OBSERVED_ONLY = new Set(["check", ...PR_ROW_TYPES]);
|
|
21644
|
+
|
|
21645
|
+
// packages/workit-core/src/core/policy/derive.ts
|
|
21646
|
+
var RULES = {
|
|
21647
|
+
test: "check:test",
|
|
21648
|
+
self: "verdict:self",
|
|
21649
|
+
nonAuthor: "verdict:non-author",
|
|
21650
|
+
verified: "verdict:verified",
|
|
21651
|
+
product: "decision:product",
|
|
21652
|
+
plan: "plan"
|
|
21653
|
+
};
|
|
21654
|
+
var derived2 = (spec) => ({
|
|
21655
|
+
...spec,
|
|
21656
|
+
id: requirementId({
|
|
21657
|
+
ruleId: spec.ruleId,
|
|
21658
|
+
scope: spec.scope,
|
|
21659
|
+
satisfaction: spec.satisfaction,
|
|
21660
|
+
before: spec.before,
|
|
21661
|
+
dependentAction: spec.dependentAction
|
|
21662
|
+
})
|
|
21663
|
+
});
|
|
21664
|
+
var sorted = (values) => [...new Set(values)].toSorted();
|
|
21665
|
+
var normalizedScope = (scope) => ({
|
|
21666
|
+
description: scope.description,
|
|
21667
|
+
paths: sorted(scope.paths),
|
|
21668
|
+
exclusions: sorted(scope.exclusions)
|
|
21669
|
+
});
|
|
21670
|
+
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
21671
|
+
const out = [];
|
|
21672
|
+
const base = { scope, dependentAction: null };
|
|
21673
|
+
if (judgment.behaviorChange)
|
|
21674
|
+
out.push(derived2({
|
|
21675
|
+
...base,
|
|
21676
|
+
ruleId: RULES.test,
|
|
21677
|
+
dimension: "testing",
|
|
21678
|
+
reason: "Behavior changes: the configured test check must pass on the final tree.",
|
|
21679
|
+
satisfaction: "Run `workit check test` on the final tree.",
|
|
21680
|
+
before: "close",
|
|
21681
|
+
acceptanceAllowed: false
|
|
21682
|
+
}));
|
|
21683
|
+
if (judgment.behaviorChange && judgment.riskTier === "normal")
|
|
21684
|
+
out.push(verification === "independent" ? derived2({
|
|
21685
|
+
...base,
|
|
21686
|
+
ruleId: RULES.nonAuthor,
|
|
21687
|
+
dimension: "review",
|
|
21688
|
+
reason: "This workspace requires independent verification: a session that did not author the change records the verdict.",
|
|
21689
|
+
satisfaction: "An independent session records a passing verdict: `workit ledger verdict verified --how <what it exercised>`.",
|
|
21690
|
+
before: "close",
|
|
21691
|
+
acceptanceAllowed: false
|
|
21692
|
+
}) : derived2({
|
|
21693
|
+
...base,
|
|
21694
|
+
ruleId: RULES.self,
|
|
21695
|
+
dimension: "review",
|
|
21696
|
+
reason: "A behavior change needs a recorded verdict; the author's own is allowed and is shown as self-reviewed, never verified.",
|
|
21697
|
+
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).",
|
|
21698
|
+
before: "close",
|
|
21699
|
+
acceptanceAllowed: false
|
|
21700
|
+
}));
|
|
21701
|
+
if (judgment.riskTier === "high")
|
|
21702
|
+
out.push(derived2({
|
|
21703
|
+
...base,
|
|
21704
|
+
ruleId: RULES.verified,
|
|
21705
|
+
dimension: "review",
|
|
21706
|
+
reason: "High risk: an independent verifier must exercise the live surface.",
|
|
21707
|
+
satisfaction: "An independent verifier runs the real surface and records `workit ledger verdict verified --kind live --how <what it exercised>`.",
|
|
21708
|
+
before: "close",
|
|
21709
|
+
acceptanceAllowed: false
|
|
21710
|
+
}));
|
|
21711
|
+
if (judgment.productChoiceOpen)
|
|
21712
|
+
out.push(derived2({
|
|
21713
|
+
...base,
|
|
21714
|
+
ruleId: RULES.product,
|
|
21715
|
+
dimension: "decisions",
|
|
21716
|
+
reason: "A product or preference choice is open; the user decides it before code is written.",
|
|
21717
|
+
satisfaction: 'Ask the user (with your recommendation), then record the answer: `workit ledger decision "<choice>" --why "<reason>"`.',
|
|
21718
|
+
before: "write",
|
|
21719
|
+
acceptanceAllowed: false
|
|
21720
|
+
}));
|
|
21721
|
+
if (judgment.needsPlan || judgment.riskTier === "high")
|
|
21722
|
+
out.push(derived2({
|
|
21723
|
+
...base,
|
|
21724
|
+
ruleId: RULES.plan,
|
|
21725
|
+
dimension: "artifacts",
|
|
21726
|
+
reason: judgment.needsPlan ? "The work needs a written plan or spec before implementation." : "High-risk work needs a written plan before implementation.",
|
|
21727
|
+
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>`.",
|
|
21728
|
+
before: "write",
|
|
21729
|
+
acceptanceAllowed: true
|
|
21730
|
+
}));
|
|
21731
|
+
for (const constraint of constraints)
|
|
21732
|
+
for (const obligation of constraint.requires)
|
|
21733
|
+
out.push(derived2({
|
|
21734
|
+
ruleId: `project-constraint:${constraint.id}:${obligation.kind}:${sha256({
|
|
21735
|
+
kind: obligation.kind,
|
|
21736
|
+
scope: normalizedScope(obligation.scope),
|
|
21737
|
+
refs: obligation.refs.map((ref) => JSON.stringify(ref)).toSorted(),
|
|
21738
|
+
method: obligation.method,
|
|
21739
|
+
before: obligation.before,
|
|
21740
|
+
dependentAction: obligation.dependentAction
|
|
21741
|
+
})}`,
|
|
21742
|
+
dimension: obligation.method ?? (obligation.kind === "check" ? "verification" : obligation.kind === "decision" ? "decisions" : "challenge"),
|
|
21743
|
+
scope: obligation.scope,
|
|
21744
|
+
reason: `${constraint.kind} constraint requires ${obligation.kind}: ${constraint.statement}`,
|
|
21745
|
+
satisfaction: `Satisfy the constraint obligation using its referenced ${obligation.kind}.`,
|
|
21746
|
+
before: obligation.before,
|
|
21747
|
+
dependentAction: obligation.dependentAction,
|
|
21748
|
+
acceptanceAllowed: constraint.acceptanceAllowed && obligation.kind !== "decision"
|
|
21749
|
+
}));
|
|
21750
|
+
const seen = new Set;
|
|
21751
|
+
return out.filter((item) => seen.has(item.id) ? false : (seen.add(item.id), true));
|
|
21752
|
+
}
|
|
21753
|
+
var latestJudgment = (task) => task.judgments?.at(-1)?.data ?? null;
|
|
21754
|
+
var ledgerView = (root) => {
|
|
21755
|
+
let branch;
|
|
21756
|
+
let rows;
|
|
21757
|
+
return {
|
|
21758
|
+
branch: () => branch === undefined ? branch = currentBranch(root) : branch,
|
|
21759
|
+
rows: () => {
|
|
21760
|
+
if (rows === undefined) {
|
|
21761
|
+
const read = readLedger(root);
|
|
21762
|
+
rows = read.ok ? read.value.rows : [];
|
|
21763
|
+
}
|
|
21764
|
+
return rows;
|
|
21765
|
+
}
|
|
21766
|
+
};
|
|
21767
|
+
};
|
|
21768
|
+
var productDecision = (task, ledger) => {
|
|
21769
|
+
const decisions = task.decisions.filter((entry) => entry.data.revoked === null && (entry.data.response === "approved" || entry.data.response === "stated") && entry.data.purpose !== "limitation");
|
|
21770
|
+
if (decisions.length)
|
|
21771
|
+
return {
|
|
21772
|
+
met: true,
|
|
21773
|
+
reason: "the user's decision is recorded on the task",
|
|
21774
|
+
decisionIds: decisions.map((entry) => entry.id)
|
|
21775
|
+
};
|
|
21776
|
+
const since = Date.parse(task.createdAt);
|
|
21777
|
+
const branch = ledger.branch();
|
|
21778
|
+
const row = ledger.rows().findLast((item) => item.type === "decision" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
|
|
21779
|
+
return row ? { met: true, reason: `ledger decision ${row.id} records the choice`, decisionIds: [] } : {
|
|
21780
|
+
met: false,
|
|
21781
|
+
reason: 'open product choice: ask the user, then `workit ledger decision "<choice>" --why "<reason>"` (or re-judge productChoiceOpen=false if it is not open)',
|
|
21782
|
+
decisionIds: []
|
|
21783
|
+
};
|
|
21784
|
+
};
|
|
21785
|
+
var planRecorded = (task, root) => {
|
|
21786
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync3(path10.resolve(root, ref.path)));
|
|
21787
|
+
if (cited)
|
|
21788
|
+
return {
|
|
21789
|
+
met: true,
|
|
21790
|
+
reason: `plan recorded: ${cited.kind === "file" ? cited.path : cited.kind === "external" ? cited.url : ""}`,
|
|
21791
|
+
decisionIds: []
|
|
21792
|
+
};
|
|
21793
|
+
const artifact = task.evidence.some((entry) => entry.data.kind === "artifact" && entry.data.result === "passed");
|
|
21794
|
+
return artifact ? { met: true, reason: "a passing artifact records the plan", decisionIds: [] } : {
|
|
21795
|
+
met: false,
|
|
21796
|
+
reason: "no plan recorded: write it (docs/ and Markdown writes stay allowed), then `workit policy assess --ref <path>`",
|
|
21797
|
+
decisionIds: []
|
|
21798
|
+
};
|
|
21799
|
+
};
|
|
21800
|
+
var verdict = (requirement, root, ledger) => {
|
|
21801
|
+
const branch = ledger.branch();
|
|
21802
|
+
if (!branch)
|
|
21803
|
+
return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
|
|
21804
|
+
const check = checkVerdicts(root, branch, ledger.rows());
|
|
21805
|
+
const failing = check.accepted.reasons.includes("failing_verdict");
|
|
21806
|
+
const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
|
|
21807
|
+
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)));
|
|
21808
|
+
if (met)
|
|
21809
|
+
return {
|
|
21810
|
+
met: true,
|
|
21811
|
+
reason: `${met.accepted ? "accepted independent" : "self-reviewed (author's own)"} ${String(met.verdict.result)} verdict ${met.verdict.id}`,
|
|
21812
|
+
decisionIds: []
|
|
21813
|
+
};
|
|
21814
|
+
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, " ");
|
|
21815
|
+
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>\``;
|
|
21816
|
+
return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
|
|
21817
|
+
};
|
|
21818
|
+
function derivedRuleStatus(task, requirement, root, ledger) {
|
|
21819
|
+
switch (requirement.ruleId) {
|
|
21820
|
+
case RULES.product:
|
|
21821
|
+
return productDecision(task, ledger);
|
|
21822
|
+
case RULES.plan:
|
|
21823
|
+
return planRecorded(task, root);
|
|
21824
|
+
case RULES.self:
|
|
21825
|
+
case RULES.nonAuthor:
|
|
21826
|
+
case RULES.verified:
|
|
21827
|
+
return verdict(requirement, root, ledger);
|
|
21828
|
+
default:
|
|
21829
|
+
return null;
|
|
21830
|
+
}
|
|
21831
|
+
}
|
|
21832
|
+
|
|
21833
|
+
// packages/workit-core/src/core/task-evaluation.ts
|
|
21834
|
+
var digestBytes = (value) => createHash7("sha256").update(value).digest("hex");
|
|
21835
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path11.sep}`);
|
|
21836
|
+
var relative = (root, candidate) => path11.relative(root, candidate).split(path11.sep).join("/") || ".";
|
|
21837
|
+
var canonicalPath = (value) => {
|
|
21838
|
+
if (typeof value !== "string" || !value)
|
|
21839
|
+
return null;
|
|
21840
|
+
if (value.includes("\\") || value.split("/").includes(".."))
|
|
21841
|
+
return null;
|
|
21842
|
+
const normalized = path11.posix.normalize(value);
|
|
21843
|
+
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
21844
|
+
return null;
|
|
21845
|
+
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
21846
|
+
};
|
|
21847
|
+
var canonicalScope = (scope) => {
|
|
21848
|
+
const paths = scope.paths.length ? scope.paths : ["."];
|
|
21849
|
+
const normalizedPaths = paths.map(canonicalPath);
|
|
21850
|
+
const normalizedExclusions = scope.exclusions.map(canonicalPath);
|
|
21851
|
+
if (normalizedPaths.some((value) => value === null) || normalizedExclusions.some((value) => value === null))
|
|
21852
|
+
return null;
|
|
21853
|
+
return {
|
|
21854
|
+
description: scope.description,
|
|
21855
|
+
paths: [...new Set(normalizedPaths)],
|
|
21856
|
+
exclusions: [...new Set(normalizedExclusions)]
|
|
21857
|
+
};
|
|
21858
|
+
};
|
|
21859
|
+
var excluded = (value, exclusions) => exclusions.some((item) => item === value || item !== "." && value.startsWith(`${item}/`));
|
|
21860
|
+
var scopeMatches = (value, scope) => {
|
|
21861
|
+
const normalized = canonicalScope(scope);
|
|
21862
|
+
if (!normalized)
|
|
21863
|
+
return false;
|
|
21864
|
+
const paths = normalized.paths;
|
|
21865
|
+
return paths.some((item) => item === "." || value === item || value.startsWith(`${item}/`)) && !excluded(value, normalized.exclusions);
|
|
21866
|
+
};
|
|
21867
|
+
var pathRelevant = (value, scope) => scopeMatches(value, scope);
|
|
21868
|
+
var compareCodeUnits2 = (left, right) => {
|
|
21869
|
+
const length = Math.min(left.length, right.length);
|
|
21870
|
+
for (let index = 0;index < length; index += 1) {
|
|
21871
|
+
const difference = left.charCodeAt(index) - right.charCodeAt(index);
|
|
21872
|
+
if (difference !== 0)
|
|
21873
|
+
return difference;
|
|
21874
|
+
}
|
|
21875
|
+
return left.length - right.length;
|
|
21876
|
+
};
|
|
21877
|
+
var gitPaths = (root) => {
|
|
21878
|
+
const result = spawnSync6("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
21879
|
+
cwd: root,
|
|
21880
|
+
encoding: "buffer"
|
|
21881
|
+
});
|
|
21882
|
+
if (result.status !== 0) {
|
|
21883
|
+
const stderr = result.stderr?.toString("utf8") ?? "";
|
|
21884
|
+
return {
|
|
21885
|
+
paths: [],
|
|
21272
21886
|
uncertain: !/not a git repository/i.test(stderr),
|
|
21273
21887
|
git: !/not a git repository/i.test(stderr)
|
|
21274
21888
|
};
|
|
21275
21889
|
}
|
|
21276
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
21277
|
-
const stagedDeleted =
|
|
21890
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path11.sep).join("/")) : [];
|
|
21891
|
+
const stagedDeleted = spawnSync6("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
21278
21892
|
if (stagedDeleted.status !== 0) {
|
|
21279
21893
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
21280
21894
|
if (!/not a git repository/i.test(stderr))
|
|
21281
21895
|
return { paths, uncertain: true, git: true };
|
|
21282
21896
|
} else if (stagedDeleted.stdout) {
|
|
21283
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
21897
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path11.sep).join("/")));
|
|
21284
21898
|
}
|
|
21285
21899
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
21286
21900
|
};
|
|
21287
21901
|
var walk = (root, directory, output) => {
|
|
21288
21902
|
let names;
|
|
21289
21903
|
try {
|
|
21290
|
-
names =
|
|
21904
|
+
names = fs11.readdirSync(directory);
|
|
21291
21905
|
} catch {
|
|
21292
21906
|
return true;
|
|
21293
21907
|
}
|
|
@@ -21295,10 +21909,10 @@ var walk = (root, directory, output) => {
|
|
|
21295
21909
|
for (const name of names) {
|
|
21296
21910
|
if (name === ".git" || name === ".workit")
|
|
21297
21911
|
continue;
|
|
21298
|
-
const target =
|
|
21912
|
+
const target = path11.join(directory, name);
|
|
21299
21913
|
let stat;
|
|
21300
21914
|
try {
|
|
21301
|
-
stat =
|
|
21915
|
+
stat = fs11.lstatSync(target);
|
|
21302
21916
|
} catch {
|
|
21303
21917
|
uncertain = true;
|
|
21304
21918
|
continue;
|
|
@@ -21313,14 +21927,14 @@ var walk = (root, directory, output) => {
|
|
|
21313
21927
|
var scopeRoots = (root, scope) => {
|
|
21314
21928
|
const roots = [];
|
|
21315
21929
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
21316
|
-
const target =
|
|
21930
|
+
const target = path11.resolve(root, item);
|
|
21317
21931
|
if (!inside(root, target))
|
|
21318
21932
|
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` }] });
|
|
21319
21933
|
let ancestor = target;
|
|
21320
|
-
while (!
|
|
21321
|
-
ancestor =
|
|
21934
|
+
while (!fs11.existsSync(ancestor) && ancestor !== root)
|
|
21935
|
+
ancestor = path11.dirname(ancestor);
|
|
21322
21936
|
try {
|
|
21323
|
-
if (!inside(root,
|
|
21937
|
+
if (!inside(root, fs11.realpathSync(ancestor)))
|
|
21324
21938
|
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` }] });
|
|
21325
21939
|
} catch {
|
|
21326
21940
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -21332,7 +21946,7 @@ var scopeRoots = (root, scope) => {
|
|
|
21332
21946
|
function captureCandidate(root, scope, environment = []) {
|
|
21333
21947
|
let checkout;
|
|
21334
21948
|
try {
|
|
21335
|
-
checkout =
|
|
21949
|
+
checkout = fs11.realpathSync(root);
|
|
21336
21950
|
} catch {
|
|
21337
21951
|
return failure2("invalid_input", "candidate root does not exist");
|
|
21338
21952
|
}
|
|
@@ -21350,11 +21964,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
21350
21964
|
if (item === ".git" || item === ".workit")
|
|
21351
21965
|
continue;
|
|
21352
21966
|
names.add(item);
|
|
21353
|
-
if (!
|
|
21967
|
+
if (!fs11.existsSync(target))
|
|
21354
21968
|
continue;
|
|
21355
21969
|
let stat;
|
|
21356
21970
|
try {
|
|
21357
|
-
stat =
|
|
21971
|
+
stat = fs11.lstatSync(target);
|
|
21358
21972
|
} catch {
|
|
21359
21973
|
uncertain = true;
|
|
21360
21974
|
continue;
|
|
@@ -21370,17 +21984,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
21370
21984
|
for (const item of [...names].toSorted()) {
|
|
21371
21985
|
if (!scopeMatches(item, normalizedScope))
|
|
21372
21986
|
continue;
|
|
21373
|
-
const target =
|
|
21987
|
+
const target = path11.join(checkout, item);
|
|
21374
21988
|
try {
|
|
21375
|
-
const stat =
|
|
21989
|
+
const stat = fs11.lstatSync(target);
|
|
21376
21990
|
if (stat.isSymbolicLink()) {
|
|
21377
|
-
const link =
|
|
21991
|
+
const link = fs11.readlinkSync(target);
|
|
21378
21992
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
21379
21993
|
} else if (stat.isFile()) {
|
|
21380
21994
|
files.push({
|
|
21381
21995
|
path: item,
|
|
21382
21996
|
kind: "file",
|
|
21383
|
-
digest: digestBytes(
|
|
21997
|
+
digest: digestBytes(fs11.readFileSync(target)),
|
|
21384
21998
|
executable: (stat.mode & 73) !== 0
|
|
21385
21999
|
});
|
|
21386
22000
|
} else if (!stat.isDirectory()) {
|
|
@@ -21405,7 +22019,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
21405
22019
|
const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
|
|
21406
22020
|
if (values.some(({ value }) => value === null))
|
|
21407
22021
|
completeness = "uncertain";
|
|
21408
|
-
const headResult =
|
|
22022
|
+
const headResult = spawnSync6("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
|
|
21409
22023
|
if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
|
|
21410
22024
|
completeness = "uncertain";
|
|
21411
22025
|
const head = headResult.status === 0 ? headResult.stdout.trim() : null;
|
|
@@ -21545,7 +22159,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
21545
22159
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
21546
22160
|
let root;
|
|
21547
22161
|
try {
|
|
21548
|
-
root =
|
|
22162
|
+
root = fs11.realpathSync(checkoutRoot);
|
|
21549
22163
|
} catch {
|
|
21550
22164
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
21551
22165
|
}
|
|
@@ -21554,23 +22168,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
21554
22168
|
continue;
|
|
21555
22169
|
if (!reference.digest)
|
|
21556
22170
|
return failure2("invalid_input", "document references require a byte digest");
|
|
21557
|
-
const target =
|
|
21558
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
22171
|
+
const target = path11.resolve(root, reference.path);
|
|
22172
|
+
if (target !== root && !target.startsWith(`${root}${path11.sep}`))
|
|
21559
22173
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
21560
22174
|
fields: [
|
|
21561
22175
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
21562
22176
|
]
|
|
21563
22177
|
});
|
|
21564
22178
|
try {
|
|
21565
|
-
const below =
|
|
22179
|
+
const below = path11.relative(root, target);
|
|
21566
22180
|
let current = root;
|
|
21567
|
-
for (const segment of below.split(
|
|
22181
|
+
for (const segment of below.split(path11.sep)) {
|
|
21568
22182
|
if (!segment)
|
|
21569
22183
|
continue;
|
|
21570
|
-
current =
|
|
21571
|
-
const stat =
|
|
21572
|
-
const real =
|
|
21573
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
22184
|
+
current = path11.join(current, segment);
|
|
22185
|
+
const stat = fs11.lstatSync(current);
|
|
22186
|
+
const real = fs11.realpathSync(current);
|
|
22187
|
+
if (real !== root && !real.startsWith(`${root}${path11.sep}`))
|
|
21574
22188
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
21575
22189
|
fields: [
|
|
21576
22190
|
{
|
|
@@ -21586,7 +22200,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
21586
22200
|
if (current !== target && !stat.isDirectory())
|
|
21587
22201
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
21588
22202
|
}
|
|
21589
|
-
if (digestBytes(
|
|
22203
|
+
if (digestBytes(fs11.readFileSync(target)) !== reference.digest)
|
|
21590
22204
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
21591
22205
|
} catch {
|
|
21592
22206
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -21671,7 +22285,35 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
21671
22285
|
decisionIds: [],
|
|
21672
22286
|
reason: "stored policy version is unsupported; reassessment is required"
|
|
21673
22287
|
}));
|
|
22288
|
+
const ledger = ledgerView(checkoutRoot);
|
|
21674
22289
|
return (task.policy?.requirements ?? []).map((requirement) => {
|
|
22290
|
+
const derived = derivedRuleStatus(task, requirement, checkoutRoot, ledger);
|
|
22291
|
+
if (derived) {
|
|
22292
|
+
if (derived.met)
|
|
22293
|
+
return {
|
|
22294
|
+
requirementId: requirement.id,
|
|
22295
|
+
status: "satisfied",
|
|
22296
|
+
evidenceIds: [],
|
|
22297
|
+
decisionIds: derived.decisionIds,
|
|
22298
|
+
reason: derived.reason
|
|
22299
|
+
};
|
|
22300
|
+
const waived = requirement.acceptanceAllowed ? applicableDecision(task, workspace, requirement, checkoutRoot) : [];
|
|
22301
|
+
if (waived.length)
|
|
22302
|
+
return {
|
|
22303
|
+
requirementId: requirement.id,
|
|
22304
|
+
status: "accepted_limitation",
|
|
22305
|
+
evidenceIds: [],
|
|
22306
|
+
decisionIds: waived.map((decision) => task.decisions.find((entry) => entry.data === decision).id),
|
|
22307
|
+
reason: "an applicable approved limitation permits the missing evidence"
|
|
22308
|
+
};
|
|
22309
|
+
return {
|
|
22310
|
+
requirementId: requirement.id,
|
|
22311
|
+
status: "unsatisfied",
|
|
22312
|
+
evidenceIds: [],
|
|
22313
|
+
decisionIds: [],
|
|
22314
|
+
reason: derived.reason
|
|
22315
|
+
};
|
|
22316
|
+
}
|
|
21675
22317
|
const gate = observedGate(requirement) ? evaluateObservedGate(task, requirement, statuses, configFor()) : null;
|
|
21676
22318
|
if (gate?.satisfied.length)
|
|
21677
22319
|
return {
|
|
@@ -22086,14 +22728,14 @@ var gitContext = (workspaceRoot, paths = []) => {
|
|
|
22086
22728
|
import {
|
|
22087
22729
|
copyFileSync as copyFileSync2,
|
|
22088
22730
|
cpSync as cpSync2,
|
|
22089
|
-
existsSync as
|
|
22731
|
+
existsSync as existsSync5,
|
|
22090
22732
|
mkdirSync as mkdirSync2,
|
|
22091
22733
|
readFileSync as readFileSync3,
|
|
22092
22734
|
readdirSync as readdirSync3,
|
|
22093
22735
|
writeFileSync as writeFileSync2
|
|
22094
22736
|
} from "node:fs";
|
|
22095
22737
|
import os2 from "node:os";
|
|
22096
|
-
import
|
|
22738
|
+
import path12 from "node:path";
|
|
22097
22739
|
|
|
22098
22740
|
// packages/workit-core/src/core/boundary.ts
|
|
22099
22741
|
import { statSync as statSync2 } from "node:fs";
|
|
@@ -22152,7 +22794,7 @@ var mergePreset = (preset, input = {}, current = {
|
|
|
22152
22794
|
protected: preset === "custom" ? input.protectedNames ?? current.branchPolicy.protected : [...defs.protected]
|
|
22153
22795
|
};
|
|
22154
22796
|
};
|
|
22155
|
-
var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
22797
|
+
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");
|
|
22156
22798
|
var migratedDir = null;
|
|
22157
22799
|
var migrationFailed = false;
|
|
22158
22800
|
var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
@@ -22162,12 +22804,12 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
22162
22804
|
migratedDir = dir;
|
|
22163
22805
|
return dir;
|
|
22164
22806
|
}
|
|
22165
|
-
const legacy =
|
|
22166
|
-
if (!
|
|
22807
|
+
const legacy = path12.join(process.env.XDG_CONFIG_HOME || path12.join(os2.homedir(), ".config"), "workflow-toolkit");
|
|
22808
|
+
if (!existsSync5(legacy)) {
|
|
22167
22809
|
migratedDir = dir;
|
|
22168
22810
|
return dir;
|
|
22169
22811
|
}
|
|
22170
|
-
if (!migrationFailed &&
|
|
22812
|
+
if (!migrationFailed && existsSync5(dir)) {
|
|
22171
22813
|
migratedDir = dir;
|
|
22172
22814
|
return dir;
|
|
22173
22815
|
}
|
|
@@ -22175,9 +22817,9 @@ var ensureConfigDir = (dir = resolveConfigDir()) => {
|
|
|
22175
22817
|
mkdirSync2(dir, { recursive: true });
|
|
22176
22818
|
diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
|
|
22177
22819
|
for (const entry of readdirSync3(legacy, { withFileTypes: true })) {
|
|
22178
|
-
const src =
|
|
22179
|
-
const dest =
|
|
22180
|
-
if (
|
|
22820
|
+
const src = path12.join(legacy, entry.name);
|
|
22821
|
+
const dest = path12.join(dir, entry.name);
|
|
22822
|
+
if (existsSync5(dest))
|
|
22181
22823
|
continue;
|
|
22182
22824
|
try {
|
|
22183
22825
|
if (entry.isDirectory())
|
|
@@ -22248,7 +22890,7 @@ var parseConfigResult = (raw, file) => {
|
|
|
22248
22890
|
};
|
|
22249
22891
|
};
|
|
22250
22892
|
var readConfigTyped = (dir) => {
|
|
22251
|
-
const file =
|
|
22893
|
+
const file = path12.join(dir ?? configDir(), "config.json");
|
|
22252
22894
|
return parseConfigResult(readSafe(file), file);
|
|
22253
22895
|
};
|
|
22254
22896
|
var readConfig = () => {
|
|
@@ -22293,8 +22935,9 @@ var resolveCommitPolicy = (config, workspace) => {
|
|
|
22293
22935
|
};
|
|
22294
22936
|
|
|
22295
22937
|
// packages/workit-core/src/core/workspaces.ts
|
|
22938
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
22296
22939
|
import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
|
|
22297
|
-
import
|
|
22940
|
+
import path13 from "node:path";
|
|
22298
22941
|
var nonBlank = string2().refine((value) => value.trim().length > 0, "must not be blank");
|
|
22299
22942
|
var branchPreset = custom((value) => typeof value === "string" && Object.hasOwn(PRESETS, value), "unsupported branch preset");
|
|
22300
22943
|
var commitPreset = custom((value) => typeof value === "string" && COMMIT_PRESETS.includes(value), "unsupported commit preset");
|
|
@@ -22347,7 +22990,7 @@ var releaseTrackSchema = object2({
|
|
|
22347
22990
|
context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
|
|
22348
22991
|
}
|
|
22349
22992
|
}
|
|
22350
|
-
if (track.versionSource.kind === "package-json" && (
|
|
22993
|
+
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(".."))) {
|
|
22351
22994
|
context.addIssue({
|
|
22352
22995
|
code: "custom",
|
|
22353
22996
|
path: ["versionSource", "path"],
|
|
@@ -22366,6 +23009,7 @@ var workspaceConfigSchema = object2({
|
|
|
22366
23009
|
}).passthrough().optional(),
|
|
22367
23010
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
22368
23011
|
defaultEndpoint: _enum(["commit", "pr"]).optional(),
|
|
23012
|
+
verification: _enum(["self", "independent"]).optional(),
|
|
22369
23013
|
autonomy: object2(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
22370
23014
|
kind,
|
|
22371
23015
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -22406,6 +23050,72 @@ var workspacesFileSchema = object2({ workspaces: array(workspaceConfigSchema).op
|
|
|
22406
23050
|
names.add(workspace.name);
|
|
22407
23051
|
}
|
|
22408
23052
|
});
|
|
23053
|
+
var workspacesRevision = (contents) => createHash8("sha256").update(contents ?? "<missing>").digest("hex");
|
|
23054
|
+
var validateWorkspacesDocument = (parsed, file) => {
|
|
23055
|
+
if (!isConfigObject(parsed)) {
|
|
23056
|
+
return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
|
|
23057
|
+
}
|
|
23058
|
+
const validated = workspacesFileSchema.safeParse(parsed, { reportInput: true });
|
|
23059
|
+
if (!validated.success) {
|
|
23060
|
+
const detail = validated.error.issues.map((issue) => {
|
|
23061
|
+
const field = String(issue.path[issue.path.length - 1] ?? "");
|
|
23062
|
+
const attempted = ["provider", "preset", "strategy"].includes(field) && typeof issue.input === "string" ? ` (${JSON.stringify(issue.input)})` : "";
|
|
23063
|
+
return `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}${attempted}`;
|
|
23064
|
+
}).join("; ");
|
|
23065
|
+
return {
|
|
23066
|
+
status: "invalid",
|
|
23067
|
+
path: file,
|
|
23068
|
+
entries: [],
|
|
23069
|
+
error: `${file} has invalid workspace configuration: ${detail}`
|
|
23070
|
+
};
|
|
23071
|
+
}
|
|
23072
|
+
for (const [index, workspace] of (validated.data.workspaces ?? []).entries()) {
|
|
23073
|
+
const glob = validateWorkspaceGlob(workspace.glob);
|
|
23074
|
+
if (!glob.ok) {
|
|
23075
|
+
return {
|
|
23076
|
+
status: "invalid",
|
|
23077
|
+
path: file,
|
|
23078
|
+
entries: [],
|
|
23079
|
+
error: `${file} workspaces.${index}.glob: ${glob.error}`
|
|
23080
|
+
};
|
|
23081
|
+
}
|
|
23082
|
+
}
|
|
23083
|
+
return {
|
|
23084
|
+
status: "valid",
|
|
23085
|
+
path: file,
|
|
23086
|
+
entries: validated.data.workspaces ?? [],
|
|
23087
|
+
document: parsed
|
|
23088
|
+
};
|
|
23089
|
+
};
|
|
23090
|
+
var readWorkspacesResult = (dir = configDir()) => {
|
|
23091
|
+
const file = path13.join(dir, "workspaces.json");
|
|
23092
|
+
let raw;
|
|
23093
|
+
try {
|
|
23094
|
+
raw = readFileSync4(file, "utf8");
|
|
23095
|
+
} catch (error) {
|
|
23096
|
+
if (error.code === "ENOENT")
|
|
23097
|
+
return { status: "missing", path: file, entries: [], revision: workspacesRevision(null) };
|
|
23098
|
+
return {
|
|
23099
|
+
status: "malformed",
|
|
23100
|
+
path: file,
|
|
23101
|
+
entries: [],
|
|
23102
|
+
error: `${file} could not be read: ${error instanceof Error ? error.message : String(error)}`
|
|
23103
|
+
};
|
|
23104
|
+
}
|
|
23105
|
+
let parsed;
|
|
23106
|
+
try {
|
|
23107
|
+
parsed = JSON.parse(raw);
|
|
23108
|
+
} catch {
|
|
23109
|
+
return {
|
|
23110
|
+
status: "malformed",
|
|
23111
|
+
path: file,
|
|
23112
|
+
entries: [],
|
|
23113
|
+
revision: workspacesRevision(raw),
|
|
23114
|
+
error: `${file} is not valid JSON`
|
|
23115
|
+
};
|
|
23116
|
+
}
|
|
23117
|
+
return { ...validateWorkspacesDocument(parsed, file), revision: workspacesRevision(raw) };
|
|
23118
|
+
};
|
|
22409
23119
|
var globToRegExp = (glob) => {
|
|
22410
23120
|
let out = "";
|
|
22411
23121
|
for (let i = 0;i < glob.length; i++) {
|
|
@@ -22503,6 +23213,30 @@ var selectWorkspaceMatch = (matches, cwd, workspaceName) => {
|
|
|
22503
23213
|
}
|
|
22504
23214
|
return mostSpecific[0];
|
|
22505
23215
|
};
|
|
23216
|
+
var resolveWorkspaceFromEntries = (cwd, entries, workspaceName) => {
|
|
23217
|
+
const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
|
|
23218
|
+
const matches = [];
|
|
23219
|
+
for (const ws of entries) {
|
|
23220
|
+
const glob = ws.glob.replaceAll("\\", "/");
|
|
23221
|
+
const canonical = canonicalGlob(glob);
|
|
23222
|
+
let matched = false;
|
|
23223
|
+
for (const target of targets) {
|
|
23224
|
+
if (matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target)) {
|
|
23225
|
+
matched = true;
|
|
23226
|
+
break;
|
|
23227
|
+
}
|
|
23228
|
+
}
|
|
23229
|
+
if (matched)
|
|
23230
|
+
matches.push(ws);
|
|
23231
|
+
}
|
|
23232
|
+
return selectWorkspaceMatch(matches, cwd, workspaceName) ?? null;
|
|
23233
|
+
};
|
|
23234
|
+
var resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
|
|
23235
|
+
const result = readWorkspacesResult(dir);
|
|
23236
|
+
if (result.status === "malformed" || result.status === "invalid")
|
|
23237
|
+
throw new Error(result.error);
|
|
23238
|
+
return resolveWorkspaceFromEntries(cwd, result.entries, workspaceName);
|
|
23239
|
+
};
|
|
22506
23240
|
var runtimeWorkspacePolicySchema = (kind) => {
|
|
22507
23241
|
const key = kind === "branch" ? "branchPolicy" : kind === "commit" ? "commitPolicy" : "vcs";
|
|
22508
23242
|
const policySchema = kind === "branch" ? workspaceBranchPolicySchema : kind === "commit" ? workspaceCommitPolicySchema : object2({
|
|
@@ -22525,7 +23259,7 @@ var runtimeWorkspaceIndexSchema = object2({
|
|
|
22525
23259
|
workspaces: array(object2({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
|
|
22526
23260
|
}).passthrough();
|
|
22527
23261
|
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
22528
|
-
const file =
|
|
23262
|
+
const file = path13.join(configDir(), "workspaces.json");
|
|
22529
23263
|
let raw;
|
|
22530
23264
|
try {
|
|
22531
23265
|
raw = readFileSync4(file, "utf8");
|
|
@@ -22588,13 +23322,13 @@ var resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
|
|
|
22588
23322
|
};
|
|
22589
23323
|
|
|
22590
23324
|
// packages/workit-core/src/core/vcs-config.ts
|
|
22591
|
-
import
|
|
22592
|
-
import
|
|
22593
|
-
import { spawnSync as
|
|
22594
|
-
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ??
|
|
23325
|
+
import fs12 from "node:fs";
|
|
23326
|
+
import path14 from "node:path";
|
|
23327
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
23328
|
+
var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path14.join(configDir(), "vcs.json");
|
|
22595
23329
|
var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
22596
23330
|
var remoteProvider = (cwd) => {
|
|
22597
|
-
const r =
|
|
23331
|
+
const r = spawnSync7("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
|
|
22598
23332
|
if (r.status !== 0)
|
|
22599
23333
|
return null;
|
|
22600
23334
|
const url = (r.stdout ?? "").trim();
|
|
@@ -22631,7 +23365,7 @@ var remoteEndpoint = (remote) => {
|
|
|
22631
23365
|
var configuredSshEndpoint = (host) => {
|
|
22632
23366
|
if (host.startsWith("-"))
|
|
22633
23367
|
return null;
|
|
22634
|
-
const resolved =
|
|
23368
|
+
const resolved = spawnSync7("ssh", ["-G", host], {
|
|
22635
23369
|
encoding: "utf8",
|
|
22636
23370
|
timeout: 5000,
|
|
22637
23371
|
maxBuffer: 1024 * 1024,
|
|
@@ -22681,7 +23415,7 @@ var readVcsConfig = () => {
|
|
|
22681
23415
|
const file = vcsConfigPath();
|
|
22682
23416
|
let raw;
|
|
22683
23417
|
try {
|
|
22684
|
-
raw =
|
|
23418
|
+
raw = fs12.readFileSync(file, "utf8");
|
|
22685
23419
|
} catch {
|
|
22686
23420
|
return { status: "missing", path: file, config: {} };
|
|
22687
23421
|
}
|
|
@@ -22747,7 +23481,7 @@ function vcsConfig(mode, cwd) {
|
|
|
22747
23481
|
const prov = cfg[provider] ?? {};
|
|
22748
23482
|
const out = {
|
|
22749
23483
|
ok: true,
|
|
22750
|
-
configPath:
|
|
23484
|
+
configPath: path14.resolve(cfgPath),
|
|
22751
23485
|
provider,
|
|
22752
23486
|
defaultTargetBranch: defaultTarget,
|
|
22753
23487
|
pr: cfg.pr ?? {},
|
|
@@ -22785,7 +23519,7 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
22785
23519
|
`).filter((l) => l.startsWith("## ")).map((l) => l.trim()),
|
|
22786
23520
|
descriptionPreview: desc.slice(0, 600)
|
|
22787
23521
|
});
|
|
22788
|
-
const remoteResult =
|
|
23522
|
+
const remoteResult = spawnSync7("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
22789
23523
|
cwd,
|
|
22790
23524
|
encoding: "utf8"
|
|
22791
23525
|
});
|
|
@@ -22801,7 +23535,7 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
22801
23535
|
if (!hostingApiHostMatches(remote, host))
|
|
22802
23536
|
return { ok: false, error: "PR style target does not match the provider host" };
|
|
22803
23537
|
if (provider === "gitlab") {
|
|
22804
|
-
const run = (args) =>
|
|
23538
|
+
const run = (args) => spawnSync7("glab", ["api", ...args], {
|
|
22805
23539
|
cwd,
|
|
22806
23540
|
encoding: "utf8",
|
|
22807
23541
|
env
|
|
@@ -22821,7 +23555,7 @@ function mergedPrStyle(limit = 6, cwd) {
|
|
|
22821
23555
|
});
|
|
22822
23556
|
}
|
|
22823
23557
|
} else if (provider === "github") {
|
|
22824
|
-
const r =
|
|
23558
|
+
const r = spawnSync7("gh", [
|
|
22825
23559
|
"pr",
|
|
22826
23560
|
"list",
|
|
22827
23561
|
"--repo",
|
|
@@ -22870,305 +23604,34 @@ var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspaceP
|
|
|
22870
23604
|
import { realpathSync as realpathSync4 } from "node:fs";
|
|
22871
23605
|
|
|
22872
23606
|
// packages/workit-core/src/core/policy-resolver.ts
|
|
22873
|
-
|
|
22874
|
-
|
|
22875
|
-
assessment: assessmentSchema,
|
|
22876
|
-
constraints: array(constraintSchema),
|
|
22877
|
-
prior: object2({
|
|
22878
|
-
decisions: array(decisionSchema),
|
|
22879
|
-
findings: array(findingSchema),
|
|
22880
|
-
requirements: array(requirementSchema)
|
|
22881
|
-
}).strict(),
|
|
22882
|
-
preferences: object2({ fast: boolean2().optional(), thorough: boolean2().optional() }).strict().optional()
|
|
22883
|
-
}).strict();
|
|
22884
|
-
var compareCodeUnits3 = (left, right) => {
|
|
22885
|
-
const length = Math.min(left.length, right.length);
|
|
22886
|
-
for (let index = 0;index < length; index += 1) {
|
|
22887
|
-
const difference = left.charCodeAt(index) - right.charCodeAt(index);
|
|
22888
|
-
if (difference !== 0)
|
|
22889
|
-
return difference;
|
|
22890
|
-
}
|
|
22891
|
-
return left.length - right.length;
|
|
22892
|
-
};
|
|
22893
|
-
var stableUnique = (values) => [...new Set(values)].toSorted(compareCodeUnits3);
|
|
22894
|
-
var stableList = (values) => [...values].toSorted((left, right) => compareCodeUnits3(canonicalJson(left), canonicalJson(right)));
|
|
22895
|
-
var normalizedScope = (scope) => ({
|
|
22896
|
-
description: scope.description,
|
|
22897
|
-
paths: stableUnique(scope.paths),
|
|
22898
|
-
exclusions: stableUnique(scope.exclusions)
|
|
22899
|
-
});
|
|
22900
|
-
var normalizedRefList = (values) => stableList(values);
|
|
22901
|
-
var normalizeAssessment = (assessment) => ({
|
|
22902
|
-
facts: stableList(assessment.facts.map((fact) => ({ ...fact, refs: normalizedRefList(fact.refs) }))),
|
|
22903
|
-
signals: Object.fromEntries(Object.entries(assessment.signals).map(([name, signal]) => [
|
|
22904
|
-
name,
|
|
22905
|
-
{ ...signal, refs: normalizedRefList(signal.refs) }
|
|
22906
|
-
])),
|
|
22907
|
-
consequences: stableList(assessment.consequences.map((consequence) => ({
|
|
22908
|
-
...consequence,
|
|
22909
|
-
fact: { ...consequence.fact, refs: normalizedRefList(consequence.fact.refs) }
|
|
22910
|
-
}))),
|
|
22911
|
-
verification: stableList(assessment.verification.map((verification) => ({
|
|
22912
|
-
...verification,
|
|
22913
|
-
scope: normalizedScope(verification.scope),
|
|
22914
|
-
availableChecks: normalizedRefList(verification.availableChecks),
|
|
22915
|
-
gaps: stableUnique(verification.gaps)
|
|
22916
|
-
})))
|
|
22917
|
-
});
|
|
22918
|
-
var normalizeConstraint = (constraint) => ({
|
|
22919
|
-
...constraint,
|
|
22920
|
-
source: constraint.source,
|
|
22921
|
-
requires: stableList(constraint.requires.map((requirement) => ({
|
|
22922
|
-
...requirement,
|
|
22923
|
-
scope: normalizedScope(requirement.scope),
|
|
22924
|
-
refs: normalizedRefList(requirement.refs)
|
|
22925
|
-
})))
|
|
22926
|
-
});
|
|
22927
|
-
var normalizeDecision = (decision) => ({
|
|
22928
|
-
...decision,
|
|
22929
|
-
binding: {
|
|
22930
|
-
...decision.binding,
|
|
22931
|
-
scope: normalizedScope(decision.binding.scope),
|
|
22932
|
-
contentRefs: normalizedRefList(decision.binding.contentRefs)
|
|
22933
|
-
},
|
|
22934
|
-
requirementIds: stableUnique(decision.requirementIds)
|
|
22935
|
-
});
|
|
22936
|
-
var normalizeIntent = (intent) => ({
|
|
22937
|
-
...intent,
|
|
22938
|
-
scope: normalizedScope(intent.scope),
|
|
22939
|
-
authorityRefs: normalizedRefList(intent.authorityRefs)
|
|
22940
|
-
});
|
|
22941
|
-
var normalizePrior = (prior) => ({
|
|
22942
|
-
decisions: stableList(prior.decisions.map(normalizeDecision)),
|
|
22943
|
-
findings: stableList(prior.findings.map((finding) => ({
|
|
22944
|
-
...finding,
|
|
22945
|
-
scope: normalizedScope(finding.scope),
|
|
22946
|
-
refs: normalizedRefList(finding.refs),
|
|
22947
|
-
resolution: finding.resolution ? {
|
|
22948
|
-
...finding.resolution,
|
|
22949
|
-
evidenceIds: stableUnique(finding.resolution.evidenceIds),
|
|
22950
|
-
decisionIds: stableUnique(finding.resolution.decisionIds)
|
|
22951
|
-
} : null
|
|
22952
|
-
}))),
|
|
22953
|
-
requirements: stableList(prior.requirements.map((requirement) => ({
|
|
22954
|
-
...requirement,
|
|
22955
|
-
scope: normalizedScope(requirement.scope)
|
|
22956
|
-
})))
|
|
22957
|
-
});
|
|
22958
|
-
var invalidFields = (error) => ({
|
|
22959
|
-
fields: error.issues.map((issue) => ({
|
|
22960
|
-
path: issue.code === "unrecognized_keys" ? issue.keys.join(".") : issue.path.join("."),
|
|
22961
|
-
reason: issue.message
|
|
22962
|
-
}))
|
|
22963
|
-
});
|
|
22964
|
-
function normalizeResolverInput(input) {
|
|
22965
|
-
const parsed = resolverInputSchema.safeParse(input);
|
|
22966
|
-
if (!parsed.success)
|
|
22967
|
-
return failure2("invalid_input", "resolver input is invalid", invalidFields(parsed.error));
|
|
22968
|
-
const value = parsed.data;
|
|
22969
|
-
const signals = value.assessment.signals;
|
|
22970
|
-
if (signals.behaviorChange.value === true && signals.mechanicalLowRisk.value === true)
|
|
22971
|
-
return failure2("invalid_input", "behavioral change cannot be wholly mechanical", {
|
|
22972
|
-
fields: [
|
|
22973
|
-
{
|
|
22974
|
-
path: "assessment.signals",
|
|
22975
|
-
reason: "contradictory behaviorChange and mechanicalLowRisk"
|
|
22976
|
-
}
|
|
22977
|
-
]
|
|
22978
|
-
});
|
|
22979
|
-
const ids = value.constraints.map((constraint) => constraint.id);
|
|
23607
|
+
function resolvePolicy(input) {
|
|
23608
|
+
const ids = input.constraints.map((constraint) => constraint.id);
|
|
22980
23609
|
if (new Set(ids).size !== ids.length)
|
|
22981
23610
|
return failure2("invalid_input", "constraint IDs must be unique", {
|
|
22982
23611
|
fields: [{ path: "constraints", reason: "duplicate constraint id" }]
|
|
22983
23612
|
});
|
|
22984
|
-
if (value.preferences?.fast && value.preferences.thorough)
|
|
22985
|
-
return failure2("invalid_input", "fast and thorough preferences contradict", {
|
|
22986
|
-
fields: [{ path: "preferences", reason: "fast and thorough cannot both be enabled" }]
|
|
22987
|
-
});
|
|
22988
|
-
const normalized = {
|
|
22989
|
-
intent: normalizeIntent(value.intent),
|
|
22990
|
-
assessment: normalizeAssessment(value.assessment),
|
|
22991
|
-
constraints: stableList(value.constraints.map(normalizeConstraint)),
|
|
22992
|
-
prior: normalizePrior(value.prior),
|
|
22993
|
-
...value.preferences ? { preferences: { ...value.preferences } } : {},
|
|
22994
|
-
policyVersion: POLICY_VERSION
|
|
22995
|
-
};
|
|
22996
|
-
return success(null, null, normalized);
|
|
22997
|
-
}
|
|
22998
|
-
var requirement = (spec) => ({
|
|
22999
|
-
...spec,
|
|
23000
|
-
id: requirementId({
|
|
23001
|
-
ruleId: spec.ruleId,
|
|
23002
|
-
scope: spec.scope,
|
|
23003
|
-
satisfaction: spec.satisfaction,
|
|
23004
|
-
before: spec.before,
|
|
23005
|
-
dependentAction: spec.dependentAction
|
|
23006
|
-
})
|
|
23007
|
-
});
|
|
23008
|
-
var signalReason = (input, name) => input.assessment.signals[name].reason;
|
|
23009
|
-
function resolveRequirements(input) {
|
|
23010
|
-
const { signals } = input.assessment;
|
|
23011
|
-
const scope = input.intent.scope;
|
|
23012
|
-
const requirements = [];
|
|
23013
|
-
const hasUnknownSignal = Object.values(signals).some((signal) => signal.value === "unknown");
|
|
23014
|
-
const hasUnknownFact = [
|
|
23015
|
-
...input.assessment.facts,
|
|
23016
|
-
...input.assessment.consequences.map(({ fact }) => fact)
|
|
23017
|
-
].some((fact) => fact.basis === "unknown");
|
|
23018
|
-
if (hasUnknownSignal || hasUnknownFact)
|
|
23019
|
-
requirements.push(requirement({
|
|
23020
|
-
ruleId: "consequential-unknown",
|
|
23021
|
-
dimension: "investigation",
|
|
23022
|
-
scope,
|
|
23023
|
-
reason: `Resolve consequential uncertainty before the dependent action: ${[
|
|
23024
|
-
...Object.entries(signals).filter(([, signal]) => signal.value === "unknown").map(([name]) => name),
|
|
23025
|
-
...hasUnknownFact ? ["unknown fact"] : []
|
|
23026
|
-
].join(", ")}`,
|
|
23027
|
-
satisfaction: "An observed or inferred fact resolves the uncertainty and records its supporting references.",
|
|
23028
|
-
before: "dependent_action",
|
|
23029
|
-
dependentAction: null,
|
|
23030
|
-
acceptanceAllowed: false
|
|
23031
|
-
}));
|
|
23032
|
-
if (signals.productChoiceOpen.value === true)
|
|
23033
|
-
requirements.push(requirement({
|
|
23034
|
-
ruleId: "product-decision",
|
|
23035
|
-
dimension: "decisions",
|
|
23036
|
-
scope,
|
|
23037
|
-
reason: `A consequential product choice remains open: ${signalReason(input, "productChoiceOpen")}`,
|
|
23038
|
-
satisfaction: "Present the material alternatives and record the user's decision before the dependent action.",
|
|
23039
|
-
before: "dependent_action",
|
|
23040
|
-
dependentAction: null,
|
|
23041
|
-
acceptanceAllowed: false
|
|
23042
|
-
}));
|
|
23043
|
-
const independentBehaviorReview = input.preferences?.thorough === true || input.assessment.consequences.some((consequence) => ["security", "data", "public_contract", "operations"].includes(consequence.area));
|
|
23044
|
-
if (signals.behaviorChange.value === true)
|
|
23045
|
-
requirements.push(requirement({
|
|
23046
|
-
ruleId: "behavioral-verification",
|
|
23047
|
-
dimension: "testing",
|
|
23048
|
-
scope,
|
|
23049
|
-
reason: `Behavior, side effects, permissions, or data handling may change: ${signalReason(input, "behaviorChange")}`,
|
|
23050
|
-
satisfaction: "A useful behavioral check passes against the affected behavior and current candidate.",
|
|
23051
|
-
before: "close",
|
|
23052
|
-
dependentAction: null,
|
|
23053
|
-
acceptanceAllowed: false
|
|
23054
|
-
}), requirement({
|
|
23055
|
-
ruleId: independentBehaviorReview ? "fresh-context-review" : "self-review",
|
|
23056
|
-
dimension: "review",
|
|
23057
|
-
scope,
|
|
23058
|
-
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.",
|
|
23059
|
-
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.",
|
|
23060
|
-
before: "close",
|
|
23061
|
-
dependentAction: null,
|
|
23062
|
-
acceptanceAllowed: !independentBehaviorReview
|
|
23063
|
-
}));
|
|
23064
|
-
else if (signals.mechanicalLowRisk.value === true)
|
|
23065
|
-
requirements.push(requirement({
|
|
23066
|
-
ruleId: "mechanical-existing-checks",
|
|
23067
|
-
dimension: "verification",
|
|
23068
|
-
scope,
|
|
23069
|
-
reason: `The change is demonstrably mechanical and low-risk: ${signalReason(input, "mechanicalLowRisk")}`,
|
|
23070
|
-
satisfaction: "Run the relevant existing checks for affected consumers; do not add tautological tests.",
|
|
23071
|
-
before: "close",
|
|
23072
|
-
dependentAction: null,
|
|
23073
|
-
acceptanceAllowed: true
|
|
23074
|
-
}), requirement({
|
|
23075
|
-
ruleId: input.preferences?.thorough ? "fresh-context-review" : "self-review",
|
|
23076
|
-
dimension: "review",
|
|
23077
|
-
scope,
|
|
23078
|
-
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.",
|
|
23079
|
-
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.",
|
|
23080
|
-
before: "close",
|
|
23081
|
-
dependentAction: null,
|
|
23082
|
-
acceptanceAllowed: !input.preferences?.thorough
|
|
23083
|
-
}));
|
|
23084
|
-
if (signals.behaviorChange.value === true || signals.mechanicalLowRisk.value === true)
|
|
23085
|
-
requirements.push(requirement({
|
|
23086
|
-
ruleId: "pre-pr-cleanup",
|
|
23087
|
-
dimension: "verification",
|
|
23088
|
-
scope,
|
|
23089
|
-
reason: "Implementation output must be deslopped before it is delivered.",
|
|
23090
|
-
satisfaction: "Run the workit-deslop pass and record passing check evidence, or record an approved limitation decision waiving cleanup.",
|
|
23091
|
-
before: "dependent_action",
|
|
23092
|
-
dependentAction: "hosting.pull_request",
|
|
23093
|
-
acceptanceAllowed: true
|
|
23094
|
-
}));
|
|
23095
|
-
if (signals.durableAgreementNeeded.value === true)
|
|
23096
|
-
requirements.push(requirement({
|
|
23097
|
-
ruleId: "durable-spec",
|
|
23098
|
-
dimension: "artifacts",
|
|
23099
|
-
scope,
|
|
23100
|
-
reason: `The work needs a durable agreement about intended behavior: ${signalReason(input, "durableAgreementNeeded")}`,
|
|
23101
|
-
satisfaction: "Record the agreed behavior in a durable specification before implementation writes.",
|
|
23102
|
-
before: "write",
|
|
23103
|
-
dependentAction: null,
|
|
23104
|
-
acceptanceAllowed: true
|
|
23105
|
-
}));
|
|
23106
|
-
if (signals.coordinationPlanNeeded.value === true)
|
|
23107
|
-
requirements.push(requirement({
|
|
23108
|
-
ruleId: "coordination-plan",
|
|
23109
|
-
dimension: "continuity",
|
|
23110
|
-
scope,
|
|
23111
|
-
reason: `Dependencies, sequencing, or resumption need durable coordination: ${signalReason(input, "coordinationPlanNeeded")}`,
|
|
23112
|
-
satisfaction: "Record useful sequencing, dependencies, and the next action in a compact plan.",
|
|
23113
|
-
before: "dependent_action",
|
|
23114
|
-
dependentAction: null,
|
|
23115
|
-
acceptanceAllowed: true
|
|
23116
|
-
}));
|
|
23117
|
-
if (signals.helperUseful.value === true && !input.preferences?.fast)
|
|
23118
|
-
requirements.push(requirement({
|
|
23119
|
-
ruleId: "helper-usefulness",
|
|
23120
|
-
dimension: "delegation",
|
|
23121
|
-
scope,
|
|
23122
|
-
reason: `Independent judgment or investigation justifies a scoped helper: ${signalReason(input, "helperUseful")}`,
|
|
23123
|
-
satisfaction: "Use a bounded helper only for the independent objective and reconcile its result.",
|
|
23124
|
-
before: "dependent_action",
|
|
23125
|
-
dependentAction: null,
|
|
23126
|
-
acceptanceAllowed: true
|
|
23127
|
-
}));
|
|
23128
|
-
for (const constraint of input.constraints)
|
|
23129
|
-
for (const obligation of constraint.requires) {
|
|
23130
|
-
const dimension = obligation.method ?? (obligation.kind === "check" ? "verification" : obligation.kind === "decision" ? "decisions" : "challenge");
|
|
23131
|
-
requirements.push(requirement({
|
|
23132
|
-
ruleId: `project-constraint:${constraint.id}:${obligation.kind}:${sha256({
|
|
23133
|
-
kind: obligation.kind,
|
|
23134
|
-
scope: normalizedScope(obligation.scope),
|
|
23135
|
-
refs: normalizedRefList(obligation.refs),
|
|
23136
|
-
method: obligation.method,
|
|
23137
|
-
before: obligation.before,
|
|
23138
|
-
dependentAction: obligation.dependentAction
|
|
23139
|
-
})}`,
|
|
23140
|
-
dimension,
|
|
23141
|
-
scope: obligation.scope,
|
|
23142
|
-
reason: `${constraint.kind} constraint requires ${obligation.kind}: ${constraint.statement}`,
|
|
23143
|
-
satisfaction: `Satisfy the constraint obligation using its referenced ${obligation.kind}.`,
|
|
23144
|
-
before: obligation.before,
|
|
23145
|
-
dependentAction: obligation.dependentAction,
|
|
23146
|
-
acceptanceAllowed: constraint.acceptanceAllowed && obligation.kind !== "decision"
|
|
23147
|
-
}));
|
|
23148
|
-
}
|
|
23149
|
-
const seen = new Set;
|
|
23150
|
-
return requirements.filter((item) => {
|
|
23151
|
-
if (seen.has(item.id))
|
|
23152
|
-
return false;
|
|
23153
|
-
seen.add(item.id);
|
|
23154
|
-
return true;
|
|
23155
|
-
});
|
|
23156
|
-
}
|
|
23157
|
-
function resolvePolicy(input) {
|
|
23158
|
-
const normalized = normalizeResolverInput(input);
|
|
23159
|
-
if (!normalized.ok)
|
|
23160
|
-
return normalized;
|
|
23161
|
-
const requirements = resolveRequirements(normalized.data);
|
|
23162
23613
|
const policy = {
|
|
23163
23614
|
policyVersion: POLICY_VERSION,
|
|
23164
|
-
inputDigest: sha256(canonicalJson(
|
|
23165
|
-
|
|
23615
|
+
inputDigest: sha256(canonicalJson({
|
|
23616
|
+
judgment: input.judgment,
|
|
23617
|
+
scope: input.intent.scope,
|
|
23618
|
+
constraints: input.constraints,
|
|
23619
|
+
verification: input.verification ?? "self",
|
|
23620
|
+
policyVersion: POLICY_VERSION
|
|
23621
|
+
})),
|
|
23622
|
+
requirements: deriveRequirements(input.judgment, input.intent.scope, input.constraints, input.verification)
|
|
23166
23623
|
};
|
|
23167
23624
|
const parsed = policySchema.safeParse(policy);
|
|
23168
23625
|
if (!parsed.success)
|
|
23169
|
-
return failure2("invalid_input", "resolved policy is invalid",
|
|
23626
|
+
return failure2("invalid_input", "resolved policy is invalid", {
|
|
23627
|
+
fields: parsed.error.issues.map((issue) => ({
|
|
23628
|
+
path: issue.path.join("."),
|
|
23629
|
+
reason: issue.message
|
|
23630
|
+
}))
|
|
23631
|
+
});
|
|
23170
23632
|
return success(null, null, parsed.data);
|
|
23171
23633
|
}
|
|
23634
|
+
var compareCodeUnits3 = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
23172
23635
|
function diffPolicy(previous, next, reason, now) {
|
|
23173
23636
|
if (!policySchema.safeParse(previous).success && previous !== null)
|
|
23174
23637
|
throw new TypeError("previous policy is invalid");
|
|
@@ -23196,7 +23659,222 @@ function diffPolicy(previous, next, reason, now) {
|
|
|
23196
23659
|
return parsed.data;
|
|
23197
23660
|
}
|
|
23198
23661
|
|
|
23662
|
+
// packages/workit-core/src/core/policy/judgment.ts
|
|
23663
|
+
var isObject6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23664
|
+
var squash = (key) => key.toLowerCase().replace(/[_-]/g, "");
|
|
23665
|
+
var ALIASES = {
|
|
23666
|
+
riskTier: ["risktier", "risk", "risklevel", "tier"],
|
|
23667
|
+
behaviorChange: ["behaviorchange", "behavior", "behaviour", "behaviourchange"],
|
|
23668
|
+
productChoiceOpen: ["productchoiceopen", "openproductchoice", "productchoice", "openchoice"],
|
|
23669
|
+
needsPlan: ["needsplan", "plan", "needsspec", "spec"],
|
|
23670
|
+
note: ["note", "notes", "reason", "why"],
|
|
23671
|
+
refs: ["refs", "ref", "references"]
|
|
23672
|
+
};
|
|
23673
|
+
var LEGACY_KEYS = new Set(["assessment", "facts", "signals", "consequences", "verification"]);
|
|
23674
|
+
var RISK = {
|
|
23675
|
+
trivial: "trivial",
|
|
23676
|
+
low: "trivial",
|
|
23677
|
+
minor: "trivial",
|
|
23678
|
+
mechanical: "trivial",
|
|
23679
|
+
none: "trivial",
|
|
23680
|
+
normal: "normal",
|
|
23681
|
+
medium: "normal",
|
|
23682
|
+
med: "normal",
|
|
23683
|
+
moderate: "normal",
|
|
23684
|
+
high: "high",
|
|
23685
|
+
critical: "high",
|
|
23686
|
+
major: "high"
|
|
23687
|
+
};
|
|
23688
|
+
var YES = new Set(["true", "yes", "y", "1", "on"]);
|
|
23689
|
+
var NO = new Set(["false", "no", "n", "0", "off", "none"]);
|
|
23690
|
+
var flag = (value, unknownAs) => {
|
|
23691
|
+
if (typeof value === "boolean")
|
|
23692
|
+
return { ok: true, value };
|
|
23693
|
+
if (typeof value === "number" && (value === 0 || value === 1))
|
|
23694
|
+
return { ok: true, value: value === 1 };
|
|
23695
|
+
if (typeof value !== "string")
|
|
23696
|
+
return { ok: false };
|
|
23697
|
+
const text = value.trim().toLowerCase();
|
|
23698
|
+
if (YES.has(text))
|
|
23699
|
+
return { ok: true, value: true };
|
|
23700
|
+
if (NO.has(text))
|
|
23701
|
+
return { ok: true, value: false };
|
|
23702
|
+
if (text === "unknown" || text === "maybe")
|
|
23703
|
+
return { ok: true, value: unknownAs };
|
|
23704
|
+
return { ok: false };
|
|
23705
|
+
};
|
|
23706
|
+
var fromLegacy = (raw, previous) => {
|
|
23707
|
+
const source = isObject6(raw.assessment) ? raw.assessment : raw;
|
|
23708
|
+
const signals = isObject6(source.signals) ? source.signals : {};
|
|
23709
|
+
const value = (name) => isObject6(signals[name]) ? signals[name].value : undefined;
|
|
23710
|
+
const known = (name, fallback) => typeof value(name) === "boolean" ? value(name) : fallback;
|
|
23711
|
+
const behaviorChange = known("behaviorChange", previous?.behaviorChange ?? false);
|
|
23712
|
+
const consequences = Array.isArray(source.consequences) ? source.consequences : [];
|
|
23713
|
+
const freshReview = value("behaviorChange") === true && consequences.some((item) => isObject6(item) && ["security", "data", "public_contract", "operations"].includes(String(item.area)));
|
|
23714
|
+
return {
|
|
23715
|
+
riskTier: freshReview ? "high" : value("mechanicalLowRisk") === true && !behaviorChange ? "trivial" : "normal",
|
|
23716
|
+
behaviorChange,
|
|
23717
|
+
productChoiceOpen: known("productChoiceOpen", previous?.productChoiceOpen ?? false),
|
|
23718
|
+
needsPlan: known("durableAgreementNeeded", previous?.needsPlan ?? false) || value("coordinationPlanNeeded") === true
|
|
23719
|
+
};
|
|
23720
|
+
};
|
|
23721
|
+
var DEFAULT = {
|
|
23722
|
+
riskTier: "normal",
|
|
23723
|
+
behaviorChange: false,
|
|
23724
|
+
productChoiceOpen: false,
|
|
23725
|
+
needsPlan: false,
|
|
23726
|
+
note: null,
|
|
23727
|
+
refs: []
|
|
23728
|
+
};
|
|
23729
|
+
function normalizeJudgment(raw, previous) {
|
|
23730
|
+
const input = isObject6(raw) ? raw : {};
|
|
23731
|
+
const legacy = Object.keys(input).some((key) => LEGACY_KEYS.has(key));
|
|
23732
|
+
const base = { ...DEFAULT, ...previous, refs: [...previous?.refs ?? []] };
|
|
23733
|
+
const judgment = legacy ? { ...base, ...fromLegacy(input, previous) } : { ...base };
|
|
23734
|
+
const ignored = [];
|
|
23735
|
+
const fields = [];
|
|
23736
|
+
for (const [key, value] of Object.entries(input)) {
|
|
23737
|
+
if (LEGACY_KEYS.has(key))
|
|
23738
|
+
continue;
|
|
23739
|
+
const name = Object.keys(ALIASES).find((field) => ALIASES[field].includes(squash(key)));
|
|
23740
|
+
if (!name) {
|
|
23741
|
+
ignored.push(key);
|
|
23742
|
+
continue;
|
|
23743
|
+
}
|
|
23744
|
+
if (name === "riskTier") {
|
|
23745
|
+
const tier = typeof value === "string" ? RISK[value.trim().toLowerCase()] : undefined;
|
|
23746
|
+
if (tier)
|
|
23747
|
+
judgment.riskTier = tier;
|
|
23748
|
+
else
|
|
23749
|
+
fields.push({ path: key, reason: "expected trivial|normal|high (or low|medium)" });
|
|
23750
|
+
} else if (name === "note") {
|
|
23751
|
+
judgment.note = typeof value === "string" && value.trim() ? value.trim() : null;
|
|
23752
|
+
} else if (name === "refs") {
|
|
23753
|
+
const items = Array.isArray(value) ? value : [value];
|
|
23754
|
+
judgment.refs.push(...items.filter((item) => item !== null && item !== "").map(refOf));
|
|
23755
|
+
} else {
|
|
23756
|
+
if (name === "needsPlan" && typeof value === "string" && !flag(value, false).ok && value.trim()) {
|
|
23757
|
+
judgment.needsPlan = true;
|
|
23758
|
+
judgment.refs.push(refOf(value.trim()));
|
|
23759
|
+
continue;
|
|
23760
|
+
}
|
|
23761
|
+
const parsed = flag(value, name === "behaviorChange");
|
|
23762
|
+
if (parsed.ok)
|
|
23763
|
+
judgment[name] = parsed.value;
|
|
23764
|
+
else
|
|
23765
|
+
fields.push({ path: key, reason: "expected true|false (yes|no accepted)" });
|
|
23766
|
+
}
|
|
23767
|
+
}
|
|
23768
|
+
if (fields.length)
|
|
23769
|
+
return failure2("invalid_input", "judgment values are invalid", { fields });
|
|
23770
|
+
const seen = new Set;
|
|
23771
|
+
judgment.refs = judgment.refs.filter((ref) => {
|
|
23772
|
+
const key = JSON.stringify(ref);
|
|
23773
|
+
return seen.has(key) ? false : (seen.add(key), true);
|
|
23774
|
+
});
|
|
23775
|
+
const parsed = judgmentSchema.safeParse(judgment);
|
|
23776
|
+
if (!parsed.success)
|
|
23777
|
+
return failure2("invalid_input", "judgment is invalid", {
|
|
23778
|
+
fields: parsed.error.issues.map((issue) => ({
|
|
23779
|
+
path: issue.path.join("."),
|
|
23780
|
+
reason: issue.message
|
|
23781
|
+
}))
|
|
23782
|
+
});
|
|
23783
|
+
return success(null, null, { judgment: parsed.data, ignored, legacy });
|
|
23784
|
+
}
|
|
23785
|
+
|
|
23786
|
+
// packages/workit-core/src/autonomy.ts
|
|
23787
|
+
import os3 from "node:os";
|
|
23788
|
+
import path15 from "node:path";
|
|
23789
|
+
var accountHome = () => {
|
|
23790
|
+
try {
|
|
23791
|
+
return os3.userInfo().homedir;
|
|
23792
|
+
} catch {
|
|
23793
|
+
return os3.homedir();
|
|
23794
|
+
}
|
|
23795
|
+
};
|
|
23796
|
+
var grantsHome = { resolve: accountHome };
|
|
23797
|
+
var grantsDir = () => path15.join(grantsHome.resolve(), ".config", "workit");
|
|
23798
|
+
var grantsOverride = () => {
|
|
23799
|
+
const configured = path15.resolve(resolveConfigDir());
|
|
23800
|
+
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`;
|
|
23801
|
+
};
|
|
23802
|
+
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
23803
|
+
var DEFAULT_GRANTS = Object.freeze({
|
|
23804
|
+
push: true,
|
|
23805
|
+
pr: true,
|
|
23806
|
+
merge: false,
|
|
23807
|
+
release: false,
|
|
23808
|
+
rerun: true
|
|
23809
|
+
});
|
|
23810
|
+
var DEFAULT_ENDPOINT = "commit";
|
|
23811
|
+
var DEFAULT_VERIFICATION = "self";
|
|
23812
|
+
var verificationOf = (entry) => entry?.verification === "independent" ? "independent" : "self";
|
|
23813
|
+
var endpointOf = (entry) => entry?.defaultEndpoint === "pr" ? "pr" : "commit";
|
|
23814
|
+
var isGrantValue = (value) => value === true || value === false || value === "verified";
|
|
23815
|
+
var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
|
|
23816
|
+
var fromAutoApprove = (value) => {
|
|
23817
|
+
if (value === true)
|
|
23818
|
+
return { push: true, pr: true, merge: "verified" };
|
|
23819
|
+
if (!Array.isArray(value))
|
|
23820
|
+
return null;
|
|
23821
|
+
const grants = {};
|
|
23822
|
+
for (const item of value)
|
|
23823
|
+
if (item === "push" || item === "pr")
|
|
23824
|
+
grants[item] = true;
|
|
23825
|
+
else if (item === "merge")
|
|
23826
|
+
grants.merge = "verified";
|
|
23827
|
+
return Object.keys(grants).length > 0 ? grants : null;
|
|
23828
|
+
};
|
|
23829
|
+
function configuredGrants(entry) {
|
|
23830
|
+
const record = entry ?? {};
|
|
23831
|
+
const raw = record.autonomy;
|
|
23832
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
23833
|
+
const grants = {};
|
|
23834
|
+
for (const kind of GRANT_KINDS) {
|
|
23835
|
+
const value = raw[kind];
|
|
23836
|
+
if (isGrantValue(value))
|
|
23837
|
+
grants[kind] = normalized(kind, value);
|
|
23838
|
+
}
|
|
23839
|
+
return { grants, source: "autonomy" };
|
|
23840
|
+
}
|
|
23841
|
+
const legacy = fromAutoApprove(record.autoApprove);
|
|
23842
|
+
return legacy ? { grants: legacy, source: "autoApprove" } : { grants: {}, source: "default" };
|
|
23843
|
+
}
|
|
23844
|
+
function resolveAutonomy(cwd) {
|
|
23845
|
+
const override = grantsOverride();
|
|
23846
|
+
if (override)
|
|
23847
|
+
return {
|
|
23848
|
+
workspace: null,
|
|
23849
|
+
grants: { ...DEFAULT_GRANTS },
|
|
23850
|
+
configured: [],
|
|
23851
|
+
source: "default",
|
|
23852
|
+
accountConfigured: false,
|
|
23853
|
+
defaultEndpoint: DEFAULT_ENDPOINT,
|
|
23854
|
+
verification: DEFAULT_VERIFICATION,
|
|
23855
|
+
note: override
|
|
23856
|
+
};
|
|
23857
|
+
const workspace = resolveWorkspaceFrom(cwd, grantsDir(), process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined);
|
|
23858
|
+
const { grants, source } = configuredGrants(workspace);
|
|
23859
|
+
return {
|
|
23860
|
+
workspace: workspace?.name ?? null,
|
|
23861
|
+
grants: { ...DEFAULT_GRANTS, ...grants },
|
|
23862
|
+
configured: GRANT_KINDS.filter((kind) => grants[kind] !== undefined),
|
|
23863
|
+
source,
|
|
23864
|
+
accountConfigured: Boolean(workspace?.vcs?.account),
|
|
23865
|
+
defaultEndpoint: endpointOf(workspace),
|
|
23866
|
+
verification: verificationOf(workspace)
|
|
23867
|
+
};
|
|
23868
|
+
}
|
|
23869
|
+
|
|
23199
23870
|
// packages/workit-core/src/core/task-engine.ts
|
|
23871
|
+
var workspaceVerification = (root) => {
|
|
23872
|
+
try {
|
|
23873
|
+
return resolveAutonomy(root).verification;
|
|
23874
|
+
} catch {
|
|
23875
|
+
return "self";
|
|
23876
|
+
}
|
|
23877
|
+
};
|
|
23200
23878
|
var provenance = (context, kind = context.provenanceKind ?? "host_observed") => ({
|
|
23201
23879
|
kind,
|
|
23202
23880
|
host: context.caller.host,
|
|
@@ -23389,22 +24067,7 @@ var mapRef = (ref, ids) => {
|
|
|
23389
24067
|
return ref;
|
|
23390
24068
|
return { ...ref, id: ids.get(ref.id) ?? ref.id };
|
|
23391
24069
|
};
|
|
23392
|
-
var portableRefs = (refs) => refs.filter((ref) => ref.kind === "file" || ref.kind === "record");
|
|
23393
24070
|
var portableRef = (ref) => ref && (ref.kind === "file" || ref.kind === "record") ? ref : null;
|
|
23394
|
-
var portableFact = (fact) => {
|
|
23395
|
-
const refs = portableRefs(fact.refs);
|
|
23396
|
-
return fact.basis === "observed" && refs.length === 0 ? { ...fact, basis: "unknown", refs } : { ...fact, refs };
|
|
23397
|
-
};
|
|
23398
|
-
var portableSignal = (signal) => {
|
|
23399
|
-
const refs = portableRefs(signal.refs);
|
|
23400
|
-
return signal.basis === "observed" && refs.length === 0 ? {
|
|
23401
|
-
...signal,
|
|
23402
|
-
value: "unknown",
|
|
23403
|
-
basis: "unknown",
|
|
23404
|
-
reason: "portable supporting reference was removed",
|
|
23405
|
-
refs
|
|
23406
|
-
} : { ...signal, refs };
|
|
23407
|
-
};
|
|
23408
24071
|
var importedProvenance = (context) => ({
|
|
23409
24072
|
kind: "imported",
|
|
23410
24073
|
host: context.caller.host,
|
|
@@ -23416,18 +24079,6 @@ var portableTask = (task) => {
|
|
|
23416
24079
|
if (typeof stripped !== "object" || stripped === null || Array.isArray(stripped))
|
|
23417
24080
|
throw new TypeError("portable task rewrite produced a non-record");
|
|
23418
24081
|
const clone = structuredClone(stripped);
|
|
23419
|
-
const demote = (facts, signals, consequences) => {
|
|
23420
|
-
for (const fact of facts)
|
|
23421
|
-
Object.assign(fact, portableFact(fact));
|
|
23422
|
-
for (const name of Object.keys(signals)) {
|
|
23423
|
-
const signal = signals[name];
|
|
23424
|
-
Object.assign(signal, portableSignal(signal));
|
|
23425
|
-
}
|
|
23426
|
-
for (const consequence of consequences)
|
|
23427
|
-
Object.assign(consequence.fact, portableFact(consequence.fact));
|
|
23428
|
-
};
|
|
23429
|
-
for (const entry of clone.assessments)
|
|
23430
|
-
demote(entry.data.facts, entry.data.signals, entry.data.consequences);
|
|
23431
24082
|
clone.candidates = [];
|
|
23432
24083
|
return clone;
|
|
23433
24084
|
};
|
|
@@ -23444,6 +24095,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
23444
24095
|
for (const entry of [
|
|
23445
24096
|
source.intent,
|
|
23446
24097
|
...source.assessments,
|
|
24098
|
+
...source.judgments ?? [],
|
|
23447
24099
|
...source.evidence,
|
|
23448
24100
|
...source.decisions,
|
|
23449
24101
|
...source.findings,
|
|
@@ -23467,9 +24119,16 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
23467
24119
|
id: fresh(entry.id),
|
|
23468
24120
|
recordedAt: timestamp,
|
|
23469
24121
|
provenance: imported,
|
|
23470
|
-
data: rewriteRecordRefs(data,
|
|
24122
|
+
data: rewriteRecordRefs(data, legacyAssessmentSchema, remap)
|
|
23471
24123
|
};
|
|
23472
24124
|
});
|
|
24125
|
+
const judgments = source.judgments?.map((entry) => ({
|
|
24126
|
+
...entry,
|
|
24127
|
+
id: fresh(entry.id),
|
|
24128
|
+
recordedAt: timestamp,
|
|
24129
|
+
provenance: imported,
|
|
24130
|
+
data: rewriteRecordRefs(entry.data, judgmentSchema, remap)
|
|
24131
|
+
}));
|
|
23473
24132
|
const evidence = source.evidence.map((entry) => ({
|
|
23474
24133
|
...entry,
|
|
23475
24134
|
id: fresh(entry.id),
|
|
@@ -23537,6 +24196,7 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
|
|
|
23537
24196
|
status: "paused",
|
|
23538
24197
|
closure: null,
|
|
23539
24198
|
assessments,
|
|
24199
|
+
...judgments ? { judgments } : {},
|
|
23540
24200
|
evidence,
|
|
23541
24201
|
decisions: decisions.map((entry) => {
|
|
23542
24202
|
const data = rewriteRecordRefs(entry.data, decisionSchema, remap);
|
|
@@ -23606,7 +24266,8 @@ class WorkitCore {
|
|
|
23606
24266
|
retryOmittedRevisions(request, run) {
|
|
23607
24267
|
return retryFilledRevisions(filledRevisions(request), run);
|
|
23608
24268
|
}
|
|
23609
|
-
implicitTask(family,
|
|
24269
|
+
implicitTask(family, raw) {
|
|
24270
|
+
const request = normalizeOperationInput(family, raw, this.context.caller.host);
|
|
23610
24271
|
if (typeof request !== "object" || request === null || Array.isArray(request))
|
|
23611
24272
|
return success(null, null, request);
|
|
23612
24273
|
const value = request;
|
|
@@ -23860,7 +24521,11 @@ class WorkitCore {
|
|
|
23860
24521
|
if ((this.context.workerId ?? null) !== null)
|
|
23861
24522
|
return failure2("permission_denied", "helpers cannot change task requirements");
|
|
23862
24523
|
this.fillRevisions(input, task.data);
|
|
23863
|
-
const
|
|
24524
|
+
const judged = normalizeJudgment(input.judgment, latestJudgment(task.data));
|
|
24525
|
+
if (!judged.ok)
|
|
24526
|
+
return judged;
|
|
24527
|
+
const judgment = judged.data.judgment;
|
|
24528
|
+
const resolved = this.resolve(task.data, judgment);
|
|
23864
24529
|
if (!resolved.ok)
|
|
23865
24530
|
return resolved;
|
|
23866
24531
|
if (input.action === "preview")
|
|
@@ -23871,16 +24536,17 @@ class WorkitCore {
|
|
|
23871
24536
|
if (current.status === "closed")
|
|
23872
24537
|
return failure2("invalid_transition", "closed task cannot be assessed");
|
|
23873
24538
|
const nextChange = diffPolicy(current.policy, resolved.data, "policy reassessed", mutation.now);
|
|
23874
|
-
const
|
|
24539
|
+
const entry = {
|
|
23875
24540
|
id: newId(),
|
|
23876
24541
|
recordedAt: mutation.now,
|
|
23877
24542
|
provenance: provenance(this.context),
|
|
23878
|
-
data:
|
|
24543
|
+
data: judgment
|
|
23879
24544
|
};
|
|
23880
24545
|
const next = {
|
|
23881
24546
|
...current,
|
|
23882
24547
|
constraints: this.context.constraints,
|
|
23883
|
-
|
|
24548
|
+
judgments: [...current.judgments ?? [], entry],
|
|
24549
|
+
critical: [...new Set([...current.critical ?? [], "judgments"])],
|
|
23884
24550
|
policy: resolved.data,
|
|
23885
24551
|
policyChanges: nextChange ? [...current.policyChanges, nextChange] : current.policyChanges
|
|
23886
24552
|
};
|
|
@@ -24082,9 +24748,14 @@ class WorkitCore {
|
|
|
24082
24748
|
return workspace;
|
|
24083
24749
|
if (!workspace.data)
|
|
24084
24750
|
return failure2("not_found", "workspace not found");
|
|
24085
|
-
|
|
24751
|
+
const binding = {
|
|
24752
|
+
...input.binding,
|
|
24753
|
+
taskId: input.binding.taskId ?? task.data.id,
|
|
24754
|
+
workspaceId: input.binding.workspaceId ?? workspace.data.id
|
|
24755
|
+
};
|
|
24756
|
+
if (binding.taskId !== task.data.id || binding.workspaceId !== workspace.data.id)
|
|
24086
24757
|
return failure2("invalid_input", "decision task or workspace binding is invalid");
|
|
24087
|
-
const content = verifyDecisionContentAtRoot(this.store.root,
|
|
24758
|
+
const content = verifyDecisionContentAtRoot(this.store.root, binding);
|
|
24088
24759
|
if (!content.ok)
|
|
24089
24760
|
return content;
|
|
24090
24761
|
const knownRequirements = new Set(task.data.policy?.requirements.map((item) => item.id) ?? []);
|
|
@@ -24092,7 +24763,7 @@ class WorkitCore {
|
|
|
24092
24763
|
return failure2("invalid_input", "decision references an unknown requirement");
|
|
24093
24764
|
const base = {
|
|
24094
24765
|
purpose: input.purpose,
|
|
24095
|
-
binding
|
|
24766
|
+
binding,
|
|
24096
24767
|
response: input.response,
|
|
24097
24768
|
requirementIds: input.requirementIds,
|
|
24098
24769
|
revoked: null
|
|
@@ -24451,16 +25122,12 @@ class WorkitCore {
|
|
|
24451
25122
|
now: trustedNow(this.context)
|
|
24452
25123
|
});
|
|
24453
25124
|
}
|
|
24454
|
-
resolve(task,
|
|
25125
|
+
resolve(task, judgment) {
|
|
24455
25126
|
return resolvePolicy({
|
|
24456
25127
|
intent: task.intent.data,
|
|
24457
|
-
|
|
25128
|
+
judgment,
|
|
24458
25129
|
constraints: this.context.constraints,
|
|
24459
|
-
|
|
24460
|
-
decisions: task.decisions.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data),
|
|
24461
|
-
findings: task.findings.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data),
|
|
24462
|
-
requirements: task.origin || task.policy === null ? [] : task.policy.requirements
|
|
24463
|
-
}
|
|
25130
|
+
verification: workspaceVerification(this.context.root)
|
|
24464
25131
|
});
|
|
24465
25132
|
}
|
|
24466
25133
|
summary(task) {
|
|
@@ -24786,9 +25453,9 @@ class WorkitCore {
|
|
|
24786
25453
|
}
|
|
24787
25454
|
}
|
|
24788
25455
|
// packages/workit-core/src/core/repo-context.ts
|
|
24789
|
-
import { spawnSync as
|
|
24790
|
-
import { existsSync as
|
|
24791
|
-
import
|
|
25456
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
25457
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, readdirSync as readdirSync4 } from "node:fs";
|
|
25458
|
+
import path16 from "node:path";
|
|
24792
25459
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
24793
25460
|
try {
|
|
24794
25461
|
gitRevisionParts(value);
|
|
@@ -24815,7 +25482,7 @@ var invalidRange = (cwd) => ({
|
|
|
24815
25482
|
cwd
|
|
24816
25483
|
});
|
|
24817
25484
|
var runGit = (cwd, args) => {
|
|
24818
|
-
const result =
|
|
25485
|
+
const result = spawnSync8("git", args, { cwd, encoding: "utf8" });
|
|
24819
25486
|
return {
|
|
24820
25487
|
stdout: (result.stdout ?? "").trimEnd(),
|
|
24821
25488
|
stderr: (result.stderr ?? "").trimEnd(),
|
|
@@ -24826,11 +25493,11 @@ var repoRoot = (cwd) => {
|
|
|
24826
25493
|
const r = runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
24827
25494
|
return r.exitCode === 0 && r.stdout ? r.stdout : cwd;
|
|
24828
25495
|
};
|
|
24829
|
-
var
|
|
25496
|
+
var currentBranch2 = (cwd) => {
|
|
24830
25497
|
const r = runGit(cwd, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
24831
25498
|
return r.exitCode === 0 && r.stdout ? r.stdout : "unknown";
|
|
24832
25499
|
};
|
|
24833
|
-
var
|
|
25500
|
+
var defaultBase2 = (cwd) => {
|
|
24834
25501
|
for (const ref of [
|
|
24835
25502
|
"origin/main",
|
|
24836
25503
|
"main",
|
|
@@ -24844,11 +25511,11 @@ var defaultBase = (cwd) => {
|
|
|
24844
25511
|
}
|
|
24845
25512
|
return "HEAD~1";
|
|
24846
25513
|
};
|
|
24847
|
-
var rangeArgOrDefault = (arg, cwd) => arg ? arg : `${
|
|
25514
|
+
var rangeArgOrDefault = (arg, cwd) => arg ? arg : `${defaultBase2(cwd)}...HEAD`;
|
|
24848
25515
|
var isProtectedBranch = (branch) => ["main", "master", "develop", "prod", "production"].includes(branch);
|
|
24849
25516
|
var isPrBranch = (branch) => branch.startsWith("feature/") || branch.startsWith("bugfix/");
|
|
24850
25517
|
function resolvePrBranchContext(cwd) {
|
|
24851
|
-
const branch =
|
|
25518
|
+
const branch = currentBranch2(cwd);
|
|
24852
25519
|
if (isProtectedBranch(branch)) {
|
|
24853
25520
|
return {
|
|
24854
25521
|
ok: false,
|
|
@@ -24922,7 +25589,7 @@ var findPrTemplate = (cwd) => {
|
|
|
24922
25589
|
]) {
|
|
24923
25590
|
const parts = rel.split("/");
|
|
24924
25591
|
const base = parts[parts.length - 1];
|
|
24925
|
-
const dir = parts.length > 1 ?
|
|
25592
|
+
const dir = parts.length > 1 ? path16.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
24926
25593
|
const actual = probeCaseInsensitive(dir, base);
|
|
24927
25594
|
if (actual)
|
|
24928
25595
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -24953,8 +25620,8 @@ var diffStatForRange = (cwd, range) => {
|
|
|
24953
25620
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
24954
25621
|
};
|
|
24955
25622
|
var packageScripts = (cwd, keys) => {
|
|
24956
|
-
const pkgPath =
|
|
24957
|
-
if (!
|
|
25623
|
+
const pkgPath = path16.join(cwd, "package.json");
|
|
25624
|
+
if (!existsSync6(pkgPath))
|
|
24958
25625
|
return [];
|
|
24959
25626
|
try {
|
|
24960
25627
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
@@ -24979,9 +25646,9 @@ var documentationFiles = (cwd) => {
|
|
|
24979
25646
|
if (entry.isDirectory()) {
|
|
24980
25647
|
if (excluded.has(entry.name))
|
|
24981
25648
|
continue;
|
|
24982
|
-
walk(
|
|
25649
|
+
walk(path16.join(dir, entry.name), depth + 1);
|
|
24983
25650
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
24984
|
-
matches.push("./" +
|
|
25651
|
+
matches.push("./" + path16.relative(cwd, path16.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
24985
25652
|
}
|
|
24986
25653
|
}
|
|
24987
25654
|
};
|
|
@@ -24989,7 +25656,7 @@ var documentationFiles = (cwd) => {
|
|
|
24989
25656
|
return matches.toSorted().slice(0, 200);
|
|
24990
25657
|
};
|
|
24991
25658
|
var readTrimmed2 = (file, maxLines) => {
|
|
24992
|
-
if (!
|
|
25659
|
+
if (!existsSync6(file))
|
|
24993
25660
|
return "";
|
|
24994
25661
|
const lines = readFileSync5(file, "utf8").split(`
|
|
24995
25662
|
`);
|
|
@@ -24997,7 +25664,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
24997
25664
|
`);
|
|
24998
25665
|
};
|
|
24999
25666
|
function prReadyContext(root, range) {
|
|
25000
|
-
const cwd =
|
|
25667
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
25001
25668
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
25002
25669
|
return invalidRange(cwd);
|
|
25003
25670
|
let stdout = "";
|
|
@@ -25013,7 +25680,7 @@ function prReadyContext(root, range) {
|
|
|
25013
25680
|
stdout += printSection("Repository");
|
|
25014
25681
|
stdout += `root: ${cwd}
|
|
25015
25682
|
`;
|
|
25016
|
-
stdout += `branch: ${
|
|
25683
|
+
stdout += `branch: ${currentBranch2(cwd)}
|
|
25017
25684
|
`;
|
|
25018
25685
|
stdout += `range: ${range}
|
|
25019
25686
|
`;
|
|
@@ -25055,7 +25722,7 @@ ${status.stderr}` : "");
|
|
|
25055
25722
|
stdout += `template_path: ${template}
|
|
25056
25723
|
|
|
25057
25724
|
`;
|
|
25058
|
-
stdout += readTrimmed2(
|
|
25725
|
+
stdout += readTrimmed2(path16.join(cwd, template), 220);
|
|
25059
25726
|
} else {
|
|
25060
25727
|
stdout += `template_path: none
|
|
25061
25728
|
|
|
@@ -25065,7 +25732,7 @@ ${status.stderr}` : "");
|
|
|
25065
25732
|
stdout += `
|
|
25066
25733
|
`;
|
|
25067
25734
|
stdout += printSection("Recent Validation Signals");
|
|
25068
|
-
if (
|
|
25735
|
+
if (existsSync6(path16.join(cwd, "package.json"))) {
|
|
25069
25736
|
stdout += `package.json detected. Common scripts:
|
|
25070
25737
|
`;
|
|
25071
25738
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -25074,7 +25741,7 @@ ${status.stderr}` : "");
|
|
|
25074
25741
|
`) + `
|
|
25075
25742
|
`;
|
|
25076
25743
|
}
|
|
25077
|
-
if (
|
|
25744
|
+
if (existsSync6(path16.join(cwd, "Cargo.toml")) || existsSync6(path16.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
25078
25745
|
stdout += `Rust project detected.
|
|
25079
25746
|
`;
|
|
25080
25747
|
}
|
|
@@ -25107,7 +25774,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
25107
25774
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
25108
25775
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
25109
25776
|
function changelogContext(root, range) {
|
|
25110
|
-
const cwd =
|
|
25777
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
25111
25778
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
25112
25779
|
return invalidRange(cwd);
|
|
25113
25780
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -25117,7 +25784,7 @@ function changelogContext(root, range) {
|
|
|
25117
25784
|
stdout += printSection("Repository");
|
|
25118
25785
|
stdout += `root: ${cwd}
|
|
25119
25786
|
`;
|
|
25120
|
-
stdout += `branch: ${
|
|
25787
|
+
stdout += `branch: ${currentBranch2(cwd)}
|
|
25121
25788
|
`;
|
|
25122
25789
|
stdout += `range: ${resolvedRange}
|
|
25123
25790
|
`;
|
|
@@ -25125,8 +25792,8 @@ function changelogContext(root, range) {
|
|
|
25125
25792
|
stdout += CHANGELOG_RULES + `
|
|
25126
25793
|
`;
|
|
25127
25794
|
stdout += printSection("Existing CHANGELOG.md");
|
|
25128
|
-
const changelogPath =
|
|
25129
|
-
if (
|
|
25795
|
+
const changelogPath = path16.join(cwd, "CHANGELOG.md");
|
|
25796
|
+
if (existsSync6(changelogPath)) {
|
|
25130
25797
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
25131
25798
|
`;
|
|
25132
25799
|
} else {
|
|
@@ -25145,7 +25812,7 @@ function changelogContext(root, range) {
|
|
|
25145
25812
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
25146
25813
|
}
|
|
25147
25814
|
function docsRefreshContext(root, range) {
|
|
25148
|
-
const cwd =
|
|
25815
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
25149
25816
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
25150
25817
|
return invalidRange(cwd);
|
|
25151
25818
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -25155,7 +25822,7 @@ function docsRefreshContext(root, range) {
|
|
|
25155
25822
|
stdout += printSection("Repository");
|
|
25156
25823
|
stdout += `root: ${cwd}
|
|
25157
25824
|
`;
|
|
25158
|
-
stdout += `branch: ${
|
|
25825
|
+
stdout += `branch: ${currentBranch2(cwd)}
|
|
25159
25826
|
`;
|
|
25160
25827
|
stdout += `range: ${resolvedRange}
|
|
25161
25828
|
`;
|
|
@@ -25167,8 +25834,8 @@ function docsRefreshContext(root, range) {
|
|
|
25167
25834
|
`) + `
|
|
25168
25835
|
`;
|
|
25169
25836
|
stdout += printSection("README Preview");
|
|
25170
|
-
const readme =
|
|
25171
|
-
if (
|
|
25837
|
+
const readme = path16.join(cwd, "README.md");
|
|
25838
|
+
if (existsSync6(readme)) {
|
|
25172
25839
|
stdout += readTrimmed2(readme, 220) + `
|
|
25173
25840
|
`;
|
|
25174
25841
|
} else {
|
|
@@ -25176,8 +25843,8 @@ function docsRefreshContext(root, range) {
|
|
|
25176
25843
|
`;
|
|
25177
25844
|
}
|
|
25178
25845
|
stdout += printSection("Package Scripts");
|
|
25179
|
-
const pkgPath =
|
|
25180
|
-
if (
|
|
25846
|
+
const pkgPath = path16.join(cwd, "package.json");
|
|
25847
|
+
if (existsSync6(pkgPath)) {
|
|
25181
25848
|
try {
|
|
25182
25849
|
const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
|
|
25183
25850
|
stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
|
|
@@ -25190,7 +25857,7 @@ function docsRefreshContext(root, range) {
|
|
|
25190
25857
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
25191
25858
|
}
|
|
25192
25859
|
function releaseNotesContext(root, rangeOrTag) {
|
|
25193
|
-
const cwd =
|
|
25860
|
+
const cwd = path16.resolve(repoRoot(root));
|
|
25194
25861
|
if (!rangeOrTag) {
|
|
25195
25862
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
25196
25863
|
`, exitCode: 1, cwd };
|
|
@@ -25204,7 +25871,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
25204
25871
|
stdout += printSection("Repository");
|
|
25205
25872
|
stdout += `root: ${cwd}
|
|
25206
25873
|
`;
|
|
25207
|
-
stdout += `branch: ${
|
|
25874
|
+
stdout += `branch: ${currentBranch2(cwd)}
|
|
25208
25875
|
`;
|
|
25209
25876
|
stdout += `requested: ${rangeOrTag}
|
|
25210
25877
|
`;
|
|
@@ -25228,7 +25895,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
25228
25895
|
`;
|
|
25229
25896
|
stdout += printSection("Existing Release Files");
|
|
25230
25897
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
25231
|
-
if (
|
|
25898
|
+
if (existsSync6(path16.join(cwd, rel)))
|
|
25232
25899
|
stdout += rel + `
|
|
25233
25900
|
`;
|
|
25234
25901
|
}
|
|
@@ -25256,12 +25923,12 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
25256
25923
|
}
|
|
25257
25924
|
|
|
25258
25925
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
25259
|
-
import { spawnSync as
|
|
25926
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
25260
25927
|
|
|
25261
25928
|
// packages/workit-core/src/core/pr-create.ts
|
|
25262
|
-
import
|
|
25263
|
-
import
|
|
25264
|
-
import { spawnSync as
|
|
25929
|
+
import fs13 from "node:fs";
|
|
25930
|
+
import path17 from "node:path";
|
|
25931
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
25265
25932
|
// packages/workit-core/src/forge/pr-body.ts
|
|
25266
25933
|
function parseGhIssue(value) {
|
|
25267
25934
|
const m = /issues\/(\d+)/.exec(value);
|
|
@@ -25271,13 +25938,13 @@ function parseGhIssue(value) {
|
|
|
25271
25938
|
// packages/workit-core/src/core/pr-create.ts
|
|
25272
25939
|
function whichOnPath(tool) {
|
|
25273
25940
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
25274
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
25941
|
+
for (const dir of (process.env.PATH ?? "").split(path17.delimiter)) {
|
|
25275
25942
|
if (!dir)
|
|
25276
25943
|
continue;
|
|
25277
25944
|
for (const name of names) {
|
|
25278
|
-
const candidate =
|
|
25945
|
+
const candidate = path17.join(dir, name);
|
|
25279
25946
|
try {
|
|
25280
|
-
|
|
25947
|
+
fs13.accessSync(candidate, fs13.constants.X_OK);
|
|
25281
25948
|
return candidate;
|
|
25282
25949
|
} catch {}
|
|
25283
25950
|
}
|
|
@@ -25286,7 +25953,7 @@ function whichOnPath(tool) {
|
|
|
25286
25953
|
}
|
|
25287
25954
|
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
25288
25955
|
var pushRemote = (cwd) => {
|
|
25289
|
-
const result =
|
|
25956
|
+
const result = spawnSync9("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
25290
25957
|
cwd,
|
|
25291
25958
|
encoding: "utf8"
|
|
25292
25959
|
});
|
|
@@ -25317,7 +25984,7 @@ var defaultCli = (bin, available) => {
|
|
|
25317
25984
|
if (!available)
|
|
25318
25985
|
return null;
|
|
25319
25986
|
return (args, root) => {
|
|
25320
|
-
const out =
|
|
25987
|
+
const out = spawnSync10(bin, args, {
|
|
25321
25988
|
cwd: root,
|
|
25322
25989
|
encoding: "utf8",
|
|
25323
25990
|
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
@@ -25395,8 +26062,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
25395
26062
|
};
|
|
25396
26063
|
|
|
25397
26064
|
// packages/workit-core/src/core/youtrack.ts
|
|
25398
|
-
import
|
|
25399
|
-
import
|
|
26065
|
+
import fs14 from "node:fs";
|
|
26066
|
+
import path18 from "node:path";
|
|
25400
26067
|
|
|
25401
26068
|
// packages/workit-core/src/core/templates.ts
|
|
25402
26069
|
var repoRoot2 = assetRoot();
|
|
@@ -25409,22 +26076,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
25409
26076
|
// packages/workit-core/src/core/youtrack.ts
|
|
25410
26077
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
25411
26078
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
25412
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
25413
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
26079
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(configDir(), "youtrack.json");
|
|
26080
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path18.join(resolveConfigDir(), "youtrack.json");
|
|
25414
26081
|
var youTrackTokenModeOk = (p) => {
|
|
25415
26082
|
if (process.platform === "win32")
|
|
25416
26083
|
return true;
|
|
25417
|
-
const mode =
|
|
26084
|
+
const mode = fs14.statSync(p).mode & 511;
|
|
25418
26085
|
return mode === 384;
|
|
25419
26086
|
};
|
|
25420
26087
|
function readYouTrackConfig(required) {
|
|
25421
26088
|
const cfgPath = youTrackConfigPath();
|
|
25422
|
-
if (!
|
|
26089
|
+
if (!fs14.existsSync(cfgPath)) {
|
|
25423
26090
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
25424
26091
|
}
|
|
25425
26092
|
let parsed;
|
|
25426
26093
|
try {
|
|
25427
|
-
parsed = JSON.parse(
|
|
26094
|
+
parsed = JSON.parse(fs14.readFileSync(cfgPath, "utf8"));
|
|
25428
26095
|
} catch {
|
|
25429
26096
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
25430
26097
|
}
|
|
@@ -25435,11 +26102,11 @@ function readYouTrackConfig(required) {
|
|
|
25435
26102
|
}
|
|
25436
26103
|
var readYouTrackContextConfig = () => {
|
|
25437
26104
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
25438
|
-
if (!
|
|
26105
|
+
if (!fs14.existsSync(cfgPath))
|
|
25439
26106
|
return { data: {} };
|
|
25440
26107
|
let parsed;
|
|
25441
26108
|
try {
|
|
25442
|
-
parsed = JSON.parse(
|
|
26109
|
+
parsed = JSON.parse(fs14.readFileSync(cfgPath, "utf8"));
|
|
25443
26110
|
} catch {
|
|
25444
26111
|
return { error: "YouTrack configuration is unavailable" };
|
|
25445
26112
|
}
|
|
@@ -25478,12 +26145,12 @@ var youTrackToken = () => {
|
|
|
25478
26145
|
if ("error" in loaded)
|
|
25479
26146
|
return loaded;
|
|
25480
26147
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
25481
|
-
const tokenPath = tokenFile ?
|
|
25482
|
-
if (!tokenPath || !
|
|
26148
|
+
const tokenPath = tokenFile ? path18.isAbsolute(tokenFile) ? path18.resolve(tokenFile) : path18.resolve(process.cwd(), tokenFile) : "";
|
|
26149
|
+
if (!tokenPath || !fs14.existsSync(tokenPath))
|
|
25483
26150
|
return { error: "missing youtrack.token" };
|
|
25484
26151
|
if (!youTrackTokenModeOk(tokenPath))
|
|
25485
26152
|
return { error: "youtrack.token mode must be 0600" };
|
|
25486
|
-
const token =
|
|
26153
|
+
const token = fs14.readFileSync(tokenPath, "utf8").trim();
|
|
25487
26154
|
if (!token)
|
|
25488
26155
|
return { error: "empty token file" };
|
|
25489
26156
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -25548,9 +26215,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
25548
26215
|
} catch {
|
|
25549
26216
|
continue;
|
|
25550
26217
|
}
|
|
25551
|
-
if (!
|
|
26218
|
+
if (!fs14.existsSync(full))
|
|
25552
26219
|
continue;
|
|
25553
|
-
const text =
|
|
26220
|
+
const text = fs14.readFileSync(full, "utf8");
|
|
25554
26221
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
25555
26222
|
if (m)
|
|
25556
26223
|
return m[1];
|
|
@@ -25820,22 +26487,22 @@ function resolveGitRevision(root, value) {
|
|
|
25820
26487
|
}
|
|
25821
26488
|
function resolveInside(root, candidate) {
|
|
25822
26489
|
const base = realpathSync5(root);
|
|
25823
|
-
const target =
|
|
25824
|
-
if (target !== base && !target.startsWith(base +
|
|
26490
|
+
const target = path19.resolve(base, candidate);
|
|
26491
|
+
if (target !== base && !target.startsWith(base + path19.sep)) {
|
|
25825
26492
|
throw new Error("path must stay inside repository root");
|
|
25826
26493
|
}
|
|
25827
26494
|
let ancestor = target;
|
|
25828
|
-
while (!
|
|
25829
|
-
ancestor =
|
|
26495
|
+
while (!existsSync7(ancestor))
|
|
26496
|
+
ancestor = path19.dirname(ancestor);
|
|
25830
26497
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
25831
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
26498
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path19.sep)) {
|
|
25832
26499
|
throw new Error("path must stay inside repository root");
|
|
25833
26500
|
}
|
|
25834
26501
|
return target;
|
|
25835
26502
|
}
|
|
25836
26503
|
function run2(root, executable, args, env = {}) {
|
|
25837
26504
|
const cwd = realpathSync5(root);
|
|
25838
|
-
const result =
|
|
26505
|
+
const result = spawnSync11(executable, args, {
|
|
25839
26506
|
cwd,
|
|
25840
26507
|
encoding: "utf8",
|
|
25841
26508
|
env: { ...process.env, ...env }
|
|
@@ -25849,7 +26516,7 @@ function run2(root, executable, args, env = {}) {
|
|
|
25849
26516
|
}
|
|
25850
26517
|
|
|
25851
26518
|
// packages/workit-core/src/core/logger.ts
|
|
25852
|
-
import
|
|
26519
|
+
import os4 from "node:os";
|
|
25853
26520
|
var REDACTED = "[REDACTED]";
|
|
25854
26521
|
var SECRET_VALUE = /\b(?:Bearer|Basic|Digest|Token)\s+\S+/gi;
|
|
25855
26522
|
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;
|
|
@@ -25892,7 +26559,7 @@ var SENSITIVE_WORDS = new Set([
|
|
|
25892
26559
|
]);
|
|
25893
26560
|
var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
|
|
25894
26561
|
var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
25895
|
-
var homePattern = new RegExp(`${escapeRegExp(
|
|
26562
|
+
var homePattern = new RegExp(`${escapeRegExp(os4.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
|
|
25896
26563
|
var applyPatterns = (value) => {
|
|
25897
26564
|
let out = value;
|
|
25898
26565
|
out = out.replace(SECRET_VALUE, REDACTED);
|
|
@@ -25943,13 +26610,13 @@ var rootVariants = (workspaceRoot) => {
|
|
|
25943
26610
|
if (!workspaceRoot)
|
|
25944
26611
|
return [];
|
|
25945
26612
|
const roots = new Set;
|
|
25946
|
-
for (const candidate of [workspaceRoot,
|
|
25947
|
-
if (
|
|
26613
|
+
for (const candidate of [workspaceRoot, path20.resolve(workspaceRoot)]) {
|
|
26614
|
+
if (path20.isAbsolute(candidate) && candidate !== path20.parse(candidate).root)
|
|
25948
26615
|
roots.add(candidate);
|
|
25949
26616
|
}
|
|
25950
26617
|
try {
|
|
25951
26618
|
const canonical = realpathSync6(workspaceRoot);
|
|
25952
|
-
if (canonical !==
|
|
26619
|
+
if (canonical !== path20.parse(canonical).root)
|
|
25953
26620
|
roots.add(canonical);
|
|
25954
26621
|
} catch {}
|
|
25955
26622
|
return [...roots].toSorted((left, right) => right.length - left.length);
|
|
@@ -25971,39 +26638,20 @@ function assertMcpHost(host) {
|
|
|
25971
26638
|
throw new Error("MCP host must be cursor, codex_cli, or codex_desktop");
|
|
25972
26639
|
}
|
|
25973
26640
|
}
|
|
25974
|
-
var
|
|
25975
|
-
var
|
|
25976
|
-
var
|
|
25977
|
-
|
|
25978
|
-
|
|
25979
|
-
|
|
25980
|
-
|
|
25981
|
-
|
|
25982
|
-
var readOnlyBranches = (family) => schemaBranches(boundedOperationJsonSchema(family)).filter((branch) => {
|
|
25983
|
-
const action = branch.properties?.action?.const;
|
|
25984
|
-
return typeof action === "string" && READ_ONLY_ACTIONS.has(action);
|
|
25985
|
-
});
|
|
25986
|
-
var advertisedToolSchemas = (attested) => {
|
|
25987
|
-
if (attested)
|
|
25988
|
-
return OPERATION_FAMILIES.map((family) => ({
|
|
26641
|
+
var READ_ONLY_ACTIONS2 = new Set(["list", "inspect", "preview", "explain", "export"]);
|
|
26642
|
+
var requiresCallerIdentity = (input) => typeof input !== "object" || input === null || !READ_ONLY_ACTIONS2.has(String(input.action));
|
|
26643
|
+
var advertisedToolSchemas = (attested) => OPERATION_FAMILIES.flatMap((family) => {
|
|
26644
|
+
const schema = flatOperationJsonSchema(family, { readOnly: !attested });
|
|
26645
|
+
if (!schema)
|
|
26646
|
+
return [];
|
|
26647
|
+
return [
|
|
26648
|
+
{
|
|
25989
26649
|
name: `workit_${family}`,
|
|
25990
|
-
description: operationDescription(family)
|
|
25991
|
-
inputSchema:
|
|
25992
|
-
}
|
|
25993
|
-
|
|
25994
|
-
|
|
25995
|
-
if (!branches.length)
|
|
25996
|
-
return [];
|
|
25997
|
-
const schema = boundedOperationJsonSchema(family);
|
|
25998
|
-
return [
|
|
25999
|
-
{
|
|
26000
|
-
name: `workit_${family}`,
|
|
26001
|
-
description: `${operationDescription(family)} Unattested MCP callers can only run read-only actions; mutating operations require an attested host session.`,
|
|
26002
|
-
inputSchema: { type: "object", ...schema, oneOf: branches }
|
|
26003
|
-
}
|
|
26004
|
-
];
|
|
26005
|
-
});
|
|
26006
|
-
};
|
|
26650
|
+
description: attested ? operationDescription(family) : `${operationDescription(family)} Unattested MCP callers can only run read-only actions; mutating operations require an attested host session.`,
|
|
26651
|
+
inputSchema: schema
|
|
26652
|
+
}
|
|
26653
|
+
];
|
|
26654
|
+
});
|
|
26007
26655
|
var sanitizeFailure = (result, workspaceRoot) => {
|
|
26008
26656
|
if (result.ok)
|
|
26009
26657
|
return result;
|
|
@@ -26050,12 +26698,12 @@ var thrownResult = (tool, error, workspaceRoot) => {
|
|
|
26050
26698
|
};
|
|
26051
26699
|
function createMcpServer(host, contextProvider) {
|
|
26052
26700
|
assertMcpHost(host);
|
|
26053
|
-
const here =
|
|
26701
|
+
const here = path20.dirname(fileURLToPath2(import.meta.url));
|
|
26054
26702
|
const sourceMarker = markSourcesLoaded([
|
|
26055
|
-
|
|
26056
|
-
|
|
26057
|
-
|
|
26058
|
-
].filter((file) =>
|
|
26703
|
+
path20.join(here, "server.ts"),
|
|
26704
|
+
path20.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
|
|
26705
|
+
path20.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
26706
|
+
].filter((file) => existsSync8(file)));
|
|
26059
26707
|
let staleWarned = false;
|
|
26060
26708
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
26061
26709
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -26179,7 +26827,7 @@ function createMcpServer(host, contextProvider) {
|
|
|
26179
26827
|
details: { operation: family }
|
|
26180
26828
|
}, workspaceRoot);
|
|
26181
26829
|
}
|
|
26182
|
-
const parsed =
|
|
26830
|
+
const parsed = parseAdvertisedOperation(family, request.params.arguments, host);
|
|
26183
26831
|
if (!parsed.ok)
|
|
26184
26832
|
return resultForClient(parsed, workspaceRoot);
|
|
26185
26833
|
if (context.callerAttested !== true && requiresCallerIdentity(parsed.data))
|
|
@@ -26234,8 +26882,8 @@ var main = async () => {
|
|
|
26234
26882
|
process.exitCode = 2;
|
|
26235
26883
|
}
|
|
26236
26884
|
};
|
|
26237
|
-
var entryPath = process.argv[1] ?
|
|
26238
|
-
if (entryPath ===
|
|
26885
|
+
var entryPath = process.argv[1] ? path21.resolve(process.argv[1]) : "";
|
|
26886
|
+
if (entryPath === path21.resolve(fileURLToPath3(import.meta.url)))
|
|
26239
26887
|
await main();
|
|
26240
26888
|
export {
|
|
26241
26889
|
McpCapabilityUnavailableError,
|