@mestreyoda/fabrica 0.2.23 → 0.2.25
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/index.js +343 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -113905,8 +113905,8 @@ import fsSync from "node:fs";
|
|
|
113905
113905
|
import path5 from "node:path";
|
|
113906
113906
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
113907
113907
|
function getCurrentVersion() {
|
|
113908
|
-
if ("0.2.
|
|
113909
|
-
return "0.2.
|
|
113908
|
+
if ("0.2.25") {
|
|
113909
|
+
return "0.2.25";
|
|
113910
113910
|
}
|
|
113911
113911
|
try {
|
|
113912
113912
|
const pkgPath = path5.join(THIS_DIR, "..", "..", "package.json");
|
|
@@ -122268,7 +122268,9 @@ async function clearIssueRuntime(workspaceDir, slugOrChannelId, issueId) {
|
|
|
122268
122268
|
decompositionMode: existing.decompositionMode ?? null,
|
|
122269
122269
|
decompositionStatus: existing.parentIssueId ? "completed" : existing.decompositionStatus ?? null,
|
|
122270
122270
|
sessionCompletedAt: existing.sessionCompletedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
122271
|
-
artifactOfRecord: existing.artifactOfRecord ?? null
|
|
122271
|
+
artifactOfRecord: existing.artifactOfRecord ?? null,
|
|
122272
|
+
qualityCriticality: existing.qualityCriticality ?? null,
|
|
122273
|
+
riskProfile: existing.riskProfile ?? null
|
|
122272
122274
|
} : null;
|
|
122273
122275
|
if (preserved && (preserved.parentIssueId != null || (preserved.childIssueIds?.length ?? 0) > 0 || (preserved.dependencyIssueIds?.length ?? 0) > 0 || preserved.decompositionMode != null)) {
|
|
122274
122276
|
project.issueRuntime[key] = preserved;
|
|
@@ -125107,6 +125109,10 @@ ${event.summary}`;
|
|
|
125107
125109
|
if (event.summary) {
|
|
125108
125110
|
msg += `
|
|
125109
125111
|
${event.summary}`;
|
|
125112
|
+
}
|
|
125113
|
+
if (event.acceptanceSummary) {
|
|
125114
|
+
msg += `
|
|
125115
|
+
\u{1F9FE} ${event.acceptanceSummary}`;
|
|
125110
125116
|
}
|
|
125111
125117
|
if (event.prUrl) msg += `
|
|
125112
125118
|
\u{1F517} ${prLink(event.prUrl)}`;
|
|
@@ -125223,6 +125229,8 @@ ${event.summary}`;
|
|
|
125223
125229
|
let msg = `\u{1F3C1} Issue completed: #${event.issueId} \u2014 ${event.issueTitle}`;
|
|
125224
125230
|
msg += `
|
|
125225
125231
|
\u{1F4E6} Project: ${event.project}`;
|
|
125232
|
+
if (event.acceptanceSummary) msg += `
|
|
125233
|
+
\u{1F9FE} ${event.acceptanceSummary}`;
|
|
125226
125234
|
if (event.prUrl) msg += `
|
|
125227
125235
|
\u{1F517} ${prLink(event.prUrl)}`;
|
|
125228
125236
|
msg += `
|
|
@@ -125610,10 +125618,14 @@ function formatChildRollupLine(child) {
|
|
|
125610
125618
|
const prUrl = runtime?.artifactOfRecord?.url ?? runtime?.currentPrUrl ?? null;
|
|
125611
125619
|
const mergedAt = runtime?.artifactOfRecord?.mergedAt ?? null;
|
|
125612
125620
|
const headSha = runtime?.artifactOfRecord?.headSha ?? runtime?.lastHeadSha ?? null;
|
|
125621
|
+
const qualityCriticality = runtime?.qualityCriticality ?? null;
|
|
125622
|
+
const riskProfile = runtime?.riskProfile ?? [];
|
|
125613
125623
|
const extras = [
|
|
125614
125624
|
prUrl ? `PR: ${prUrl}` : null,
|
|
125615
125625
|
mergedAt ? `mergedAt: ${mergedAt}` : null,
|
|
125616
|
-
headSha ? `headSha: ${headSha}` : null
|
|
125626
|
+
headSha ? `headSha: ${headSha}` : null,
|
|
125627
|
+
qualityCriticality ? `qualityCriticality: ${qualityCriticality}` : null,
|
|
125628
|
+
riskProfile.length > 0 ? `risks: ${riskProfile.join(",")}` : null
|
|
125617
125629
|
].filter(Boolean);
|
|
125618
125630
|
return `- #${child.issueId} \u2014 ${state}${extras.length > 0 ? ` \u2014 ${extras.join(" \u2014 ")}` : ""}`;
|
|
125619
125631
|
}
|
|
@@ -125622,12 +125634,14 @@ var PARENT_ROLLUP_END = "<!-- fabrica:parent-rollup:end -->";
|
|
|
125622
125634
|
function buildParentRollupComment(status, completedChildIds, blockedChildIds, children) {
|
|
125623
125635
|
const allChildIds = children.map((child) => child.issueId);
|
|
125624
125636
|
const pendingChildIds = allChildIds.filter((id) => !completedChildIds.includes(id) && !blockedChildIds.includes(id));
|
|
125637
|
+
const highCriticalityChildren = children.filter((child) => child.runtime?.qualityCriticality === "high").map((child) => child.issueId);
|
|
125625
125638
|
return [
|
|
125626
125639
|
"## Parent Rollup",
|
|
125627
125640
|
`- Status: ${status}`,
|
|
125628
125641
|
`- Completed children (${completedChildIds.length}/${allChildIds.length}): ${completedChildIds.length > 0 ? completedChildIds.map((id) => `#${id}`).join(", ") : "none"}`,
|
|
125629
125642
|
`- Pending children: ${pendingChildIds.length > 0 ? pendingChildIds.map((id) => `#${id}`).join(", ") : "none"}`,
|
|
125630
125643
|
`- Blocked children: ${blockedChildIds.length > 0 ? blockedChildIds.map((id) => `#${id}`).join(", ") : "none"}`,
|
|
125644
|
+
`- High-criticality children: ${highCriticalityChildren.length > 0 ? highCriticalityChildren.map((id) => `#${id}`).join(", ") : "none"}`,
|
|
125631
125645
|
"",
|
|
125632
125646
|
"### Child Status",
|
|
125633
125647
|
...children.map((child) => formatChildRollupLine(child))
|
|
@@ -125704,6 +125718,200 @@ async function reconcileParentLifecycleForIssue(opts) {
|
|
|
125704
125718
|
// lib/services/pipeline.ts
|
|
125705
125719
|
init_workflow();
|
|
125706
125720
|
init_context3();
|
|
125721
|
+
|
|
125722
|
+
// lib/quality/quality-gates.ts
|
|
125723
|
+
var BASE_POLICIES = {
|
|
125724
|
+
api: {
|
|
125725
|
+
archetype: "api",
|
|
125726
|
+
requiredEvidence: ["request-level verification", "acceptance criteria traceability"],
|
|
125727
|
+
requiredChecks: ["startup/build", "endpoint validation", "error-handling review"],
|
|
125728
|
+
autoRejectConditions: ["missing input validation", "missing auth review on sensitive flows", "no meaningful API evidence"],
|
|
125729
|
+
qualityCriticalityFloor: "medium"
|
|
125730
|
+
},
|
|
125731
|
+
"web-ui": {
|
|
125732
|
+
archetype: "web-ui",
|
|
125733
|
+
requiredEvidence: ["primary flow verification", "loading/error behavior"],
|
|
125734
|
+
requiredChecks: ["build/render", "interaction smoke", "a11y sanity"],
|
|
125735
|
+
autoRejectConditions: ["missing primary flow evidence", "missing loading/error handling"],
|
|
125736
|
+
qualityCriticalityFloor: "medium"
|
|
125737
|
+
},
|
|
125738
|
+
cli: {
|
|
125739
|
+
archetype: "cli",
|
|
125740
|
+
requiredEvidence: ["command smoke", "help output", "exit-code behavior"],
|
|
125741
|
+
requiredChecks: ["binary/script execution", "argv validation"],
|
|
125742
|
+
autoRejectConditions: ["missing help output", "wrong exit-code behavior", "no command evidence"],
|
|
125743
|
+
qualityCriticalityFloor: "low"
|
|
125744
|
+
},
|
|
125745
|
+
library: {
|
|
125746
|
+
archetype: "library",
|
|
125747
|
+
requiredEvidence: ["public API tests", "usage validation"],
|
|
125748
|
+
requiredChecks: ["build/install", "public export verification"],
|
|
125749
|
+
autoRejectConditions: ["no public API evidence", "broken exports"],
|
|
125750
|
+
qualityCriticalityFloor: "low"
|
|
125751
|
+
},
|
|
125752
|
+
automation: {
|
|
125753
|
+
archetype: "automation",
|
|
125754
|
+
requiredEvidence: ["main flow verification", "failure-path evidence"],
|
|
125755
|
+
requiredChecks: ["timeout/retry review", "idempotency review when applicable"],
|
|
125756
|
+
autoRejectConditions: ["unsafe retries", "no failure-path evidence"],
|
|
125757
|
+
qualityCriticalityFloor: "medium"
|
|
125758
|
+
},
|
|
125759
|
+
hybrid: {
|
|
125760
|
+
archetype: "hybrid",
|
|
125761
|
+
requiredEvidence: ["API and UI flow evidence", "integration behavior"],
|
|
125762
|
+
requiredChecks: ["build", "API smoke", "UI smoke"],
|
|
125763
|
+
autoRejectConditions: ["missing UI evidence", "missing API evidence"],
|
|
125764
|
+
qualityCriticalityFloor: "medium"
|
|
125765
|
+
},
|
|
125766
|
+
unknown: {
|
|
125767
|
+
archetype: "unknown",
|
|
125768
|
+
requiredEvidence: ["basic behavioral evidence"],
|
|
125769
|
+
requiredChecks: ["build or execution smoke"],
|
|
125770
|
+
autoRejectConditions: ["no runnable evidence"],
|
|
125771
|
+
qualityCriticalityFloor: "low"
|
|
125772
|
+
}
|
|
125773
|
+
};
|
|
125774
|
+
function resolveQualityGatePolicy(opts) {
|
|
125775
|
+
const archetype = opts.deliverable ?? "unknown";
|
|
125776
|
+
const base = BASE_POLICIES[archetype] ?? BASE_POLICIES.unknown;
|
|
125777
|
+
const ORDER = ["low", "medium", "high"];
|
|
125778
|
+
const qualityCriticalityFloor = opts.qualityCriticality ? ORDER.indexOf(opts.qualityCriticality) > ORDER.indexOf(base.qualityCriticalityFloor) ? opts.qualityCriticality : base.qualityCriticalityFloor : base.qualityCriticalityFloor;
|
|
125779
|
+
return {
|
|
125780
|
+
...base,
|
|
125781
|
+
qualityCriticalityFloor
|
|
125782
|
+
};
|
|
125783
|
+
}
|
|
125784
|
+
|
|
125785
|
+
// lib/quality/done-policies.ts
|
|
125786
|
+
var BASE_POLICIES2 = {
|
|
125787
|
+
api: {
|
|
125788
|
+
archetype: "api",
|
|
125789
|
+
requiredArtifacts: ["implemented endpoint surface", "runtime contract"],
|
|
125790
|
+
requiredEvidence: ["API behavior evidence", "acceptance criteria coverage"],
|
|
125791
|
+
behavioralChecks: ["startup works", "error paths handled", "validation enforced"],
|
|
125792
|
+
disqualifyingConditions: ["missing endpoint evidence", "missing validation on exposed boundaries"],
|
|
125793
|
+
qualityCriticalityFloor: "medium"
|
|
125794
|
+
},
|
|
125795
|
+
"web-ui": {
|
|
125796
|
+
archetype: "web-ui",
|
|
125797
|
+
requiredArtifacts: ["main user flow", "UI states"],
|
|
125798
|
+
requiredEvidence: ["interaction evidence", "loading/error evidence"],
|
|
125799
|
+
behavioralChecks: ["main flow works", "render path stable"],
|
|
125800
|
+
disqualifyingConditions: ["missing main flow evidence", "broken loading/error handling"],
|
|
125801
|
+
qualityCriticalityFloor: "medium"
|
|
125802
|
+
},
|
|
125803
|
+
cli: {
|
|
125804
|
+
archetype: "cli",
|
|
125805
|
+
requiredArtifacts: ["command entrypoint", "help contract"],
|
|
125806
|
+
requiredEvidence: ["command execution evidence", "exit-code evidence"],
|
|
125807
|
+
behavioralChecks: ["help works", "main command succeeds", "invalid args fail cleanly"],
|
|
125808
|
+
disqualifyingConditions: ["missing help", "no command execution evidence"],
|
|
125809
|
+
qualityCriticalityFloor: "low"
|
|
125810
|
+
},
|
|
125811
|
+
library: {
|
|
125812
|
+
archetype: "library",
|
|
125813
|
+
requiredArtifacts: ["public API surface"],
|
|
125814
|
+
requiredEvidence: ["public API tests or examples"],
|
|
125815
|
+
behavioralChecks: ["exports resolve", "usage path works"],
|
|
125816
|
+
disqualifyingConditions: ["broken exports", "no public API evidence"],
|
|
125817
|
+
qualityCriticalityFloor: "low"
|
|
125818
|
+
},
|
|
125819
|
+
automation: {
|
|
125820
|
+
archetype: "automation",
|
|
125821
|
+
requiredArtifacts: ["orchestration flow"],
|
|
125822
|
+
requiredEvidence: ["main flow evidence", "failure-path evidence"],
|
|
125823
|
+
behavioralChecks: ["timeouts are bounded", "retry semantics are safe where needed"],
|
|
125824
|
+
disqualifyingConditions: ["unsafe retry behavior", "no failure-path evidence"],
|
|
125825
|
+
qualityCriticalityFloor: "medium"
|
|
125826
|
+
},
|
|
125827
|
+
hybrid: {
|
|
125828
|
+
archetype: "hybrid",
|
|
125829
|
+
requiredArtifacts: ["API surface", "UI/main interaction flow"],
|
|
125830
|
+
requiredEvidence: ["API evidence", "UI evidence"],
|
|
125831
|
+
behavioralChecks: ["integrated path works"],
|
|
125832
|
+
disqualifyingConditions: ["missing one side of hybrid evidence"],
|
|
125833
|
+
qualityCriticalityFloor: "medium"
|
|
125834
|
+
},
|
|
125835
|
+
unknown: {
|
|
125836
|
+
archetype: "unknown",
|
|
125837
|
+
requiredArtifacts: ["core deliverable"],
|
|
125838
|
+
requiredEvidence: ["basic runnable evidence"],
|
|
125839
|
+
behavioralChecks: ["main path works"],
|
|
125840
|
+
disqualifyingConditions: ["no evidence of working main path"],
|
|
125841
|
+
qualityCriticalityFloor: "low"
|
|
125842
|
+
}
|
|
125843
|
+
};
|
|
125844
|
+
function resolveDonePolicy(opts) {
|
|
125845
|
+
const archetype = opts.deliverable ?? "unknown";
|
|
125846
|
+
const base = BASE_POLICIES2[archetype] ?? BASE_POLICIES2.unknown;
|
|
125847
|
+
const ORDER = ["low", "medium", "high"];
|
|
125848
|
+
const qualityCriticalityFloor = opts.qualityCriticality ? ORDER.indexOf(opts.qualityCriticality) > ORDER.indexOf(base.qualityCriticalityFloor) ? opts.qualityCriticality : base.qualityCriticalityFloor : base.qualityCriticalityFloor;
|
|
125849
|
+
return {
|
|
125850
|
+
...base,
|
|
125851
|
+
qualityCriticalityFloor
|
|
125852
|
+
};
|
|
125853
|
+
}
|
|
125854
|
+
|
|
125855
|
+
// lib/services/pipeline.ts
|
|
125856
|
+
function hasMeaningfulCompletionEvidence(summary, prUrl, createdTasks) {
|
|
125857
|
+
if (summary && summary.trim().length >= 12) return true;
|
|
125858
|
+
if (prUrl && prUrl.trim().length > 0) return true;
|
|
125859
|
+
if (createdTasks && createdTasks.length > 0) return true;
|
|
125860
|
+
return false;
|
|
125861
|
+
}
|
|
125862
|
+
function hasArchetypeSpecificEvidence(deliverable, summary, prUrl, createdTasks) {
|
|
125863
|
+
if (prUrl && prUrl.trim().length > 0) return true;
|
|
125864
|
+
if (createdTasks && createdTasks.length > 0) return true;
|
|
125865
|
+
const text = (summary ?? "").toLowerCase();
|
|
125866
|
+
if (!text) return deliverable === "unknown";
|
|
125867
|
+
if (deliverable === "cli") return /cli|command|help|exit|argv|flag|terminal/.test(text);
|
|
125868
|
+
if (deliverable === "api") return /api|endpoint|route|request|response|validation|handler/.test(text);
|
|
125869
|
+
if (deliverable === "web-ui") return /ui|screen|page|render|flow|loading|error|form/.test(text);
|
|
125870
|
+
if (deliverable === "hybrid") return /api|ui|flow|integration|endpoint|screen/.test(text);
|
|
125871
|
+
return true;
|
|
125872
|
+
}
|
|
125873
|
+
function buildFinalAcceptanceSummary(opts) {
|
|
125874
|
+
const openConcerns = [];
|
|
125875
|
+
if (!opts.hasEvidence) openConcerns.push("missing_meaningful_completion_evidence");
|
|
125876
|
+
if (opts.deliverable === "unknown") openConcerns.push("deliverable_inference_is_unknown");
|
|
125877
|
+
if (!opts.closeRequested) openConcerns.push("completion_did_not_request_close");
|
|
125878
|
+
if (opts.qualityCriticality === "high") openConcerns.push("quality_criticality_high_requires_conservative_review");
|
|
125879
|
+
if (opts.riskProfile.length > 0) openConcerns.push(...opts.riskProfile.map((risk) => `risk:${risk}`));
|
|
125880
|
+
return {
|
|
125881
|
+
deliverable: opts.deliverable,
|
|
125882
|
+
fidelityStatus: opts.deliverable === "unknown" ? "warn" : "pass",
|
|
125883
|
+
qualityGateStatus: opts.qualityPolicy.requiredChecks.length > 0 ? "pass" : "warn",
|
|
125884
|
+
evidenceStatus: opts.hasEvidence ? "pass" : "fail",
|
|
125885
|
+
donePolicyStatus: opts.donePolicy.requiredArtifacts.length > 0 ? "pass" : "warn",
|
|
125886
|
+
openConcerns
|
|
125887
|
+
};
|
|
125888
|
+
}
|
|
125889
|
+
function resolvePipelineDeliverable(project) {
|
|
125890
|
+
const stack = project?.environment?.stack ?? project?.stack ?? null;
|
|
125891
|
+
if (stack === "nextjs") return "web-ui";
|
|
125892
|
+
if (stack === "node-cli" || stack === "python-cli") return "cli";
|
|
125893
|
+
if (stack === "express" || stack === "fastapi" || stack === "flask" || stack === "django") return "api";
|
|
125894
|
+
const nameText = `${project?.name ?? ""} ${project?.slug ?? ""} ${project?.repo ?? ""}`.toLowerCase();
|
|
125895
|
+
if (/\bcli\b|command/.test(nameText)) return "cli";
|
|
125896
|
+
if (/\bapi\b|service|backend/.test(nameText)) return "api";
|
|
125897
|
+
if (/dashboard|frontend|web|ui/.test(nameText)) return "web-ui";
|
|
125898
|
+
return "unknown";
|
|
125899
|
+
}
|
|
125900
|
+
function buildHumanAcceptanceSummary(summary) {
|
|
125901
|
+
const badges = [];
|
|
125902
|
+
badges.push(`deliverable=${summary.deliverable}`);
|
|
125903
|
+
badges.push(`evidence=${summary.evidenceStatus}`);
|
|
125904
|
+
if (summary.fidelityStatus !== "pass") badges.push(`fidelity=${summary.fidelityStatus}`);
|
|
125905
|
+
if (summary.qualityGateStatus !== "pass") badges.push(`quality=${summary.qualityGateStatus}`);
|
|
125906
|
+
if (summary.donePolicyStatus !== "pass") badges.push(`done=${summary.donePolicyStatus}`);
|
|
125907
|
+
const concerns = summary.openConcerns.filter(
|
|
125908
|
+
(c) => c === "quality_criticality_high_requires_conservative_review" || c.startsWith("risk:") || c === "deliverable_inference_is_unknown"
|
|
125909
|
+
);
|
|
125910
|
+
if (concerns.length > 0) {
|
|
125911
|
+
badges.push(`concerns=${concerns.slice(0, 3).join(",")}`);
|
|
125912
|
+
}
|
|
125913
|
+
return badges.join(" | ");
|
|
125914
|
+
}
|
|
125707
125915
|
async function persistMergedArtifact(opts) {
|
|
125708
125916
|
const { workspaceDir, projectSlug, issueId, issueRuntime, prUrl, headSha } = opts;
|
|
125709
125917
|
const prNumber = issueRuntime?.currentPrNumber ?? null;
|
|
@@ -125782,7 +125990,74 @@ async function executeCompletion(opts) {
|
|
|
125782
125990
|
let mergedArtifactHeadSha;
|
|
125783
125991
|
const project = await loadProjectBySlug(workspaceDir, projectSlug);
|
|
125784
125992
|
const issueRuntime = project ? getIssueRuntime(project, issueId) : void 0;
|
|
125993
|
+
const deliverable = resolvePipelineDeliverable(project);
|
|
125994
|
+
const qualityCriticality = issueRuntime?.qualityCriticality ?? "medium";
|
|
125995
|
+
const qualityPolicy = resolveQualityGatePolicy({ deliverable, qualityCriticality });
|
|
125996
|
+
const donePolicy = resolveDonePolicy({ deliverable, qualityCriticality: qualityPolicy.qualityCriticalityFloor });
|
|
125785
125997
|
const prSelector = issueRuntime?.currentPrNumber ? { prNumber: issueRuntime.currentPrNumber } : void 0;
|
|
125998
|
+
const closeRequested = completionRule.actions.includes(Action.CLOSE_ISSUE);
|
|
125999
|
+
const hasEvidence = hasMeaningfulCompletionEvidence(effectiveSummary, prUrl, createdTasks);
|
|
126000
|
+
const hasArchetypeEvidence = hasArchetypeSpecificEvidence(deliverable, effectiveSummary, prUrl, createdTasks);
|
|
126001
|
+
const finalAcceptance = buildFinalAcceptanceSummary({
|
|
126002
|
+
deliverable,
|
|
126003
|
+
qualityPolicy,
|
|
126004
|
+
donePolicy,
|
|
126005
|
+
hasEvidence,
|
|
126006
|
+
closeRequested,
|
|
126007
|
+
qualityCriticality,
|
|
126008
|
+
riskProfile: issueRuntime?.riskProfile ?? []
|
|
126009
|
+
});
|
|
126010
|
+
await log(workspaceDir, "completion_policy_snapshot", {
|
|
126011
|
+
project: projectName,
|
|
126012
|
+
issue: issueId,
|
|
126013
|
+
role,
|
|
126014
|
+
deliverable,
|
|
126015
|
+
qualityGateChecks: qualityPolicy.requiredChecks,
|
|
126016
|
+
requiredEvidence: qualityPolicy.requiredEvidence,
|
|
126017
|
+
doneArtifacts: donePolicy.requiredArtifacts,
|
|
126018
|
+
doneEvidence: donePolicy.requiredEvidence,
|
|
126019
|
+
qualityCriticality,
|
|
126020
|
+
qualityCriticalityFloor: qualityPolicy.qualityCriticalityFloor,
|
|
126021
|
+
riskProfile: issueRuntime?.riskProfile ?? [],
|
|
126022
|
+
finalAcceptance
|
|
126023
|
+
}).catch(() => {
|
|
126024
|
+
});
|
|
126025
|
+
await log(workspaceDir, "final_acceptance_summary", {
|
|
126026
|
+
project: projectName,
|
|
126027
|
+
issue: issueId,
|
|
126028
|
+
role,
|
|
126029
|
+
...finalAcceptance
|
|
126030
|
+
}).catch(() => {
|
|
126031
|
+
});
|
|
126032
|
+
if (closeRequested && !hasEvidence) {
|
|
126033
|
+
await log(workspaceDir, "completion_policy_block", {
|
|
126034
|
+
project: projectName,
|
|
126035
|
+
issue: issueId,
|
|
126036
|
+
role,
|
|
126037
|
+
result,
|
|
126038
|
+
reason: "missing_completion_evidence",
|
|
126039
|
+
deliverable,
|
|
126040
|
+
requiredEvidence: donePolicy.requiredEvidence
|
|
126041
|
+
}).catch(() => {
|
|
126042
|
+
});
|
|
126043
|
+
throw new Error(
|
|
126044
|
+
`Refusing to complete issue #${issueId} without meaningful completion evidence for ${deliverable}. Provide a substantive summary, PR evidence, or created-task evidence before close.`
|
|
126045
|
+
);
|
|
126046
|
+
}
|
|
126047
|
+
if (closeRequested && !hasArchetypeEvidence) {
|
|
126048
|
+
await log(workspaceDir, "completion_policy_block", {
|
|
126049
|
+
project: projectName,
|
|
126050
|
+
issue: issueId,
|
|
126051
|
+
role,
|
|
126052
|
+
result,
|
|
126053
|
+
reason: "missing_archetype_specific_evidence",
|
|
126054
|
+
deliverable
|
|
126055
|
+
}).catch(() => {
|
|
126056
|
+
});
|
|
126057
|
+
throw new Error(
|
|
126058
|
+
`Refusing to complete issue #${issueId} because the final summary lacks ${deliverable}-specific evidence. Describe the relevant ${deliverable} behavior more concretely or attach PR/task evidence.`
|
|
126059
|
+
);
|
|
126060
|
+
}
|
|
125786
126061
|
const shouldBlockMergeBeforeTest = completionRule.actions.includes(Action.MERGE_PR) && role === "reviewer" && (workflow.testPolicy ?? TestPolicy.SKIP) === TestPolicy.AGENT;
|
|
125787
126062
|
if (shouldBlockMergeBeforeTest) {
|
|
125788
126063
|
completionRule = {
|
|
@@ -125930,6 +126205,7 @@ async function executeCompletion(opts) {
|
|
|
125930
126205
|
}
|
|
125931
126206
|
const notifyConfig = getNotificationConfig(pluginConfig);
|
|
125932
126207
|
await provider.transitionLabel(issueId, completionRule.from, completionRule.to);
|
|
126208
|
+
const acceptanceSummary = buildHumanAcceptanceSummary(finalAcceptance);
|
|
125933
126209
|
for (const action of completionRule.actions) {
|
|
125934
126210
|
switch (action) {
|
|
125935
126211
|
case Action.CLOSE_ISSUE:
|
|
@@ -125951,7 +126227,8 @@ async function executeCompletion(opts) {
|
|
|
125951
126227
|
issueId,
|
|
125952
126228
|
issueUrl: issue2.web_url,
|
|
125953
126229
|
issueTitle: issue2.title,
|
|
125954
|
-
prUrl
|
|
126230
|
+
prUrl,
|
|
126231
|
+
acceptanceSummary
|
|
125955
126232
|
},
|
|
125956
126233
|
{
|
|
125957
126234
|
workspaceDir,
|
|
@@ -126015,6 +126292,7 @@ async function executeCompletion(opts) {
|
|
|
126015
126292
|
name: workerName,
|
|
126016
126293
|
result: effectiveResult,
|
|
126017
126294
|
summary: effectiveSummary,
|
|
126295
|
+
acceptanceSummary,
|
|
126018
126296
|
nextState,
|
|
126019
126297
|
prUrl,
|
|
126020
126298
|
createdTasks,
|
|
@@ -126132,7 +126410,8 @@ ${nextState}.`;
|
|
|
126132
126410
|
prUrl,
|
|
126133
126411
|
issueUrl: issue2.web_url,
|
|
126134
126412
|
issueClosed: completionRule.actions.includes(Action.CLOSE_ISSUE),
|
|
126135
|
-
issueReopened: completionRule.actions.includes(Action.REOPEN_ISSUE)
|
|
126413
|
+
issueReopened: completionRule.actions.includes(Action.REOPEN_ISSUE),
|
|
126414
|
+
finalAcceptance
|
|
126136
126415
|
};
|
|
126137
126416
|
}
|
|
126138
126417
|
function describeStateByLabel(workflow, label) {
|
|
@@ -129055,6 +129334,7 @@ async function dispatchTask(opts) {
|
|
|
129055
129334
|
pluginConfig,
|
|
129056
129335
|
runtime
|
|
129057
129336
|
} = opts;
|
|
129337
|
+
const triggerSource = opts.triggerSource ?? "unknown";
|
|
129058
129338
|
const slotIndex = opts.slotIndex ?? 0;
|
|
129059
129339
|
const rc = opts.runCommand;
|
|
129060
129340
|
const resolvedConfig = await loadConfig(workspaceDir, project.slug);
|
|
@@ -129101,6 +129381,8 @@ async function dispatchTask(opts) {
|
|
|
129101
129381
|
role,
|
|
129102
129382
|
level,
|
|
129103
129383
|
fromLabel,
|
|
129384
|
+
triggerSource,
|
|
129385
|
+
dispatchSemantic: "feedback_redispatch",
|
|
129104
129386
|
sessionKey: existingSessionKey,
|
|
129105
129387
|
reason: "developer_feedback_cycle_requires_fresh_context"
|
|
129106
129388
|
}).catch(() => {
|
|
@@ -129147,6 +129429,8 @@ async function dispatchTask(opts) {
|
|
|
129147
129429
|
issue: issueId,
|
|
129148
129430
|
role,
|
|
129149
129431
|
level,
|
|
129432
|
+
triggerSource,
|
|
129433
|
+
dispatchSemantic: feedbackFreshSession ? "feedback_redispatch" : "fresh_dispatch",
|
|
129150
129434
|
sessionKey,
|
|
129151
129435
|
reason: feedbackFreshSession ? "gateway_session_alive_without_local_slot_tracking_after_feedback_reset" : "gateway_session_alive_without_local_slot_tracking"
|
|
129152
129436
|
}).catch(() => {
|
|
@@ -129444,7 +129728,8 @@ async function dispatchTask(opts) {
|
|
|
129444
129728
|
originalModel: effectiveModel.downgraded ? resolvedModel : void 0,
|
|
129445
129729
|
effectiveModel: effectiveModel.downgraded ? model : void 0,
|
|
129446
129730
|
dispatchCycleId,
|
|
129447
|
-
dispatchRunId: sendResult.runId
|
|
129731
|
+
dispatchRunId: sendResult.runId,
|
|
129732
|
+
triggerSource
|
|
129448
129733
|
},
|
|
129449
129734
|
{
|
|
129450
129735
|
workspaceDir,
|
|
@@ -129478,6 +129763,8 @@ async function dispatchTask(opts) {
|
|
|
129478
129763
|
modelAvailability: effectiveModel.availableModels,
|
|
129479
129764
|
workflowMeta: resolvedConfig.workflowMeta,
|
|
129480
129765
|
sessionAction,
|
|
129766
|
+
dispatchSemantic,
|
|
129767
|
+
triggerSource,
|
|
129481
129768
|
sessionKey,
|
|
129482
129769
|
fromLabel,
|
|
129483
129770
|
toLabel,
|
|
@@ -129514,7 +129801,9 @@ async function auditDispatch(workspaceDir, opts) {
|
|
|
129514
129801
|
role: opts.role,
|
|
129515
129802
|
level: opts.level,
|
|
129516
129803
|
sessionAction: opts.sessionAction,
|
|
129804
|
+
dispatchSemantic: opts.dispatchSemantic,
|
|
129517
129805
|
sessionKey: opts.sessionKey,
|
|
129806
|
+
triggerSource: opts.triggerSource,
|
|
129518
129807
|
sessionLabel: opts.sessionLabel,
|
|
129519
129808
|
sessionLabelFull: opts.sessionLabelFull,
|
|
129520
129809
|
labelTransition: `${opts.fromLabel} \u2192 ${opts.toLabel}`
|
|
@@ -129525,6 +129814,8 @@ async function auditDispatch(workspaceDir, opts) {
|
|
|
129525
129814
|
role: opts.role,
|
|
129526
129815
|
level: opts.level,
|
|
129527
129816
|
sessionAction: opts.sessionAction,
|
|
129817
|
+
dispatchSemantic: opts.dispatchSemantic,
|
|
129818
|
+
triggerSource: opts.triggerSource,
|
|
129528
129819
|
sessionKey: opts.sessionKey,
|
|
129529
129820
|
sessionLabel: opts.sessionLabel,
|
|
129530
129821
|
sessionLabelFull: opts.sessionLabelFull
|
|
@@ -141231,6 +141522,8 @@ var triageStep = {
|
|
|
141231
141522
|
childReadyForDispatch: decision.targetState === "To Do",
|
|
141232
141523
|
parallelizable: child.draft.parallelizable,
|
|
141233
141524
|
recommendedLevel: child.draft.recommendedLevel,
|
|
141525
|
+
qualityCriticality: decision.qualityCriticality,
|
|
141526
|
+
riskProfile: decision.riskProfile,
|
|
141234
141527
|
decompositionMode: "none",
|
|
141235
141528
|
decompositionStatus: null
|
|
141236
141529
|
}).catch(() => {
|
|
@@ -141239,6 +141532,8 @@ var triageStep = {
|
|
|
141239
141532
|
await updateIssueRuntime(ctx.workspaceDir, projectSlug, issue2.number, {
|
|
141240
141533
|
childIssueIds: createdChildIssueNumbers,
|
|
141241
141534
|
maxParallelChildren: computeMaxParallelChildren(decompositionDrafts),
|
|
141535
|
+
qualityCriticality: decision.qualityCriticality,
|
|
141536
|
+
riskProfile: decision.riskProfile,
|
|
141242
141537
|
decompositionMode: "parent_child",
|
|
141243
141538
|
decompositionStatus: "active"
|
|
141244
141539
|
}).catch(() => {
|
|
@@ -141356,6 +141651,14 @@ var triageStep = {
|
|
|
141356
141651
|
}).catch(() => {
|
|
141357
141652
|
});
|
|
141358
141653
|
}
|
|
141654
|
+
const resolvedProjectSlug = payload.metadata?.project_slug ?? payload.scaffold?.project_slug ?? null;
|
|
141655
|
+
if (resolvedProjectSlug) {
|
|
141656
|
+
await updateIssueRuntime(ctx.workspaceDir, resolvedProjectSlug, issue2.number, {
|
|
141657
|
+
qualityCriticality: decision.qualityCriticality,
|
|
141658
|
+
riskProfile: decision.riskProfile
|
|
141659
|
+
}).catch(() => {
|
|
141660
|
+
});
|
|
141661
|
+
}
|
|
141359
141662
|
return {
|
|
141360
141663
|
...payload,
|
|
141361
141664
|
step: "triage",
|
|
@@ -141843,6 +142146,7 @@ async function projectTick(opts) {
|
|
|
141843
142146
|
instanceName,
|
|
141844
142147
|
runCommand
|
|
141845
142148
|
} = opts;
|
|
142149
|
+
const triggerSource = opts.triggerSource ?? (targetRole ? "followup_tick" : "heartbeat_periodic");
|
|
141846
142150
|
const ensureEnvironment = opts.ensureEnvironmentReady ?? ensureEnvironmentReady;
|
|
141847
142151
|
const dispatch = opts.dispatchTask ?? dispatchTask;
|
|
141848
142152
|
const project = getProject(await readProjects(workspaceDir), projectSlug);
|
|
@@ -142122,7 +142426,8 @@ async function projectTick(opts) {
|
|
|
142122
142426
|
runtime,
|
|
142123
142427
|
slotIndex: freeSlot,
|
|
142124
142428
|
instanceName,
|
|
142125
|
-
runCommand
|
|
142429
|
+
runCommand,
|
|
142430
|
+
triggerSource
|
|
142126
142431
|
});
|
|
142127
142432
|
pickups.push({
|
|
142128
142433
|
project: project.name,
|
|
@@ -142133,6 +142438,7 @@ async function projectTick(opts) {
|
|
|
142133
142438
|
role,
|
|
142134
142439
|
level: dr.level,
|
|
142135
142440
|
sessionAction: dr.sessionAction,
|
|
142441
|
+
triggerSource,
|
|
142136
142442
|
announcement: dr.announcement
|
|
142137
142443
|
});
|
|
142138
142444
|
await recordDispatch(workspaceDir, dispatchId).catch(() => {
|
|
@@ -142557,6 +142863,19 @@ function buildTopicDeepLink(chatId, topicId) {
|
|
|
142557
142863
|
function buildDmAck(projectName, topicLink, language = "pt") {
|
|
142558
142864
|
return BOOTSTRAP_MESSAGES.registered[language](projectName, topicLink);
|
|
142559
142865
|
}
|
|
142866
|
+
function buildBootstrapRuntimeStatusNote(session, language = "pt") {
|
|
142867
|
+
const issueUrl = session.issueUrl ?? null;
|
|
142868
|
+
const triageErrors = session.triageErrors ?? [];
|
|
142869
|
+
if (session.triageReadyForDispatch === false && issueUrl) {
|
|
142870
|
+
return language === "en" ? `\u26A0\uFE0F Automatic dispatch is currently paused. GitHub issue created: ${issueUrl}
|
|
142871
|
+
Triage blockers: ${triageErrors.length > 0 ? triageErrors.join(", ") : "unspecified"}` : `\u26A0\uFE0F O dispatch autom\xE1tico est\xE1 pausado no momento. Issue criada no GitHub: ${issueUrl}
|
|
142872
|
+
Bloqueios do triage: ${triageErrors.length > 0 ? triageErrors.join(", ") : "n\xE3o especificado"}`;
|
|
142873
|
+
}
|
|
142874
|
+
if (issueUrl) {
|
|
142875
|
+
return language === "en" ? `\u2705 GitHub issue created: ${issueUrl}` : `\u2705 Issue criada no GitHub: ${issueUrl}`;
|
|
142876
|
+
}
|
|
142877
|
+
return null;
|
|
142878
|
+
}
|
|
142560
142879
|
function buildTopicKickoff(projectName, idea, language = "pt") {
|
|
142561
142880
|
const header = language === "en" ? `\u{1F9F1} Project automatically registered by Fabrica.
|
|
142562
142881
|
Project: ${projectName}
|
|
@@ -143278,7 +143597,12 @@ async function completeRegisteredBootstrap(ctx, workspaceDir, session) {
|
|
|
143278
143597
|
const resolvedProjectName = session.projectName ?? session.projectSlug ?? "projeto";
|
|
143279
143598
|
const sessionLang = session.language ?? "pt";
|
|
143280
143599
|
if (!session.topicKickoffSentAt) {
|
|
143281
|
-
|
|
143600
|
+
let kickoffMessage = buildTopicKickoff(resolvedProjectName, session.rawIdea, sessionLang);
|
|
143601
|
+
const statusNote = buildBootstrapRuntimeStatusNote(session, sessionLang);
|
|
143602
|
+
if (statusNote) kickoffMessage += `
|
|
143603
|
+
|
|
143604
|
+
${statusNote}`;
|
|
143605
|
+
await sendTelegramText(ctx, projectChannelId, kickoffMessage, {
|
|
143282
143606
|
accountId: projectRoute.accountId ?? void 0,
|
|
143283
143607
|
messageThreadId
|
|
143284
143608
|
});
|
|
@@ -143311,7 +143635,8 @@ async function completeRegisteredBootstrap(ctx, workspaceDir, session) {
|
|
|
143311
143635
|
pluginConfig: ctx.pluginConfig,
|
|
143312
143636
|
runtime: ctx.runtime,
|
|
143313
143637
|
runCommand: ctx.runCommand,
|
|
143314
|
-
maxPickups: 1
|
|
143638
|
+
maxPickups: 1,
|
|
143639
|
+
triggerSource: "bootstrap_immediate_tick"
|
|
143315
143640
|
});
|
|
143316
143641
|
} catch (error48) {
|
|
143317
143642
|
logBootstrapWarning(ctx, `[telegram-bootstrap] immediate projectTick failed: ${error48 instanceof Error ? error48.message : String(error48)}`);
|
|
@@ -143336,10 +143661,15 @@ async function completeRegisteredBootstrap(ctx, workspaceDir, session) {
|
|
|
143336
143661
|
}
|
|
143337
143662
|
session = ownership.session;
|
|
143338
143663
|
if (!session.completionAckSentAt) {
|
|
143664
|
+
let dmAck = buildDmAck(resolvedProjectName, buildTopicDeepLink(String(projectChannelId), messageThreadId), sessionLang);
|
|
143665
|
+
const statusNote = buildBootstrapRuntimeStatusNote(session, sessionLang);
|
|
143666
|
+
if (statusNote) dmAck += `
|
|
143667
|
+
|
|
143668
|
+
${statusNote}`;
|
|
143339
143669
|
await sendTelegramText(
|
|
143340
143670
|
ctx,
|
|
143341
143671
|
session.conversationId,
|
|
143342
|
-
|
|
143672
|
+
dmAck
|
|
143343
143673
|
);
|
|
143344
143674
|
session = await persistDispatchProgress(workspaceDir, session, {
|
|
143345
143675
|
projectRoute,
|
|
@@ -145881,7 +146211,8 @@ async function tick(opts) {
|
|
|
145881
146211
|
maxPickups: remaining,
|
|
145882
146212
|
instanceName,
|
|
145883
146213
|
runtime,
|
|
145884
|
-
runCommand
|
|
146214
|
+
runCommand,
|
|
146215
|
+
triggerSource: "heartbeat_periodic"
|
|
145885
146216
|
});
|
|
145886
146217
|
result.totalPickups += tickResult.pickups.length;
|
|
145887
146218
|
result.totalSkipped += tickResult.skipped.length;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mestreyoda/fabrica",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.25",
|
|
4
4
|
"description": "Autonomous software engineering pipeline for OpenClaw. Turns ideas into deployed code via intake, dispatch, review, test, and merge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|