@williambeto/ai-workflow 2.8.0 → 2.8.2
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/CHANGELOG.md +41 -0
- package/bin/ai-workflow.js +193 -49
- package/bin/ai-workflow.js.map +1 -1
- package/{chunk-LI76KI7C.js → chunk-4FI5ODAM.js} +259 -296
- package/{chunk-Y4RLP6ZM.js → chunk-GLYX3HEY.js} +353 -39
- package/{chunk-XW747GIG.js → chunk-H7GIKXFO.js} +339 -14
- package/core/index.d.ts +20 -1
- package/core/index.js +2 -2
- package/dist-assets/agents/atlas.md +25 -6
- package/dist-assets/commands/atlas.md +7 -2
- package/dist-assets/docs/QUICKSTART.md +10 -1
- package/dist-assets/docs/compatibility/provider-usage.md +16 -1
- package/dist-assets/docs/compatibility/runtime-matrix.md +13 -2
- package/dist-assets/docs/policies/06-FINAL_EVIDENCE_CONTRACT.md +11 -1
- package/dist-assets/docs/policies/SKILLS_COMMON_GOVERNANCE.md +16 -0
- package/dist-assets/schemas/README.md +4 -0
- package/dist-assets/schemas/evidence.schema.json +60 -1
- package/package.json +1 -1
- package/{validate-IEHLAVZ6.js → validate-DDKN6ORS.js} +3 -3
|
@@ -112,17 +112,17 @@ var StackDetector = class {
|
|
|
112
112
|
// src/core/validation/delivery-decision-engine.ts
|
|
113
113
|
import fs2 from "fs/promises";
|
|
114
114
|
import path2 from "path";
|
|
115
|
-
function
|
|
115
|
+
function parseDeliveryRequest(request = "") {
|
|
116
116
|
const req = request.toLowerCase();
|
|
117
117
|
let targetStack = null;
|
|
118
118
|
if (/\b(vue|nuxt|quasar)\b/.test(req)) targetStack = "vue";
|
|
119
|
-
else if (/\b(
|
|
119
|
+
else if (/\breact\b|\bnext(?:\.js|js)\b|\bnext\s+js\b/.test(req)) targetStack = "react";
|
|
120
120
|
else if (/\b(angular|svelte)\b/.test(req)) targetStack = "svelte-or-angular";
|
|
121
|
-
else if (/\b(laravel|django|rails|express|nest|fastify)\b/.test(req)) {
|
|
121
|
+
else if (/\b(laravel|django|rails|express|nest|fastify|flask)\b/.test(req)) {
|
|
122
122
|
if (/\blaravel\b/.test(req)) targetStack = "laravel";
|
|
123
123
|
else targetStack = "backend";
|
|
124
124
|
} else if (/\b(wordpress|wp)\b/.test(req)) targetStack = "wordpress";
|
|
125
|
-
else if (/\b(cli|command|bin)\b/.test(req)) targetStack = "cli";
|
|
125
|
+
else if (/\b(cli|command[- ]line|executable|bin)\b/.test(req) || /\b(add|create|implement|build|scaffold)\b.{0,40}\bcommand\b/.test(req)) targetStack = "cli";
|
|
126
126
|
let intent = "implementation";
|
|
127
127
|
if (/\b(document|doc|readme)\b/.test(req) || /\b(create|add|write|update|edit)\b.*\b[\w.-]+\.md\b/.test(req)) intent = "docs";
|
|
128
128
|
else if (/\b(audit|security|inspect|dependencies)\b/.test(req)) intent = "audit";
|
|
@@ -281,7 +281,7 @@ var DeliveryDecisionEngine = class {
|
|
|
281
281
|
const isLaravel = ctx.isLaravel || false;
|
|
282
282
|
const isCli = ctx.isCli || false;
|
|
283
283
|
const usesJsFramework = ctx.usesJsFramework || null;
|
|
284
|
-
const parsed =
|
|
284
|
+
const parsed = parseDeliveryRequest(userRequest);
|
|
285
285
|
const targetStack = parsed.targetStack;
|
|
286
286
|
const intent = parsed.intent;
|
|
287
287
|
let decision = "IMPLEMENT_IN_EXISTING_CONTEXT";
|
|
@@ -289,6 +289,7 @@ var DeliveryDecisionEngine = class {
|
|
|
289
289
|
let forbiddenSubstitutes = ["success-without-validation"];
|
|
290
290
|
let validationRequired = ["build-or-equivalent"];
|
|
291
291
|
let approvalRequired = false;
|
|
292
|
+
let reason = null;
|
|
292
293
|
if (intent === "audit") {
|
|
293
294
|
decision = "READ_ONLY_AUDIT";
|
|
294
295
|
allowedArtifactShape = "audit-report";
|
|
@@ -321,13 +322,22 @@ var DeliveryDecisionEngine = class {
|
|
|
321
322
|
validationRequired.push("dependency-install-or-verification", "runtime-smoke-or-equivalent");
|
|
322
323
|
}
|
|
323
324
|
} else {
|
|
324
|
-
const
|
|
325
|
+
const requestedFrontend = targetStack === "vue" || targetStack === "react" || targetStack === "svelte-or-angular";
|
|
326
|
+
const detectedFrontend = detectedStacks.find((stack) => ["react", "next", "vue", "nuxt", "svelte", "@angular/core", "quasar"].includes(stack));
|
|
327
|
+
const frontendMatches = targetStack === "react" && ["react", "next"].includes(detectedFrontend || "") || targetStack === "vue" && ["vue", "nuxt", "quasar"].includes(detectedFrontend || "") || targetStack === "svelte-or-angular" && ["svelte", "@angular/core"].includes(detectedFrontend || "");
|
|
328
|
+
const hasConflict = requestedFrontend && Boolean(detectedFrontend) && !frontendMatches || targetStack === "laravel" && !detectedStacks.includes("laravel") && detectedStacks.length > 0 || targetStack === "wordpress" && !detectedStacks.includes("wordpress") && detectedStacks.length > 0;
|
|
325
329
|
if (hasConflict) {
|
|
326
330
|
decision = "BLOCK_STACK_CONFLICT";
|
|
331
|
+
reason = `Requested stack '${targetStack}' conflicts with the active project context (${detectedStacks.join(", ") || "unknown"}).`;
|
|
332
|
+
} else if (requestedFrontend && !frontendMatches) {
|
|
333
|
+
decision = "BLOCK_UNSUPPORTED_CONTEXT";
|
|
334
|
+
reason = `Requested stack '${targetStack}' is not present in the active project context.`;
|
|
327
335
|
} else if (targetStack === "laravel" && !detectedStacks.includes("laravel")) {
|
|
328
336
|
decision = "BLOCK_UNSUPPORTED_CONTEXT";
|
|
337
|
+
reason = "Laravel was requested, but the active project is not a Laravel project.";
|
|
329
338
|
} else if (targetStack === "wordpress" && !detectedStacks.includes("wordpress")) {
|
|
330
339
|
decision = "BLOCK_UNSUPPORTED_CONTEXT";
|
|
340
|
+
reason = "WordPress was requested, but the active project is not a WordPress project.";
|
|
331
341
|
} else {
|
|
332
342
|
if (targetStack === "vue" || targetStack === "react") {
|
|
333
343
|
decision = "IMPLEMENT_IN_EXISTING_CONTEXT";
|
|
@@ -342,6 +352,7 @@ var DeliveryDecisionEngine = class {
|
|
|
342
352
|
}
|
|
343
353
|
if (userRequest && userRequest.trim().length < 3) {
|
|
344
354
|
decision = "BLOCK_UNCLEAR_SCOPE";
|
|
355
|
+
reason = "The request is too short to establish a safe delivery scope.";
|
|
345
356
|
}
|
|
346
357
|
return {
|
|
347
358
|
intent: intent === "implementation" ? "implementation" : intent,
|
|
@@ -354,7 +365,9 @@ var DeliveryDecisionEngine = class {
|
|
|
354
365
|
allowedArtifactShape,
|
|
355
366
|
forbiddenSubstitutes,
|
|
356
367
|
validationRequired,
|
|
357
|
-
approvalRequired: explicitApprovals.length > 0
|
|
368
|
+
approvalRequired: explicitApprovals.length > 0,
|
|
369
|
+
deliveryOutcome: decision.startsWith("BLOCK_") ? "NEEDS_CLARIFICATION" : null,
|
|
370
|
+
reason
|
|
358
371
|
};
|
|
359
372
|
}
|
|
360
373
|
};
|
|
@@ -531,11 +544,12 @@ var ArtifactFidelityGate = class {
|
|
|
531
544
|
violations.push("Release/deploy task proceeds without explicit approval token mismatch.");
|
|
532
545
|
}
|
|
533
546
|
}
|
|
534
|
-
const
|
|
535
|
-
const
|
|
536
|
-
const
|
|
537
|
-
const
|
|
538
|
-
const
|
|
547
|
+
const requestedStack = Object.hasOwn(deliveryDecision, "requestedStack") ? deliveryDecision.requestedStack : parseDeliveryRequest(userRequest).targetStack;
|
|
548
|
+
const reqReact = requestedStack === "react";
|
|
549
|
+
const reqVue = requestedStack === "vue";
|
|
550
|
+
const reqBackend = requestedStack === "backend" || requestedStack === "laravel";
|
|
551
|
+
const reqWp = requestedStack === "wordpress";
|
|
552
|
+
const reqCli = requestedStack === "cli";
|
|
539
553
|
if (reqReact && this.deliveryClass === "standalone-web" && !/prototype/.test(req)) {
|
|
540
554
|
violations.push("React framework page requested, but a standalone HTML/CSS substitute mismatch was delivered instead.");
|
|
541
555
|
}
|
|
@@ -746,10 +760,321 @@ async function hasWpPluginHeader(cwd, file) {
|
|
|
746
760
|
}
|
|
747
761
|
}
|
|
748
762
|
|
|
763
|
+
// src/core/request-router.ts
|
|
764
|
+
import path4 from "path";
|
|
765
|
+
import fs4 from "fs";
|
|
766
|
+
var RequestRouter = class {
|
|
767
|
+
cwd;
|
|
768
|
+
constructor({ cwd = process.cwd() } = {}) {
|
|
769
|
+
this.cwd = cwd;
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Understands and classifies a natural language request.
|
|
773
|
+
*/
|
|
774
|
+
understand(request) {
|
|
775
|
+
const text = String(request).trim();
|
|
776
|
+
if (!text) {
|
|
777
|
+
throw new Error("Cannot classify an empty request.");
|
|
778
|
+
}
|
|
779
|
+
const { operationType, requestedCapability } = this.extractCapability(text);
|
|
780
|
+
const mutationIntent = this.determineMutationIntent(text, operationType);
|
|
781
|
+
const deliveryMode = this.determineDeliveryMode(text, mutationIntent);
|
|
782
|
+
const capabilityRoles = this.determineCapabilityRoles(text);
|
|
783
|
+
const riskAssessment = this.determineRiskAssessment(text, mutationIntent);
|
|
784
|
+
const riskLevel = riskAssessment.riskLevel;
|
|
785
|
+
const { safetyConstraints, requiredEvidence } = this.determineSafetyAndEvidence(text, mutationIntent);
|
|
786
|
+
return {
|
|
787
|
+
rawRequest: text,
|
|
788
|
+
taskGoal: `Perform ${operationType} operation under ${mutationIntent} intent`,
|
|
789
|
+
requestedActor: void 0,
|
|
790
|
+
invalidActor: void 0,
|
|
791
|
+
requestedCapability,
|
|
792
|
+
capabilityRoles,
|
|
793
|
+
operationType,
|
|
794
|
+
mutationIntent,
|
|
795
|
+
deliveryMode,
|
|
796
|
+
riskLevel,
|
|
797
|
+
requiredEvidence,
|
|
798
|
+
safetyConstraints,
|
|
799
|
+
specPolicy: riskAssessment.specPolicy,
|
|
800
|
+
matchedSignals: riskAssessment.matchedSignals,
|
|
801
|
+
riskDrivers: riskAssessment.riskDrivers
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
extractCapability(text) {
|
|
805
|
+
let operationType = "explain";
|
|
806
|
+
let requestedCapability = void 0;
|
|
807
|
+
const capabilityMappings = [
|
|
808
|
+
{ op: "discover", pattern: /\b(discover|inspect|map|scan|structure|architecture|repo|search|find)\b/i },
|
|
809
|
+
{ op: "validate", pattern: /\b(validate|audit|review|check|verify|test)\b/i },
|
|
810
|
+
{ op: "implement", pattern: /\b(implement|implementa|build|create|crie|cria|add|develop|write|change|modify|atualiza|atualizar|refatora|refatorar)\b/i },
|
|
811
|
+
{ op: "remediate", pattern: /\b(remediate|remedia|heal|healing)\b/i },
|
|
812
|
+
{ op: "fix", pattern: /\b(fix|repair|correct|corrige|corrigir|solve)\b/i },
|
|
813
|
+
{ op: "release", pattern: /\b(release|tag|prepare release|prepara(?:r)?\s+(?:a\s+)?release)\b/i },
|
|
814
|
+
{ op: "deploy", pattern: /\b(deploy|publish|push|produção|producao)\b/i }
|
|
815
|
+
];
|
|
816
|
+
for (const mapping of capabilityMappings) {
|
|
817
|
+
const match = text.match(mapping.pattern);
|
|
818
|
+
if (match) {
|
|
819
|
+
operationType = mapping.op;
|
|
820
|
+
requestedCapability = match[1];
|
|
821
|
+
break;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
return { operationType, requestedCapability };
|
|
825
|
+
}
|
|
826
|
+
determineMutationIntent(text, operationType) {
|
|
827
|
+
const writePatterns = /\b(implement|implementa|build|fix|corrige|write|change|modify|create|crie|cria|add|refactor|refatora|refatorar|update|atualiza|atualizar)\b/i;
|
|
828
|
+
const readOnlyPatterns = /\b(analyze|check|inspect|review|find|search|show|list|read|view|verify|validate|discover|map)\b/i;
|
|
829
|
+
let mutationIntent = "write";
|
|
830
|
+
if (/\b(read-only|readonly)\b/i.test(text)) {
|
|
831
|
+
mutationIntent = "readonly";
|
|
832
|
+
} else if (writePatterns.test(text)) {
|
|
833
|
+
mutationIntent = "write";
|
|
834
|
+
} else if (readOnlyPatterns.test(text) || ["discover", "validate"].includes(operationType)) {
|
|
835
|
+
mutationIntent = "readonly";
|
|
836
|
+
}
|
|
837
|
+
if (/\b(publish|release|deploy|produção|producao|push to npm|push to remote)\b/i.test(text)) {
|
|
838
|
+
mutationIntent = "publish";
|
|
839
|
+
}
|
|
840
|
+
return mutationIntent;
|
|
841
|
+
}
|
|
842
|
+
determineDeliveryMode(text, mutationIntent) {
|
|
843
|
+
if (mutationIntent === "readonly") return "answer-only";
|
|
844
|
+
const explicitAnswerOnlyMarker = /\b(answer only|preview only|example only|snippet only|apenas (?:responda|mostre|um exemplo|um trecho)|somente (?:resposta|preview|exemplo|trecho))\b/i;
|
|
845
|
+
const workspaceTarget = "(?:files?|workspace|repository|repo|code(?:base)?|project)";
|
|
846
|
+
const portugueseWorkspaceTarget = "(?:arquivos?|workspace|reposit[o\xF3]rio|repo|c[o\xF3]digo|codebase|projeto)";
|
|
847
|
+
const explicitNoWorkspaceEdit = new RegExp(
|
|
848
|
+
`\\b(?:(?:without\\s+(?:editing|changing|modifying)|(?:do not|don't)\\s+(?:edit|change|modify))\\s+(?:the\\s+)?${workspaceTarget}|no\\s+${workspaceTarget}\\s+changes?|(?:sem\\s+(?:editar|alterar|modificar)|n[a\xE3]o\\s+(?:editar|alterar|modificar|edite|altere|modifique))\\s+(?:(?:os?|as?)\\s+)?${portugueseWorkspaceTarget})\\b`,
|
|
849
|
+
"i"
|
|
850
|
+
);
|
|
851
|
+
const standaloneNoEdit = /(?:\b(?:without\s+(?:editing|changing|modifying)|(?:do not|don't)\s+(?:edit|change|modify)|sem\s+(?:editar|alterar|modificar)|n[aã]o\s+(?:editar|alterar|modificar|edite|altere|modifique)))\s*[.!?]?\s*$/i;
|
|
852
|
+
return explicitAnswerOnlyMarker.test(text) || explicitNoWorkspaceEdit.test(text) || standaloneNoEdit.test(text) ? "answer-only" : "workspace";
|
|
853
|
+
}
|
|
854
|
+
determineCapabilityRoles(text) {
|
|
855
|
+
const roles = [];
|
|
856
|
+
const add = (role, pattern) => {
|
|
857
|
+
if (pattern.test(text)) roles.push(role);
|
|
858
|
+
};
|
|
859
|
+
add("frontend-development", /\b(frontend|react|vue|angular|svelte|component|interface|ui|css|html)\b/i);
|
|
860
|
+
add("backend-development", /\b(backend|api|endpoint|server|database|laravel|django|express|nest|fastify)\b/i);
|
|
861
|
+
add("documentation", /\b(document|documentation|docs?|readme|markdown)\b/i);
|
|
862
|
+
add("qa-workflow", /\b(test|tests|validate|audit|review|verify|quality)\b/i);
|
|
863
|
+
add("release-workflow", /\b(release|publish|deploy|tag)\b/i);
|
|
864
|
+
return [...new Set(roles)];
|
|
865
|
+
}
|
|
866
|
+
determineRiskAssessment(text, mutationIntent) {
|
|
867
|
+
const matchedSignals = [];
|
|
868
|
+
const add = (signal, pattern) => {
|
|
869
|
+
if (pattern.test(text)) {
|
|
870
|
+
matchedSignals.push(signal);
|
|
871
|
+
return true;
|
|
872
|
+
}
|
|
873
|
+
return false;
|
|
874
|
+
};
|
|
875
|
+
const taskTypeSignals = {
|
|
876
|
+
docsCopy: add("task:docs-copy", /\b(README|docs?|documenta[cç][aã]o|typo|copy|frase|comment|coment[áa]rio)\b/i),
|
|
877
|
+
refactor: add("task:refactor", /\b(refactor|refatora|refatorar|simplificar|mais simples de manter|manuten[cç][aã]o)\b/i),
|
|
878
|
+
noBehaviorChange: add("scope:no-behavior-change", /\b(sem alterar (?:o )?comportamento|without changing behavior|without behaviour change|no behavior change|comportamento existente)\b/i),
|
|
879
|
+
landingPage: add("task:landing-page", /\b(landing page|hotsite|p[áa]gina de venda|p[áa]gina comercial)\b/i),
|
|
880
|
+
dashboard: add("task:dashboard", /\b(dashboard|painel)\b/i),
|
|
881
|
+
bugfix: add("task:bugfix", /\b(bug|fix|corrige|corrigir|erro|falha|n[aã]o envia|não envia)\b/i),
|
|
882
|
+
release: add("task:release", /\b(release|deploy|produ[cç][aã]o|publish|publicar|lan[cç]amento)\b/i)
|
|
883
|
+
};
|
|
884
|
+
const domainRiskSignals = {
|
|
885
|
+
adult: add("domain:adult", /\b(er[óo]tic[oa]s?|adult[oa]s?|sexual|sexo|porn(?:o|ografia)?|18\+)\b/i),
|
|
886
|
+
auth: add("domain:auth", /\b(login|auth|autentica[cç][aã]o|autoriza[cç][aã]o|permiss(?:ão|oes|ões)|admin|administrador|roles?|rbac)\b/i),
|
|
887
|
+
payment: add("domain:payment", /\b(pagamento|checkout|billing|cobran[cç]a|fatura[cç][aã]o|assinatura|cart[aã]o|stripe|paypal)\b/i),
|
|
888
|
+
securityGovernance: add("domain:security-governance", /\b(SECURITY\.md|seguran[cç]a|security policy|pol[ií]tica de seguran[cç]a|governance|governan[cç]a|compliance|branch safety)\b/i)
|
|
889
|
+
};
|
|
890
|
+
const impactSignals = {
|
|
891
|
+
personalData: add("impact:personal-data", /\b(leads?|dados pessoais|personal data|crm|contatos?|contactos?|clientes?|exporta[cç][aã]o|exportar|armazenamento|armazenar|integra[cç][aã]o|integrar)\b/i),
|
|
892
|
+
productionRelease: mutationIntent === "publish" || add("impact:production-release", /\b(produ[cç][aã]o|release|deploy|publish|publicar|npm)\b/i),
|
|
893
|
+
adminPermissions: add("impact:admin-permissions", /\b(admin|administrador|permiss(?:ão|oes|ões)|roles?|rbac)\b/i)
|
|
894
|
+
};
|
|
895
|
+
if (mutationIntent === "publish" && !matchedSignals.includes("impact:production-release")) {
|
|
896
|
+
matchedSignals.push("impact:production-release");
|
|
897
|
+
}
|
|
898
|
+
const scopeSignals = {
|
|
899
|
+
localized: add("scope:localized", /\b(localizado|uma frase|typo|pequeno|small|tiny|simples|README)\b/i),
|
|
900
|
+
broad: add("scope:broad", /\b(plataforma|sistema|completo|premium|end-to-end|amplo|v[aá]rias|m[úu]ltiplas|empresa)\b/i),
|
|
901
|
+
architectural: add("scope:architectural", /\b(deep|architectural|arquitetural|migration|migra[cç][aã]o|major|full|\[deep\])\b/i),
|
|
902
|
+
explicitHigh: add("scope:explicit-high", /\b(high-risk|high risk|alto risco)\b/i),
|
|
903
|
+
explicitRequiredSpec: add("scope:explicit-required-spec", /\b(formal spec|required spec|especifica[cç][aã]o formal)\b/i),
|
|
904
|
+
explicitRequiredEvidence: add("scope:explicit-required-evidence", /\b(required evidence|evidence required|evid[eê]ncia obrigat[óo]ria)\b/i)
|
|
905
|
+
};
|
|
906
|
+
let riskLevel = "medium";
|
|
907
|
+
const riskDrivers = [];
|
|
908
|
+
const highDrivers = [
|
|
909
|
+
["scope:explicit-high", scopeSignals.explicitHigh],
|
|
910
|
+
["scope:architectural", scopeSignals.architectural],
|
|
911
|
+
["domain:adult", domainRiskSignals.adult],
|
|
912
|
+
["domain:auth", domainRiskSignals.auth],
|
|
913
|
+
["domain:payment", domainRiskSignals.payment],
|
|
914
|
+
["domain:security-governance", domainRiskSignals.securityGovernance],
|
|
915
|
+
["impact:production-release", impactSignals.productionRelease],
|
|
916
|
+
["impact:admin-permissions", impactSignals.adminPermissions],
|
|
917
|
+
["impact:personal-data", taskTypeSignals.dashboard && impactSignals.personalData]
|
|
918
|
+
];
|
|
919
|
+
for (const [signal, active] of highDrivers) {
|
|
920
|
+
if (active) {
|
|
921
|
+
riskDrivers.push(signal);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
if (riskDrivers.length > 0) {
|
|
925
|
+
riskLevel = "high";
|
|
926
|
+
} else if (mutationIntent === "readonly") {
|
|
927
|
+
riskLevel = "low";
|
|
928
|
+
riskDrivers.push("intent:readonly");
|
|
929
|
+
} else if (taskTypeSignals.docsCopy && scopeSignals.localized) {
|
|
930
|
+
riskLevel = "low";
|
|
931
|
+
riskDrivers.push("task:docs-copy");
|
|
932
|
+
} else if (taskTypeSignals.refactor || taskTypeSignals.bugfix || taskTypeSignals.landingPage || taskTypeSignals.dashboard) {
|
|
933
|
+
riskLevel = "medium";
|
|
934
|
+
if (taskTypeSignals.refactor) riskDrivers.push("task:refactor");
|
|
935
|
+
if (taskTypeSignals.bugfix) riskDrivers.push("task:bugfix");
|
|
936
|
+
if (taskTypeSignals.landingPage) riskDrivers.push("task:landing-page");
|
|
937
|
+
if (taskTypeSignals.dashboard) riskDrivers.push("task:dashboard");
|
|
938
|
+
}
|
|
939
|
+
const specPolicy = riskLevel === "high" || scopeSignals.explicitRequiredSpec || taskTypeSignals.landingPage && scopeSignals.broad ? "required" : "none";
|
|
940
|
+
return {
|
|
941
|
+
riskLevel,
|
|
942
|
+
specPolicy,
|
|
943
|
+
matchedSignals: [...new Set(matchedSignals)],
|
|
944
|
+
riskDrivers: [...new Set(riskDrivers)]
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
determineSafetyAndEvidence(text, mutationIntent) {
|
|
948
|
+
const safetyConstraints = [];
|
|
949
|
+
const requiredEvidence = [];
|
|
950
|
+
if (mutationIntent === "publish") {
|
|
951
|
+
safetyConstraints.push("Require release gate", "Awaiting user authorization");
|
|
952
|
+
requiredEvidence.push("release-decision", "tarball-hash");
|
|
953
|
+
} else if (mutationIntent === "write") {
|
|
954
|
+
safetyConstraints.push("Require branch gate", "No direct commits to main");
|
|
955
|
+
requiredEvidence.push("tests", "commit-hash");
|
|
956
|
+
} else {
|
|
957
|
+
safetyConstraints.push("No workspace mutations allowed");
|
|
958
|
+
}
|
|
959
|
+
if (/\b(bypass|force|direct)\b/i.test(text)) {
|
|
960
|
+
safetyConstraints.push("Block bypass attempts");
|
|
961
|
+
}
|
|
962
|
+
if (/\b(required evidence|evidence required)\b/i.test(text)) {
|
|
963
|
+
requiredEvidence.push("user-required-evidence");
|
|
964
|
+
}
|
|
965
|
+
if (/\b(formal spec|required spec)\b/i.test(text)) {
|
|
966
|
+
safetyConstraints.push("Require formal specification");
|
|
967
|
+
}
|
|
968
|
+
return { safetyConstraints, requiredEvidence };
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* Decides the routing decision.
|
|
972
|
+
*/
|
|
973
|
+
route(understanding) {
|
|
974
|
+
const { operationType, mutationIntent, rawRequest, deliveryMode, capabilityRoles } = understanding;
|
|
975
|
+
const requestedActor = void 0;
|
|
976
|
+
if (/\b(bypass safety|bypass validation|force push|directly to main|push to remote without gate)\b/i.test(rawRequest)) {
|
|
977
|
+
return {
|
|
978
|
+
requestedActor,
|
|
979
|
+
selectedActor: "Atlas",
|
|
980
|
+
mutationOwner: "ControlPlane",
|
|
981
|
+
capabilityRoles,
|
|
982
|
+
operationType,
|
|
983
|
+
mutationIntent,
|
|
984
|
+
deliveryMode,
|
|
985
|
+
permissionDecision: "blocked",
|
|
986
|
+
reason: "Security block: Attempt to bypass safety or push directly is forbidden.",
|
|
987
|
+
workflowPath: ["Atlas"]
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
let { selectedActor, permissionDecision, reason } = this.evaluateActorPermission(operationType, mutationIntent);
|
|
991
|
+
let workflowPath = ["Atlas"];
|
|
992
|
+
if (selectedActor === "Phoenix") {
|
|
993
|
+
const requestPath = path4.join(this.cwd, ".ai-workflow/remediation-request.json");
|
|
994
|
+
let hasFindings = false;
|
|
995
|
+
if (fs4.existsSync(requestPath)) {
|
|
996
|
+
try {
|
|
997
|
+
const reqData = JSON.parse(fs4.readFileSync(requestPath, "utf8"));
|
|
998
|
+
if (reqData.affectedChecks && reqData.affectedChecks.length > 0) {
|
|
999
|
+
hasFindings = true;
|
|
1000
|
+
}
|
|
1001
|
+
} catch {
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
if (!hasFindings) {
|
|
1005
|
+
permissionDecision = "blocked";
|
|
1006
|
+
reason = "Blocked: Phoenix acts only with concrete findings and bounded remediation.";
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
if (understanding.specPolicy === "required") {
|
|
1010
|
+
workflowPath.push("Nexus", "Orion");
|
|
1011
|
+
}
|
|
1012
|
+
if (selectedActor && !workflowPath.includes(selectedActor)) {
|
|
1013
|
+
workflowPath.push(selectedActor);
|
|
1014
|
+
}
|
|
1015
|
+
const mutationOwner = mutationIntent === "readonly" || deliveryMode === "answer-only" ? null : mutationIntent === "publish" ? "Astra" : selectedActor === "Astra" || selectedActor === "Phoenix" ? selectedActor : "ControlPlane";
|
|
1016
|
+
if (mutationOwner && mutationOwner !== "ControlPlane" && !workflowPath.includes(mutationOwner)) {
|
|
1017
|
+
workflowPath.push(mutationOwner);
|
|
1018
|
+
}
|
|
1019
|
+
if (selectedActor === "Atlas" && ["discover", "validate", "fix", "release", "deploy"].includes(operationType)) {
|
|
1020
|
+
permissionDecision = "blocked";
|
|
1021
|
+
reason = "Atlas self-execution guard: Atlas cannot silently execute specialized actor or capability requests.";
|
|
1022
|
+
}
|
|
1023
|
+
if (mutationOwner === "Astra" && understanding.riskLevel === "high") {
|
|
1024
|
+
workflowPath.push("Sage", "Phoenix", "Sage");
|
|
1025
|
+
}
|
|
1026
|
+
if (understanding.specPolicy === "none") {
|
|
1027
|
+
workflowPath = workflowPath.filter((actor) => {
|
|
1028
|
+
if (actor === "Nexus" && selectedActor !== "Nexus") return false;
|
|
1029
|
+
if (actor === "Orion" && selectedActor !== "Orion") return false;
|
|
1030
|
+
return true;
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
return {
|
|
1034
|
+
requestedActor,
|
|
1035
|
+
selectedActor,
|
|
1036
|
+
mutationOwner,
|
|
1037
|
+
capabilityRoles,
|
|
1038
|
+
operationType,
|
|
1039
|
+
mutationIntent,
|
|
1040
|
+
deliveryMode,
|
|
1041
|
+
permissionDecision,
|
|
1042
|
+
reason,
|
|
1043
|
+
workflowPath
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
evaluateActorPermission(operationType, mutationIntent) {
|
|
1047
|
+
let selectedActor;
|
|
1048
|
+
let permissionDecision = "allowed";
|
|
1049
|
+
let reason = "Routed successfully.";
|
|
1050
|
+
if (mutationIntent === "publish") {
|
|
1051
|
+
selectedActor = "Orion";
|
|
1052
|
+
reason = "Routed to Orion for release planning and approval gates.";
|
|
1053
|
+
} else if (mutationIntent === "write" && operationType !== "remediate") {
|
|
1054
|
+
selectedActor = "Astra";
|
|
1055
|
+
reason = "Routed to Astra because write intent takes mutation ownership precedence.";
|
|
1056
|
+
} else if (operationType === "discover") {
|
|
1057
|
+
selectedActor = "Nexus";
|
|
1058
|
+
} else if (operationType === "validate") {
|
|
1059
|
+
selectedActor = "Sage";
|
|
1060
|
+
} else if (operationType === "remediate") {
|
|
1061
|
+
selectedActor = "Phoenix";
|
|
1062
|
+
} else if (operationType === "implement" || operationType === "fix") {
|
|
1063
|
+
selectedActor = "Astra";
|
|
1064
|
+
} else if (operationType === "release" || operationType === "deploy") {
|
|
1065
|
+
selectedActor = "Orion";
|
|
1066
|
+
} else {
|
|
1067
|
+
selectedActor = mutationIntent === "write" ? "Astra" : "Atlas";
|
|
1068
|
+
}
|
|
1069
|
+
return { selectedActor, permissionDecision, reason };
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
|
|
749
1073
|
export {
|
|
750
1074
|
MergeGate,
|
|
751
1075
|
StackDetector,
|
|
752
1076
|
DeliveryDecisionEngine,
|
|
753
|
-
ArtifactFidelityGate
|
|
1077
|
+
ArtifactFidelityGate,
|
|
1078
|
+
RequestRouter
|
|
754
1079
|
};
|
|
755
|
-
//# sourceMappingURL=chunk-
|
|
1080
|
+
//# sourceMappingURL=chunk-H7GIKXFO.js.map
|
package/core/index.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
type DeliveryMode = "workspace" | "answer-only";
|
|
2
|
+
|
|
3
|
+
type MutationOwner = "Astra" | "Phoenix" | "ControlPlane" | null;
|
|
1
4
|
interface RequestUnderstanding {
|
|
2
5
|
rawRequest: string;
|
|
3
6
|
taskGoal: string;
|
|
4
7
|
requestedActor?: string;
|
|
5
8
|
invalidActor?: string;
|
|
6
9
|
requestedCapability?: string;
|
|
10
|
+
capabilityRoles: string[];
|
|
7
11
|
operationType: string;
|
|
8
12
|
mutationIntent: "readonly" | "write" | "publish";
|
|
13
|
+
deliveryMode: DeliveryMode;
|
|
9
14
|
riskLevel: "low" | "medium" | "high";
|
|
10
15
|
requiredEvidence: string[];
|
|
11
16
|
safetyConstraints: string[];
|
|
@@ -16,8 +21,11 @@ interface RequestUnderstanding {
|
|
|
16
21
|
interface RoutingDecision {
|
|
17
22
|
requestedActor?: string;
|
|
18
23
|
selectedActor?: string;
|
|
24
|
+
mutationOwner: MutationOwner;
|
|
25
|
+
capabilityRoles: string[];
|
|
19
26
|
operationType: string;
|
|
20
27
|
mutationIntent: "readonly" | "write" | "publish";
|
|
28
|
+
deliveryMode: DeliveryMode;
|
|
21
29
|
permissionDecision: "allowed" | "rerouted" | "blocked";
|
|
22
30
|
reason: string;
|
|
23
31
|
workflowPath: string[];
|
|
@@ -176,11 +184,20 @@ interface ExecuteOptions {
|
|
|
176
184
|
readOnly?: boolean;
|
|
177
185
|
fastTrack?: boolean;
|
|
178
186
|
requireActorConfirmation?: boolean;
|
|
187
|
+
orchestratedChild?: boolean;
|
|
188
|
+
}
|
|
189
|
+
interface ObservedCommand {
|
|
190
|
+
command: string;
|
|
191
|
+
tool: string;
|
|
192
|
+
status: "PASS" | "FAIL" | "UNKNOWN";
|
|
193
|
+
exitCode: number | null;
|
|
194
|
+
source: "sdk" | "json-event" | "text-output";
|
|
179
195
|
}
|
|
180
196
|
interface ExecuteResult {
|
|
181
197
|
success: boolean;
|
|
182
198
|
status?: string;
|
|
183
199
|
commandsRun: string[];
|
|
200
|
+
commandEvents?: ObservedCommand[];
|
|
184
201
|
eventCount: number;
|
|
185
202
|
error?: string;
|
|
186
203
|
runtimeAgentRequested?: string | null;
|
|
@@ -205,7 +222,7 @@ declare class OpenCodeAdapter {
|
|
|
205
222
|
/**
|
|
206
223
|
* Runs opencode with a prompt and options.
|
|
207
224
|
*/
|
|
208
|
-
execute(message: string, { agent, model, readOnly, fastTrack, requireActorConfirmation }?: ExecuteOptions): Promise<ExecuteResult>;
|
|
225
|
+
execute(message: string, { agent, model, readOnly, fastTrack, requireActorConfirmation, orchestratedChild }?: ExecuteOptions): Promise<ExecuteResult>;
|
|
209
226
|
}
|
|
210
227
|
|
|
211
228
|
interface DelegationControllerOptions {
|
|
@@ -351,6 +368,8 @@ declare class WorkspaceSnapshot {
|
|
|
351
368
|
});
|
|
352
369
|
isIgnored(relativePath: string): boolean;
|
|
353
370
|
capture(): Promise<WorkspaceSnapshotData>;
|
|
371
|
+
listGitVisibleFiles(): string[] | null;
|
|
372
|
+
scanFiles(relativePaths: string[], filesMap: Record<string, SnapshotFile>): Promise<void>;
|
|
354
373
|
scanDir(dir: string, filesMap: Record<string, SnapshotFile>): Promise<void>;
|
|
355
374
|
calculateHash(snapshot: WorkspaceSnapshotData): string;
|
|
356
375
|
}
|
package/core/index.js
CHANGED
|
@@ -9,10 +9,10 @@ import {
|
|
|
9
9
|
RequestClassifier,
|
|
10
10
|
SpecValidator,
|
|
11
11
|
WorkflowStateMachine
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-4FI5ODAM.js";
|
|
13
13
|
import {
|
|
14
14
|
MergeGate
|
|
15
|
-
} from "../chunk-
|
|
15
|
+
} from "../chunk-H7GIKXFO.js";
|
|
16
16
|
import {
|
|
17
17
|
BranchGate
|
|
18
18
|
} from "../chunk-AINIR25D.js";
|
|
@@ -23,7 +23,7 @@ Atlas must:
|
|
|
23
23
|
* Enforce protected-branch safety before writes.
|
|
24
24
|
* Route to specialists only when useful.
|
|
25
25
|
* Require observed validation and prevent false success.
|
|
26
|
-
*
|
|
26
|
+
* Continue from routing through delivery, validation, and a concise handoff.
|
|
27
27
|
|
|
28
28
|
## Trusted context
|
|
29
29
|
* Repository files
|
|
@@ -40,7 +40,11 @@ Atlas must:
|
|
|
40
40
|
## Constraints
|
|
41
41
|
* **High-Risk Sequence Enforcement**: For high-risk tasks (specPolicy: required), Atlas must strictly enforce that the workflow runs sequentially through `discover`, `spec-create`, `spec-review`, and `plan` phases before any implementation or coding starts. Jumping directly to implementation is forbidden.
|
|
42
42
|
* **Authoritative Routing Integrity**: Atlas must routing-delegate requests strictly according to agent capabilities (e.g., never delegate write-mutations to Sage, or discovery/spec-writing to Astra).
|
|
43
|
-
* **Implementation Delegation Constraint**: Atlas must never
|
|
43
|
+
* **Implementation Delegation Constraint**: Atlas must never modify user-requested workspace files directly. Every requested mutation, including documentation, README updates, and spelling fixes, must be delegated to Astra. Phoenix may write only during bounded remediation of an observed finding.
|
|
44
|
+
* **Workspace Delivery Constraint**: A natural request to create, fix, change, or implement repository behavior has `deliveryMode: workspace` unless the user explicitly requests an answer-only example or preview. Printed code is never workspace delivery.
|
|
45
|
+
* **Mutation Ownership Constraint**: Astra owns user-requested implementation mutations. Phoenix owns only bounded remediation of observed findings. Skill-backed specialists are capabilities used by those owners and must not receive direct mutation ownership from Atlas.
|
|
46
|
+
* **Non-Terminal Routing Constraint**: `decision: ROUTED` confirms only actor selection. Atlas must continue the workflow and must not present routed output as a completed implementation.
|
|
47
|
+
* **Context Compatibility Constraint**: Before implementation, compare an explicitly requested stack or platform with the active repository. Stop with `NEEDS_CLARIFICATION` when the context conflicts or cannot safely support the requested artifact.
|
|
44
48
|
* **Quality Gate Authoritativeness**: Any non-zero exit code or `BLOCKED` status from a safety gate, validation check, or finalizer command must immediately halt the pipeline.
|
|
45
49
|
* Never write on `main`, `master`, or another protected branch.
|
|
46
50
|
* If the protected branch is clean or has only preservable untracked files, create a scoped branch before editing.
|
|
@@ -73,13 +77,16 @@ Atlas must:
|
|
|
73
77
|
|
|
74
78
|
## Operating procedure
|
|
75
79
|
1. classify requests and select the lowest safe proportional policies.
|
|
76
|
-
2.
|
|
80
|
+
2. For workspace delivery, run `git branch --show-current` and `git status --short`, then satisfy the branch gate before any edit.
|
|
81
|
+
3. Delegate to workflow owners:
|
|
77
82
|
* Delegation is proportional, not ceremonial.
|
|
78
|
-
* Use Astra for
|
|
83
|
+
* Use Astra for every user-requested implementation mutation, including small changes.
|
|
84
|
+
* Astra may use skill-backed specialists as bounded capabilities.
|
|
85
|
+
* Atlas must not invoke a skill-backed specialist directly for workspace implementation.
|
|
79
86
|
* Use Sage for independent validation when high risk justifies it.
|
|
80
87
|
* Use Phoenix only for concrete findings that need remediation.
|
|
81
88
|
* Do not claim that an agent acted unless that handoff actually occurred.
|
|
82
|
-
|
|
89
|
+
4. For low or medium risk tasks:
|
|
83
90
|
* Inspect status.
|
|
84
91
|
* Recover branch if needed.
|
|
85
92
|
* Coordinate and delegate any code-source modifications to Astra (Atlas must never execute code implementation directly).
|
|
@@ -92,7 +99,13 @@ Atlas must:
|
|
|
92
99
|
* exit code `0` with `COMPLETED` or `COMPLETED_WITH_NOTES`: Atlas may report that exact public state;
|
|
93
100
|
* non-zero exit code or `BLOCKED`: remediate within the configured bound or report `BLOCKED`;
|
|
94
101
|
* Atlas must never infer, upgrade, or replace the finalizer result from screenshots, manual checks, build output, or its own judgement.
|
|
95
|
-
|
|
102
|
+
5. Resolve the delivery outcome after observed validation:
|
|
103
|
+
* `CHANGED` requires an actual scoped diff and passing proportional validation.
|
|
104
|
+
* `ALREADY_SATISFIED` requires explicit verification and must not manufacture a diff.
|
|
105
|
+
* `READ_ONLY_RESULT` and `PREVIEW_ONLY` must not claim workspace implementation.
|
|
106
|
+
* `NEEDS_CLARIFICATION` and `NOT_DELIVERED` are blocking outcomes.
|
|
107
|
+
* Record `testAction: reuse | extend | add | none` with a concrete reason; do not add prompt-specific tests solely to make a request pass.
|
|
108
|
+
6. For high-risk tasks requiring evidence (evidencePolicy: required), use the full workflow/orchestrator so persisted evidence and independent validation remain available.
|
|
96
109
|
|
|
97
110
|
## Expected output schema
|
|
98
111
|
Must include:
|
|
@@ -107,6 +120,12 @@ workflowPath
|
|
|
107
120
|
reason
|
|
108
121
|
requiredEvidence
|
|
109
122
|
decision: ROUTED | BLOCKED | NEEDS_CLARIFICATION
|
|
123
|
+
deliveryMode: workspace | answer-only
|
|
124
|
+
mutationOwner: Astra | Phoenix | ControlPlane | null
|
|
125
|
+
capabilityRoles
|
|
126
|
+
deliveryOutcome: CHANGED | ALREADY_SATISFIED | READ_ONLY_RESULT | PREVIEW_ONLY | NEEDS_CLARIFICATION | NOT_DELIVERED
|
|
127
|
+
testAction: reuse | extend | add | none
|
|
128
|
+
testReason
|
|
110
129
|
```
|
|
111
130
|
|
|
112
131
|
## Evidence required
|
|
@@ -27,6 +27,9 @@ Route user requests safely, proportionately, and securely according to the permi
|
|
|
27
27
|
- Always route to the correct specialized agent or fail closed.
|
|
28
28
|
- Do not execute code modifications directly.
|
|
29
29
|
- Do not claim routing execution without ledger evidence.
|
|
30
|
+
- Treat `ROUTED` as non-terminal; continue through implementation and validation.
|
|
31
|
+
- Route workspace implementation to Astra, never directly to a skill-backed specialist.
|
|
32
|
+
- Printed code is not a substitute for a requested repository change.
|
|
30
33
|
|
|
31
34
|
## Allowed tools
|
|
32
35
|
- classify request
|
|
@@ -43,7 +46,8 @@ Route user requests safely, proportionately, and securely according to the permi
|
|
|
43
46
|
2. Classify intent (write/readonly/publish) and risk level.
|
|
44
47
|
3. Resolve target agent according to the permission matrix.
|
|
45
48
|
4. Log the routing event to the ledger.
|
|
46
|
-
5. Delegate
|
|
49
|
+
5. Delegate workspace implementation to Astra; pass specialist capabilities as context only.
|
|
50
|
+
6. Continue through proportional validation and resolve the delivery outcome.
|
|
47
51
|
|
|
48
52
|
## Safety gates
|
|
49
53
|
- Enforce branch gate constraints.
|
|
@@ -58,7 +62,8 @@ Output must include:
|
|
|
58
62
|
- Classification metrics
|
|
59
63
|
- Target agent resolved
|
|
60
64
|
- Safety constraints
|
|
61
|
-
- Decision:
|
|
65
|
+
- Decision: ROUTED | BLOCKED | NEEDS_CLARIFICATION
|
|
66
|
+
- Delivery outcome and test action
|
|
62
67
|
|
|
63
68
|
## Evidence required
|
|
64
69
|
- routing ledger event log
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# AI Workflow Kit quickstart
|
|
2
2
|
|
|
3
|
-
To initialize your project, run `npx ai-workflow init --profile=standard` (default) or `npx ai-workflow init --profile=full` (adds examples).
|
|
3
|
+
To initialize your project, run `npx ai-workflow init --profile=standard` (default) or `npx ai-workflow init --profile=full` (adds examples). For enforced workspace delivery, run `npx aw execute "your natural software request"` or use the OpenCode `/run` command. Atlas chooses the lowest safe proportional policies and the closest workflow profile.
|
|
4
|
+
|
|
5
|
+
Selecting Atlas directly uses the same ownership contract: Atlas routes product
|
|
6
|
+
mutations to Astra and may use specialists only as capabilities. The CLI path is
|
|
7
|
+
the authoritative control plane for programmatic gates and persisted evidence.
|
|
4
8
|
|
|
5
9
|
## Proportional Policies
|
|
6
10
|
|
|
@@ -20,6 +24,11 @@ Write-capable work never runs on `main` or `master`. Existing relevant tests/bui
|
|
|
20
24
|
- `COMPLETED_WITH_NOTES`
|
|
21
25
|
- `BLOCKED`
|
|
22
26
|
|
|
27
|
+
The structured delivery outcome distinguishes `CHANGED`,
|
|
28
|
+
`ALREADY_SATISFIED`, `READ_ONLY_RESULT`, `PREVIEW_ONLY`,
|
|
29
|
+
`NEEDS_CLARIFICATION`, and `NOT_DELIVERED`. A routing decision alone is never a
|
|
30
|
+
completed implementation.
|
|
31
|
+
|
|
23
32
|
Workflow documents are optional unless they provide lasting value or the specification policy requires them.
|
|
24
33
|
|
|
25
34
|
## Visual & Accessibility Evidence Requirements
|