@mrciphersmith/keryx 0.3.58 → 0.3.61

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 CHANGED
@@ -16229,7 +16229,7 @@ var init_skill_length_ceilings = __esm(() => {
16229
16229
  "orchestration/context-collector": 671,
16230
16230
  "orchestration/feature-analyzer": 447,
16231
16231
  "orchestration/feature-dev": 177,
16232
- "orchestration/flow-orchestrator": 696,
16232
+ "orchestration/flow-orchestrator": 694,
16233
16233
  "orchestration/issue-analyzer": 373,
16234
16234
  "orchestration/job-documenter": 414,
16235
16235
  "orchestration/job-orchestrator": 2243,
@@ -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.58",
20107
+ version: "0.3.61",
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: {
@@ -56454,7 +56454,7 @@ async function reviewGate(input) {
56454
56454
  const verdict = await runReviewGate(input);
56455
56455
  return { name: "review", status: verdict.status, detail: verdict.detail };
56456
56456
  }
56457
- var BLOCKING_SEVERITY_FLOORS, REVIEW_GATE_SEVERITY_FLOOR_DEFAULT = "minor", REVIEW_ROUND_CAP = 3, REVIEW_GATE_CONFIG_PATH = ".metaproject/tasks.config.json", DEFAULT_REVIEW_GATE_CONFIG, SEVERITY_RANK2, SHA_PATTERN3, HUMAN_DECISION_PATTERN, CORRECT_BUT_DISMISSED, REPLY_EVIDENCE_PATTERN, durableExternalCommentsGate = async (input) => {
56457
+ var BLOCKING_SEVERITY_FLOORS, REVIEW_GATE_SEVERITY_FLOOR_DEFAULT = "minor", REVIEW_ROUND_CAP = 5, REVIEW_GATE_CONFIG_PATH = ".metaproject/tasks.config.json", DEFAULT_REVIEW_GATE_CONFIG, SEVERITY_RANK2, SHA_PATTERN3, HUMAN_DECISION_PATTERN, CORRECT_BUT_DISMISSED, REPLY_EVIDENCE_PATTERN, durableExternalCommentsGate = async (input) => {
56458
56458
  const url = input.flow.pr.url;
56459
56459
  if (url === null) {
56460
56460
  return null;
@@ -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
- bullets.push((bullet[1] ?? "").trim());
57906
- else if (line.trim().length > 0 && bullets.length > 0)
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
- ${OUTCOME_HINT}
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
- readOutcomeAuthor: () => readOutcomeAuthor,
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 ? DEFAULT_OUTCOME_AUTHOR : parseOutcomeAuthor(input.outcomeAuthor);
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 { flow, dir: path143.relative(input.cwd, absolute), contextNotes: context.notes };
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: ${readOutcomeAuthor(result.flow.outcomeAuthor)}`);
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: ${readOutcomeAuthor(flow.outcomeAuthor)}`);
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 = readOutcomeAuthor(result.flow.outcomeAuthor);
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: readOutcomeAuthor(flow.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: readOutcomeAuthor(flow.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: readOutcomeAuthor(intent.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
- return [...openHeaderLines(report), ...rows].join(`
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",