@mrciphersmith/keryx 0.3.58 → 0.3.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +537 -24
- package/dist/core.js +240 -9
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -20104,7 +20104,7 @@ var package_default;
|
|
|
20104
20104
|
var init_package = __esm(() => {
|
|
20105
20105
|
package_default = {
|
|
20106
20106
|
name: "@mrciphersmith/keryx",
|
|
20107
|
-
version: "0.3.
|
|
20107
|
+
version: "0.3.59",
|
|
20108
20108
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
20109
20109
|
private: false,
|
|
20110
20110
|
publishConfig: {
|
|
@@ -56553,6 +56553,125 @@ var init_outcome_author = __esm(() => {
|
|
|
56553
56553
|
OUTCOME_AUTHOR_READINGS = [...OUTCOME_AUTHORS, "unknown"];
|
|
56554
56554
|
});
|
|
56555
56555
|
|
|
56556
|
+
// src/flow/origin.ts
|
|
56557
|
+
function isOriginKind(value) {
|
|
56558
|
+
return typeof value === "string" && ORIGIN_KINDS.includes(value);
|
|
56559
|
+
}
|
|
56560
|
+
function nonBlank(value) {
|
|
56561
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
56562
|
+
}
|
|
56563
|
+
function readOrigin(value) {
|
|
56564
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
56565
|
+
return;
|
|
56566
|
+
const raw = value;
|
|
56567
|
+
if (!isOriginKind(raw.kind))
|
|
56568
|
+
return;
|
|
56569
|
+
const origin = { kind: raw.kind };
|
|
56570
|
+
if (typeof raw.quote === "string" && raw.quote.length > 0)
|
|
56571
|
+
origin.quote = raw.quote;
|
|
56572
|
+
if (typeof raw.source === "string" && raw.source.length > 0)
|
|
56573
|
+
origin.source = raw.source;
|
|
56574
|
+
return origin;
|
|
56575
|
+
}
|
|
56576
|
+
function readOriginKind(value) {
|
|
56577
|
+
return readOrigin(value)?.kind ?? "unknown";
|
|
56578
|
+
}
|
|
56579
|
+
function resolveOrigin(input) {
|
|
56580
|
+
if (!isOriginKind(input.kind)) {
|
|
56581
|
+
return {
|
|
56582
|
+
note: `origin kind "${input.kind}" is not one of: ${ORIGIN_KINDS.join(", ")}; origin stays unknown.`
|
|
56583
|
+
};
|
|
56584
|
+
}
|
|
56585
|
+
const hasQuote = nonBlank(input.quote);
|
|
56586
|
+
const hasSource = nonBlank(input.source);
|
|
56587
|
+
if (input.kind === "human-request") {
|
|
56588
|
+
if (!hasQuote && !hasSource) {
|
|
56589
|
+
return { note: "origin human-request needs a verbatim --quote and a --source; neither was given, origin stays unknown." };
|
|
56590
|
+
}
|
|
56591
|
+
if (!hasQuote) {
|
|
56592
|
+
return { note: "origin human-request needs a verbatim --quote of the human's first message; none was given, origin stays unknown." };
|
|
56593
|
+
}
|
|
56594
|
+
if (!hasSource) {
|
|
56595
|
+
return { note: "origin human-request needs a --source (channel, message id or time); none was given, origin stays unknown." };
|
|
56596
|
+
}
|
|
56597
|
+
} else if (!hasSource) {
|
|
56598
|
+
return { note: `origin ${input.kind} needs a --source (the check, review or test that produced it); none was given, origin stays unknown.` };
|
|
56599
|
+
}
|
|
56600
|
+
const origin = { kind: input.kind };
|
|
56601
|
+
if (hasQuote)
|
|
56602
|
+
origin.quote = input.quote;
|
|
56603
|
+
if (hasSource)
|
|
56604
|
+
origin.source = input.source;
|
|
56605
|
+
return { origin };
|
|
56606
|
+
}
|
|
56607
|
+
function effectiveOutcomeAuthor(flow) {
|
|
56608
|
+
const explicit = readOutcomeAuthor(flow.outcomeAuthor);
|
|
56609
|
+
if (explicit !== "unknown")
|
|
56610
|
+
return explicit;
|
|
56611
|
+
return readOrigin(flow.origin) === undefined ? "unknown" : "agent";
|
|
56612
|
+
}
|
|
56613
|
+
function originSummary(origin) {
|
|
56614
|
+
if (origin === undefined)
|
|
56615
|
+
return "unknown";
|
|
56616
|
+
const parts = [origin.kind];
|
|
56617
|
+
if (origin.quote !== undefined)
|
|
56618
|
+
parts.push(`quote: ${JSON.stringify(origin.quote)}`);
|
|
56619
|
+
if (origin.source !== undefined)
|
|
56620
|
+
parts.push(`source: ${origin.source}`);
|
|
56621
|
+
return parts.join("; ");
|
|
56622
|
+
}
|
|
56623
|
+
function originDetailLines(origin, indent = "") {
|
|
56624
|
+
if (origin === undefined)
|
|
56625
|
+
return [`${indent}origin: unknown`];
|
|
56626
|
+
const lines = [`${indent}origin: ${origin.kind}`];
|
|
56627
|
+
if (origin.quote !== undefined) {
|
|
56628
|
+
const [first = "", ...rest] = origin.quote.split(`
|
|
56629
|
+
`);
|
|
56630
|
+
lines.push(`${indent} quote: \xAB${first}`, ...rest.map((line) => `${indent} ${line}`));
|
|
56631
|
+
lines[lines.length - 1] += "\xBB";
|
|
56632
|
+
}
|
|
56633
|
+
if (origin.source !== undefined)
|
|
56634
|
+
lines.push(`${indent} source: ${origin.source}`);
|
|
56635
|
+
return lines;
|
|
56636
|
+
}
|
|
56637
|
+
function renderOriginBullets(origin) {
|
|
56638
|
+
const first = [];
|
|
56639
|
+
if (origin.kind === "human-request") {
|
|
56640
|
+
first.push(...bulletWithText(REQUEST_LABEL, origin.quote === undefined ? undefined : `\xAB${origin.quote}\xBB`, REQUEST_PLACEHOLDER));
|
|
56641
|
+
if (origin.source !== undefined)
|
|
56642
|
+
first[first.length - 1] += ` (source: ${origin.source})`;
|
|
56643
|
+
} else {
|
|
56644
|
+
first.push(...bulletWithText(SOURCE_LABEL, origin.source, SOURCE_PLACEHOLDER));
|
|
56645
|
+
if (origin.quote !== undefined)
|
|
56646
|
+
first.push(...bulletWithText(REQUEST_LABEL, `\xAB${origin.quote}\xBB`, REQUEST_PLACEHOLDER));
|
|
56647
|
+
}
|
|
56648
|
+
return [
|
|
56649
|
+
...first,
|
|
56650
|
+
`- ${FORMALIZATION_LABEL}: ${FORMALIZATION_PLACEHOLDER}`,
|
|
56651
|
+
`- ${OBSERVE_LABEL}: ${OBSERVE_PLACEHOLDER}`
|
|
56652
|
+
];
|
|
56653
|
+
}
|
|
56654
|
+
function bulletWithText(label, text, placeholder) {
|
|
56655
|
+
if (text === undefined)
|
|
56656
|
+
return [`- ${label}: ${placeholder}`];
|
|
56657
|
+
const lines = text.split(`
|
|
56658
|
+
`);
|
|
56659
|
+
const head = `- ${label}: ${lines[0] ?? ""}`;
|
|
56660
|
+
return [head, ...lines.slice(1).map((line) => ` ${line}`)];
|
|
56661
|
+
}
|
|
56662
|
+
var ORIGIN_KINDS, ORIGIN_READINGS, REQUEST_LABEL = "\u0417\u0430\u043F\u0440\u043E\u0441 (\u0434\u043E\u0441\u043B\u043E\u0432\u043D\u043E)", SOURCE_LABEL = "\u0418\u0441\u0442\u043E\u0447\u043D\u0438\u043A", FORMALIZATION_LABEL = "\u042D\u0444\u0444\u0435\u043A\u0442 (\u0444\u043E\u0440\u043C\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u044F \u0430\u0433\u0435\u043D\u0442\u0430)", OBSERVE_LABEL = "\u041A\u0430\u043A \u043D\u0430\u0431\u043B\u044E\u0434\u0430\u0442\u044C (\u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0430\u0433\u0435\u043D\u0442\u0430)", REQUEST_PLACEHOLDER = "<the human's request, verbatim, no translation or paraphrase>", SOURCE_PLACEHOLDER = "<where the finding or proposal came from: check, review, test or discussion>", FORMALIZATION_PLACEHOLDER = "<the effect that has to be true when this flow is done, in your own words>", OBSERVE_PLACEHOLDER = "<how the effect can be observed once the flow is done>", ORIGIN_PLACEHOLDER_BULLETS;
|
|
56663
|
+
var init_origin = __esm(() => {
|
|
56664
|
+
init_outcome_author();
|
|
56665
|
+
ORIGIN_KINDS = ["human-request", "agent-finding", "agent-proposal"];
|
|
56666
|
+
ORIGIN_READINGS = [...ORIGIN_KINDS, "unknown"];
|
|
56667
|
+
ORIGIN_PLACEHOLDER_BULLETS = [
|
|
56668
|
+
`- ${REQUEST_LABEL}: ${REQUEST_PLACEHOLDER}`,
|
|
56669
|
+
`- ${SOURCE_LABEL}: ${SOURCE_PLACEHOLDER}`,
|
|
56670
|
+
`- ${FORMALIZATION_LABEL}: ${FORMALIZATION_PLACEHOLDER}`,
|
|
56671
|
+
`- ${OBSERVE_LABEL}: ${OBSERVE_PLACEHOLDER}`
|
|
56672
|
+
];
|
|
56673
|
+
});
|
|
56674
|
+
|
|
56556
56675
|
// src/flow/identity.ts
|
|
56557
56676
|
function resolveSignerIdentity(input) {
|
|
56558
56677
|
const stated = input.stated?.trim();
|
|
@@ -56999,6 +57118,23 @@ function flowStateSchema() {
|
|
|
56999
57118
|
enum: ["agent", "human"],
|
|
57000
57119
|
description: "Who wrote the outcome criterion. Written by `flow init --outcome-author` (default `agent`; `human` only when the flag says so, never inferred) and changed only by `flow outcome author`, which journals the change. Absent on flows created before the field existed, and read as `unknown` (neither agent nor human). Gates nothing."
|
|
57001
57120
|
},
|
|
57121
|
+
origin: {
|
|
57122
|
+
type: "object",
|
|
57123
|
+
additionalProperties: false,
|
|
57124
|
+
required: ["kind"],
|
|
57125
|
+
description: "Where the flow came from. Written by `flow init --origin <kind> [--quote] [--source]` and changed only by `flow origin set`, which journals the change. `human-request` is recorded only with a verbatim quote and a source; `agent-finding` and `agent-proposal` only with a source. Absent on flows created before the field existed and on flows whose evidence was not enough, and read as `unknown`. Gates nothing.",
|
|
57126
|
+
properties: {
|
|
57127
|
+
kind: { type: "string", enum: ["human-request", "agent-finding", "agent-proposal"] },
|
|
57128
|
+
quote: {
|
|
57129
|
+
type: "string",
|
|
57130
|
+
description: "The human's request, verbatim and byte for byte: no translation, no paraphrase."
|
|
57131
|
+
},
|
|
57132
|
+
source: {
|
|
57133
|
+
type: "string",
|
|
57134
|
+
description: "Where the request or finding came from: channel, message id, time, or the check that produced it."
|
|
57135
|
+
}
|
|
57136
|
+
}
|
|
57137
|
+
},
|
|
57002
57138
|
signatures: {
|
|
57003
57139
|
type: "array",
|
|
57004
57140
|
description: "Append-only signing record for `flow ac confirm` and a passing `flow complete`. Absent means never signed under this field's existence.",
|
|
@@ -57897,17 +58033,28 @@ function outcomeBulletsFrom(markdown) {
|
|
|
57897
58033
|
if (body === null)
|
|
57898
58034
|
return null;
|
|
57899
58035
|
const bullets = [];
|
|
58036
|
+
let skipping = false;
|
|
57900
58037
|
for (const line of body.split(/\r?\n/)) {
|
|
57901
58038
|
const bullet = /^\s*[-*]\s+(.*)$/.exec(line);
|
|
57902
58039
|
if ((bullet?.[1] ?? line).trim() === OUTCOME_HINT)
|
|
57903
58040
|
continue;
|
|
57904
|
-
if (bullet !== null)
|
|
57905
|
-
|
|
57906
|
-
|
|
58041
|
+
if (bullet !== null) {
|
|
58042
|
+
skipping = isOriginTemplateBullet(bullet[1] ?? "");
|
|
58043
|
+
if (!skipping)
|
|
58044
|
+
bullets.push((bullet[1] ?? "").trim());
|
|
58045
|
+
} else if (line.trim().length > 0 && !skipping && bullets.length > 0)
|
|
57907
58046
|
bullets[bullets.length - 1] += ` ${line.trim()}`;
|
|
57908
58047
|
}
|
|
57909
58048
|
return bullets.filter((bullet) => bullet.length > 0);
|
|
57910
58049
|
}
|
|
58050
|
+
function isOriginTemplateBullet(text) {
|
|
58051
|
+
const trimmed = text.trim();
|
|
58052
|
+
return trimmed.startsWith(`${REQUEST_LABEL}:`) || trimmed.startsWith(`${SOURCE_LABEL}:`) || ORIGIN_PLACEHOLDER_BULLETS.some((placeholder) => placeholder.slice(2) === trimmed);
|
|
58053
|
+
}
|
|
58054
|
+
function isOriginPlaceholderBullet(text) {
|
|
58055
|
+
const trimmed = text.trim();
|
|
58056
|
+
return ORIGIN_PLACEHOLDER_BULLETS.some((placeholder) => placeholder.slice(2) === trimmed);
|
|
58057
|
+
}
|
|
57911
58058
|
function initIntentNote(description) {
|
|
57912
58059
|
return flowStatementFrom(description) === null ? INIT_INTENT_NOTE : null;
|
|
57913
58060
|
}
|
|
@@ -57920,6 +58067,7 @@ async function intentNoteForNewFlow(cwd, dir) {
|
|
|
57920
58067
|
}
|
|
57921
58068
|
var OUTCOME_HINT = 'State an outcome criterion: what you would look at afterwards to see this helped, as a bullet. Or write "- not measured \u2014 <reason>".', PLACEHOLDERS2, FLOW_STATEMENT_HEADINGS, STATEMENT_LIMIT = 300, INIT_INTENT_NOTE = "note: no intent statement extractable from description.md \u2014 the product index will hold no statement for this flow";
|
|
57922
58069
|
var init_description_intent = __esm(() => {
|
|
58070
|
+
init_origin();
|
|
57923
58071
|
PLACEHOLDERS2 = ["Describe the problem precisely", "What must be true when this flow is done"];
|
|
57924
58072
|
FLOW_STATEMENT_HEADINGS = [/^problem$/i, /^expected outcome$/i];
|
|
57925
58073
|
});
|
|
@@ -58508,7 +58656,9 @@ var init_folder_committed = __esm(() => {
|
|
|
58508
58656
|
});
|
|
58509
58657
|
|
|
58510
58658
|
// src/flow/templates.ts
|
|
58511
|
-
function renderDescription(title, source) {
|
|
58659
|
+
function renderDescription(title, source, origin) {
|
|
58660
|
+
const outcome = origin === undefined ? OUTCOME_HINT : renderOriginBullets(origin).join(`
|
|
58661
|
+
`);
|
|
58512
58662
|
return `# ${title}
|
|
58513
58663
|
|
|
58514
58664
|
Status: draft (flow-init skill formalizes this)
|
|
@@ -58524,7 +58674,7 @@ What must be true when this flow is done.
|
|
|
58524
58674
|
|
|
58525
58675
|
## Outcome criteria
|
|
58526
58676
|
|
|
58527
|
-
${
|
|
58677
|
+
${outcome}
|
|
58528
58678
|
|
|
58529
58679
|
## Out of Scope
|
|
58530
58680
|
|
|
@@ -58903,6 +59053,7 @@ whose status is \`implemented\`.
|
|
|
58903
59053
|
var init_templates4 = __esm(() => {
|
|
58904
59054
|
init_default_tasks();
|
|
58905
59055
|
init_description_intent();
|
|
59056
|
+
init_origin();
|
|
58906
59057
|
});
|
|
58907
59058
|
|
|
58908
59059
|
// src/flow/service.ts
|
|
@@ -58910,6 +59061,8 @@ var exports_service2 = {};
|
|
|
58910
59061
|
__export(exports_service2, {
|
|
58911
59062
|
AC_CHECK_TOKEN_BUDGET: () => AC_CHECK_TOKEN_BUDGET,
|
|
58912
59063
|
CONFIRMATION_CAVEAT: () => CONFIRMATION_CAVEAT,
|
|
59064
|
+
ORIGIN_KINDS: () => ORIGIN_KINDS,
|
|
59065
|
+
ORIGIN_READINGS: () => ORIGIN_READINGS,
|
|
58913
59066
|
OUTCOME_AUTHOR_READINGS: () => OUTCOME_AUTHOR_READINGS,
|
|
58914
59067
|
REVIEW_GATE_CONFIG_PATH: () => REVIEW_GATE_CONFIG_PATH,
|
|
58915
59068
|
acCheckCacheKey: () => acCheckCacheKey,
|
|
@@ -58924,6 +59077,7 @@ __export(exports_service2, {
|
|
|
58924
59077
|
confirmPreconditionError: () => confirmPreconditionError,
|
|
58925
59078
|
createFlowService: () => createFlowService,
|
|
58926
59079
|
describeAcKind: () => describeAcKind,
|
|
59080
|
+
effectiveOutcomeAuthor: () => effectiveOutcomeAuthor,
|
|
58927
59081
|
evaluatedVerdict: () => evaluatedVerdict,
|
|
58928
59082
|
extractCriterionTokens: () => extractCriterionTokens,
|
|
58929
59083
|
factsOnlyVerdict: () => factsOnlyVerdict,
|
|
@@ -58933,6 +59087,7 @@ __export(exports_service2, {
|
|
|
58933
59087
|
intentNoteForNewFlow: () => intentNoteForNewFlow,
|
|
58934
59088
|
isFrozen: () => isFrozen,
|
|
58935
59089
|
notCheckableVerdict: () => notCheckableVerdict,
|
|
59090
|
+
originDetailLines: () => originDetailLines,
|
|
58936
59091
|
outcomeBulletsFrom: () => outcomeBulletsFrom,
|
|
58937
59092
|
parseAcceptanceCriteria: () => parseAcceptanceCriteria,
|
|
58938
59093
|
proseOutsideFences: () => proseOutsideFences,
|
|
@@ -58940,7 +59095,8 @@ __export(exports_service2, {
|
|
|
58940
59095
|
readAcCheckEnabled: () => readAcCheckEnabled,
|
|
58941
59096
|
readAcKindRecords: () => readAcKindRecords,
|
|
58942
59097
|
readFlow: () => readFlow,
|
|
58943
|
-
|
|
59098
|
+
readOrigin: () => readOrigin,
|
|
59099
|
+
readOriginKind: () => readOriginKind,
|
|
58944
59100
|
renderAcCheckAdvisoryNotice: () => renderAcCheckAdvisoryNotice,
|
|
58945
59101
|
renderAcCheckReport: () => renderAcCheckReport,
|
|
58946
59102
|
renderAcKindDistribution: () => renderAcKindDistribution,
|
|
@@ -59203,7 +59359,24 @@ function createFlowService(deps) {
|
|
|
59203
59359
|
if (input.owner !== undefined && !input.owner.trim()) {
|
|
59204
59360
|
throw new Error(BLANK_OWNER_MESSAGE);
|
|
59205
59361
|
}
|
|
59206
|
-
const outcomeAuthor = input.outcomeAuthor === undefined ?
|
|
59362
|
+
const outcomeAuthor = input.outcomeAuthor === undefined ? undefined : parseOutcomeAuthor(input.outcomeAuthor);
|
|
59363
|
+
let originNote;
|
|
59364
|
+
let origin;
|
|
59365
|
+
let originDraft;
|
|
59366
|
+
if (input.origin !== undefined) {
|
|
59367
|
+
const resolution = resolveOrigin({ kind: input.origin, quote: input.originQuote, source: input.originSource });
|
|
59368
|
+
origin = resolution.origin;
|
|
59369
|
+
originNote = resolution.note;
|
|
59370
|
+
if (isOriginKind(input.origin)) {
|
|
59371
|
+
originDraft = {
|
|
59372
|
+
kind: input.origin,
|
|
59373
|
+
...input.originQuote?.trim() ? { quote: input.originQuote } : {},
|
|
59374
|
+
...input.originSource?.trim() ? { source: input.originSource } : {}
|
|
59375
|
+
};
|
|
59376
|
+
}
|
|
59377
|
+
} else if (input.originQuote !== undefined || input.originSource !== undefined) {
|
|
59378
|
+
originNote = "--quote/--source were given without --origin; origin stays unknown.";
|
|
59379
|
+
}
|
|
59207
59380
|
const trackerReady = deps.tracker ? await deps.tracker.detect() : false;
|
|
59208
59381
|
const tracker = trackerReady ? deps.tracker : null;
|
|
59209
59382
|
const issueRef = input.issue && deps.tracker ? deps.tracker.parseRef(input.issue) : null;
|
|
@@ -59259,12 +59432,13 @@ function createFlowService(deps) {
|
|
|
59259
59432
|
pr: { url: null },
|
|
59260
59433
|
...input.baseBranch === undefined ? {} : { baseBranch: input.baseBranch },
|
|
59261
59434
|
...input.owner?.trim() ? { owner: ownerIdentity(input.owner.trim(), "`--owner` flag on `flow init`") } : {},
|
|
59262
|
-
outcomeAuthor,
|
|
59435
|
+
...outcomeAuthor !== undefined ? { outcomeAuthor } : origin === undefined ? { outcomeAuthor: DEFAULT_OUTCOME_AUTHOR } : {},
|
|
59436
|
+
...origin === undefined ? {} : { origin },
|
|
59263
59437
|
tasks: DEFAULT_TASKS.map((task) => ({ ...task, status: "todo" })),
|
|
59264
59438
|
history: [{ at: createdAt, event: "created" }]
|
|
59265
59439
|
};
|
|
59266
59440
|
const sourceLabel = input.issue ?? "user description";
|
|
59267
|
-
await writeFileAtomic(path143.join(absolute, "description.md"), renderDescription(title, sourceLabel));
|
|
59441
|
+
await writeFileAtomic(path143.join(absolute, "description.md"), renderDescription(title, originDraft === undefined ? sourceLabel : `${sourceLabel} (origin: ${originDraft.kind})`, originDraft));
|
|
59268
59442
|
await writeFileAtomic(path143.join(absolute, "context.md"), context.markdown);
|
|
59269
59443
|
await writeFileAtomic(path143.join(absolute, "plan.md"), renderPlan());
|
|
59270
59444
|
await writeFileAtomic(path143.join(absolute, "tasks.md"), renderTasksDoc());
|
|
@@ -59272,7 +59446,12 @@ function createFlowService(deps) {
|
|
|
59272
59446
|
await writeFileAtomic(path143.join(absolute, "journal.md"), renderJournal(createdAt));
|
|
59273
59447
|
await writeFlow(input.cwd, dir, flow);
|
|
59274
59448
|
await recordAllocation(scope, { id, dir, at: createdAt, project: scope.project });
|
|
59275
|
-
return {
|
|
59449
|
+
return {
|
|
59450
|
+
flow,
|
|
59451
|
+
dir: path143.relative(input.cwd, absolute),
|
|
59452
|
+
contextNotes: context.notes,
|
|
59453
|
+
...originNote === undefined ? {} : { originNote }
|
|
59454
|
+
};
|
|
59276
59455
|
});
|
|
59277
59456
|
},
|
|
59278
59457
|
async list({ cwd }) {
|
|
@@ -59336,6 +59515,50 @@ function createFlowService(deps) {
|
|
|
59336
59515
|
});
|
|
59337
59516
|
return { flow, previous, changed };
|
|
59338
59517
|
},
|
|
59518
|
+
async originSet({ cwd, id, kind, reason, quote, source }) {
|
|
59519
|
+
if (!reason?.trim()) {
|
|
59520
|
+
throw new Error('flow origin set requires --reason "<why>"');
|
|
59521
|
+
}
|
|
59522
|
+
validateSingleLineReason(reason);
|
|
59523
|
+
let previous = "unknown";
|
|
59524
|
+
let next = "unknown";
|
|
59525
|
+
let changed = false;
|
|
59526
|
+
let note;
|
|
59527
|
+
const flow = await mutate(cwd, id, async ({ dir, flow: current }) => {
|
|
59528
|
+
const before = readOrigin(current.origin);
|
|
59529
|
+
previous = before?.kind ?? "unknown";
|
|
59530
|
+
let after;
|
|
59531
|
+
if (kind !== "unknown" && !isOriginKind(kind)) {
|
|
59532
|
+
note = `origin kind must be one of: human-request, agent-finding, agent-proposal, unknown (got "${kind}"); the origin is unchanged.`;
|
|
59533
|
+
next = previous;
|
|
59534
|
+
return current;
|
|
59535
|
+
}
|
|
59536
|
+
if (kind !== "unknown") {
|
|
59537
|
+
const resolution = resolveOrigin({
|
|
59538
|
+
kind,
|
|
59539
|
+
quote: quote !== undefined && quote.length > 0 ? quote : before?.quote,
|
|
59540
|
+
source: source !== undefined && source.length > 0 ? source : before?.source
|
|
59541
|
+
});
|
|
59542
|
+
if (resolution.origin === undefined) {
|
|
59543
|
+
note = resolution.note;
|
|
59544
|
+
next = previous;
|
|
59545
|
+
return current;
|
|
59546
|
+
}
|
|
59547
|
+
after = resolution.origin;
|
|
59548
|
+
}
|
|
59549
|
+
next = after?.kind ?? "unknown";
|
|
59550
|
+
if (JSON.stringify(before ?? null) === JSON.stringify(after ?? null)) {
|
|
59551
|
+
return current;
|
|
59552
|
+
}
|
|
59553
|
+
changed = true;
|
|
59554
|
+
if (after === undefined)
|
|
59555
|
+
delete current.origin;
|
|
59556
|
+
else
|
|
59557
|
+
current.origin = after;
|
|
59558
|
+
return save(cwd, dir, current, "origin-set", `${originSummary(before)} -> ${originSummary(after)} (${reason.trim()})`);
|
|
59559
|
+
});
|
|
59560
|
+
return { flow, previous, next, changed, ...note === undefined ? {} : { note } };
|
|
59561
|
+
},
|
|
59339
59562
|
async freeze({ cwd, id }) {
|
|
59340
59563
|
return mutate(cwd, id, async ({ dir, flow }) => {
|
|
59341
59564
|
assertTransition(flow.status, "ready");
|
|
@@ -60176,7 +60399,9 @@ var init_service9 = __esm(() => {
|
|
|
60176
60399
|
init_machine();
|
|
60177
60400
|
init_review_gate();
|
|
60178
60401
|
init_outcome_author();
|
|
60402
|
+
init_origin();
|
|
60179
60403
|
init_outcome_author();
|
|
60404
|
+
init_origin();
|
|
60180
60405
|
init_flow_move();
|
|
60181
60406
|
init_ac_reseal();
|
|
60182
60407
|
init_schema3();
|
|
@@ -100970,6 +101195,12 @@ var init_help_groups = __esm(() => {
|
|
|
100970
101195
|
group: "Managed work",
|
|
100971
101196
|
summary: "List doc sections that likely went stale because of the working diff, with Jev."
|
|
100972
101197
|
},
|
|
101198
|
+
{
|
|
101199
|
+
kind: "slash",
|
|
101200
|
+
name: "/flow",
|
|
101201
|
+
group: "Managed work",
|
|
101202
|
+
summary: `Show a flow's origin, or set it with a reason \u2014 /flow origin [<id>] | /flow origin <id> <kind> --reason "...".`
|
|
101203
|
+
},
|
|
100973
101204
|
{
|
|
100974
101205
|
kind: "slash",
|
|
100975
101206
|
name: "/opencomments",
|
|
@@ -105351,6 +105582,8 @@ async function flowCommand(args) {
|
|
|
105351
105582
|
return await runOwner(args.slice(1));
|
|
105352
105583
|
case "outcome":
|
|
105353
105584
|
return await runOutcome(args.slice(1));
|
|
105585
|
+
case "origin":
|
|
105586
|
+
return await runOrigin(args.slice(1));
|
|
105354
105587
|
case "implemented":
|
|
105355
105588
|
return await runImplemented(args.slice(1));
|
|
105356
105589
|
case "complete":
|
|
@@ -105394,7 +105627,10 @@ async function runInit(args) {
|
|
|
105394
105627
|
baseBranch: optionValue(args, "--base"),
|
|
105395
105628
|
owner: optionValue(args, "--owner"),
|
|
105396
105629
|
requireConfirmation: args.includes("--require-confirmation"),
|
|
105397
|
-
outcomeAuthor: outcomeAuthorFlag(args)
|
|
105630
|
+
outcomeAuthor: outcomeAuthorFlag(args),
|
|
105631
|
+
origin: optionValue(args, "--origin"),
|
|
105632
|
+
originQuote: textFlag(args, "--quote"),
|
|
105633
|
+
originSource: textFlag(args, "--source")
|
|
105398
105634
|
});
|
|
105399
105635
|
banner("flow init", `Created flow ${result.flow.id}`);
|
|
105400
105636
|
console.log(` ${style.green(symbols.ok)} ${style.bold(result.flow.title)}`);
|
|
@@ -105404,7 +105640,12 @@ async function runInit(args) {
|
|
|
105404
105640
|
console.log(` base: ${result.flow.baseBranch}`);
|
|
105405
105641
|
}
|
|
105406
105642
|
console.log(` owner: ${result.flow.owner?.value ?? style.dim("not set")}`);
|
|
105407
|
-
console.log(` outcome author: ${
|
|
105643
|
+
console.log(` outcome author: ${outcomeAuthorLine(result.flow)}`);
|
|
105644
|
+
for (const line of originDetailLines(readOrigin(result.flow.origin), " "))
|
|
105645
|
+
console.log(line);
|
|
105646
|
+
if (result.originNote !== undefined) {
|
|
105647
|
+
console.log(` ${style.yellow(WARN)} ${result.originNote}`);
|
|
105648
|
+
}
|
|
105408
105649
|
if (result.flow.gates?.confirmation) {
|
|
105409
105650
|
console.log(` confirmation: required ${style.dim("(a terminal-minted token: `keryx flow confirm <id>`)")}`);
|
|
105410
105651
|
}
|
|
@@ -105423,6 +105664,18 @@ async function runInit(args) {
|
|
|
105423
105664
|
`Freeze and start: ${style.cyan(`keryx flow freeze ${result.flow.id}`)} then ${style.cyan(`flow start ${result.flow.id}`)}.`
|
|
105424
105665
|
]);
|
|
105425
105666
|
}
|
|
105667
|
+
function textFlag(args, name) {
|
|
105668
|
+
const index = args.indexOf(name);
|
|
105669
|
+
if (index >= 0) {
|
|
105670
|
+
const next = args[index + 1];
|
|
105671
|
+
return next !== undefined && !/^--[A-Za-z][\w-]*(=|$)/.test(next) ? next : undefined;
|
|
105672
|
+
}
|
|
105673
|
+
return args.find((argument) => argument.startsWith(`${name}=`))?.slice(name.length + 1);
|
|
105674
|
+
}
|
|
105675
|
+
function outcomeAuthorLine(flow) {
|
|
105676
|
+
const reading = effectiveOutcomeAuthor(flow);
|
|
105677
|
+
return flow.outcomeAuthor === undefined && reading !== "unknown" ? `${reading} (derived from origin)` : reading;
|
|
105678
|
+
}
|
|
105426
105679
|
function outcomeAuthorFlag(args) {
|
|
105427
105680
|
const given = args.filter((arg) => arg === "--outcome-author" || arg.startsWith("--outcome-author=")).length;
|
|
105428
105681
|
if (given > 1) {
|
|
@@ -105545,7 +105798,9 @@ async function runStatus(args) {
|
|
|
105545
105798
|
const acLabel = flow.acChecksum ? style.green("frozen") : style.yellow("not frozen");
|
|
105546
105799
|
console.log(` AC: ${acLabel}, ${Object.keys(flow.acConfirmed).length} confirmed`);
|
|
105547
105800
|
console.log(` PR: ${flow.pr.url ? style.cyan(flow.pr.url) : style.dim("none")}`);
|
|
105548
|
-
console.log(` outcome author: ${
|
|
105801
|
+
console.log(` outcome author: ${outcomeAuthorLine(flow)}`);
|
|
105802
|
+
for (const line of originDetailLines(readOrigin(flow.origin), " "))
|
|
105803
|
+
console.log(line);
|
|
105549
105804
|
console.log(` owner: ${flow.owner?.value ? `${flow.owner.value} ${style.dim(`[${flow.owner.basis}]`)}` : style.dim("not set")}`);
|
|
105550
105805
|
const latestSignature = flow.signatures?.at(-1);
|
|
105551
105806
|
console.log(` signed: ${latestSignature ? `${latestSignature.identity.value ?? "unknown"} ${style.dim(`[${latestSignature.identity.basis}]`)} ${style.dim(`(${latestSignature.kind}, ${latestSignature.at})`)}${latestSignature.confirmation ? ` ${style.dim(`+ terminal token ${latestSignature.confirmation.tokenRef}`)}` : ""}` : style.dim("no signatures yet")}`);
|
|
@@ -105814,9 +106069,43 @@ async function runOutcome(args) {
|
|
|
105814
106069
|
author,
|
|
105815
106070
|
reason: optionValue(rest, "--reason") ?? ""
|
|
105816
106071
|
});
|
|
105817
|
-
const after =
|
|
106072
|
+
const after = effectiveOutcomeAuthor(result.flow);
|
|
105818
106073
|
console.log(result.changed ? ` ${style.green(symbols.ok)} Outcome author ${result.previous} ${style.cyan(symbols.arrow)} ${style.bold(after)}` : ` ${style.dim(symbols.bullet)} Outcome author already ${style.bold(after)}; nothing written`);
|
|
105819
106074
|
}
|
|
106075
|
+
async function runOrigin(args) {
|
|
106076
|
+
if (args[0] !== "set") {
|
|
106077
|
+
throw new Error(ORIGIN_USAGE);
|
|
106078
|
+
}
|
|
106079
|
+
const rest = args.slice(1);
|
|
106080
|
+
const positionals = [];
|
|
106081
|
+
for (let i = 0;i < rest.length; i += 1) {
|
|
106082
|
+
const arg = rest[i] ?? "";
|
|
106083
|
+
if (arg === "--reason" || arg === "--quote" || arg === "--source") {
|
|
106084
|
+
i += 1;
|
|
106085
|
+
} else if (!arg.startsWith("--")) {
|
|
106086
|
+
positionals.push(arg);
|
|
106087
|
+
}
|
|
106088
|
+
}
|
|
106089
|
+
const id = positionals[0];
|
|
106090
|
+
const kind = positionals[1];
|
|
106091
|
+
if (!id || kind === undefined || positionals.length > 2) {
|
|
106092
|
+
throw new Error(ORIGIN_USAGE);
|
|
106093
|
+
}
|
|
106094
|
+
const result = await getService().originSet({
|
|
106095
|
+
cwd: process.cwd(),
|
|
106096
|
+
id,
|
|
106097
|
+
kind,
|
|
106098
|
+
reason: optionValue(rest, "--reason") ?? "",
|
|
106099
|
+
quote: textFlag(rest, "--quote"),
|
|
106100
|
+
source: textFlag(rest, "--source")
|
|
106101
|
+
});
|
|
106102
|
+
if (result.note !== undefined) {
|
|
106103
|
+
console.log(` ${style.yellow(WARN)} ${result.note}`);
|
|
106104
|
+
console.log(` ${style.dim(symbols.bullet)} Origin stays ${style.bold(result.previous)}; nothing written`);
|
|
106105
|
+
return;
|
|
106106
|
+
}
|
|
106107
|
+
console.log(result.changed ? ` ${style.green(symbols.ok)} Origin ${result.previous} ${style.cyan(symbols.arrow)} ${style.bold(result.next)}` : ` ${style.dim(symbols.bullet)} Origin already ${style.bold(result.next)}; nothing written`);
|
|
106108
|
+
}
|
|
105820
106109
|
async function runAc(args) {
|
|
105821
106110
|
const sub = args[0];
|
|
105822
106111
|
if (sub === "confirm") {
|
|
@@ -106204,6 +106493,7 @@ function printHelp2() {
|
|
|
106204
106493
|
'keryx flow task depends <id> <taskId> --on T1,T2|none --reason "<why>" (repair an unsatisfiable dependsOn)',
|
|
106205
106494
|
'keryx flow owner set <id> --owner "<name>" --reason "<why>" (the human accountable; never inferred)',
|
|
106206
106495
|
'keryx flow outcome author <id> agent|human --reason "<why>" (who wrote the outcome criterion; journaled, gates nothing)',
|
|
106496
|
+
'keryx flow origin set <id> human-request|agent-finding|agent-proposal|unknown --reason "<why>" [--quote "<verbatim>"] [--source "<ref>"] (where the flow came from; journaled, gates nothing)',
|
|
106207
106497
|
'keryx flow ac confirm <id> <ACn> [--note "<evidence>"] [--signed-by "<name>"]',
|
|
106208
106498
|
'keryx flow ac update <id> --reason "<why>" (re-freeze the file as already edited; VOIDS prior confirmations)',
|
|
106209
106499
|
'keryx flow ac update <id> --criterion ACn --text "<criterion>" --reason "<why>" (rewrite/append that one criterion, then re-freeze; VOIDS prior confirmations)',
|
|
@@ -106225,9 +106515,10 @@ function printHelp2() {
|
|
|
106225
106515
|
]);
|
|
106226
106516
|
note("`--signed-by` names the signer explicitly (stated). Falls back to KERYX_ACTOR (also " + "stated), then to `git config user.email` in this checkout (derived \u2014 the person who " + "RAN the command, not necessarily who signed), then to `unknown`. None of these is proof a human signed: a flag, an environment variable, and a local git identity can all be set " + "by an agent. `--owner` is never inferred at all \u2014 see docs/decisions/keryx-harness/.");
|
|
106227
106517
|
note("`flow init --outcome-author agent|human` records who wrote the outcome criterion: `agent` (the default when the flag " + "is absent) or `human`, and `human` only when the flag says so \u2014 never inferred from a git " + "identity, an owner or the environment. A flow without the field reads `unknown`. The flag labels a sample and gates nothing.");
|
|
106518
|
+
note('`flow init --origin human-request|agent-finding|agent-proposal --quote "<verbatim>" --source "<ref>"` records where the flow came from. Evidence, not assertion: `human-request` is recorded only with a verbatim `--quote` of the human\'s first message with the idea AND a `--source` (channel, message id or time); `agent-finding` and `agent-proposal` need a `--source`. Without the evidence, or with an invalid kind, the origin stays `unknown`, the command still succeeds and says why. The Outcome criteria template then holds the request (or the source), the agent\'s formalization and how to observe it. `flow origin set` changes it later, with a reason. The origin labels a sample and gates nothing.');
|
|
106228
106519
|
note("`flow confirm` mints a short-lived, single-use token only for a flow created with `--require-confirmation` (or `completion.require_confirmation: true`). " + CONFIRMATION_CAVEAT + " See docs/decisions/keryx-harness/TM-03-terminal-confirmation-token.md.");
|
|
106229
106520
|
}
|
|
106230
|
-
var VALID_TASK_KINDS, WARN = "!", AC_CONFIRM_FLAGS, AC_UPDATE_FLAGS, AC_RESEAL_FLAGS, VALID_DISPOSITIONS, service4 = null, OUTCOME_AUTHOR_USAGE = 'Usage: keryx flow outcome author <id> agent|human --reason "<why>"', CHALLENGE_ALPHABET = "abcdefghjkmnpqrstuvwxyz23456789";
|
|
106521
|
+
var VALID_TASK_KINDS, WARN = "!", AC_CONFIRM_FLAGS, AC_UPDATE_FLAGS, AC_RESEAL_FLAGS, VALID_DISPOSITIONS, service4 = null, OUTCOME_AUTHOR_USAGE = 'Usage: keryx flow outcome author <id> agent|human --reason "<why>"', ORIGIN_USAGE = 'Usage: keryx flow origin set <id> human-request|agent-finding|agent-proposal|unknown --reason "<why>" [--quote "<verbatim>"] [--source "<ref>"]', CHALLENGE_ALPHABET = "abcdefghjkmnpqrstuvwxyz23456789";
|
|
106231
106522
|
var init_flow = __esm(() => {
|
|
106232
106523
|
init_args();
|
|
106233
106524
|
init_fs();
|
|
@@ -106876,6 +107167,10 @@ function flowsInSession(flows, opts) {
|
|
|
106876
107167
|
return explicitFlowMention(flow, text);
|
|
106877
107168
|
});
|
|
106878
107169
|
}
|
|
107170
|
+
function originField(raw) {
|
|
107171
|
+
const origin = readOrigin(raw);
|
|
107172
|
+
return origin === undefined ? {} : { origin };
|
|
107173
|
+
}
|
|
106879
107174
|
function flowItemFromState(flow, dir) {
|
|
106880
107175
|
const sessionIds = [
|
|
106881
107176
|
...new Set(flow.tasks.map((task) => task.runLink?.sessionId).filter((id) => typeof id === "string" && id.length > 0))
|
|
@@ -106894,7 +107189,8 @@ function flowItemFromState(flow, dir) {
|
|
|
106894
107189
|
updatedAt: flow.updatedAt,
|
|
106895
107190
|
source: flow.source.ref ?? flow.source.type,
|
|
106896
107191
|
tasks: flow.tasks.map((task) => ({ id: task.id, title: task.title, status: task.status })),
|
|
106897
|
-
outcomeAuthor:
|
|
107192
|
+
outcomeAuthor: effectiveOutcomeAuthor(flow),
|
|
107193
|
+
...originField(flow.origin)
|
|
106898
107194
|
};
|
|
106899
107195
|
}
|
|
106900
107196
|
function localSacGuard() {
|
|
@@ -107123,6 +107419,7 @@ function formatFlowDetailLines(item) {
|
|
|
107123
107419
|
`${item.id} ${item.title}`,
|
|
107124
107420
|
`Status ${item.status}`,
|
|
107125
107421
|
`Outcome author ${item.outcomeAuthor ?? "unknown"}`,
|
|
107422
|
+
...originDetailLines(item.origin),
|
|
107126
107423
|
...item.interrupted ? [` ${item.interrupted}`] : [],
|
|
107127
107424
|
...item.uncommitted ? [` note: ${item.uncommitted}`] : [],
|
|
107128
107425
|
...(item.hygiene?.notes ?? []).map((note) => ` ${note}`),
|
|
@@ -107381,6 +107678,7 @@ function openFlows(otui, chrome, options) {
|
|
|
107381
107678
|
}
|
|
107382
107679
|
var FLOWS_COMMAND = "/flows", AC_COMMAND = "/ac", FLOWS_FOOTER, MARKER_GLYPH;
|
|
107383
107680
|
var init_flow_inspector = __esm(() => {
|
|
107681
|
+
init_service9();
|
|
107384
107682
|
init_modal_host();
|
|
107385
107683
|
init_ac_kinds_surface();
|
|
107386
107684
|
init_inspector_sources();
|
|
@@ -107490,6 +107788,7 @@ function extractFlowIntent(source, repoPath) {
|
|
|
107490
107788
|
const closed = status === "done";
|
|
107491
107789
|
const title = text(flow.title) ?? /^#\s+(.+)$/m.exec(description)?.[1]?.trim() ?? repoPath.split("/").at(-1) ?? repoPath;
|
|
107492
107790
|
const criterion = outcomeCriterionFrom(description) ?? outcomeCriterionFrom(source.criteria ?? "");
|
|
107791
|
+
const origin = readOrigin(flow.origin);
|
|
107493
107792
|
return {
|
|
107494
107793
|
id: text(flow.id) ?? repoPath.split("/").at(-1)?.slice(0, 3) ?? repoPath,
|
|
107495
107794
|
source: "flow",
|
|
@@ -107501,7 +107800,8 @@ function extractFlowIntent(source, repoPath) {
|
|
|
107501
107800
|
flowStatus: status,
|
|
107502
107801
|
closedAt: closed ? closedAtOf(flow) : null,
|
|
107503
107802
|
outcome: { criterion, ...observationFrom(source.journal) },
|
|
107504
|
-
outcomeAuthor:
|
|
107803
|
+
outcomeAuthor: effectiveOutcomeAuthor(flow),
|
|
107804
|
+
...origin === undefined ? {} : { origin }
|
|
107505
107805
|
};
|
|
107506
107806
|
}
|
|
107507
107807
|
function extractDocpackIntent(source, repoPath) {
|
|
@@ -107717,6 +108017,32 @@ var init_corpus = __esm(() => {
|
|
|
107717
108017
|
};
|
|
107718
108018
|
});
|
|
107719
108019
|
|
|
108020
|
+
// src/product/by-origin.ts
|
|
108021
|
+
function g1aByOrigin(index) {
|
|
108022
|
+
const criterion = new Map;
|
|
108023
|
+
const notMeasured = new Map;
|
|
108024
|
+
for (const intent of index.intents) {
|
|
108025
|
+
if (intent.source !== "flow")
|
|
108026
|
+
continue;
|
|
108027
|
+
const reading = readOriginKind(intent.origin);
|
|
108028
|
+
const bucket = hasInstrument(intent.outcome.criterion) ? criterion : notMeasured;
|
|
108029
|
+
bucket.set(reading, (bucket.get(reading) ?? 0) + 1);
|
|
108030
|
+
}
|
|
108031
|
+
return ORIGIN_READINGS.map((origin) => ({ origin, criterion: criterion.get(origin) ?? 0, notMeasured: notMeasured.get(origin) ?? 0 }));
|
|
108032
|
+
}
|
|
108033
|
+
function g1aLines(rows) {
|
|
108034
|
+
if (rows === undefined || rows.every((row) => row.criterion + row.notMeasured === 0))
|
|
108035
|
+
return [];
|
|
108036
|
+
return [
|
|
108037
|
+
"G1a by origin (flows with a real criterion / not measured):",
|
|
108038
|
+
...rows.map((row) => ` ${row.origin}: ${row.criterion} real criterion, ${row.notMeasured} not measured`)
|
|
108039
|
+
];
|
|
108040
|
+
}
|
|
108041
|
+
var init_by_origin = __esm(() => {
|
|
108042
|
+
init_service9();
|
|
108043
|
+
init_extract3();
|
|
108044
|
+
});
|
|
108045
|
+
|
|
107720
108046
|
// src/product/store.ts
|
|
107721
108047
|
import { readFile as readFile114 } from "fs/promises";
|
|
107722
108048
|
import path242 from "path";
|
|
@@ -107775,6 +108101,13 @@ function indexProblem(value) {
|
|
|
107775
108101
|
return "index.json holds an observed intent without a verdict";
|
|
107776
108102
|
if (intent.outcomeAuthor !== undefined && !OUTCOME_AUTHOR_READINGS.includes(intent.outcomeAuthor))
|
|
107777
108103
|
return "index.json holds an intent with an unknown outcome author";
|
|
108104
|
+
if (intent.origin !== undefined) {
|
|
108105
|
+
const origin = intent.origin;
|
|
108106
|
+
if (!isRecord10(origin) || !ORIGIN_KINDS.includes(origin.kind))
|
|
108107
|
+
return "index.json holds an intent with an unknown origin";
|
|
108108
|
+
if (origin.quote !== undefined && typeof origin.quote !== "string" || origin.source !== undefined && typeof origin.source !== "string")
|
|
108109
|
+
return "index.json holds an intent with an unusable origin";
|
|
108110
|
+
}
|
|
107778
108111
|
}
|
|
107779
108112
|
return null;
|
|
107780
108113
|
}
|
|
@@ -107826,7 +108159,9 @@ function buildOpenReport(index) {
|
|
|
107826
108159
|
closedAt: intent.closedAt,
|
|
107827
108160
|
outcome: hasInstrument(intent.outcome.criterion) ? intent.outcome.criterion : intent.outcome.criterion ?? NO_INSTRUMENT,
|
|
107828
108161
|
hasCriterion: hasInstrument(intent.outcome.criterion),
|
|
107829
|
-
outcomeAuthor:
|
|
108162
|
+
outcomeAuthor: effectiveOutcomeAuthor({ outcomeAuthor: intent.outcomeAuthor, origin: intent.origin }),
|
|
108163
|
+
origin: readOriginKind(intent.origin),
|
|
108164
|
+
...originFields(intent.origin)
|
|
107830
108165
|
})).sort((a, b) => (b.closedAt ?? "").localeCompare(a.closedAt ?? "") || Number.parseInt(b.id, 10) - Number.parseInt(a.id, 10) || (a.path < b.path ? -1 : 1));
|
|
107831
108166
|
const { counts } = index;
|
|
107832
108167
|
return {
|
|
@@ -107840,7 +108175,15 @@ function buildOpenReport(index) {
|
|
|
107840
108175
|
harmed: counts.harmed,
|
|
107841
108176
|
inconclusive: counts.inconclusive,
|
|
107842
108177
|
failures: index.failures.length,
|
|
107843
|
-
entries
|
|
108178
|
+
entries,
|
|
108179
|
+
g1a: g1aByOrigin(index)
|
|
108180
|
+
};
|
|
108181
|
+
}
|
|
108182
|
+
function originFields(raw) {
|
|
108183
|
+
const origin = readOrigin(raw);
|
|
108184
|
+
return {
|
|
108185
|
+
...origin?.quote === undefined ? {} : { originQuote: origin.quote },
|
|
108186
|
+
...origin?.source === undefined ? {} : { originSource: origin.source }
|
|
107844
108187
|
};
|
|
107845
108188
|
}
|
|
107846
108189
|
function openHeaderLines(report) {
|
|
@@ -107862,12 +108205,14 @@ function openEntryLines(entry) {
|
|
|
107862
108205
|
`${label} ${entry.title}`,
|
|
107863
108206
|
`${pad}outcome: ${entry.outcome}`,
|
|
107864
108207
|
`${pad}outcome author: ${entry.outcomeAuthor}`,
|
|
108208
|
+
...originDetailLines(entry.origin === undefined || entry.origin === "unknown" ? undefined : { kind: entry.origin, quote: entry.originQuote, source: entry.originSource }, pad),
|
|
107865
108209
|
`${pad}closed ${entry.closedAt === null ? "at an unrecorded time" : entry.closedAt.slice(0, 10)}`
|
|
107866
108210
|
];
|
|
107867
108211
|
}
|
|
107868
108212
|
function renderOpen(report) {
|
|
107869
108213
|
const rows = report.entries.flatMap((entry) => ["", ...openEntryLines(entry).map((line) => ` ${line}`)]);
|
|
107870
|
-
|
|
108214
|
+
const g1a = g1aLines(report.g1a);
|
|
108215
|
+
return [...openHeaderLines(report), ...rows, ...g1a.length > 0 ? ["", ...g1a] : []].join(`
|
|
107871
108216
|
`);
|
|
107872
108217
|
}
|
|
107873
108218
|
async function loadOpenReport(cwd) {
|
|
@@ -107900,6 +108245,7 @@ function renderIndexSummary(index, file) {
|
|
|
107900
108245
|
var REBUILD = "Run `keryx product index`.";
|
|
107901
108246
|
var init_open = __esm(() => {
|
|
107902
108247
|
init_service9();
|
|
108248
|
+
init_by_origin();
|
|
107903
108249
|
init_extract3();
|
|
107904
108250
|
init_store7();
|
|
107905
108251
|
});
|
|
@@ -107908,6 +108254,7 @@ var init_open = __esm(() => {
|
|
|
107908
108254
|
var init_service14 = __esm(() => {
|
|
107909
108255
|
init_corpus();
|
|
107910
108256
|
init_extract3();
|
|
108257
|
+
init_by_origin();
|
|
107911
108258
|
init_open();
|
|
107912
108259
|
init_store7();
|
|
107913
108260
|
});
|
|
@@ -115426,6 +115773,11 @@ var init_agent_commands = __esm(() => {
|
|
|
115426
115773
|
description: "List doc sections that likely went stale because of the working diff, with Jev",
|
|
115427
115774
|
modes: AGENT_ONLY
|
|
115428
115775
|
},
|
|
115776
|
+
{
|
|
115777
|
+
name: "/flow",
|
|
115778
|
+
description: `Show a flow's origin, or set it \u2014 /flow origin [<id>] | /flow origin <id> <kind> --reason "..."`,
|
|
115779
|
+
modes: AGENT_ONLY
|
|
115780
|
+
},
|
|
115429
115781
|
{
|
|
115430
115782
|
name: "/opencomments",
|
|
115431
115783
|
description: "List open PR review comments with a Jev resolved/still-open/escalation label \u2014 /opencomments <owner/repo> <pr>",
|
|
@@ -115852,12 +116204,25 @@ function proseEntries(body) {
|
|
|
115852
116204
|
const entries = [];
|
|
115853
116205
|
let sawHint = false;
|
|
115854
116206
|
let open = false;
|
|
116207
|
+
let skippingOrigin = false;
|
|
115855
116208
|
for (const raw of body.split(/\r?\n/)) {
|
|
115856
116209
|
const line = raw.trim();
|
|
115857
116210
|
if (line.length === 0) {
|
|
115858
116211
|
open = false;
|
|
115859
116212
|
continue;
|
|
115860
116213
|
}
|
|
116214
|
+
if (/^[-*]\s+/.test(line)) {
|
|
116215
|
+
const text = line.replace(/^[-*]\s+/, "");
|
|
116216
|
+
skippingOrigin = isOriginTemplateBullet(text);
|
|
116217
|
+
if (skippingOrigin) {
|
|
116218
|
+
if (isOriginPlaceholderBullet(text))
|
|
116219
|
+
sawHint = true;
|
|
116220
|
+
open = false;
|
|
116221
|
+
continue;
|
|
116222
|
+
}
|
|
116223
|
+
} else if (skippingOrigin && /^\s+\S/.test(raw)) {
|
|
116224
|
+
continue;
|
|
116225
|
+
}
|
|
115861
116226
|
const unquoted = line.replace(/^>\s*/, "").replace(/^[-*]\s+/, "");
|
|
115862
116227
|
if (unquoted === OUTCOME_HINT) {
|
|
115863
116228
|
sawHint = true;
|
|
@@ -129625,6 +129990,115 @@ var init_jev_scenarios_command = __esm(() => {
|
|
|
129625
129990
|
init_scope();
|
|
129626
129991
|
});
|
|
129627
129992
|
|
|
129993
|
+
// src/tui/flow-origin-command.ts
|
|
129994
|
+
function isFlowOriginCommand(name) {
|
|
129995
|
+
return name === FLOW_ORIGIN_COMMAND;
|
|
129996
|
+
}
|
|
129997
|
+
function splitShellWords(line) {
|
|
129998
|
+
const words = [];
|
|
129999
|
+
let current = "";
|
|
130000
|
+
let quote;
|
|
130001
|
+
let started = false;
|
|
130002
|
+
for (const char of line) {
|
|
130003
|
+
if (quote !== undefined) {
|
|
130004
|
+
if (char === quote)
|
|
130005
|
+
quote = undefined;
|
|
130006
|
+
else
|
|
130007
|
+
current += char;
|
|
130008
|
+
} else if (char === '"' || char === "'") {
|
|
130009
|
+
quote = char;
|
|
130010
|
+
started = true;
|
|
130011
|
+
} else if (/\s/.test(char)) {
|
|
130012
|
+
if (started || current.length > 0)
|
|
130013
|
+
words.push(current);
|
|
130014
|
+
current = "";
|
|
130015
|
+
started = false;
|
|
130016
|
+
} else {
|
|
130017
|
+
current += char;
|
|
130018
|
+
started = true;
|
|
130019
|
+
}
|
|
130020
|
+
}
|
|
130021
|
+
if (started || current.length > 0)
|
|
130022
|
+
words.push(current);
|
|
130023
|
+
return words;
|
|
130024
|
+
}
|
|
130025
|
+
function parseOriginArgs(words) {
|
|
130026
|
+
const parsed = { positionals: [] };
|
|
130027
|
+
for (let i = 0;i < words.length; i += 1) {
|
|
130028
|
+
const word = words[i] ?? "";
|
|
130029
|
+
if (word === "--reason" || word === "--quote" || word === "--source") {
|
|
130030
|
+
const value = words[i + 1];
|
|
130031
|
+
i += 1;
|
|
130032
|
+
if (word === "--reason")
|
|
130033
|
+
parsed.reason = value;
|
|
130034
|
+
else if (word === "--quote")
|
|
130035
|
+
parsed.quote = value;
|
|
130036
|
+
else
|
|
130037
|
+
parsed.source = value;
|
|
130038
|
+
} else {
|
|
130039
|
+
parsed.positionals.push(word);
|
|
130040
|
+
}
|
|
130041
|
+
}
|
|
130042
|
+
return parsed;
|
|
130043
|
+
}
|
|
130044
|
+
function renderFlowOrigin(id, origin) {
|
|
130045
|
+
return [`${id}`, ...originDetailLines(readOrigin(origin), " ")].join(`
|
|
130046
|
+
`);
|
|
130047
|
+
}
|
|
130048
|
+
async function runFlowOriginForShell(cwd, line, service = createFlowService(flowServiceDeps())) {
|
|
130049
|
+
const words = splitShellWords(line.trim());
|
|
130050
|
+
const [, sub, ...rest] = words;
|
|
130051
|
+
if (sub !== "origin") {
|
|
130052
|
+
return `Usage: ${FLOW_ORIGIN_USAGE}`;
|
|
130053
|
+
}
|
|
130054
|
+
const args = parseOriginArgs(rest);
|
|
130055
|
+
const [id, kind, ...extra] = args.positionals;
|
|
130056
|
+
if (extra.length > 0) {
|
|
130057
|
+
return `Usage: ${FLOW_ORIGIN_USAGE}`;
|
|
130058
|
+
}
|
|
130059
|
+
if (id === undefined) {
|
|
130060
|
+
const flows = await service.list({ cwd });
|
|
130061
|
+
if (flows.length === 0)
|
|
130062
|
+
return "No flows.";
|
|
130063
|
+
const blocks = [];
|
|
130064
|
+
for (const summary of flows) {
|
|
130065
|
+
const flow = await service.get({ cwd, id: summary.id });
|
|
130066
|
+
blocks.push(renderFlowOrigin(flow.id, flow.origin));
|
|
130067
|
+
}
|
|
130068
|
+
return blocks.join(`
|
|
130069
|
+
`);
|
|
130070
|
+
}
|
|
130071
|
+
if (kind === undefined) {
|
|
130072
|
+
const flow = await service.get({ cwd, id });
|
|
130073
|
+
return renderFlowOrigin(flow.id, flow.origin);
|
|
130074
|
+
}
|
|
130075
|
+
if (args.reason === undefined || args.reason.trim().length === 0) {
|
|
130076
|
+
return `/flow origin ${id} ${kind} needs --reason "<why>".
|
|
130077
|
+
Usage: ${FLOW_ORIGIN_USAGE}`;
|
|
130078
|
+
}
|
|
130079
|
+
const result = await service.originSet({
|
|
130080
|
+
cwd,
|
|
130081
|
+
id,
|
|
130082
|
+
kind,
|
|
130083
|
+
reason: args.reason,
|
|
130084
|
+
quote: args.quote,
|
|
130085
|
+
source: args.source
|
|
130086
|
+
});
|
|
130087
|
+
if (result.note !== undefined) {
|
|
130088
|
+
return `${result.note}
|
|
130089
|
+
Nothing written; origin stays ${result.previous}.
|
|
130090
|
+
${renderFlowOrigin(result.flow.id, result.flow.origin)}`;
|
|
130091
|
+
}
|
|
130092
|
+
const head = result.changed ? `origin ${result.previous} -> ${result.next}` : `origin already ${result.next}; nothing written`;
|
|
130093
|
+
return `${head}
|
|
130094
|
+
${renderFlowOrigin(result.flow.id, result.flow.origin)}`;
|
|
130095
|
+
}
|
|
130096
|
+
var FLOW_ORIGIN_COMMAND = "/flow", FLOW_ORIGIN_USAGE = '/flow origin [<id>] | /flow origin <id> <kind> --reason "<why>" [--quote "<verbatim>"] [--source "<ref>"] (kind: human-request, agent-finding, agent-proposal, unknown)';
|
|
130097
|
+
var init_flow_origin_command = __esm(() => {
|
|
130098
|
+
init_flow();
|
|
130099
|
+
init_service9();
|
|
130100
|
+
});
|
|
130101
|
+
|
|
129628
130102
|
// src/review/jev-docs-config.ts
|
|
129629
130103
|
import { readFile as readFile138 } from "fs/promises";
|
|
129630
130104
|
import path277 from "path";
|
|
@@ -150959,6 +151433,19 @@ ${formatThemeList(getThemeId())}`);
|
|
|
150959
151433
|
`);
|
|
150960
151434
|
} catch (error) {
|
|
150961
151435
|
io.onSystem?.(`review-jev-docs: ${error instanceof Error ? error.message : String(error)}
|
|
151436
|
+
`);
|
|
151437
|
+
}
|
|
151438
|
+
});
|
|
151439
|
+
return;
|
|
151440
|
+
}
|
|
151441
|
+
if (isFlowOriginCommand(command.name)) {
|
|
151442
|
+
const cwd = inspectorCwd();
|
|
151443
|
+
trackCommandWork(async () => {
|
|
151444
|
+
try {
|
|
151445
|
+
io.onSystem?.(`${await runFlowOriginForShell(cwd, line)}
|
|
151446
|
+
`);
|
|
151447
|
+
} catch (error) {
|
|
151448
|
+
io.onSystem?.(`flow origin: ${error instanceof Error ? error.message : String(error)}
|
|
150962
151449
|
`);
|
|
150963
151450
|
}
|
|
150964
151451
|
});
|
|
@@ -151888,6 +152375,7 @@ var init_tui_shell = __esm(() => {
|
|
|
151888
152375
|
init_jev_contract_command();
|
|
151889
152376
|
init_jev_triage_command();
|
|
151890
152377
|
init_jev_scenarios_command();
|
|
152378
|
+
init_flow_origin_command();
|
|
151891
152379
|
init_jev_docs_command();
|
|
151892
152380
|
init_jev_comments_command();
|
|
151893
152381
|
init_ci_triage_source();
|
|
@@ -177285,7 +177773,7 @@ var init_command_registry = __esm(() => {
|
|
|
177285
177773
|
{
|
|
177286
177774
|
module: "tasks",
|
|
177287
177775
|
command: "flow status",
|
|
177288
|
-
summary: "One flow's full status: lifecycle state, outcome author (agent, human or unknown), AC freeze/confirmation count, PR, owner, latest signature, task list, and recent history.",
|
|
177776
|
+
summary: "One flow's full status: lifecycle state, outcome author (agent, human or unknown), origin (kind, quote, source), AC freeze/confirmation count, PR, owner, latest signature, task list, and recent history.",
|
|
177289
177777
|
intent: ["\u0441\u0442\u0430\u0442\u0443\u0441 \u0444\u043B\u043E\u0443", "flow status", "flow details", "show one flow"],
|
|
177290
177778
|
args: [{ name: "<id>", type: "string", required: true, desc: "flow id" }],
|
|
177291
177779
|
json: false,
|
|
@@ -177294,7 +177782,7 @@ var init_command_registry = __esm(() => {
|
|
|
177294
177782
|
{
|
|
177295
177783
|
module: "tasks",
|
|
177296
177784
|
command: "flow init",
|
|
177297
|
-
summary: "Create a new flow (managed work item): allocates an id, scaffolds its directory, and collects initial " + "context from the source issue if one is given. `--owner` is NEVER inferred \u2014 only an explicit value " + "on this command populates it. `--outcome-author` records who wrote the outcome criterion (default " + "`agent`; `human` only when the flag says so).",
|
|
177785
|
+
summary: "Create a new flow (managed work item): allocates an id, scaffolds its directory, and collects initial " + "context from the source issue if one is given. `--owner` is NEVER inferred \u2014 only an explicit value " + "on this command populates it. `--outcome-author` records who wrote the outcome criterion (default " + "`agent`; `human` only when the flag says so). `--origin` records where the flow came from, with the " + "evidence its kind needs (`human-request`: a verbatim `--quote` and a `--source`; `agent-finding` and " + "`agent-proposal`: a `--source`); without it the origin stays `unknown`.",
|
|
177298
177786
|
intent: ["\u0441\u043E\u0437\u0434\u0430\u0439 \u0444\u043B\u043E\u0443", "flow init", "start a new flow", "new managed work item", "\u0437\u0430\u0432\u0435\u0434\u0438 \u0444\u043B\u043E\u0443"],
|
|
177299
177787
|
args: [
|
|
177300
177788
|
{ name: "title", type: "string", required: false, desc: "work title; required unless --issue is given" },
|
|
@@ -177313,7 +177801,15 @@ var init_command_registry = __esm(() => {
|
|
|
177313
177801
|
type: "string",
|
|
177314
177802
|
required: false,
|
|
177315
177803
|
desc: "who wrote the outcome criterion: agent (the default when omitted) or human; never inferred, any other value is refused before the flow is created; gates nothing"
|
|
177316
|
-
}
|
|
177804
|
+
},
|
|
177805
|
+
{
|
|
177806
|
+
name: "origin",
|
|
177807
|
+
type: "string",
|
|
177808
|
+
required: false,
|
|
177809
|
+
desc: "where the flow came from: human-request, agent-finding or agent-proposal; recorded only with its evidence, otherwise it stays unknown and the command still succeeds; an invalid kind is reported and ignored; gates nothing"
|
|
177810
|
+
},
|
|
177811
|
+
{ name: "quote", type: "string", required: false, desc: "the human's request, verbatim and byte for byte (required with --origin human-request)" },
|
|
177812
|
+
{ name: "source", type: "string", required: false, desc: "where the request or finding came from: channel, message id, time or check (required with every --origin kind)" }
|
|
177317
177813
|
],
|
|
177318
177814
|
json: false,
|
|
177319
177815
|
read: false,
|
|
@@ -177350,6 +177846,22 @@ var init_command_registry = __esm(() => {
|
|
|
177350
177846
|
read: false,
|
|
177351
177847
|
sideEffects: ["writes flow.json's outcomeAuthor field and appends one journal.md line and a history entry"]
|
|
177352
177848
|
},
|
|
177849
|
+
{
|
|
177850
|
+
module: "tasks",
|
|
177851
|
+
command: "flow origin set",
|
|
177852
|
+
summary: "Change where a flow came from (human-request, agent-finding, agent-proposal, or unknown to clear it). Requires a reason; " + "the evidence rule applies (a verbatim quote and a source for human-request, a source for the agent kinds), and without it " + "nothing is written and the reason is printed. Appends one journal line naming the old origin, the new one and the reason. " + "Works on any flow, never changes the outcome author, gates nothing.",
|
|
177853
|
+
intent: ["\u043E\u0442\u043A\u0443\u0434\u0430 \u043F\u0440\u0438\u0448\u0451\u043B \u0444\u043B\u043E\u0443", "flow origin set", "set flow origin", "mark flow as human request", "\u043F\u0440\u043E\u0438\u0441\u0445\u043E\u0436\u0434\u0435\u043D\u0438\u0435 \u0444\u043B\u043E\u0443"],
|
|
177854
|
+
args: [
|
|
177855
|
+
{ name: "<id>", type: "string", required: true, desc: "flow id" },
|
|
177856
|
+
{ name: "<kind>", type: "string", required: true, desc: "human-request, agent-finding, agent-proposal or unknown" },
|
|
177857
|
+
{ name: "reason", type: "string", required: true, desc: "why the origin is being set or changed; one line" },
|
|
177858
|
+
{ name: "quote", type: "string", required: false, desc: "the human's request, verbatim; kept from the flow when omitted" },
|
|
177859
|
+
{ name: "source", type: "string", required: false, desc: "channel, message id, time or check; kept from the flow when omitted" }
|
|
177860
|
+
],
|
|
177861
|
+
json: false,
|
|
177862
|
+
read: false,
|
|
177863
|
+
sideEffects: ["writes flow.json's origin field and appends one journal.md line and a history entry"]
|
|
177864
|
+
},
|
|
177353
177865
|
{
|
|
177354
177866
|
module: "tasks",
|
|
177355
177867
|
command: "flow ac confirm",
|
|
@@ -205542,6 +206054,7 @@ var init_group_subcommands = __esm(() => {
|
|
|
205542
206054
|
"check-ac",
|
|
205543
206055
|
"owner",
|
|
205544
206056
|
"outcome",
|
|
206057
|
+
"origin",
|
|
205545
206058
|
"implemented",
|
|
205546
206059
|
"complete",
|
|
205547
206060
|
"check-complete",
|
package/dist/core.js
CHANGED
|
@@ -24100,6 +24100,8 @@ __export(exports_service2, {
|
|
|
24100
24100
|
AC_KINDS: () => AC_KINDS,
|
|
24101
24101
|
CONFIRMATION_CAVEAT: () => CONFIRMATION_CAVEAT,
|
|
24102
24102
|
DEFAULT_OUTCOME_AUTHOR: () => DEFAULT_OUTCOME_AUTHOR,
|
|
24103
|
+
ORIGIN_KINDS: () => ORIGIN_KINDS,
|
|
24104
|
+
ORIGIN_READINGS: () => ORIGIN_READINGS,
|
|
24103
24105
|
OUTCOME_AUTHORS: () => OUTCOME_AUTHORS,
|
|
24104
24106
|
OUTCOME_AUTHOR_READINGS: () => OUTCOME_AUTHOR_READINGS,
|
|
24105
24107
|
OUTCOME_HINT: () => OUTCOME_HINT,
|
|
@@ -24119,6 +24121,7 @@ __export(exports_service2, {
|
|
|
24119
24121
|
confirmPreconditionError: () => confirmPreconditionError,
|
|
24120
24122
|
createFlowService: () => createFlowService,
|
|
24121
24123
|
describeAcKind: () => describeAcKind,
|
|
24124
|
+
effectiveOutcomeAuthor: () => effectiveOutcomeAuthor,
|
|
24122
24125
|
evaluatedVerdict: () => evaluatedVerdict,
|
|
24123
24126
|
extractCriterionTokens: () => extractCriterionTokens,
|
|
24124
24127
|
factsOnlyVerdict: () => factsOnlyVerdict,
|
|
@@ -24128,7 +24131,10 @@ __export(exports_service2, {
|
|
|
24128
24131
|
hashDiff: () => hashDiff,
|
|
24129
24132
|
intentNoteForNewFlow: () => intentNoteForNewFlow,
|
|
24130
24133
|
isFrozen: () => isFrozen,
|
|
24134
|
+
isOriginKind: () => isOriginKind,
|
|
24131
24135
|
notCheckableVerdict: () => notCheckableVerdict,
|
|
24136
|
+
originDetailLines: () => originDetailLines,
|
|
24137
|
+
originSummary: () => originSummary,
|
|
24132
24138
|
outcomeBulletsFrom: () => outcomeBulletsFrom,
|
|
24133
24139
|
parseAcceptanceCriteria: () => parseAcceptanceCriteria,
|
|
24134
24140
|
parseOutcomeAuthor: () => parseOutcomeAuthor,
|
|
@@ -24137,12 +24143,15 @@ __export(exports_service2, {
|
|
|
24137
24143
|
readAcCheckEnabled: () => readAcCheckEnabled,
|
|
24138
24144
|
readAcKindRecords: () => readAcKindRecords,
|
|
24139
24145
|
readFlow: () => readFlow,
|
|
24146
|
+
readOrigin: () => readOrigin,
|
|
24147
|
+
readOriginKind: () => readOriginKind,
|
|
24140
24148
|
readOutcomeAuthor: () => readOutcomeAuthor,
|
|
24141
24149
|
renderAcCheckAdvisoryNotice: () => renderAcCheckAdvisoryNotice,
|
|
24142
24150
|
renderAcCheckReport: () => renderAcCheckReport,
|
|
24143
24151
|
renderAcKindDistribution: () => renderAcKindDistribution,
|
|
24144
24152
|
reportFromRecords: () => reportFromRecords,
|
|
24145
24153
|
resolveFlowDir: () => resolveFlowDir,
|
|
24154
|
+
resolveOrigin: () => resolveOrigin,
|
|
24146
24155
|
sectionOf: () => sectionOf,
|
|
24147
24156
|
selectMatchedHunks: () => selectMatchedHunks,
|
|
24148
24157
|
statementFrom: () => statementFrom,
|
|
@@ -25613,6 +25622,129 @@ function parseOutcomeAuthor(raw, flag = "--outcome-author") {
|
|
|
25613
25622
|
const shown = typeof raw === "string" ? `"${raw}"` : "nothing";
|
|
25614
25623
|
throw new Error(`${flag} must be one of: ${OUTCOME_AUTHORS.join(", ")} (got ${shown}).`);
|
|
25615
25624
|
}
|
|
25625
|
+
|
|
25626
|
+
// src/flow/origin.ts
|
|
25627
|
+
var ORIGIN_KINDS = ["human-request", "agent-finding", "agent-proposal"];
|
|
25628
|
+
var ORIGIN_READINGS = [...ORIGIN_KINDS, "unknown"];
|
|
25629
|
+
function isOriginKind(value) {
|
|
25630
|
+
return typeof value === "string" && ORIGIN_KINDS.includes(value);
|
|
25631
|
+
}
|
|
25632
|
+
function nonBlank(value) {
|
|
25633
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
25634
|
+
}
|
|
25635
|
+
function readOrigin(value) {
|
|
25636
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
25637
|
+
return;
|
|
25638
|
+
const raw = value;
|
|
25639
|
+
if (!isOriginKind(raw.kind))
|
|
25640
|
+
return;
|
|
25641
|
+
const origin = { kind: raw.kind };
|
|
25642
|
+
if (typeof raw.quote === "string" && raw.quote.length > 0)
|
|
25643
|
+
origin.quote = raw.quote;
|
|
25644
|
+
if (typeof raw.source === "string" && raw.source.length > 0)
|
|
25645
|
+
origin.source = raw.source;
|
|
25646
|
+
return origin;
|
|
25647
|
+
}
|
|
25648
|
+
function readOriginKind(value) {
|
|
25649
|
+
return readOrigin(value)?.kind ?? "unknown";
|
|
25650
|
+
}
|
|
25651
|
+
function resolveOrigin(input) {
|
|
25652
|
+
if (!isOriginKind(input.kind)) {
|
|
25653
|
+
return {
|
|
25654
|
+
note: `origin kind "${input.kind}" is not one of: ${ORIGIN_KINDS.join(", ")}; origin stays unknown.`
|
|
25655
|
+
};
|
|
25656
|
+
}
|
|
25657
|
+
const hasQuote = nonBlank(input.quote);
|
|
25658
|
+
const hasSource = nonBlank(input.source);
|
|
25659
|
+
if (input.kind === "human-request") {
|
|
25660
|
+
if (!hasQuote && !hasSource) {
|
|
25661
|
+
return { note: "origin human-request needs a verbatim --quote and a --source; neither was given, origin stays unknown." };
|
|
25662
|
+
}
|
|
25663
|
+
if (!hasQuote) {
|
|
25664
|
+
return { note: "origin human-request needs a verbatim --quote of the human's first message; none was given, origin stays unknown." };
|
|
25665
|
+
}
|
|
25666
|
+
if (!hasSource) {
|
|
25667
|
+
return { note: "origin human-request needs a --source (channel, message id or time); none was given, origin stays unknown." };
|
|
25668
|
+
}
|
|
25669
|
+
} else if (!hasSource) {
|
|
25670
|
+
return { note: `origin ${input.kind} needs a --source (the check, review or test that produced it); none was given, origin stays unknown.` };
|
|
25671
|
+
}
|
|
25672
|
+
const origin = { kind: input.kind };
|
|
25673
|
+
if (hasQuote)
|
|
25674
|
+
origin.quote = input.quote;
|
|
25675
|
+
if (hasSource)
|
|
25676
|
+
origin.source = input.source;
|
|
25677
|
+
return { origin };
|
|
25678
|
+
}
|
|
25679
|
+
function effectiveOutcomeAuthor(flow) {
|
|
25680
|
+
const explicit = readOutcomeAuthor(flow.outcomeAuthor);
|
|
25681
|
+
if (explicit !== "unknown")
|
|
25682
|
+
return explicit;
|
|
25683
|
+
return readOrigin(flow.origin) === undefined ? "unknown" : "agent";
|
|
25684
|
+
}
|
|
25685
|
+
function originSummary(origin) {
|
|
25686
|
+
if (origin === undefined)
|
|
25687
|
+
return "unknown";
|
|
25688
|
+
const parts = [origin.kind];
|
|
25689
|
+
if (origin.quote !== undefined)
|
|
25690
|
+
parts.push(`quote: ${JSON.stringify(origin.quote)}`);
|
|
25691
|
+
if (origin.source !== undefined)
|
|
25692
|
+
parts.push(`source: ${origin.source}`);
|
|
25693
|
+
return parts.join("; ");
|
|
25694
|
+
}
|
|
25695
|
+
function originDetailLines(origin, indent = "") {
|
|
25696
|
+
if (origin === undefined)
|
|
25697
|
+
return [`${indent}origin: unknown`];
|
|
25698
|
+
const lines = [`${indent}origin: ${origin.kind}`];
|
|
25699
|
+
if (origin.quote !== undefined) {
|
|
25700
|
+
const [first = "", ...rest] = origin.quote.split(`
|
|
25701
|
+
`);
|
|
25702
|
+
lines.push(`${indent} quote: \xAB${first}`, ...rest.map((line) => `${indent} ${line}`));
|
|
25703
|
+
lines[lines.length - 1] += "\xBB";
|
|
25704
|
+
}
|
|
25705
|
+
if (origin.source !== undefined)
|
|
25706
|
+
lines.push(`${indent} source: ${origin.source}`);
|
|
25707
|
+
return lines;
|
|
25708
|
+
}
|
|
25709
|
+
var REQUEST_LABEL = "\u0417\u0430\u043F\u0440\u043E\u0441 (\u0434\u043E\u0441\u043B\u043E\u0432\u043D\u043E)";
|
|
25710
|
+
var SOURCE_LABEL = "\u0418\u0441\u0442\u043E\u0447\u043D\u0438\u043A";
|
|
25711
|
+
var FORMALIZATION_LABEL = "\u042D\u0444\u0444\u0435\u043A\u0442 (\u0444\u043E\u0440\u043C\u0430\u043B\u0438\u0437\u0430\u0446\u0438\u044F \u0430\u0433\u0435\u043D\u0442\u0430)";
|
|
25712
|
+
var OBSERVE_LABEL = "\u041A\u0430\u043A \u043D\u0430\u0431\u043B\u044E\u0434\u0430\u0442\u044C (\u043F\u0440\u0435\u0434\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0430\u0433\u0435\u043D\u0442\u0430)";
|
|
25713
|
+
var REQUEST_PLACEHOLDER = "<the human's request, verbatim, no translation or paraphrase>";
|
|
25714
|
+
var SOURCE_PLACEHOLDER = "<where the finding or proposal came from: check, review, test or discussion>";
|
|
25715
|
+
var FORMALIZATION_PLACEHOLDER = "<the effect that has to be true when this flow is done, in your own words>";
|
|
25716
|
+
var OBSERVE_PLACEHOLDER = "<how the effect can be observed once the flow is done>";
|
|
25717
|
+
var ORIGIN_PLACEHOLDER_BULLETS = [
|
|
25718
|
+
`- ${REQUEST_LABEL}: ${REQUEST_PLACEHOLDER}`,
|
|
25719
|
+
`- ${SOURCE_LABEL}: ${SOURCE_PLACEHOLDER}`,
|
|
25720
|
+
`- ${FORMALIZATION_LABEL}: ${FORMALIZATION_PLACEHOLDER}`,
|
|
25721
|
+
`- ${OBSERVE_LABEL}: ${OBSERVE_PLACEHOLDER}`
|
|
25722
|
+
];
|
|
25723
|
+
function renderOriginBullets(origin) {
|
|
25724
|
+
const first = [];
|
|
25725
|
+
if (origin.kind === "human-request") {
|
|
25726
|
+
first.push(...bulletWithText(REQUEST_LABEL, origin.quote === undefined ? undefined : `\xAB${origin.quote}\xBB`, REQUEST_PLACEHOLDER));
|
|
25727
|
+
if (origin.source !== undefined)
|
|
25728
|
+
first[first.length - 1] += ` (source: ${origin.source})`;
|
|
25729
|
+
} else {
|
|
25730
|
+
first.push(...bulletWithText(SOURCE_LABEL, origin.source, SOURCE_PLACEHOLDER));
|
|
25731
|
+
if (origin.quote !== undefined)
|
|
25732
|
+
first.push(...bulletWithText(REQUEST_LABEL, `\xAB${origin.quote}\xBB`, REQUEST_PLACEHOLDER));
|
|
25733
|
+
}
|
|
25734
|
+
return [
|
|
25735
|
+
...first,
|
|
25736
|
+
`- ${FORMALIZATION_LABEL}: ${FORMALIZATION_PLACEHOLDER}`,
|
|
25737
|
+
`- ${OBSERVE_LABEL}: ${OBSERVE_PLACEHOLDER}`
|
|
25738
|
+
];
|
|
25739
|
+
}
|
|
25740
|
+
function bulletWithText(label, text, placeholder) {
|
|
25741
|
+
if (text === undefined)
|
|
25742
|
+
return [`- ${label}: ${placeholder}`];
|
|
25743
|
+
const lines = text.split(`
|
|
25744
|
+
`);
|
|
25745
|
+
const head = `- ${label}: ${lines[0] ?? ""}`;
|
|
25746
|
+
return [head, ...lines.slice(1).map((line) => ` ${line}`)];
|
|
25747
|
+
}
|
|
25616
25748
|
// src/flow/identity.ts
|
|
25617
25749
|
function resolveSignerIdentity(input) {
|
|
25618
25750
|
const stated = input.stated?.trim();
|
|
@@ -25908,6 +26040,23 @@ function flowStateSchema() {
|
|
|
25908
26040
|
enum: ["agent", "human"],
|
|
25909
26041
|
description: "Who wrote the outcome criterion. Written by `flow init --outcome-author` (default `agent`; `human` only when the flag says so, never inferred) and changed only by `flow outcome author`, which journals the change. Absent on flows created before the field existed, and read as `unknown` (neither agent nor human). Gates nothing."
|
|
25910
26042
|
},
|
|
26043
|
+
origin: {
|
|
26044
|
+
type: "object",
|
|
26045
|
+
additionalProperties: false,
|
|
26046
|
+
required: ["kind"],
|
|
26047
|
+
description: "Where the flow came from. Written by `flow init --origin <kind> [--quote] [--source]` and changed only by `flow origin set`, which journals the change. `human-request` is recorded only with a verbatim quote and a source; `agent-finding` and `agent-proposal` only with a source. Absent on flows created before the field existed and on flows whose evidence was not enough, and read as `unknown`. Gates nothing.",
|
|
26048
|
+
properties: {
|
|
26049
|
+
kind: { type: "string", enum: ["human-request", "agent-finding", "agent-proposal"] },
|
|
26050
|
+
quote: {
|
|
26051
|
+
type: "string",
|
|
26052
|
+
description: "The human's request, verbatim and byte for byte: no translation, no paraphrase."
|
|
26053
|
+
},
|
|
26054
|
+
source: {
|
|
26055
|
+
type: "string",
|
|
26056
|
+
description: "Where the request or finding came from: channel, message id, time, or the check that produced it."
|
|
26057
|
+
}
|
|
26058
|
+
}
|
|
26059
|
+
},
|
|
25911
26060
|
signatures: {
|
|
25912
26061
|
type: "array",
|
|
25913
26062
|
description: "Append-only signing record for `flow ac confirm` and a passing `flow complete`. Absent means never signed under this field's existence.",
|
|
@@ -26845,17 +26994,24 @@ function outcomeBulletsFrom(markdown) {
|
|
|
26845
26994
|
if (body === null)
|
|
26846
26995
|
return null;
|
|
26847
26996
|
const bullets = [];
|
|
26997
|
+
let skipping = false;
|
|
26848
26998
|
for (const line of body.split(/\r?\n/)) {
|
|
26849
26999
|
const bullet = /^\s*[-*]\s+(.*)$/.exec(line);
|
|
26850
27000
|
if ((bullet?.[1] ?? line).trim() === OUTCOME_HINT)
|
|
26851
27001
|
continue;
|
|
26852
|
-
if (bullet !== null)
|
|
26853
|
-
|
|
26854
|
-
|
|
27002
|
+
if (bullet !== null) {
|
|
27003
|
+
skipping = isOriginTemplateBullet(bullet[1] ?? "");
|
|
27004
|
+
if (!skipping)
|
|
27005
|
+
bullets.push((bullet[1] ?? "").trim());
|
|
27006
|
+
} else if (line.trim().length > 0 && !skipping && bullets.length > 0)
|
|
26855
27007
|
bullets[bullets.length - 1] += ` ${line.trim()}`;
|
|
26856
27008
|
}
|
|
26857
27009
|
return bullets.filter((bullet) => bullet.length > 0);
|
|
26858
27010
|
}
|
|
27011
|
+
function isOriginTemplateBullet(text) {
|
|
27012
|
+
const trimmed = text.trim();
|
|
27013
|
+
return trimmed.startsWith(`${REQUEST_LABEL}:`) || trimmed.startsWith(`${SOURCE_LABEL}:`) || ORIGIN_PLACEHOLDER_BULLETS.some((placeholder) => placeholder.slice(2) === trimmed);
|
|
27014
|
+
}
|
|
26859
27015
|
var INIT_INTENT_NOTE = "note: no intent statement extractable from description.md \u2014 the product index will hold no statement for this flow";
|
|
26860
27016
|
function initIntentNote(description) {
|
|
26861
27017
|
return flowStatementFrom(description) === null ? INIT_INTENT_NOTE : null;
|
|
@@ -27417,7 +27573,9 @@ async function flowFoldersInHead(cwd, dirs) {
|
|
|
27417
27573
|
}
|
|
27418
27574
|
|
|
27419
27575
|
// src/flow/templates.ts
|
|
27420
|
-
function renderDescription(title, source) {
|
|
27576
|
+
function renderDescription(title, source, origin) {
|
|
27577
|
+
const outcome = origin === undefined ? OUTCOME_HINT : renderOriginBullets(origin).join(`
|
|
27578
|
+
`);
|
|
27421
27579
|
return `# ${title}
|
|
27422
27580
|
|
|
27423
27581
|
Status: draft (flow-init skill formalizes this)
|
|
@@ -27433,7 +27591,7 @@ What must be true when this flow is done.
|
|
|
27433
27591
|
|
|
27434
27592
|
## Outcome criteria
|
|
27435
27593
|
|
|
27436
|
-
${
|
|
27594
|
+
${outcome}
|
|
27437
27595
|
|
|
27438
27596
|
## Out of Scope
|
|
27439
27597
|
|
|
@@ -27750,7 +27908,24 @@ function createFlowService(deps) {
|
|
|
27750
27908
|
if (input.owner !== undefined && !input.owner.trim()) {
|
|
27751
27909
|
throw new Error(BLANK_OWNER_MESSAGE);
|
|
27752
27910
|
}
|
|
27753
|
-
const outcomeAuthor = input.outcomeAuthor === undefined ?
|
|
27911
|
+
const outcomeAuthor = input.outcomeAuthor === undefined ? undefined : parseOutcomeAuthor(input.outcomeAuthor);
|
|
27912
|
+
let originNote;
|
|
27913
|
+
let origin;
|
|
27914
|
+
let originDraft;
|
|
27915
|
+
if (input.origin !== undefined) {
|
|
27916
|
+
const resolution = resolveOrigin({ kind: input.origin, quote: input.originQuote, source: input.originSource });
|
|
27917
|
+
origin = resolution.origin;
|
|
27918
|
+
originNote = resolution.note;
|
|
27919
|
+
if (isOriginKind(input.origin)) {
|
|
27920
|
+
originDraft = {
|
|
27921
|
+
kind: input.origin,
|
|
27922
|
+
...input.originQuote?.trim() ? { quote: input.originQuote } : {},
|
|
27923
|
+
...input.originSource?.trim() ? { source: input.originSource } : {}
|
|
27924
|
+
};
|
|
27925
|
+
}
|
|
27926
|
+
} else if (input.originQuote !== undefined || input.originSource !== undefined) {
|
|
27927
|
+
originNote = "--quote/--source were given without --origin; origin stays unknown.";
|
|
27928
|
+
}
|
|
27754
27929
|
const trackerReady = deps.tracker ? await deps.tracker.detect() : false;
|
|
27755
27930
|
const tracker = trackerReady ? deps.tracker : null;
|
|
27756
27931
|
const issueRef = input.issue && deps.tracker ? deps.tracker.parseRef(input.issue) : null;
|
|
@@ -27806,12 +27981,13 @@ function createFlowService(deps) {
|
|
|
27806
27981
|
pr: { url: null },
|
|
27807
27982
|
...input.baseBranch === undefined ? {} : { baseBranch: input.baseBranch },
|
|
27808
27983
|
...input.owner?.trim() ? { owner: ownerIdentity(input.owner.trim(), "`--owner` flag on `flow init`") } : {},
|
|
27809
|
-
outcomeAuthor,
|
|
27984
|
+
...outcomeAuthor !== undefined ? { outcomeAuthor } : origin === undefined ? { outcomeAuthor: DEFAULT_OUTCOME_AUTHOR } : {},
|
|
27985
|
+
...origin === undefined ? {} : { origin },
|
|
27810
27986
|
tasks: DEFAULT_TASKS.map((task) => ({ ...task, status: "todo" })),
|
|
27811
27987
|
history: [{ at: createdAt, event: "created" }]
|
|
27812
27988
|
};
|
|
27813
27989
|
const sourceLabel = input.issue ?? "user description";
|
|
27814
|
-
await writeFileAtomic(path56.join(absolute, "description.md"), renderDescription(title, sourceLabel));
|
|
27990
|
+
await writeFileAtomic(path56.join(absolute, "description.md"), renderDescription(title, originDraft === undefined ? sourceLabel : `${sourceLabel} (origin: ${originDraft.kind})`, originDraft));
|
|
27815
27991
|
await writeFileAtomic(path56.join(absolute, "context.md"), context.markdown);
|
|
27816
27992
|
await writeFileAtomic(path56.join(absolute, "plan.md"), renderPlan());
|
|
27817
27993
|
await writeFileAtomic(path56.join(absolute, "tasks.md"), renderTasksDoc());
|
|
@@ -27819,7 +27995,12 @@ function createFlowService(deps) {
|
|
|
27819
27995
|
await writeFileAtomic(path56.join(absolute, "journal.md"), renderJournal(createdAt));
|
|
27820
27996
|
await writeFlow(input.cwd, dir, flow);
|
|
27821
27997
|
await recordAllocation(scope, { id, dir, at: createdAt, project: scope.project });
|
|
27822
|
-
return {
|
|
27998
|
+
return {
|
|
27999
|
+
flow,
|
|
28000
|
+
dir: path56.relative(input.cwd, absolute),
|
|
28001
|
+
contextNotes: context.notes,
|
|
28002
|
+
...originNote === undefined ? {} : { originNote }
|
|
28003
|
+
};
|
|
27823
28004
|
});
|
|
27824
28005
|
},
|
|
27825
28006
|
async list({ cwd }) {
|
|
@@ -27883,6 +28064,50 @@ function createFlowService(deps) {
|
|
|
27883
28064
|
});
|
|
27884
28065
|
return { flow, previous, changed };
|
|
27885
28066
|
},
|
|
28067
|
+
async originSet({ cwd, id, kind, reason, quote, source }) {
|
|
28068
|
+
if (!reason?.trim()) {
|
|
28069
|
+
throw new Error('flow origin set requires --reason "<why>"');
|
|
28070
|
+
}
|
|
28071
|
+
validateSingleLineReason(reason);
|
|
28072
|
+
let previous = "unknown";
|
|
28073
|
+
let next = "unknown";
|
|
28074
|
+
let changed = false;
|
|
28075
|
+
let note;
|
|
28076
|
+
const flow = await mutate(cwd, id, async ({ dir, flow: current }) => {
|
|
28077
|
+
const before = readOrigin(current.origin);
|
|
28078
|
+
previous = before?.kind ?? "unknown";
|
|
28079
|
+
let after;
|
|
28080
|
+
if (kind !== "unknown" && !isOriginKind(kind)) {
|
|
28081
|
+
note = `origin kind must be one of: human-request, agent-finding, agent-proposal, unknown (got "${kind}"); the origin is unchanged.`;
|
|
28082
|
+
next = previous;
|
|
28083
|
+
return current;
|
|
28084
|
+
}
|
|
28085
|
+
if (kind !== "unknown") {
|
|
28086
|
+
const resolution = resolveOrigin({
|
|
28087
|
+
kind,
|
|
28088
|
+
quote: quote !== undefined && quote.length > 0 ? quote : before?.quote,
|
|
28089
|
+
source: source !== undefined && source.length > 0 ? source : before?.source
|
|
28090
|
+
});
|
|
28091
|
+
if (resolution.origin === undefined) {
|
|
28092
|
+
note = resolution.note;
|
|
28093
|
+
next = previous;
|
|
28094
|
+
return current;
|
|
28095
|
+
}
|
|
28096
|
+
after = resolution.origin;
|
|
28097
|
+
}
|
|
28098
|
+
next = after?.kind ?? "unknown";
|
|
28099
|
+
if (JSON.stringify(before ?? null) === JSON.stringify(after ?? null)) {
|
|
28100
|
+
return current;
|
|
28101
|
+
}
|
|
28102
|
+
changed = true;
|
|
28103
|
+
if (after === undefined)
|
|
28104
|
+
delete current.origin;
|
|
28105
|
+
else
|
|
28106
|
+
current.origin = after;
|
|
28107
|
+
return save(cwd, dir, current, "origin-set", `${originSummary(before)} -> ${originSummary(after)} (${reason.trim()})`);
|
|
28108
|
+
});
|
|
28109
|
+
return { flow, previous, next, changed, ...note === undefined ? {} : { note } };
|
|
28110
|
+
},
|
|
27886
28111
|
async freeze({ cwd, id }) {
|
|
27887
28112
|
return mutate(cwd, id, async ({ dir, flow }) => {
|
|
27888
28113
|
assertTransition(flow.status, "ready");
|
|
@@ -41023,6 +41248,12 @@ var HELP_GROUPS = [
|
|
|
41023
41248
|
group: "Managed work",
|
|
41024
41249
|
summary: "List doc sections that likely went stale because of the working diff, with Jev."
|
|
41025
41250
|
},
|
|
41251
|
+
{
|
|
41252
|
+
kind: "slash",
|
|
41253
|
+
name: "/flow",
|
|
41254
|
+
group: "Managed work",
|
|
41255
|
+
summary: `Show a flow's origin, or set it with a reason \u2014 /flow origin [<id>] | /flow origin <id> <kind> --reason "...".`
|
|
41256
|
+
},
|
|
41026
41257
|
{
|
|
41027
41258
|
kind: "slash",
|
|
41028
41259
|
name: "/opencomments",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.59",
|
|
4
4
|
"description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|