From bdecec3e4444c47c25f25f9528ed75f11ed92104 Mon Sep 17 00:00:00 2001 From: "Deepak Jain (DJ)" Date: Mon, 31 Aug 2026 19:34:35 -0700 Subject: [PATCH 1/6] feat: make workflow deadline configurable Signed-off-by: Deepak Jain --- .env.example | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.env.example b/.env.example index 1515f29e..9983e991 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,10 @@ ENV=dev # options: dev|s # Options: openai | anthropic | anthropic_proxy | nv_build SKILLSPECTOR_PROVIDER= +# Aggregate deadline for one complete scan workflow. Defaults to 60 seconds; +# raise this for large skills that require a complete rather than partial scan. +# SKILLSPECTOR_MAX_WORKFLOW_SECONDS=120 + # Provider credentials — set the one matching SKILLSPECTOR_PROVIDER (or # leave SKILLSPECTOR_PROVIDER unset and set NVIDIA_INFERENCE_KEY for the # default nv_build path). From 6e73680a78f4c889a45b861ae1a520e0cfc42c62 Mon Sep 17 00:00:00 2001 From: "Deepak Jain (DJ)" Date: Mon, 31 Aug 2026 19:34:39 -0700 Subject: [PATCH 2/6] feat: make workflow deadline configurable Signed-off-by: Deepak Jain --- docs/ANALYSIS_RESOURCE_BOUNDS.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/ANALYSIS_RESOURCE_BOUNDS.md b/docs/ANALYSIS_RESOURCE_BOUNDS.md index ea556630..4cceb8aa 100644 --- a/docs/ANALYSIS_RESOURCE_BOUNDS.md +++ b/docs/ANALYSIS_RESOURCE_BOUNDS.md @@ -211,3 +211,13 @@ When relevant analysis is incomplete: A low score or zero findings must not be interpreted as complete coverage when `analysis_completeness.is_complete` is false. + + +## Configuring the aggregate workflow deadline + +The scanner limits one complete workflow to 60 seconds by default. Set +`SKILLSPECTOR_MAX_WORKFLOW_SECONDS` to a positive finite number of seconds to +raise that aggregate deadline for large skills. The setting applies to direct, +CLI, recursive, and multi-skill scans; byte and artifact ceilings remain in +effect. Invalid, zero, negative, infinite, or NaN values safely keep the +60-second default. From daa30b2bf61d0226eac1d4721595dc2ec7292fc5 Mon Sep 17 00:00:00 2001 From: "Deepak Jain (DJ)" Date: Mon, 31 Aug 2026 19:34:43 -0700 Subject: [PATCH 3/6] feat: make workflow deadline configurable Signed-off-by: Deepak Jain --- src/skillspector/state.py | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/skillspector/state.py b/src/skillspector/state.py index c5f80b6e..3c6ede4c 100644 --- a/src/skillspector/state.py +++ b/src/skillspector/state.py @@ -17,7 +17,9 @@ from __future__ import annotations +import math import operator +import os from dataclasses import dataclass, field from time import monotonic from typing import Annotated, NotRequired @@ -36,9 +38,40 @@ LedgerRecordType, ledger_event, ) +from skillspector.logging_config import get_logger from skillspector.models import Finding -MAX_WORKFLOW_SECONDS = 60.0 +logger = get_logger(__name__) + +DEFAULT_MAX_WORKFLOW_SECONDS = 60.0 + + +def _workflow_max_seconds_from_environment(value: str | None) -> float: + """Return a positive finite workflow deadline or the safe default.""" + if value is None: + return DEFAULT_MAX_WORKFLOW_SECONDS + try: + seconds = float(value) + except ValueError: + logger.warning( + "SKILLSPECTOR_MAX_WORKFLOW_SECONDS=%r is not numeric, using default %.1fs", + value, + DEFAULT_MAX_WORKFLOW_SECONDS, + ) + return DEFAULT_MAX_WORKFLOW_SECONDS + if not math.isfinite(seconds) or seconds <= 0: + logger.warning( + "SKILLSPECTOR_MAX_WORKFLOW_SECONDS=%r must be finite and positive, using default %.1fs", + value, + DEFAULT_MAX_WORKFLOW_SECONDS, + ) + return DEFAULT_MAX_WORKFLOW_SECONDS + return seconds + + +MAX_WORKFLOW_SECONDS = _workflow_max_seconds_from_environment( + os.environ.get("SKILLSPECTOR_MAX_WORKFLOW_SECONDS") +) MAX_WORKFLOW_BYTES = 64 * 1024 * 1024 MAX_WORKFLOW_ARTIFACTS = 10_000 MAX_WORKFLOW_LIMITATION_RECORDS = 256 From 0f75be0d4dc27664ffe20ce02e5c64f8359d75ed Mon Sep 17 00:00:00 2001 From: "Deepak Jain (DJ)" Date: Mon, 31 Aug 2026 19:34:47 -0700 Subject: [PATCH 4/6] feat: make workflow deadline configurable Signed-off-by: Deepak Jain --- src/skillspector/cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/skillspector/cli.py b/src/skillspector/cli.py index 838f838c..43ff51f0 100644 --- a/src/skillspector/cli.py +++ b/src/skillspector/cli.py @@ -56,7 +56,7 @@ from skillspector.multi_skill import MultiSkillDetectionResult, SkillDirectory, detect_skills from skillspector.nodes.report import report from skillspector.sarif_models import SARIF_SCHEMA_URI, validate_sarif_report -from skillspector.state import MAX_WORKFLOW_BYTES +from skillspector.state import MAX_WORKFLOW_BYTES, MAX_WORKFLOW_SECONDS from skillspector.suppression import ( Baseline, build_baseline_dict, @@ -100,7 +100,7 @@ def _ensure_utf8_streams() -> None: _TRANSITIVE_MAX_TARGETS = 32 _TRANSITIVE_MAX_BYTES = 10 * 1024 * 1024 -_TRANSITIVE_MAX_SECONDS = 60.0 +_TRANSITIVE_MAX_SECONDS = MAX_WORKFLOW_SECONDS _TRANSITIVE_MAX_ARTIFACTS = 10_000 _TRANSITIVE_MAX_FINDINGS = 10_000 _TRANSITIVE_MAX_COMPONENTS = 10_000 From 48be872c572921f60c6b59bb7a460ea7b89c972e Mon Sep 17 00:00:00 2001 From: "Deepak Jain (DJ)" Date: Mon, 31 Aug 2026 19:34:51 -0700 Subject: [PATCH 5/6] feat: make workflow deadline configurable Signed-off-by: Deepak Jain --- tests/nodes/test_build_context.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/nodes/test_build_context.py b/tests/nodes/test_build_context.py index f841d2bd..a9590e1a 100644 --- a/tests/nodes/test_build_context.py +++ b/tests/nodes/test_build_context.py @@ -38,11 +38,13 @@ from skillspector.providers.openai import OpenAIProvider from skillspector.python_ast import ParsedPythonFile, get_python_ast from skillspector.state import ( + DEFAULT_MAX_WORKFLOW_SECONDS, MAX_WORKFLOW_ARTIFACTS, MAX_WORKFLOW_BYTES, MAX_WORKFLOW_SECONDS, SkillspectorState, WorkflowResourceBudget, + _workflow_max_seconds_from_environment, ) _OMS_FIXTURE = Path(__file__).parents[1] / "fixtures" / "oms" / "mcore-split-pr.skill.oms.sig" @@ -188,6 +190,24 @@ def test_build_context_starts_and_returns_default_graph_wide_budget(tmp_path: Pa assert budget.scanned_artifacts == 1 +@pytest.mark.parametrize( + ("value", "expected"), + [ + (None, DEFAULT_MAX_WORKFLOW_SECONDS), + ("120", 120.0), + ("0.5", 0.5), + ("0", DEFAULT_MAX_WORKFLOW_SECONDS), + ("-1", DEFAULT_MAX_WORKFLOW_SECONDS), + ("nan", DEFAULT_MAX_WORKFLOW_SECONDS), + ("not-a-number", DEFAULT_MAX_WORKFLOW_SECONDS), + ], +) +def test_workflow_budget_seconds_environment_parsing( + value: str | None, expected: float +) -> None: + assert _workflow_max_seconds_from_environment(value) == expected + + def test_build_context_reuses_supplied_stricter_transitive_budget(tmp_path: Path) -> None: from skillspector.cli import _TransitiveBudget, _TransitiveTraversalState From 0b1d86319407b61032961668266ded9397d99fc7 Mon Sep 17 00:00:00 2001 From: Deepak Jain Date: Mon, 31 Aug 2026 20:20:13 -0700 Subject: [PATCH 6/6] test: format workflow deadline coverage Signed-off-by: Deepak Jain --- tests/nodes/test_build_context.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/nodes/test_build_context.py b/tests/nodes/test_build_context.py index a9590e1a..d7748035 100644 --- a/tests/nodes/test_build_context.py +++ b/tests/nodes/test_build_context.py @@ -202,9 +202,7 @@ def test_build_context_starts_and_returns_default_graph_wide_budget(tmp_path: Pa ("not-a-number", DEFAULT_MAX_WORKFLOW_SECONDS), ], ) -def test_workflow_budget_seconds_environment_parsing( - value: str | None, expected: float -) -> None: +def test_workflow_budget_seconds_environment_parsing(value: str | None, expected: float) -> None: assert _workflow_max_seconds_from_environment(value) == expected