@lsctech/polaris 0.4.9 → 0.5.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.
Files changed (42) hide show
  1. package/dist/autoresearch/gates.js +47 -0
  2. package/dist/autoresearch/proposal.js +183 -1
  3. package/dist/autoresearch/score.js +276 -0
  4. package/dist/cli/init.js +17 -0
  5. package/dist/cluster-state/store.js +70 -1
  6. package/dist/config/defaults.js +25 -0
  7. package/dist/config/validator.js +390 -0
  8. package/dist/finalize/artifact-policy.js +3 -1
  9. package/dist/finalize/index.js +73 -17
  10. package/dist/finalize/linear.js +45 -0
  11. package/dist/finalize/run-report.js +68 -12
  12. package/dist/loop/adapters/agent-subtask.js +19 -0
  13. package/dist/loop/adapters/cli-subtask-bridge.js +0 -1
  14. package/dist/loop/adapters/terminal-cli.js +194 -14
  15. package/dist/loop/checkpoint.js +40 -0
  16. package/dist/loop/dispatch-boundary.js +29 -0
  17. package/dist/loop/dispatch.js +193 -22
  18. package/dist/loop/orphan-recovery.js +56 -0
  19. package/dist/loop/parent.js +197 -22
  20. package/dist/loop/router/engine.js +229 -0
  21. package/dist/loop/router/index.js +5 -0
  22. package/dist/loop/router/types.js +2 -0
  23. package/dist/loop/worker-packet.js +16 -0
  24. package/dist/qc/artifacts.js +117 -0
  25. package/dist/qc/attribution.js +266 -0
  26. package/dist/qc/autofix.js +77 -0
  27. package/dist/qc/index.js +30 -0
  28. package/dist/qc/orchestration.js +150 -0
  29. package/dist/qc/policy.js +70 -0
  30. package/dist/qc/provider.js +28 -0
  31. package/dist/qc/providers/coderabbit.js +214 -0
  32. package/dist/qc/providers/index.js +5 -0
  33. package/dist/qc/registry.js +16 -0
  34. package/dist/qc/routing.js +55 -0
  35. package/dist/qc/runner.js +137 -0
  36. package/dist/qc/schemas.js +110 -0
  37. package/dist/qc/security-category.js +11 -0
  38. package/dist/qc/severity.js +78 -0
  39. package/dist/qc/triggers.js +92 -0
  40. package/dist/qc/types.js +9 -0
  41. package/dist/runtime/scheduling/child-selector.js +81 -0
  42. package/package.json +1 -1
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SEVERITY_ORDER = exports.DEFAULT_SEVERITY_MAPPING = void 0;
4
+ exports.normalizeSeverity = normalizeSeverity;
5
+ exports.compareSeverity = compareSeverity;
6
+ exports.maxSeverity = maxSeverity;
7
+ /**
8
+ * Built-in severity label mappings for common QC providers.
9
+ * Provider-specific configs can override these.
10
+ */
11
+ exports.DEFAULT_SEVERITY_MAPPING = {
12
+ // Common explicit labels
13
+ critical: "critical",
14
+ severe: "critical",
15
+ blocker: "critical",
16
+ high: "high",
17
+ major: "high",
18
+ error: "high",
19
+ medium: "medium",
20
+ moderate: "medium",
21
+ warning: "medium",
22
+ low: "low",
23
+ minor: "low",
24
+ info: "info",
25
+ informational: "info",
26
+ note: "info",
27
+ suggestion: "info",
28
+ // CodeRabbit-style labels
29
+ "needs-action": "high",
30
+ "needs-review": "medium",
31
+ nitpick: "low",
32
+ };
33
+ /**
34
+ * Normalize a provider severity label into a Polaris severity level.
35
+ * Unknown or empty labels fall back to "info" so that nothing is silently lost.
36
+ */
37
+ function normalizeSeverity(label, mapping) {
38
+ const raw = (label ?? "").toString().trim().toLowerCase();
39
+ if (!raw) {
40
+ return "info";
41
+ }
42
+ const combined = { ...exports.DEFAULT_SEVERITY_MAPPING, ...mapping };
43
+ if (Object.hasOwn(combined, raw)) {
44
+ return combined[raw];
45
+ }
46
+ // Substring fallbacks for noisy provider labels
47
+ if (raw.includes("critical") || raw.includes("severe") || raw.includes("blocker")) {
48
+ return "critical";
49
+ }
50
+ if (raw.includes("high") || raw.includes("major") || raw.includes("error")) {
51
+ return "high";
52
+ }
53
+ if (raw.includes("medium") || raw.includes("moderate") || raw.includes("warning")) {
54
+ return "medium";
55
+ }
56
+ if (raw.includes("low") || raw.includes("minor") || raw.includes("nitpick")) {
57
+ return "low";
58
+ }
59
+ if (raw.includes("info") || raw.includes("note") || raw.includes("suggestion")) {
60
+ return "info";
61
+ }
62
+ return "info";
63
+ }
64
+ /** Ordered severity levels from least to most severe. */
65
+ exports.SEVERITY_ORDER = ["info", "low", "medium", "high", "critical"];
66
+ /**
67
+ * Compare two severities. Returns negative if a is less severe than b,
68
+ * positive if more severe, or zero if equal.
69
+ */
70
+ function compareSeverity(a, b) {
71
+ return exports.SEVERITY_ORDER.indexOf(a) - exports.SEVERITY_ORDER.indexOf(b);
72
+ }
73
+ /**
74
+ * Return the more severe of two severity levels.
75
+ */
76
+ function maxSeverity(a, b) {
77
+ return compareSeverity(a, b) >= 0 ? a : b;
78
+ }
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ /**
3
+ * QC trigger selection logic.
4
+ *
5
+ * Decides which lifecycle trigger a configured provider participates in, and
6
+ * whether child-level QC should be selected for a given dispatch.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.effectiveProviderTrigger = effectiveProviderTrigger;
10
+ exports.activeProvidersForTrigger = activeProvidersForTrigger;
11
+ exports.isHighRiskScope = isHighRiskScope;
12
+ exports.isChildQcSelected = isChildQcSelected;
13
+ exports.selectChildQcTrigger = selectChildQcTrigger;
14
+ /** High-risk scope patterns that make a child eligible for child-level QC. */
15
+ const HIGH_RISK_SCOPE_PATTERNS = [
16
+ /src\/auth/i,
17
+ /src\/security/i,
18
+ /src\/payments/i,
19
+ /src\/crypto/i,
20
+ /src\/db/i,
21
+ /src\/database/i,
22
+ /src\/api/i,
23
+ ];
24
+ /**
25
+ * Derive the effective trigger for a provider.
26
+ * Falls back to a sensible default derived from the provider mode, then the
27
+ * cluster-wide default trigger.
28
+ */
29
+ function effectiveProviderTrigger(provider, defaultTrigger) {
30
+ if (provider.trigger)
31
+ return provider.trigger;
32
+ switch (provider.mode) {
33
+ case "pr":
34
+ return "pr";
35
+ case "local":
36
+ return "completed-cluster";
37
+ case "metrics-import":
38
+ return "completed-cluster";
39
+ default:
40
+ return defaultTrigger;
41
+ }
42
+ }
43
+ /**
44
+ * Return configured providers that participate in the given trigger.
45
+ */
46
+ function activeProvidersForTrigger(config, trigger) {
47
+ if (!config?.enabled)
48
+ return [];
49
+ const defaultTrigger = config.defaultTrigger ?? "completed-cluster";
50
+ return Object.entries(config.providers ?? {}).filter(([, provider]) => {
51
+ return effectiveProviderTrigger(provider, defaultTrigger) === trigger;
52
+ });
53
+ }
54
+ /**
55
+ * Returns true when a scope touches files generally considered high-risk.
56
+ */
57
+ function isHighRiskScope(scope) {
58
+ return scope.some((pattern) => HIGH_RISK_SCOPE_PATTERNS.some((re) => re.test(pattern)));
59
+ }
60
+ /**
61
+ * Decide whether child-level QC should be selected for a child.
62
+ *
63
+ * Child-level QC is intentionally opt-in. It is selected only when:
64
+ * - QC is enabled and at least one provider is configured for the child trigger.
65
+ * - A route policy explicitly enables child-level QC for this route, OR
66
+ * - The child is explicitly tagged with "qc-child", OR
67
+ * - The child's scope contains high-risk paths (auth, security, payments, etc.).
68
+ */
69
+ function isChildQcSelected(config, childId, allowedScope, labels, routeName) {
70
+ if (!config?.enabled)
71
+ return false;
72
+ const childProviders = activeProvidersForTrigger(config, "child");
73
+ if (childProviders.length === 0)
74
+ return false;
75
+ const routePolicy = routeName ? config.routes?.[routeName] : undefined;
76
+ if (routePolicy?.childLevel)
77
+ return true;
78
+ if (labels?.includes("qc-child"))
79
+ return true;
80
+ if (isHighRiskScope(allowedScope))
81
+ return true;
82
+ return false;
83
+ }
84
+ /**
85
+ * Select the QC trigger for a child dispatch.
86
+ * Returns "child" when child-level QC is selected, otherwise null.
87
+ */
88
+ function selectChildQcTrigger(config, childId, allowedScope, labels, routeName) {
89
+ return isChildQcSelected(config, childId, allowedScope, labels, routeName)
90
+ ? "child"
91
+ : null;
92
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ /**
3
+ * Provider-neutral Quality Control types.
4
+ *
5
+ * These types mirror the normalized finding schema defined in the QC architecture
6
+ * spec. They intentionally do not reference tracker-specific or provider-specific
7
+ * shapes so that Polaris can support multiple QC backends without vendor lock-in.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.selectNextChild = selectNextChild;
4
+ exports.selectChildSlotClaims = selectChildSlotClaims;
4
5
  /**
5
6
  * Select the next child to execute.
6
7
  *
@@ -14,3 +15,83 @@ function selectNextChild(state) {
14
15
  return null;
15
16
  return [...state.open_children].sort()[0] ?? null;
16
17
  }
18
+ function countActiveSlotsByProvider(claims) {
19
+ const activeSlotsByProvider = {};
20
+ for (const claim of claims) {
21
+ if (!claim.provider)
22
+ continue;
23
+ activeSlotsByProvider[claim.provider] = (activeSlotsByProvider[claim.provider] ?? 0) + 1;
24
+ }
25
+ return activeSlotsByProvider;
26
+ }
27
+ function selectChildSlotClaims(args) {
28
+ const now = args.now ?? new Date();
29
+ const nowMs = now.getTime();
30
+ const openSet = new Set(args.open_children);
31
+ const completedSet = new Set(args.completed_children);
32
+ const expired_claims = [];
33
+ const retainedClaims = args.existing_claims.filter((claim) => {
34
+ const expiresAt = new Date(claim.expires_at).getTime();
35
+ const notExpired = Number.isFinite(expiresAt) && expiresAt > nowMs;
36
+ const stillOpen = openSet.has(claim.child_id);
37
+ if (!notExpired || !stillOpen) {
38
+ expired_claims.push(claim.child_id);
39
+ return false;
40
+ }
41
+ return true;
42
+ });
43
+ const rejected_children = {};
44
+ const claimedChildren = new Set(retainedClaims.map((claim) => claim.child_id));
45
+ const availableSlots = Math.max(0, args.max_concurrent - retainedClaims.length);
46
+ const newClaims = [];
47
+ if (availableSlots > 0) {
48
+ for (const childId of args.open_children) {
49
+ if (newClaims.length >= availableSlots)
50
+ break;
51
+ if (childId === args.active_child)
52
+ continue;
53
+ if (claimedChildren.has(childId))
54
+ continue;
55
+ const dependencies = args.get_dependencies(childId);
56
+ const unmetDependencies = dependencies.filter((dependency) => openSet.has(dependency) && !completedSet.has(dependency));
57
+ if (unmetDependencies.length > 0) {
58
+ rejected_children[childId] = "blocked-dependency";
59
+ continue;
60
+ }
61
+ const decision = args.decide_route({
62
+ childId,
63
+ activeSlotsByProvider: countActiveSlotsByProvider([...retainedClaims, ...newClaims]),
64
+ });
65
+ const hardIneligibleReasons = new Set([
66
+ "role-disabled",
67
+ "not-in-policy",
68
+ "quota-exhausted",
69
+ "trust-too-low",
70
+ "capability-mismatch",
71
+ "cost-policy",
72
+ "no-slot",
73
+ ]);
74
+ const routingExhausted = decision.exhaustedReason !== undefined &&
75
+ (hardIneligibleReasons.has(decision.exhaustedReason) || decision.mode !== "delegated");
76
+ const canClaim = decision.selectedProvider !== undefined || decision.mode === "delegated";
77
+ if (routingExhausted || !canClaim) {
78
+ rejected_children[childId] = "router-ineligible";
79
+ continue;
80
+ }
81
+ newClaims.push({
82
+ child_id: childId,
83
+ provider: decision.selectedProvider ?? null,
84
+ claimed_at: now.toISOString(),
85
+ expires_at: new Date(nowMs + args.claim_ttl_ms).toISOString(),
86
+ selection_reason: decision.selectionReason,
87
+ });
88
+ }
89
+ }
90
+ const slot_claims = [...retainedClaims, ...newClaims];
91
+ return {
92
+ selected_child: slot_claims[0]?.child_id ?? null,
93
+ slot_claims,
94
+ rejected_children,
95
+ expired_claims,
96
+ };
97
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lsctech/polaris",
3
- "version": "0.4.9",
3
+ "version": "0.5.2",
4
4
  "description": "Polaris — standalone taskchain orchestration framework for governed AI agent workflows",
5
5
  "bin": {
6
6
  "polaris": "dist/cli/index.js",