@kaddo/cli 3.73.0 → 3.74.1
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/core.js
CHANGED
|
@@ -3686,6 +3686,9 @@ var RECOMMENDED_SKILLS = [...SKILL_GROUPS.delivery, ...SKILL_GROUPS.tech];
|
|
|
3686
3686
|
function skillInstallPath(id) {
|
|
3687
3687
|
return `knowledge/skills/${id}/skill.md`;
|
|
3688
3688
|
}
|
|
3689
|
+
function skillById(id) {
|
|
3690
|
+
return SKILLS.find((s) => s.id === id);
|
|
3691
|
+
}
|
|
3689
3692
|
|
|
3690
3693
|
// src/agents/responsibility.ts
|
|
3691
3694
|
var RESPONSIBILITY_MATRIX = {
|
|
@@ -7721,6 +7724,194 @@ import fs2 from "fs";
|
|
|
7721
7724
|
import path2 from "path";
|
|
7722
7725
|
import crypto from "crypto";
|
|
7723
7726
|
import matter7 from "gray-matter";
|
|
7727
|
+
|
|
7728
|
+
// src/core/knowledge-levels.ts
|
|
7729
|
+
var WORK_ITEM_TYPES2 = ["feature", "bugfix", "hotfix", "spike", "chore"];
|
|
7730
|
+
var TYPE_ALIASES = {
|
|
7731
|
+
setup: "chore",
|
|
7732
|
+
maintenance: "chore",
|
|
7733
|
+
tooling: "chore",
|
|
7734
|
+
infrastructure: "chore",
|
|
7735
|
+
infra: "chore",
|
|
7736
|
+
config: "chore",
|
|
7737
|
+
configuration: "chore",
|
|
7738
|
+
refactor: "chore"
|
|
7739
|
+
};
|
|
7740
|
+
var LEVELS = {
|
|
7741
|
+
K0: {
|
|
7742
|
+
level: "K0",
|
|
7743
|
+
description: "Trivial change. No formal knowledge required.",
|
|
7744
|
+
questions: [],
|
|
7745
|
+
qualityGate: []
|
|
7746
|
+
},
|
|
7747
|
+
K1: {
|
|
7748
|
+
level: "K1",
|
|
7749
|
+
description: "Simple fix or hotfix.",
|
|
7750
|
+
questions: [
|
|
7751
|
+
{
|
|
7752
|
+
id: "problem",
|
|
7753
|
+
prompt: "What problem does this fix?",
|
|
7754
|
+
placeholder: "e.g. Button is not clickable on mobile",
|
|
7755
|
+
frontMatterField: "problem",
|
|
7756
|
+
required: true
|
|
7757
|
+
},
|
|
7758
|
+
{
|
|
7759
|
+
id: "expected_result",
|
|
7760
|
+
prompt: "What is the expected result after the fix?",
|
|
7761
|
+
placeholder: "e.g. Button works correctly on all screen sizes",
|
|
7762
|
+
frontMatterField: "expected_result",
|
|
7763
|
+
required: true
|
|
7764
|
+
}
|
|
7765
|
+
],
|
|
7766
|
+
qualityGate: ["Problem is clear.", "Expected result is defined."]
|
|
7767
|
+
},
|
|
7768
|
+
K2: {
|
|
7769
|
+
level: "K2",
|
|
7770
|
+
description: "Feature or bugfix with functional impact.",
|
|
7771
|
+
questions: [
|
|
7772
|
+
{
|
|
7773
|
+
id: "problem",
|
|
7774
|
+
prompt: "What problem does this solve?",
|
|
7775
|
+
placeholder: "e.g. Users cannot complete checkout without email verification",
|
|
7776
|
+
frontMatterField: "problem",
|
|
7777
|
+
required: true
|
|
7778
|
+
},
|
|
7779
|
+
{
|
|
7780
|
+
id: "expected_result",
|
|
7781
|
+
prompt: "What is the expected result?",
|
|
7782
|
+
placeholder: "e.g. Users can verify email inline during checkout",
|
|
7783
|
+
frontMatterField: "expected_result",
|
|
7784
|
+
required: true
|
|
7785
|
+
},
|
|
7786
|
+
{
|
|
7787
|
+
id: "impact",
|
|
7788
|
+
prompt: "What is the impact if this is not done?",
|
|
7789
|
+
placeholder: "e.g. ~30% of users drop off at checkout",
|
|
7790
|
+
frontMatterField: "impact",
|
|
7791
|
+
required: true
|
|
7792
|
+
},
|
|
7793
|
+
{
|
|
7794
|
+
id: "acceptance_criteria",
|
|
7795
|
+
prompt: "What are the acceptance criteria? (one per line, press Enter twice to finish)",
|
|
7796
|
+
placeholder: "e.g. Email is verified before payment step",
|
|
7797
|
+
frontMatterField: "acceptance_criteria",
|
|
7798
|
+
required: true
|
|
7799
|
+
}
|
|
7800
|
+
],
|
|
7801
|
+
qualityGate: [
|
|
7802
|
+
"Problem is clear.",
|
|
7803
|
+
"Expected result is defined.",
|
|
7804
|
+
"Impact of not doing it is stated.",
|
|
7805
|
+
"Acceptance criteria are verifiable."
|
|
7806
|
+
]
|
|
7807
|
+
},
|
|
7808
|
+
K3: {
|
|
7809
|
+
level: "K3",
|
|
7810
|
+
description: "Capability, integration, or significant functional change.",
|
|
7811
|
+
questions: [
|
|
7812
|
+
{
|
|
7813
|
+
id: "problem",
|
|
7814
|
+
prompt: "What problem does this solve?",
|
|
7815
|
+
placeholder: "e.g. Payments need to support multiple providers",
|
|
7816
|
+
frontMatterField: "problem",
|
|
7817
|
+
required: true
|
|
7818
|
+
},
|
|
7819
|
+
{
|
|
7820
|
+
id: "impact",
|
|
7821
|
+
prompt: "What is the impact if this is not done?",
|
|
7822
|
+
placeholder: "e.g. We are locked into a single provider with no fallback",
|
|
7823
|
+
frontMatterField: "impact",
|
|
7824
|
+
required: true
|
|
7825
|
+
},
|
|
7826
|
+
{
|
|
7827
|
+
id: "acceptance_criteria",
|
|
7828
|
+
prompt: "What are the acceptance criteria?",
|
|
7829
|
+
placeholder: "e.g. System routes to provider B when provider A fails",
|
|
7830
|
+
frontMatterField: "acceptance_criteria",
|
|
7831
|
+
required: true
|
|
7832
|
+
},
|
|
7833
|
+
{
|
|
7834
|
+
id: "design",
|
|
7835
|
+
prompt: "What is the proposed design or approach?",
|
|
7836
|
+
placeholder: "e.g. Abstract provider interface + strategy pattern",
|
|
7837
|
+
frontMatterField: "design",
|
|
7838
|
+
required: true
|
|
7839
|
+
}
|
|
7840
|
+
],
|
|
7841
|
+
qualityGate: [
|
|
7842
|
+
"Problem is clear.",
|
|
7843
|
+
"Impact is stated.",
|
|
7844
|
+
"Acceptance criteria are verifiable.",
|
|
7845
|
+
"Design is sufficient to start."
|
|
7846
|
+
]
|
|
7847
|
+
},
|
|
7848
|
+
K4: {
|
|
7849
|
+
level: "K4",
|
|
7850
|
+
description: "Architecture change, migration, or high-risk decision.",
|
|
7851
|
+
questions: [
|
|
7852
|
+
{
|
|
7853
|
+
id: "problem",
|
|
7854
|
+
prompt: "What problem does this solve?",
|
|
7855
|
+
placeholder: "e.g. Current auth system does not support SSO",
|
|
7856
|
+
frontMatterField: "problem",
|
|
7857
|
+
required: true
|
|
7858
|
+
},
|
|
7859
|
+
{
|
|
7860
|
+
id: "impact",
|
|
7861
|
+
prompt: "What is the impact if this is not done?",
|
|
7862
|
+
placeholder: "e.g. Enterprise clients cannot onboard",
|
|
7863
|
+
frontMatterField: "impact",
|
|
7864
|
+
required: true
|
|
7865
|
+
},
|
|
7866
|
+
{
|
|
7867
|
+
id: "design",
|
|
7868
|
+
prompt: "What is the proposed design or approach?",
|
|
7869
|
+
placeholder: "e.g. Replace JWT-only auth with OIDC + JWT hybrid",
|
|
7870
|
+
frontMatterField: "design",
|
|
7871
|
+
required: true
|
|
7872
|
+
},
|
|
7873
|
+
{
|
|
7874
|
+
id: "risks",
|
|
7875
|
+
prompt: "What are the main risks?",
|
|
7876
|
+
placeholder: "e.g. Existing sessions may be invalidated during migration",
|
|
7877
|
+
frontMatterField: "risks",
|
|
7878
|
+
required: true
|
|
7879
|
+
}
|
|
7880
|
+
],
|
|
7881
|
+
qualityGate: [
|
|
7882
|
+
"Problem is clear.",
|
|
7883
|
+
"Impact is stated.",
|
|
7884
|
+
"Design is documented.",
|
|
7885
|
+
"Risks are identified.",
|
|
7886
|
+
"ADR is created or linked if applicable."
|
|
7887
|
+
]
|
|
7888
|
+
}
|
|
7889
|
+
};
|
|
7890
|
+
var TYPE_TO_LEVEL = {
|
|
7891
|
+
hotfix: "K1",
|
|
7892
|
+
bugfix: "K2",
|
|
7893
|
+
feature: "K2",
|
|
7894
|
+
spike: "K3",
|
|
7895
|
+
// Chores are maintenance/config/tooling work — low ceremony (problem + expected result).
|
|
7896
|
+
chore: "K1"
|
|
7897
|
+
};
|
|
7898
|
+
function getLevel(level) {
|
|
7899
|
+
return LEVELS[level];
|
|
7900
|
+
}
|
|
7901
|
+
function getLevelForType(type) {
|
|
7902
|
+
return TYPE_TO_LEVEL[type];
|
|
7903
|
+
}
|
|
7904
|
+
function isValidType(type) {
|
|
7905
|
+
return WORK_ITEM_TYPES2.includes(type);
|
|
7906
|
+
}
|
|
7907
|
+
function normalizeType(raw) {
|
|
7908
|
+
if (!raw) return null;
|
|
7909
|
+
const t = raw.trim().toLowerCase();
|
|
7910
|
+
if (isValidType(t)) return t;
|
|
7911
|
+
return TYPE_ALIASES[t] ?? null;
|
|
7912
|
+
}
|
|
7913
|
+
|
|
7914
|
+
// src/core/work-item-write.ts
|
|
7724
7915
|
var WORK_ITEMS_DIR = "knowledge/delivery/work-items";
|
|
7725
7916
|
var WorkItemWriteError = class extends Error {
|
|
7726
7917
|
constructor(code, message) {
|
|
@@ -8004,11 +8195,33 @@ function getWorkItemForEdit(dir, id) {
|
|
|
8004
8195
|
...editable ? {} : { editableReason: status === "ready" ? "This Work Item is Ready. Reopen it as Draft to edit its scope." : `This Work Item is ${status} and cannot be edited from the structured editor.` }
|
|
8005
8196
|
};
|
|
8006
8197
|
}
|
|
8198
|
+
var CAPTURE_SECTIONS = [
|
|
8199
|
+
{ field: "problem", heading: "Problem" },
|
|
8200
|
+
{ field: "expected_result", heading: "Expected result" },
|
|
8201
|
+
{ field: "impact", heading: "Impact" },
|
|
8202
|
+
{ field: "acceptance_criteria", heading: "Acceptance criteria", list: true },
|
|
8203
|
+
{ field: "design", heading: "Design" },
|
|
8204
|
+
{ field: "risks", heading: "Risks" }
|
|
8205
|
+
];
|
|
8206
|
+
function captureBody(title, type, answers) {
|
|
8207
|
+
const out = [`# ${title}`, "", `> Type: ${type}`];
|
|
8208
|
+
for (const { field, heading, list: list2 } of CAPTURE_SECTIONS) {
|
|
8209
|
+
const value = answers[field]?.trim();
|
|
8210
|
+
if (!value) continue;
|
|
8211
|
+
if (list2) {
|
|
8212
|
+
const items = value.split(/\r?\n/).map((l) => l.trim()).filter(Boolean).map((l) => /^[-*+]\s/.test(l) ? l : `- ${l}`);
|
|
8213
|
+
out.push("", `## ${heading}`, "", items.join("\n"));
|
|
8214
|
+
} else {
|
|
8215
|
+
out.push("", `## ${heading}`, "", value);
|
|
8216
|
+
}
|
|
8217
|
+
}
|
|
8218
|
+
return out.join("\n") + "\n";
|
|
8219
|
+
}
|
|
8007
8220
|
function createWorkItem(dir, opts) {
|
|
8008
8221
|
const intent = opts.intent.trim();
|
|
8009
8222
|
if (!intent) throw new WorkItemWriteError("INVALID_INPUT", "An intent or summary is required.");
|
|
8010
|
-
const type = opts.type.trim()
|
|
8011
|
-
if (!
|
|
8223
|
+
const type = normalizeType(opts.type.trim()) ?? "";
|
|
8224
|
+
if (!type) throw new WorkItemWriteError("INVALID_INPUT", `Unknown Work Item type "${opts.type}".`);
|
|
8012
8225
|
const id = nextWorkItemId(dir);
|
|
8013
8226
|
const title = intent.split(/\r?\n/)[0].trim().slice(0, 120);
|
|
8014
8227
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -8024,7 +8237,9 @@ function createWorkItem(dir, opts) {
|
|
|
8024
8237
|
affected_modules: [],
|
|
8025
8238
|
summary: intent
|
|
8026
8239
|
};
|
|
8027
|
-
const
|
|
8240
|
+
const answers = opts.answers ?? {};
|
|
8241
|
+
const hasAnswers = Object.values(answers).some((v) => v?.trim());
|
|
8242
|
+
const body = hasAnswers ? captureBody(title, type, answers) : freshBody({
|
|
8028
8243
|
title,
|
|
8029
8244
|
type,
|
|
8030
8245
|
summary: intent,
|
|
@@ -8036,8 +8251,7 @@ function createWorkItem(dir, opts) {
|
|
|
8036
8251
|
decisions: [],
|
|
8037
8252
|
relatedKnowledge: [],
|
|
8038
8253
|
scopeConfidence: null
|
|
8039
|
-
};
|
|
8040
|
-
const body = freshBody(input);
|
|
8254
|
+
});
|
|
8041
8255
|
const relPath = `${WORK_ITEMS_DIR}/draft/${id}-${slugify2(title)}.md`;
|
|
8042
8256
|
const filePath = join(dir, relPath);
|
|
8043
8257
|
if (exists(filePath)) throw new WorkItemWriteError("INVALID_INPUT", `Work Item file already exists: ${relPath}`);
|
|
@@ -8047,7 +8261,7 @@ function createWorkItem(dir, opts) {
|
|
|
8047
8261
|
}
|
|
8048
8262
|
function validateInput(input) {
|
|
8049
8263
|
if (!input.title.trim()) throw new WorkItemWriteError("INVALID_INPUT", "Title is required.");
|
|
8050
|
-
if (!
|
|
8264
|
+
if (!isValidType(input.type)) throw new WorkItemWriteError("INVALID_INPUT", `Unknown Work Item type "${input.type}".`);
|
|
8051
8265
|
}
|
|
8052
8266
|
function updateWorkItem(dir, id, input, expectedRevision) {
|
|
8053
8267
|
validateInput(input);
|
|
@@ -8125,11 +8339,145 @@ function transitionWorkItem(dir, id, to, expectedRevision) {
|
|
|
8125
8339
|
}
|
|
8126
8340
|
return { revision: revisionOf(nextRaw), status: to, path: targetRel };
|
|
8127
8341
|
}
|
|
8342
|
+
|
|
8343
|
+
// src/core/work-item-refinement.ts
|
|
8344
|
+
function toCapture(q) {
|
|
8345
|
+
return { id: q.id, prompt: q.prompt, placeholder: q.placeholder, field: q.frontMatterField, required: q.required };
|
|
8346
|
+
}
|
|
8347
|
+
function getWorkItemCaptureDefinition() {
|
|
8348
|
+
const questions = {};
|
|
8349
|
+
for (const type of WORK_ITEM_TYPES2) {
|
|
8350
|
+
questions[type] = getLevel(getLevelForType(type)).questions.map(toCapture);
|
|
8351
|
+
}
|
|
8352
|
+
return {
|
|
8353
|
+
types: WORK_ITEM_TYPES2.map((t) => ({ value: t, label: t.charAt(0).toUpperCase() + t.slice(1) })),
|
|
8354
|
+
questions
|
|
8355
|
+
};
|
|
8356
|
+
}
|
|
8357
|
+
function getWorkItemAgentAssets() {
|
|
8358
|
+
const agent = AGENT_PROMPTS.find((p2) => p2.fileName === "work-item-agent.md");
|
|
8359
|
+
const skill2 = skillById("work-item-refinement");
|
|
8360
|
+
return { agentPrompt: agent?.content ?? "", skill: skill2?.content ?? null };
|
|
8361
|
+
}
|
|
8362
|
+
function assembleRefinementContext(dir, workItemId) {
|
|
8363
|
+
const edit = getWorkItemForEdit(dir, workItemId);
|
|
8364
|
+
const config = loadConfig(dir);
|
|
8365
|
+
const knowledgeArtifacts = discoverKnowledge(dir).filter(
|
|
8366
|
+
(a) => !a.isWorkItem && a.type !== "skill" && a.type !== "agent" && a.layer !== "unknown"
|
|
8367
|
+
);
|
|
8368
|
+
const decisions = knowledgeArtifacts.filter((a) => a.type === "adr" || /^adr-/i.test(a.id)).map((a) => ({ id: a.id, title: a.title || a.id }));
|
|
8369
|
+
const knowledge = knowledgeArtifacts.filter((a) => !(a.type === "adr" || /^adr-/i.test(a.id))).map((a) => ({ id: a.id || a.relPath, title: a.title || a.id, layer: a.layer, type: a.type || void 0, summary: a.summary || void 0 }));
|
|
8370
|
+
const modules = ["core", ...loadMappedModules(dir).map((m) => m.id)].filter((v, i, arr) => arr.indexOf(v) === i);
|
|
8371
|
+
return {
|
|
8372
|
+
workItem: { id: edit.id, title: edit.title, status: edit.status, intent: edit.summary ?? edit.title, current: stripEdit(edit) },
|
|
8373
|
+
project: { name: config?.project.name ?? "unknown", state: config?.project.state ?? "unknown", structure: config?.project.structure ?? "unknown" },
|
|
8374
|
+
modules,
|
|
8375
|
+
knowledge,
|
|
8376
|
+
decisions,
|
|
8377
|
+
revision: edit.revision
|
|
8378
|
+
};
|
|
8379
|
+
}
|
|
8380
|
+
function stripEdit(edit) {
|
|
8381
|
+
const { id: _i, status: _s, revision: _r, path: _p, editable: _e, editableReason: _er, ...input } = edit;
|
|
8382
|
+
return input;
|
|
8383
|
+
}
|
|
8384
|
+
var VALID_COVERAGE2 = /* @__PURE__ */ new Set(["affected", "reviewed-not-affected", "unknown", "not-applicable"]);
|
|
8385
|
+
var VALID_CONFIDENCE2 = /* @__PURE__ */ new Set(["high", "medium", "low"]);
|
|
8386
|
+
function str(v) {
|
|
8387
|
+
return typeof v === "string" && v.trim() ? v.trim() : void 0;
|
|
8388
|
+
}
|
|
8389
|
+
function strList(v) {
|
|
8390
|
+
return Array.isArray(v) ? v.map((x) => typeof x === "string" ? x.trim() : "").filter(Boolean) : [];
|
|
8391
|
+
}
|
|
8392
|
+
function normalizeAndValidateProposal(dir, workItemId, proposal) {
|
|
8393
|
+
const ctx = assembleRefinementContext(dir, workItemId);
|
|
8394
|
+
const knownModules = new Set(ctx.modules);
|
|
8395
|
+
const knownDecisions = new Set(ctx.decisions.map((d) => d.id));
|
|
8396
|
+
const knownKnowledge = new Set(ctx.knowledge.map((k) => k.id));
|
|
8397
|
+
const extraFindings = [];
|
|
8398
|
+
const input = { ...ctx.workItem.current };
|
|
8399
|
+
if (str(proposal.title)) input.title = str(proposal.title);
|
|
8400
|
+
const o = proposal.outcome ?? {};
|
|
8401
|
+
if (str(o.actor) !== void 0) input.actor = str(o.actor);
|
|
8402
|
+
if (str(o.observableOutcome) !== void 0) input.outcome = str(o.observableOutcome);
|
|
8403
|
+
if (str(o.currentBehavior) !== void 0) input.currentBehavior = str(o.currentBehavior);
|
|
8404
|
+
if (str(o.targetBehavior) !== void 0) input.targetBehavior = str(o.targetBehavior);
|
|
8405
|
+
const j = proposal.journey ?? {};
|
|
8406
|
+
if (j.entryPoints) input.entryPoints = strList(j.entryPoints).join("\n");
|
|
8407
|
+
if (j.flow) input.endToEndFlow = strList(j.flow).map((s) => `- ${s}`).join("\n");
|
|
8408
|
+
if (proposal.moduleCoverage) {
|
|
8409
|
+
input.moduleCoverage = proposal.moduleCoverage.filter((c) => {
|
|
8410
|
+
if (!knownModules.has(c.id)) {
|
|
8411
|
+
extraFindings.push({ level: "warning", message: `Proposed module "${c.id}" is not registered and was not included.` });
|
|
8412
|
+
return false;
|
|
8413
|
+
}
|
|
8414
|
+
return VALID_COVERAGE2.has(c.status);
|
|
8415
|
+
}).map((c) => ({ id: c.id, status: c.status, ...str(c.reason) ? { reason: str(c.reason) } : {} }));
|
|
8416
|
+
}
|
|
8417
|
+
const affected = /* @__PURE__ */ new Set();
|
|
8418
|
+
for (const m of proposal.affectedModules ?? []) if (knownModules.has(m)) affected.add(m);
|
|
8419
|
+
for (const c of input.moduleCoverage) if (c.status === "affected") affected.add(c.id);
|
|
8420
|
+
if (proposal.affectedModules || proposal.moduleCoverage) input.affectedModules = [...affected];
|
|
8421
|
+
if (proposal.impactAnalysis) {
|
|
8422
|
+
input.impactAnalysis = proposal.impactAnalysis.filter((s) => VALID_COVERAGE2.has(s.status) && str(s.surface)).map((s) => ({ surface: str(s.surface), status: s.status, ...str(s.reason) ? { reason: str(s.reason) } : {}, ...str(s.question) ? { question: str(s.question) } : {} }));
|
|
8423
|
+
}
|
|
8424
|
+
if (proposal.scopeConfidence && VALID_CONFIDENCE2.has(proposal.scopeConfidence.level)) {
|
|
8425
|
+
input.scopeConfidence = { level: proposal.scopeConfidence.level, reasons: strList(proposal.scopeConfidence.reasons) };
|
|
8426
|
+
}
|
|
8427
|
+
if (proposal.scopeUnknowns) input.scopeUnknowns = strList(proposal.scopeUnknowns);
|
|
8428
|
+
if (proposal.acceptanceCriteria) input.acceptanceCriteria = strList(proposal.acceptanceCriteria).map((t) => ({ text: t, checked: null }));
|
|
8429
|
+
if (proposal.linkedDecisions) {
|
|
8430
|
+
input.decisions = proposal.linkedDecisions.filter((id) => {
|
|
8431
|
+
if (!knownDecisions.has(id)) {
|
|
8432
|
+
extraFindings.push({ level: "warning", message: `Proposed decision "${id}" does not exist and was not linked.` });
|
|
8433
|
+
return false;
|
|
8434
|
+
}
|
|
8435
|
+
return true;
|
|
8436
|
+
});
|
|
8437
|
+
}
|
|
8438
|
+
if (proposal.relatedKnowledge) {
|
|
8439
|
+
input.relatedKnowledge = proposal.relatedKnowledge.filter((id) => {
|
|
8440
|
+
if (!knownKnowledge.has(id)) {
|
|
8441
|
+
extraFindings.push({ level: "warning", message: `Proposed knowledge "${id}" could not be resolved and was not linked.` });
|
|
8442
|
+
return false;
|
|
8443
|
+
}
|
|
8444
|
+
return true;
|
|
8445
|
+
});
|
|
8446
|
+
}
|
|
8447
|
+
const findings = [...extraFindings, ...evaluate(input, knownModules)];
|
|
8448
|
+
const blocking = findings.filter((f) => f.level === "blocking").length;
|
|
8449
|
+
const warning = findings.filter((f) => f.level === "warning").length;
|
|
8450
|
+
const fyi = findings.filter((f) => f.level === "fyi").length;
|
|
8451
|
+
return { input, validation: { findings, blocking, warning, fyi, canApply: true } };
|
|
8452
|
+
}
|
|
8453
|
+
function evaluate(input, knownModules) {
|
|
8454
|
+
const findings = [];
|
|
8455
|
+
for (const c of input.moduleCoverage) {
|
|
8456
|
+
if (c.status === "affected" && !input.affectedModules.includes(c.id)) {
|
|
8457
|
+
findings.push({ level: "blocking", message: `${c.id} is marked affected in module coverage but is missing from affected_modules.` });
|
|
8458
|
+
}
|
|
8459
|
+
}
|
|
8460
|
+
for (const m of input.affectedModules) {
|
|
8461
|
+
if (!knownModules.has(m)) findings.push({ level: "blocking", message: `Module "${m}" is not registered in this project.` });
|
|
8462
|
+
}
|
|
8463
|
+
if (!input.targetBehavior?.trim()) findings.push({ level: "warning", message: "Target behavior is not defined." });
|
|
8464
|
+
if (input.acceptanceCriteria.length === 0) findings.push({ level: "warning", message: "No acceptance criteria have been defined." });
|
|
8465
|
+
if (input.scopeConfidence?.level === "low") findings.push({ level: "warning", message: "Scope confidence is Low." });
|
|
8466
|
+
if (!input.scopeConfidence) findings.push({ level: "warning", message: "Scope confidence has not been assessed." });
|
|
8467
|
+
for (const s of input.impactAnalysis) if (s.status === "unknown") findings.push({ level: "fyi", message: `Impact on ${s.surface} is unknown.` });
|
|
8468
|
+
return findings;
|
|
8469
|
+
}
|
|
8470
|
+
function applyRefinement(dir, workItemId, proposal, expectedRevision) {
|
|
8471
|
+
const { input } = normalizeAndValidateProposal(dir, workItemId, proposal);
|
|
8472
|
+
return updateWorkItem(dir, workItemId, input, expectedRevision);
|
|
8473
|
+
}
|
|
8128
8474
|
export {
|
|
8129
8475
|
WorkItemNotFoundError,
|
|
8130
8476
|
WorkItemWriteError,
|
|
8131
8477
|
analyzeCrossRepoEvidence,
|
|
8132
8478
|
analyzeScopeCoverage,
|
|
8479
|
+
applyRefinement,
|
|
8480
|
+
assembleRefinementContext,
|
|
8133
8481
|
buildProjectExplanation,
|
|
8134
8482
|
buildProjectRoute,
|
|
8135
8483
|
buildReadinessReport,
|
|
@@ -8139,6 +8487,8 @@ export {
|
|
|
8139
8487
|
discoverWorkItems,
|
|
8140
8488
|
exists,
|
|
8141
8489
|
getWorkItem,
|
|
8490
|
+
getWorkItemAgentAssets,
|
|
8491
|
+
getWorkItemCaptureDefinition,
|
|
8142
8492
|
getWorkItemForEdit,
|
|
8143
8493
|
getWorkItems,
|
|
8144
8494
|
getWorkItemsSummary,
|
|
@@ -8150,6 +8500,7 @@ export {
|
|
|
8150
8500
|
lifecycleStateOf,
|
|
8151
8501
|
loadConfig,
|
|
8152
8502
|
loadMappedModules,
|
|
8503
|
+
normalizeAndValidateProposal,
|
|
8153
8504
|
readFile,
|
|
8154
8505
|
transitionWorkItem,
|
|
8155
8506
|
updateWorkItem,
|