@mrciphersmith/keryx 0.3.32 → 0.3.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/cli.js +123 -8
- package/dist/core.js +44 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -646,7 +646,10 @@ Grouped by what you are trying to do, not by internal module layout.
|
|
|
646
646
|
at, each with its outcome criterion or `not measured — no instrument stated`,
|
|
647
647
|
and splits the count three ways: no outcome criterion stated, criterion
|
|
648
648
|
stated but never observed, observed (split by verdict). A criterion goes in
|
|
649
|
-
the `## Outcome criteria` slot `flow init` leaves in `description.md
|
|
649
|
+
the `## Outcome criteria` slot `flow init` leaves in `description.md`; who
|
|
650
|
+
wrote it is recorded as `outcomeAuthor` (`flow init --outcome-author
|
|
651
|
+
agent|human`, `flow outcome author <id> agent|human --reason "<why>"`), shown
|
|
652
|
+
by `product open`, and gates nothing. An
|
|
650
653
|
observation is a line starting `outcome-observed: <verdict> — <note>` in the
|
|
651
654
|
flow's `journal.md`, with the verdict one of `helped`, `no-effect`, `harmed`,
|
|
652
655
|
`inconclusive`; a requirements package uses an `## Outcome observations`
|
package/dist/cli.js
CHANGED
|
@@ -13447,7 +13447,7 @@ var package_default;
|
|
|
13447
13447
|
var init_package = __esm(() => {
|
|
13448
13448
|
package_default = {
|
|
13449
13449
|
name: "@mrciphersmith/keryx",
|
|
13450
|
-
version: "0.3.
|
|
13450
|
+
version: "0.3.33",
|
|
13451
13451
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
13452
13452
|
private: false,
|
|
13453
13453
|
publishConfig: {
|
|
@@ -48615,6 +48615,22 @@ var init_review_gate = __esm(() => {
|
|
|
48615
48615
|
REPLY_EVIDENCE_PATTERN = /(https?:\/\/\S+|#(issue|discussion)comment[-_]?\d+|\breplied\b|\breply\b|\banswered\b)/i;
|
|
48616
48616
|
});
|
|
48617
48617
|
|
|
48618
|
+
// src/flow/outcome-author.ts
|
|
48619
|
+
function readOutcomeAuthor(value) {
|
|
48620
|
+
return value === "agent" || value === "human" ? value : "unknown";
|
|
48621
|
+
}
|
|
48622
|
+
function parseOutcomeAuthor(raw, flag = "--outcome-author") {
|
|
48623
|
+
if (raw === "agent" || raw === "human")
|
|
48624
|
+
return raw;
|
|
48625
|
+
const shown = typeof raw === "string" ? `"${raw}"` : "nothing";
|
|
48626
|
+
throw new Error(`${flag} must be one of: ${OUTCOME_AUTHORS.join(", ")} (got ${shown}).`);
|
|
48627
|
+
}
|
|
48628
|
+
var OUTCOME_AUTHORS, OUTCOME_AUTHOR_READINGS, DEFAULT_OUTCOME_AUTHOR = "agent";
|
|
48629
|
+
var init_outcome_author = __esm(() => {
|
|
48630
|
+
OUTCOME_AUTHORS = ["agent", "human"];
|
|
48631
|
+
OUTCOME_AUTHOR_READINGS = [...OUTCOME_AUTHORS, "unknown"];
|
|
48632
|
+
});
|
|
48633
|
+
|
|
48618
48634
|
// src/flow/identity.ts
|
|
48619
48635
|
function resolveSignerIdentity(input) {
|
|
48620
48636
|
const stated = input.stated?.trim();
|
|
@@ -49052,6 +49068,11 @@ function flowStateSchema() {
|
|
|
49052
49068
|
$ref: "#/definitions/identity",
|
|
49053
49069
|
description: "The human accountable for this flow, or absent if one was never set. Never inferred: only ever written by an explicit `--owner` on `flow init` or `flow owner set`, so `basis` is always `stated`."
|
|
49054
49070
|
},
|
|
49071
|
+
outcomeAuthor: {
|
|
49072
|
+
type: "string",
|
|
49073
|
+
enum: ["agent", "human"],
|
|
49074
|
+
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."
|
|
49075
|
+
},
|
|
49055
49076
|
signatures: {
|
|
49056
49077
|
type: "array",
|
|
49057
49078
|
description: "Append-only signing record for `flow ac confirm` and a passing `flow complete`. Absent means never signed under this field's existence.",
|
|
@@ -50830,6 +50851,7 @@ function createFlowService(deps) {
|
|
|
50830
50851
|
if (input.owner !== undefined && !input.owner.trim()) {
|
|
50831
50852
|
throw new Error(BLANK_OWNER_MESSAGE);
|
|
50832
50853
|
}
|
|
50854
|
+
const outcomeAuthor = input.outcomeAuthor === undefined ? DEFAULT_OUTCOME_AUTHOR : parseOutcomeAuthor(input.outcomeAuthor);
|
|
50833
50855
|
const trackerReady = deps.tracker ? await deps.tracker.detect() : false;
|
|
50834
50856
|
const tracker = trackerReady ? deps.tracker : null;
|
|
50835
50857
|
const issueRef = input.issue && deps.tracker ? deps.tracker.parseRef(input.issue) : null;
|
|
@@ -50881,6 +50903,7 @@ function createFlowService(deps) {
|
|
|
50881
50903
|
pr: { url: null },
|
|
50882
50904
|
...input.baseBranch === undefined ? {} : { baseBranch: input.baseBranch },
|
|
50883
50905
|
...input.owner?.trim() ? { owner: ownerIdentity(input.owner.trim(), "`--owner` flag on `flow init`") } : {},
|
|
50906
|
+
outcomeAuthor,
|
|
50884
50907
|
tasks: DEFAULT_TASKS.map((task) => ({ ...task, status: "todo" })),
|
|
50885
50908
|
history: [{ at: createdAt, event: "created" }]
|
|
50886
50909
|
};
|
|
@@ -50938,6 +50961,25 @@ function createFlowService(deps) {
|
|
|
50938
50961
|
return save(cwd, dir, flow, previous === null ? "owner-set" : "owner-changed", detail);
|
|
50939
50962
|
});
|
|
50940
50963
|
},
|
|
50964
|
+
async outcomeAuthorSet({ cwd, id, author, reason }) {
|
|
50965
|
+
const next = parseOutcomeAuthor(author, "outcome author");
|
|
50966
|
+
if (!reason?.trim()) {
|
|
50967
|
+
throw new Error('flow outcome author requires --reason "<why>"');
|
|
50968
|
+
}
|
|
50969
|
+
validateSingleLineReason(reason);
|
|
50970
|
+
let previous = "unknown";
|
|
50971
|
+
let changed = false;
|
|
50972
|
+
const flow = await mutate(cwd, id, async ({ dir, flow: current }) => {
|
|
50973
|
+
previous = readOutcomeAuthor(current.outcomeAuthor);
|
|
50974
|
+
if (previous === next) {
|
|
50975
|
+
return current;
|
|
50976
|
+
}
|
|
50977
|
+
changed = true;
|
|
50978
|
+
current.outcomeAuthor = next;
|
|
50979
|
+
return save(cwd, dir, current, "outcome-author-set", `${previous} -> ${next} (${reason.trim()})`);
|
|
50980
|
+
});
|
|
50981
|
+
return { flow, previous, changed };
|
|
50982
|
+
},
|
|
50941
50983
|
async freeze({ cwd, id }) {
|
|
50942
50984
|
return mutate(cwd, id, async ({ dir, flow }) => {
|
|
50943
50985
|
assertTransition(flow.status, "ready");
|
|
@@ -51785,6 +51827,8 @@ var init_service9 = __esm(() => {
|
|
|
51785
51827
|
init_validator();
|
|
51786
51828
|
init_machine();
|
|
51787
51829
|
init_review_gate();
|
|
51830
|
+
init_outcome_author();
|
|
51831
|
+
init_outcome_author();
|
|
51788
51832
|
init_flow_move();
|
|
51789
51833
|
init_ac_reseal();
|
|
51790
51834
|
init_schema3();
|
|
@@ -96328,7 +96372,8 @@ function flowItemFromState(flow, dir) {
|
|
|
96328
96372
|
createdAt: flow.createdAt,
|
|
96329
96373
|
updatedAt: flow.updatedAt,
|
|
96330
96374
|
source: flow.source.ref ?? flow.source.type,
|
|
96331
|
-
tasks: flow.tasks.map((task) => ({ id: task.id, title: task.title, status: task.status }))
|
|
96375
|
+
tasks: flow.tasks.map((task) => ({ id: task.id, title: task.title, status: task.status })),
|
|
96376
|
+
outcomeAuthor: readOutcomeAuthor(flow.outcomeAuthor)
|
|
96332
96377
|
};
|
|
96333
96378
|
}
|
|
96334
96379
|
function localSacGuard() {
|
|
@@ -96548,6 +96593,7 @@ function formatFlowDetailLines(item) {
|
|
|
96548
96593
|
return [
|
|
96549
96594
|
`${item.id} ${item.title}`,
|
|
96550
96595
|
`Status ${item.status}`,
|
|
96596
|
+
`Outcome author ${item.outcomeAuthor ?? "unknown"}`,
|
|
96551
96597
|
...item.interrupted ? [` ${item.interrupted}`] : [],
|
|
96552
96598
|
...item.uncommitted ? [` note: ${item.uncommitted}`] : [],
|
|
96553
96599
|
`Dir ${item.dir}`,
|
|
@@ -96937,7 +96983,8 @@ function extractFlowIntent(source, repoPath) {
|
|
|
96937
96983
|
status: closed ? "closed" : "open",
|
|
96938
96984
|
flowStatus: status,
|
|
96939
96985
|
closedAt: closed ? closedAtOf(flow) : null,
|
|
96940
|
-
outcome: { criterion, ...observationFrom(source.journal) }
|
|
96986
|
+
outcome: { criterion, ...observationFrom(source.journal) },
|
|
96987
|
+
outcomeAuthor: readOutcomeAuthor(flow.outcomeAuthor)
|
|
96941
96988
|
};
|
|
96942
96989
|
}
|
|
96943
96990
|
function extractDocpackIntent(source, repoPath) {
|
|
@@ -97209,6 +97256,8 @@ function indexProblem(value) {
|
|
|
97209
97256
|
return "index.json holds an intent with an unusable note";
|
|
97210
97257
|
if (outcome.observed === true && outcome.verdict === null)
|
|
97211
97258
|
return "index.json holds an observed intent without a verdict";
|
|
97259
|
+
if (intent.outcomeAuthor !== undefined && !OUTCOME_AUTHOR_READINGS.includes(intent.outcomeAuthor))
|
|
97260
|
+
return "index.json holds an intent with an unknown outcome author";
|
|
97212
97261
|
}
|
|
97213
97262
|
return null;
|
|
97214
97263
|
}
|
|
@@ -97244,6 +97293,7 @@ async function checkStaleness(cwd, index) {
|
|
|
97244
97293
|
}
|
|
97245
97294
|
var COUNT_KEYS;
|
|
97246
97295
|
var init_store7 = __esm(() => {
|
|
97296
|
+
init_service9();
|
|
97247
97297
|
init_fs();
|
|
97248
97298
|
init_corpus();
|
|
97249
97299
|
init_types8();
|
|
@@ -97258,7 +97308,8 @@ function buildOpenReport(index) {
|
|
|
97258
97308
|
path: intent.path,
|
|
97259
97309
|
closedAt: intent.closedAt,
|
|
97260
97310
|
outcome: hasInstrument(intent.outcome.criterion) ? intent.outcome.criterion : intent.outcome.criterion ?? NO_INSTRUMENT,
|
|
97261
|
-
hasCriterion: hasInstrument(intent.outcome.criterion)
|
|
97311
|
+
hasCriterion: hasInstrument(intent.outcome.criterion),
|
|
97312
|
+
outcomeAuthor: readOutcomeAuthor(intent.outcomeAuthor)
|
|
97262
97313
|
})).sort((a, b) => (b.closedAt ?? "").localeCompare(a.closedAt ?? "") || Number.parseInt(b.id, 10) - Number.parseInt(a.id, 10) || (a.path < b.path ? -1 : 1));
|
|
97263
97314
|
const { counts } = index;
|
|
97264
97315
|
return {
|
|
@@ -97293,6 +97344,7 @@ function openEntryLines(entry) {
|
|
|
97293
97344
|
return [
|
|
97294
97345
|
`${label} ${entry.title}`,
|
|
97295
97346
|
`${pad}outcome: ${entry.outcome}`,
|
|
97347
|
+
`${pad}outcome author: ${entry.outcomeAuthor}`,
|
|
97296
97348
|
`${pad}closed ${entry.closedAt === null ? "at an unrecorded time" : entry.closedAt.slice(0, 10)}`
|
|
97297
97349
|
];
|
|
97298
97350
|
}
|
|
@@ -97330,6 +97382,7 @@ function renderIndexSummary(index, file) {
|
|
|
97330
97382
|
}
|
|
97331
97383
|
var REBUILD = "Run `keryx product index`.";
|
|
97332
97384
|
var init_open = __esm(() => {
|
|
97385
|
+
init_service9();
|
|
97333
97386
|
init_extract3();
|
|
97334
97387
|
init_store7();
|
|
97335
97388
|
});
|
|
@@ -122434,6 +122487,8 @@ async function flowCommand(args) {
|
|
|
122434
122487
|
return await runCheckAcCommand(args.slice(1));
|
|
122435
122488
|
case "owner":
|
|
122436
122489
|
return await runOwner(args.slice(1));
|
|
122490
|
+
case "outcome":
|
|
122491
|
+
return await runOutcome(args.slice(1));
|
|
122437
122492
|
case "implemented":
|
|
122438
122493
|
return await runImplemented(args.slice(1));
|
|
122439
122494
|
case "complete":
|
|
@@ -122474,7 +122529,8 @@ async function runInit(args) {
|
|
|
122474
122529
|
slug: optionValue(args, "--slug"),
|
|
122475
122530
|
baseBranch: optionValue(args, "--base"),
|
|
122476
122531
|
owner: optionValue(args, "--owner"),
|
|
122477
|
-
requireConfirmation: args.includes("--require-confirmation")
|
|
122532
|
+
requireConfirmation: args.includes("--require-confirmation"),
|
|
122533
|
+
outcomeAuthor: outcomeAuthorFlag(args)
|
|
122478
122534
|
});
|
|
122479
122535
|
banner("flow init", `Created flow ${result.flow.id}`);
|
|
122480
122536
|
console.log(` ${style.green(symbols.ok)} ${style.bold(result.flow.title)}`);
|
|
@@ -122484,6 +122540,7 @@ async function runInit(args) {
|
|
|
122484
122540
|
console.log(` base: ${result.flow.baseBranch}`);
|
|
122485
122541
|
}
|
|
122486
122542
|
console.log(` owner: ${result.flow.owner?.value ?? style.dim("not set")}`);
|
|
122543
|
+
console.log(` outcome author: ${readOutcomeAuthor(result.flow.outcomeAuthor)}`);
|
|
122487
122544
|
if (result.flow.gates?.confirmation) {
|
|
122488
122545
|
console.log(` confirmation: required ${style.dim("(a terminal-minted token: `keryx flow confirm <id>`)")}`);
|
|
122489
122546
|
}
|
|
@@ -122502,6 +122559,13 @@ async function runInit(args) {
|
|
|
122502
122559
|
`Freeze and start: ${style.cyan(`keryx flow freeze ${result.flow.id}`)} then ${style.cyan(`flow start ${result.flow.id}`)}.`
|
|
122503
122560
|
]);
|
|
122504
122561
|
}
|
|
122562
|
+
function outcomeAuthorFlag(args) {
|
|
122563
|
+
const given = args.filter((arg) => arg === "--outcome-author" || arg.startsWith("--outcome-author=")).length;
|
|
122564
|
+
if (given > 1) {
|
|
122565
|
+
throw new Error("--outcome-author was given more than once; pass it once, as agent or human.");
|
|
122566
|
+
}
|
|
122567
|
+
return given === 1 ? optionValue(args, "--outcome-author") ?? "" : undefined;
|
|
122568
|
+
}
|
|
122505
122569
|
async function runPlan(args) {
|
|
122506
122570
|
const id = requireId(args);
|
|
122507
122571
|
const cwd = process.cwd();
|
|
@@ -122598,6 +122662,7 @@ async function runStatus(args) {
|
|
|
122598
122662
|
const acLabel = flow.acChecksum ? style.green("frozen") : style.yellow("not frozen");
|
|
122599
122663
|
console.log(` AC: ${acLabel}, ${Object.keys(flow.acConfirmed).length} confirmed`);
|
|
122600
122664
|
console.log(` PR: ${flow.pr.url ? style.cyan(flow.pr.url) : style.dim("none")}`);
|
|
122665
|
+
console.log(` outcome author: ${readOutcomeAuthor(flow.outcomeAuthor)}`);
|
|
122601
122666
|
console.log(` owner: ${flow.owner?.value ? `${flow.owner.value} ${style.dim(`[${flow.owner.basis}]`)}` : style.dim("not set")}`);
|
|
122602
122667
|
const latestSignature = flow.signatures?.at(-1);
|
|
122603
122668
|
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")}`);
|
|
@@ -122841,6 +122906,34 @@ async function runOwner(args) {
|
|
|
122841
122906
|
}
|
|
122842
122907
|
throw new Error('Usage: keryx flow owner set <id> --owner "<name>" --reason "<why>"');
|
|
122843
122908
|
}
|
|
122909
|
+
async function runOutcome(args) {
|
|
122910
|
+
if (args[0] !== "author") {
|
|
122911
|
+
throw new Error(OUTCOME_AUTHOR_USAGE);
|
|
122912
|
+
}
|
|
122913
|
+
const rest = args.slice(1);
|
|
122914
|
+
const positionals = [];
|
|
122915
|
+
for (let i = 0;i < rest.length; i += 1) {
|
|
122916
|
+
const arg = rest[i] ?? "";
|
|
122917
|
+
if (arg === "--reason") {
|
|
122918
|
+
i += 1;
|
|
122919
|
+
} else if (!arg.startsWith("--")) {
|
|
122920
|
+
positionals.push(arg);
|
|
122921
|
+
}
|
|
122922
|
+
}
|
|
122923
|
+
const id = positionals[0];
|
|
122924
|
+
const author = positionals[1];
|
|
122925
|
+
if (!id || author === undefined || positionals.length > 2) {
|
|
122926
|
+
throw new Error(OUTCOME_AUTHOR_USAGE);
|
|
122927
|
+
}
|
|
122928
|
+
const result = await getService2().outcomeAuthorSet({
|
|
122929
|
+
cwd: process.cwd(),
|
|
122930
|
+
id,
|
|
122931
|
+
author,
|
|
122932
|
+
reason: optionValue(rest, "--reason") ?? ""
|
|
122933
|
+
});
|
|
122934
|
+
const after = readOutcomeAuthor(result.flow.outcomeAuthor);
|
|
122935
|
+
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`);
|
|
122936
|
+
}
|
|
122844
122937
|
async function runAc(args) {
|
|
122845
122938
|
const sub = args[0];
|
|
122846
122939
|
if (sub === "confirm") {
|
|
@@ -123188,6 +123281,7 @@ function printHelp5() {
|
|
|
123188
123281
|
'keryx flow task attempt <id> <taskId> --outcome started|failed|blocked [--detail "<what happened>"]',
|
|
123189
123282
|
'keryx flow task depends <id> <taskId> --on T1,T2|none --reason "<why>" (repair an unsatisfiable dependsOn)',
|
|
123190
123283
|
'keryx flow owner set <id> --owner "<name>" --reason "<why>" (the human accountable; never inferred)',
|
|
123284
|
+
'keryx flow outcome author <id> agent|human --reason "<why>" (who wrote the outcome criterion; journaled, gates nothing)',
|
|
123191
123285
|
'keryx flow ac confirm <id> <ACn> [--note "<evidence>"] [--signed-by "<name>"]',
|
|
123192
123286
|
'keryx flow ac update <id> --reason "<why>" (re-freeze the file as already edited; VOIDS prior confirmations)',
|
|
123193
123287
|
'keryx flow ac update <id> --criterion ACn --text "<criterion>" --reason "<why>" (rewrite/append that one criterion, then re-freeze; VOIDS prior confirmations)',
|
|
@@ -123207,9 +123301,10 @@ function printHelp5() {
|
|
|
123207
123301
|
"keryx flow schema [--out <path>]"
|
|
123208
123302
|
]);
|
|
123209
123303
|
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/.");
|
|
123304
|
+
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.");
|
|
123210
123305
|
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.");
|
|
123211
123306
|
}
|
|
123212
|
-
var VALID_TASK_KINDS, WARN = "!", AC_CONFIRM_FLAGS, AC_UPDATE_FLAGS, AC_RESEAL_FLAGS, VALID_DISPOSITIONS, service5 = null, CHALLENGE_ALPHABET = "abcdefghjkmnpqrstuvwxyz23456789";
|
|
123307
|
+
var VALID_TASK_KINDS, WARN = "!", AC_CONFIRM_FLAGS, AC_UPDATE_FLAGS, AC_RESEAL_FLAGS, VALID_DISPOSITIONS, service5 = null, OUTCOME_AUTHOR_USAGE = 'Usage: keryx flow outcome author <id> agent|human --reason "<why>"', CHALLENGE_ALPHABET = "abcdefghjkmnpqrstuvwxyz23456789";
|
|
123213
123308
|
var init_flow = __esm(() => {
|
|
123214
123309
|
init_args();
|
|
123215
123310
|
init_fs();
|
|
@@ -159494,7 +159589,7 @@ var init_command_registry = __esm(() => {
|
|
|
159494
159589
|
{
|
|
159495
159590
|
module: "tasks",
|
|
159496
159591
|
command: "flow status",
|
|
159497
|
-
summary: "One flow's full status: lifecycle state, AC freeze/confirmation count, PR, owner, latest signature, task list, and recent history.",
|
|
159592
|
+
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.",
|
|
159498
159593
|
intent: ["\u0441\u0442\u0430\u0442\u0443\u0441 \u0444\u043B\u043E\u0443", "flow status", "flow details", "show one flow"],
|
|
159499
159594
|
args: [{ name: "<id>", type: "string", required: true, desc: "flow id" }],
|
|
159500
159595
|
json: false,
|
|
@@ -159503,7 +159598,7 @@ var init_command_registry = __esm(() => {
|
|
|
159503
159598
|
{
|
|
159504
159599
|
module: "tasks",
|
|
159505
159600
|
command: "flow init",
|
|
159506
|
-
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.",
|
|
159601
|
+
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).",
|
|
159507
159602
|
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"],
|
|
159508
159603
|
args: [
|
|
159509
159604
|
{ name: "title", type: "string", required: false, desc: "work title; required unless --issue is given" },
|
|
@@ -159516,6 +159611,12 @@ var init_command_registry = __esm(() => {
|
|
|
159516
159611
|
type: "bool",
|
|
159517
159612
|
required: false,
|
|
159518
159613
|
desc: "opt this flow into the confirmation gate: `flow complete` then needs a token minted by `flow confirm` (flow 299)"
|
|
159614
|
+
},
|
|
159615
|
+
{
|
|
159616
|
+
name: "outcome-author",
|
|
159617
|
+
type: "string",
|
|
159618
|
+
required: false,
|
|
159619
|
+
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"
|
|
159519
159620
|
}
|
|
159520
159621
|
],
|
|
159521
159622
|
json: false,
|
|
@@ -159539,6 +159640,20 @@ var init_command_registry = __esm(() => {
|
|
|
159539
159640
|
read: false,
|
|
159540
159641
|
sideEffects: ["writes flow.json's owner field (basis: stated) and appends a history entry"]
|
|
159541
159642
|
},
|
|
159643
|
+
{
|
|
159644
|
+
module: "tasks",
|
|
159645
|
+
command: "flow outcome author",
|
|
159646
|
+
summary: "Change who wrote a flow's outcome criterion (agent or human). Requires a reason; appends one journal line " + "naming the old value (or `unknown`), the new value and the reason, and writes nothing when the value is unchanged. Gates nothing.",
|
|
159647
|
+
intent: ["\u043A\u0442\u043E \u043D\u0430\u043F\u0438\u0441\u0430\u043B \u043A\u0440\u0438\u0442\u0435\u0440\u0438\u0439 \u0438\u0441\u0445\u043E\u0434\u0430", "flow outcome author", "set outcome author", "mark outcome criterion as human", "\u0430\u0432\u0442\u043E\u0440 \u043A\u0440\u0438\u0442\u0435\u0440\u0438\u044F \u0438\u0441\u0445\u043E\u0434\u0430"],
|
|
159648
|
+
args: [
|
|
159649
|
+
{ name: "<id>", type: "string", required: true, desc: "flow id" },
|
|
159650
|
+
{ name: "<agent|human>", type: "string", required: true, desc: "who wrote the outcome criterion" },
|
|
159651
|
+
{ name: "reason", type: "string", required: true, desc: "why the author is being set or changed; one line" }
|
|
159652
|
+
],
|
|
159653
|
+
json: false,
|
|
159654
|
+
read: false,
|
|
159655
|
+
sideEffects: ["writes flow.json's outcomeAuthor field and appends one journal.md line and a history entry"]
|
|
159656
|
+
},
|
|
159542
159657
|
{
|
|
159543
159658
|
module: "tasks",
|
|
159544
159659
|
command: "flow ac confirm",
|
package/dist/core.js
CHANGED
|
@@ -21629,6 +21629,9 @@ __export(exports_service2, {
|
|
|
21629
21629
|
AC_CHECK_TOKEN_BUDGET: () => AC_CHECK_TOKEN_BUDGET,
|
|
21630
21630
|
AC_KINDS: () => AC_KINDS,
|
|
21631
21631
|
CONFIRMATION_CAVEAT: () => CONFIRMATION_CAVEAT,
|
|
21632
|
+
DEFAULT_OUTCOME_AUTHOR: () => DEFAULT_OUTCOME_AUTHOR,
|
|
21633
|
+
OUTCOME_AUTHORS: () => OUTCOME_AUTHORS,
|
|
21634
|
+
OUTCOME_AUTHOR_READINGS: () => OUTCOME_AUTHOR_READINGS,
|
|
21632
21635
|
OUTCOME_HINT: () => OUTCOME_HINT,
|
|
21633
21636
|
REVIEW_GATE_CONFIG_PATH: () => REVIEW_GATE_CONFIG_PATH,
|
|
21634
21637
|
acCheckCacheKey: () => acCheckCacheKey,
|
|
@@ -21655,11 +21658,13 @@ __export(exports_service2, {
|
|
|
21655
21658
|
isFrozen: () => isFrozen,
|
|
21656
21659
|
notCheckableVerdict: () => notCheckableVerdict,
|
|
21657
21660
|
parseAcceptanceCriteria: () => parseAcceptanceCriteria,
|
|
21661
|
+
parseOutcomeAuthor: () => parseOutcomeAuthor,
|
|
21658
21662
|
proseOutsideFences: () => proseOutsideFences,
|
|
21659
21663
|
readAcCheckCache: () => readAcCheckCache,
|
|
21660
21664
|
readAcCheckEnabled: () => readAcCheckEnabled,
|
|
21661
21665
|
readAcKindRecords: () => readAcKindRecords,
|
|
21662
21666
|
readFlow: () => readFlow,
|
|
21667
|
+
readOutcomeAuthor: () => readOutcomeAuthor,
|
|
21663
21668
|
renderAcCheckAdvisoryNotice: () => renderAcCheckAdvisoryNotice,
|
|
21664
21669
|
renderAcCheckReport: () => renderAcCheckReport,
|
|
21665
21670
|
renderAcKindDistribution: () => renderAcKindDistribution,
|
|
@@ -23122,6 +23127,19 @@ async function reviewGate(input) {
|
|
|
23122
23127
|
return { name: "review", status: verdict.status, detail: verdict.detail };
|
|
23123
23128
|
}
|
|
23124
23129
|
|
|
23130
|
+
// src/flow/outcome-author.ts
|
|
23131
|
+
var OUTCOME_AUTHORS = ["agent", "human"];
|
|
23132
|
+
var OUTCOME_AUTHOR_READINGS = [...OUTCOME_AUTHORS, "unknown"];
|
|
23133
|
+
var DEFAULT_OUTCOME_AUTHOR = "agent";
|
|
23134
|
+
function readOutcomeAuthor(value) {
|
|
23135
|
+
return value === "agent" || value === "human" ? value : "unknown";
|
|
23136
|
+
}
|
|
23137
|
+
function parseOutcomeAuthor(raw, flag = "--outcome-author") {
|
|
23138
|
+
if (raw === "agent" || raw === "human")
|
|
23139
|
+
return raw;
|
|
23140
|
+
const shown = typeof raw === "string" ? `"${raw}"` : "nothing";
|
|
23141
|
+
throw new Error(`${flag} must be one of: ${OUTCOME_AUTHORS.join(", ")} (got ${shown}).`);
|
|
23142
|
+
}
|
|
23125
23143
|
// src/flow/identity.ts
|
|
23126
23144
|
function resolveSignerIdentity(input) {
|
|
23127
23145
|
const stated = input.stated?.trim();
|
|
@@ -23408,6 +23426,11 @@ function flowStateSchema() {
|
|
|
23408
23426
|
$ref: "#/definitions/identity",
|
|
23409
23427
|
description: "The human accountable for this flow, or absent if one was never set. Never inferred: only ever written by an explicit `--owner` on `flow init` or `flow owner set`, so `basis` is always `stated`."
|
|
23410
23428
|
},
|
|
23429
|
+
outcomeAuthor: {
|
|
23430
|
+
type: "string",
|
|
23431
|
+
enum: ["agent", "human"],
|
|
23432
|
+
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."
|
|
23433
|
+
},
|
|
23411
23434
|
signatures: {
|
|
23412
23435
|
type: "array",
|
|
23413
23436
|
description: "Append-only signing record for `flow ac confirm` and a passing `flow complete`. Absent means never signed under this field's existence.",
|
|
@@ -24890,6 +24913,7 @@ function createFlowService(deps) {
|
|
|
24890
24913
|
if (input.owner !== undefined && !input.owner.trim()) {
|
|
24891
24914
|
throw new Error(BLANK_OWNER_MESSAGE);
|
|
24892
24915
|
}
|
|
24916
|
+
const outcomeAuthor = input.outcomeAuthor === undefined ? DEFAULT_OUTCOME_AUTHOR : parseOutcomeAuthor(input.outcomeAuthor);
|
|
24893
24917
|
const trackerReady = deps.tracker ? await deps.tracker.detect() : false;
|
|
24894
24918
|
const tracker = trackerReady ? deps.tracker : null;
|
|
24895
24919
|
const issueRef = input.issue && deps.tracker ? deps.tracker.parseRef(input.issue) : null;
|
|
@@ -24941,6 +24965,7 @@ function createFlowService(deps) {
|
|
|
24941
24965
|
pr: { url: null },
|
|
24942
24966
|
...input.baseBranch === undefined ? {} : { baseBranch: input.baseBranch },
|
|
24943
24967
|
...input.owner?.trim() ? { owner: ownerIdentity(input.owner.trim(), "`--owner` flag on `flow init`") } : {},
|
|
24968
|
+
outcomeAuthor,
|
|
24944
24969
|
tasks: DEFAULT_TASKS.map((task) => ({ ...task, status: "todo" })),
|
|
24945
24970
|
history: [{ at: createdAt, event: "created" }]
|
|
24946
24971
|
};
|
|
@@ -24998,6 +25023,25 @@ function createFlowService(deps) {
|
|
|
24998
25023
|
return save(cwd, dir, flow, previous === null ? "owner-set" : "owner-changed", detail);
|
|
24999
25024
|
});
|
|
25000
25025
|
},
|
|
25026
|
+
async outcomeAuthorSet({ cwd, id, author, reason }) {
|
|
25027
|
+
const next = parseOutcomeAuthor(author, "outcome author");
|
|
25028
|
+
if (!reason?.trim()) {
|
|
25029
|
+
throw new Error('flow outcome author requires --reason "<why>"');
|
|
25030
|
+
}
|
|
25031
|
+
validateSingleLineReason(reason);
|
|
25032
|
+
let previous = "unknown";
|
|
25033
|
+
let changed = false;
|
|
25034
|
+
const flow = await mutate(cwd, id, async ({ dir, flow: current }) => {
|
|
25035
|
+
previous = readOutcomeAuthor(current.outcomeAuthor);
|
|
25036
|
+
if (previous === next) {
|
|
25037
|
+
return current;
|
|
25038
|
+
}
|
|
25039
|
+
changed = true;
|
|
25040
|
+
current.outcomeAuthor = next;
|
|
25041
|
+
return save(cwd, dir, current, "outcome-author-set", `${previous} -> ${next} (${reason.trim()})`);
|
|
25042
|
+
});
|
|
25043
|
+
return { flow, previous, changed };
|
|
25044
|
+
},
|
|
25001
25045
|
async freeze({ cwd, id }) {
|
|
25002
25046
|
return mutate(cwd, id, async ({ dir, flow }) => {
|
|
25003
25047
|
assertTransition(flow.status, "ready");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.33",
|
|
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": {
|