diff --git a/.Jules/palette.md b/.Jules/palette.md index 159662d06c..9864e9e356 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -1,3 +1,5 @@ -## 2024-05-20 - Repository without UI Codebase -**Learning:** This repository is a GitHub organization profile consisting entirely of Markdown documentation and static assets, and does not contain an active UI or frontend application codebase. -**Action:** Since there is no UI, no UX enhancements can be applied. Aborting UX enhancements and PR creation as per instructions. +## 2026-09-29 - Product UI boundary confirmed + +**Learning:** This repository owns the ContextualWisdomLab GitHub Actions control plane, including production CI scripts, tests, workflows, and documentation. It does not own an interactive product UI or frontend application. + +**Action:** Do not invent product UI work in this repository. Improve the control-plane artifacts here and route reusable product UI work to its canonical product owner. diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml new file mode 100644 index 0000000000..5acde60225 --- /dev/null +++ b/.github/actionlint.yaml @@ -0,0 +1,8 @@ +# Source validation only; labels do not attest runtime isolation. +self-hosted-runner: + labels: + - cwlab-ci-isolated + - cwlab-control + - cwl-hostile-workload + - selinux + - k-csap-isolated diff --git a/.github/actions/noema-review/two_phase.py b/.github/actions/noema-review/two_phase.py index 2815d7a050..d5da7b8270 100755 --- a/.github/actions/noema-review/two_phase.py +++ b/.github/actions/noema-review/two_phase.py @@ -156,6 +156,7 @@ def prepare_verdict(repo: str, number: int, expected_head: str, path: Path) -> i return 0 expected_base = _canonical_base(pull_request) actor = _reviewer_actor() + author = gate.require_distinct_pr_author(pull_request, actor) if pull_request.get("isDraft"): print("PR is draft; Noema verdict preparation skipped.") return 0 @@ -167,16 +168,20 @@ def prepare_verdict(repo: str, number: int, expected_head: str, path: Path) -> i changed_files = gate.fetch_changed_files(repo, number) changed_paths = tuple(file_path for file_path, _status in changed_files) review_context = gate.build_review_context(repo, number, pull_request, changed_files) - verdict = gate.call_llm( - repo, - number, - pull_request, - diff, - truncated, - expected, - review_context, - changed_paths, - ) + try: + verdict = gate.call_llm( + repo, + number, + pull_request, + diff, + truncated, + expected, + review_context, + changed_paths, + ) + except gate.NoemaTransportError as exc: + _emit_transport_capacity_outputs(exc, expected_head=expected) + raise _write_envelope( path, @@ -186,6 +191,8 @@ def prepare_verdict(repo: str, number: int, expected_head: str, path: Path) -> i "pull_request_number": number, "expected_head": expected, "expected_base": expected_base, + "reviewer_actor": actor, + "pull_request_author": author, "verdict": verdict, }, ) @@ -196,6 +203,45 @@ def prepare_verdict(repo: str, number: int, expected_head: str, path: Path) -> i return 0 +def _emit_transport_capacity_outputs( + exc: gate.NoemaTransportError, + *, + expected_head: str, +) -> None: + """Publish typed capacity evidence for the workflow's bounded re-dispatch step.""" + retry_attempt = gate.current_transport_retry_attempt() + delay = gate.transport_redispatch_delay_seconds( + transport_retry_attempt=retry_attempt, + head_sha=expected_head, + retry_after_seconds=exc.retry_after_seconds, + ) + eligible = bool(exc.capacity_unavailable and delay is not None) + outputs = { + "transport_capacity_unavailable": "true" if exc.capacity_unavailable else "false", + "transport_retry_eligible": "true" if eligible else "false", + "prepared": "false", + } + if type(exc.http_status) is int: + outputs["transport_http_status"] = str(exc.http_status) + if type(exc.provider_attempt_count) is int: + outputs["provider_attempt_count"] = str(exc.provider_attempt_count) + if delay is not None: + outputs["transport_retry_delay_seconds"] = str(delay) + outputs["transport_retry_next_attempt"] = str(retry_attempt + 1) + gate.append_github_output(outputs) + if eligible: + print( + "::notice::Noema provider capacity unavailable after gateway failover; " + f"bounded continuation re-dispatch is eligible in {delay}s " + f"(attempt {retry_attempt + 1}/{gate.MAX_TRANSPORT_REDISPATCH_ATTEMPTS})." + ) + elif exc.capacity_unavailable: + print( + "::error::Noema provider capacity unavailable after gateway failover; " + "automatic re-dispatch budget is exhausted. Review remains required." + ) + + def publish_verdict(repo: str, number: int, expected_head: str, path: Path) -> int: """Publish a prepared verdict only with fresh exact-head/base reviewer authority.""" expected = _canonical_head(expected_head) @@ -207,6 +253,8 @@ def publish_verdict(repo: str, number: int, expected_head: str, path: Path) -> i "pull_request_number", "expected_head", "expected_base", + "reviewer_actor", + "pull_request_author", "verdict", } if set(payload) != required_keys: @@ -234,6 +282,14 @@ def publish_verdict(repo: str, number: int, expected_head: str, path: Path) -> i print("Pull request base advanced after model review; stale prepared verdict was not published.") return 0 actor = _reviewer_actor(allow_refreshed_app=True) + author = gate.require_distinct_pr_author(current_pull_request, actor) + if payload["pull_request_author"] != author: + raise RuntimeError("Noema pull request author changed after verdict preparation") + reviewer_actor = payload["reviewer_actor"] + if not isinstance(reviewer_actor, str) or not reviewer_actor.strip(): + raise RuntimeError("Noema prepared reviewer actor is invalid") + if reviewer_actor.strip().casefold() != actor.strip().casefold(): + raise RuntimeError("Noema refreshed reviewer actor differs from verdict preparation") if current_pull_request.get("isDraft"): print("PR became draft after model review; prepared verdict was not published.") return 0 diff --git a/.github/actions/orchestrator-free-sidecar/action.yml b/.github/actions/orchestrator-free-sidecar/action.yml index 196c86b0f6..c6a6cc3919 100644 --- a/.github/actions/orchestrator-free-sidecar/action.yml +++ b/.github/actions/orchestrator-free-sidecar/action.yml @@ -6,9 +6,9 @@ inputs: required: false default: "false" catalog_limit: - description: Maximum discovered route catalog size for the sidecar preflight. + description: Maximum discovered route catalog size for the sidecar preflight (a candidate list probed lazily to a readiness target, ADR-0029). required: false - default: "12" + default: "24" catalog_account_cap: description: Maximum routes admitted from one credential account. required: false @@ -23,9 +23,16 @@ runs: ref: ${{ github.action_ref }} path: ${{ runner.temp }}/cwl-control-plane persist-credentials: false + - name: Set up lock-compatible sidecar Python + id: sidecar_python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + update-environment: false - name: Provision contextual-orchestrator orchestrator/free shell: bash --noprofile --norc -e -o pipefail {0} env: + SIDECAR_PYTHON: ${{ steps.sidecar_python.outputs.python-path }} CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: ${{ inputs.require_zdr }} ORCHESTRATOR_CATALOG_LIMIT: ${{ inputs.catalog_limit }} ORCHESTRATOR_CATALOG_ACCOUNT_CAP: ${{ inputs.catalog_account_cap }} diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 71201af75d..0000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,15 +0,0 @@ -version: 2 -updates: - - package-ecosystem: "github-actions" - directory: "/" - target-branch: "main" - schedule: - interval: "weekly" - open-pull-requests-limit: 5 - - - package-ecosystem: "pip" - directory: "/" - target-branch: "main" - schedule: - interval: "weekly" - open-pull-requests-limit: 5 diff --git a/.github/runner-images/law-ai-agent/Dockerfile b/.github/runner-images/law-ai-agent/Dockerfile new file mode 100644 index 0000000000..5c73db15e5 --- /dev/null +++ b/.github/runner-images/law-ai-agent/Dockerfile @@ -0,0 +1,8 @@ +FROM ghcr.io/actions/actions-runner@sha256:e5496277be5d09bc968b3d64911b74e219ac4a3f2edce956a3ecf9271bea1ef4 +USER root +RUN apt-get update \ + && apt-get install -y --no-install-recommends postgresql-16 postgresql-client-16 libpq-dev python3-venv ca-certificates git curl \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* +USER runner +WORKDIR /home/runner diff --git a/.github/workflows/actions-queue-health.yml b/.github/workflows/actions-queue-health.yml new file mode 100644 index 0000000000..febf0679b2 --- /dev/null +++ b/.github/workflows/actions-queue-health.yml @@ -0,0 +1,57 @@ +name: GitHub Actions queue health + +on: + schedule: + - cron: "7 * * * *" + +concurrency: + group: github-actions-queue-health + cancel-in-progress: false + +permissions: + contents: read + actions: read + +jobs: + collect: + name: Collect exact-head queue evidence + runs-on: + group: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} + timeout-minutes: 30 + permissions: + contents: read + actions: read + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1 + with: + egress-policy: audit + + - name: Checkout trusted queue-health source + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + + - name: Collect read-only repository and runner evidence + env: + GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} + run: | + if [ -z "${GH_TOKEN:-}" ]; then + echo "::error::PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN is required for cross-repository queue reads." + exit 1 + fi + echo "::add-mask::$GH_TOKEN" + python3 scripts/ci/actions_queue_health.py \ + --allowlist config/actions_queue_health_repositories.json \ + --output-json "$RUNNER_TEMP/actions-queue-health.json" \ + --output-html "$RUNNER_TEMP/actions-queue-health.html" + + - name: Upload queue-health evidence + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: github-actions-queue-health-${{ github.run_id }} + path: | + ${{ runner.temp }}/actions-queue-health.json + ${{ runner.temp }}/actions-queue-health.html + if-no-files-found: error diff --git a/.github/workflows/agent-mention-noema-dispatch.yml b/.github/workflows/agent-mention-noema-dispatch.yml index 5bed3e8963..c2729b9819 100644 --- a/.github/workflows/agent-mention-noema-dispatch.yml +++ b/.github/workflows/agent-mention-noema-dispatch.yml @@ -8,16 +8,29 @@ on: repository_dispatch: types: [agent-mention-noema] +concurrency: + # Workflow-level admission, for the same reason strix.yml, noema-review.yml, + # opencode-review.yml and opencode-review-dispatch.yml carry theirs at this level: + # a job-level group is never evaluated while the whole run waits behind the + # organization job ceiling, so a superseded mention keeps its queue slot until a + # runner frees up and only then cancels. At workflow level the older run is + # coalesced while both are still queued, which is where the slot is actually held. + # This workflow has a single job, so the group lives here and nowhere else -- + # every workflow in this repository that carries a group at both levels + # (strix.yml, opencode-review-dispatch.yml) gives the two levels DIFFERENT names, + # because a job requesting the group its own run already holds would wait on itself. + group: agent-mention-noema-${{ github.event.client_payload.target_repository }}-${{ github.event.client_payload.pr_number || github.run_id }} + cancel-in-progress: true + permissions: contents: read jobs: validate-and-forward: if: github.repository == 'ContextualWisdomLab/.github' - concurrency: - group: agent-mention-noema-${{ github.event.client_payload.target_repository }}-${{ github.event.client_payload.pr_number || github.run_id }} - cancel-in-progress: true - runs-on: ubuntu-24.04 + runs-on: + group: ${{ (github.repository == 'ContextualWisdomLab/.github' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.repository == 'ContextualWisdomLab/.github' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} timeout-minutes: 5 permissions: actions: read diff --git a/.github/workflows/agent-mention-opencode-dispatch.yml b/.github/workflows/agent-mention-opencode-dispatch.yml index b27062ae37..221a5bdb74 100644 --- a/.github/workflows/agent-mention-opencode-dispatch.yml +++ b/.github/workflows/agent-mention-opencode-dispatch.yml @@ -8,16 +8,29 @@ on: repository_dispatch: types: [agent-mention-opencode] +concurrency: + # Workflow-level admission, for the same reason strix.yml, noema-review.yml, + # opencode-review.yml and opencode-review-dispatch.yml carry theirs at this level: + # a job-level group is never evaluated while the whole run waits behind the + # organization job ceiling, so a superseded mention keeps its queue slot until a + # runner frees up and only then cancels. At workflow level the older run is + # coalesced while both are still queued, which is where the slot is actually held. + # This workflow has a single job, so the group lives here and nowhere else -- + # every workflow in this repository that carries a group at both levels + # (strix.yml, opencode-review-dispatch.yml) gives the two levels DIFFERENT names, + # because a job requesting the group its own run already holds would wait on itself. + group: agent-mention-opencode-${{ github.event.client_payload.target_repository }}-${{ github.event.client_payload.pr_number || github.run_id }} + cancel-in-progress: true + permissions: contents: read jobs: validate-and-forward: if: github.repository == 'ContextualWisdomLab/.github' - concurrency: - group: agent-mention-opencode-${{ github.event.client_payload.target_repository }}-${{ github.event.client_payload.pr_number || github.run_id }} - cancel-in-progress: true - runs-on: ubuntu-24.04 + runs-on: + group: ${{ (github.repository == 'ContextualWisdomLab/.github' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.repository == 'ContextualWisdomLab/.github' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} timeout-minutes: 5 permissions: actions: read diff --git a/.github/workflows/agent-mention-router-quality-ci.yml b/.github/workflows/agent-mention-router-quality-ci.yml index 9c36a89119..57030437ef 100644 --- a/.github/workflows/agent-mention-router-quality-ci.yml +++ b/.github/workflows/agent-mention-router-quality-ci.yml @@ -13,7 +13,10 @@ on: - "scripts/ci/agent_mention_sweep.py" - "tests/test_agent_mention_*.py" - "tests/test_pr_review_fix_scheduler_coverage.py" + - "requirements-opencode-review-ci.txt" + - "requirements-noema-document-ci.txt" - "requirements-opencode-review-ci-hashes.txt" + - "scripts/ci/compile_opencode_review_lock.sh" push: branches: [main] paths: @@ -26,7 +29,10 @@ on: - "scripts/ci/agent_mention_sweep.py" - "tests/test_agent_mention_*.py" - "tests/test_pr_review_fix_scheduler_coverage.py" + - "requirements-opencode-review-ci.txt" + - "requirements-noema-document-ci.txt" - "requirements-opencode-review-ci-hashes.txt" + - "scripts/ci/compile_opencode_review_lock.sh" concurrency: group: agent-mention-router-quality-${{ github.repository }}-${{ github.event.pull_request.number || github.ref }} @@ -40,7 +46,9 @@ env: jobs: quality: - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] timeout-minutes: 20 steps: - name: Harden runner diff --git a/.github/workflows/agent-mention-router.yml b/.github/workflows/agent-mention-router.yml index 63ec8e3231..8b1bb88481 100644 --- a/.github/workflows/agent-mention-router.yml +++ b/.github/workflows/agent-mention-router.yml @@ -29,7 +29,9 @@ jobs: concurrency: group: review-agent-mention-router-local-${{ github.repository }}-${{ github.event.issue.number || github.run_id }} cancel-in-progress: true - runs-on: ubuntu-24.04 + runs-on: + group: CWL central control + labels: [self-hosted, linux, x64] timeout-minutes: 5 permissions: actions: read @@ -74,7 +76,9 @@ jobs: concurrency: group: review-agent-mention-router-sweep-${{ github.repository }} cancel-in-progress: false - runs-on: ubuntu-24.04 + runs-on: + group: CWL central control + labels: [self-hosted, linux, x64] timeout-minutes: 15 permissions: actions: read diff --git a/.github/workflows/agent-review-runtime-quality-ci.yml b/.github/workflows/agent-review-runtime-quality-ci.yml index 3680da8778..38f617e7d6 100644 --- a/.github/workflows/agent-review-runtime-quality-ci.yml +++ b/.github/workflows/agent-review-runtime-quality-ci.yml @@ -5,27 +5,49 @@ on: branches: [main] paths: - ".github/workflows/agent-review-runtime-quality-ci.yml" + - "scripts/ci/codeql_ghas_configuration_identity.py" + - "tests/test_codeql_ghas_configuration_identity.py" + - "tests/test_codeql_ghas_analyses_pagination.py" + - "tests/test_github_api_url_boundary.py" - ".github/workflows/noema-review.yml" - ".github/actions/noema-review/two_phase.py" - "tests/test_noema_reviewer_token_lifetime.py" - "tests/test_noema_two_phase_handoff.py" - "tests/test_noema_refreshed_app_identity.py" - "tests/test_noema_token_lifetime_stale_run_contract.py" + - "scripts/ci/noema_review_document.py" + - "scripts/ci/noema_hwp_mcp_reader.mjs" + - "scripts/ci/noema-document-reader/package.json" + - "scripts/ci/noema-document-reader/package-lock.json" + - "tests/test_noema_document_review_context.py" - "docs/doctoring/noema-review-token-lifetime.md" - "docs/product-technical-gap-baseline.md" - ".github/workflows/opencode-review-dispatch.yml" - "scripts/ci/ensure_rust_llvm19.sh" - "tests/test_opencode_rust_coverage_toolchain_contract.py" + - "scripts/ci/materialize_public_review_source.py" + - "tests/test_materialize_public_review_source.py" + - "tests/test_opencode_public_source_transport.py" + - "tests/test_rust_coverage_timeout_not_measured.py" + - "scripts/ci/resolve_base_rust_toolchain.py" + - "tests/test_resolve_base_rust_toolchain.py" + - "scripts/ci/place_maturin_extension.py" + - "tests/test_place_maturin_extension.py" + - "scripts/ci/materialize_base_javascript_packages.py" + - "tests/test_javascript_materializer_docstrings.py" - "tests/test_pr_review_autofix_nvidia_nim_contract.py" - "docs/doctoring/opencode-rust-coverage-runtime-boundary.md" - ".github/workflows/strix.yml" - "docs/doctoring/strix-legal-git-paths.md" - "docs/doctoring/strix-model-behavior-error.md" - "docs/doctoring/strix-quality-timeout-fixtures.md" + - "docs/doctoring/strix-evidence-binding-2159-2168.md" - "scripts/ci/strix_quick_gate.sh" + - "scripts/ci/strix_evidence_binding.py" - "scripts/ci/test_strix_quick_gate.sh" - "tests/test_docs_only_pr_runner_admission.py" - "tests/test_strix_changed_path_policy.py" + - "tests/test_strix_evidence_binding.py" - "tests/test_strix_model_behavior_error.py" - "tests/test_strix_nvidia_nim_not_found_fallback.py" - "tests/test_strix_workflow_dependency_hashes.py" @@ -45,10 +67,19 @@ on: - "scripts/ci/zdr_policy.py" - "scripts/ci/contextual_orchestrator_review_policy.py" - "scripts/ci/contextual_orchestrator_review_launcher.py" + - "scripts/ci/personal_review_route.py" - "scripts/ci/contextual_orchestrator_review_sidecar.sh" - "tests/test_zdr_policy.py" - "tests/test_contextual_orchestrator_review_policy.py" + - "tests/test_contextual_orchestrator_review_launcher.py" + - "tests/test_review_auto_route.py" + - "tests/test_personal_review_route.py" + - "tests/test_tokyo_review_consumer.py" + - "tests/test_review_auto_cost_admission.py" + - "tests/test_review_ai_app_independence.py" + - "tests/test_review_preflight_concurrency.py" - "tests/test_contextual_orchestrator_review_sidecar_contract.py" + - "tests/test_sidecar_disk_free_guard.py" - "tests/test_hourly_review_repair_callers.py" - "tests/test_github_hourly_conflict_repair.py" - "tests/test_hourly_scheduler_runtime_budget.py" @@ -100,7 +131,11 @@ on: - "docs/doctoring/exact-artifact-sbom-attestation.md" - "docs/doctoring/exact-artifact-sbom-quality-runner-consolidation-20260903.md" - "CHANGELOG.d/20260903-exact-artifact-quality-runner-consolidation.md" + - "requirements-opencode-review-ci.txt" - "requirements-opencode-review-ci-hashes.txt" + - "requirements-noema-document-ci.txt" + - "requirements-noema-document-ci-hashes.txt" + - "scripts/ci/compile_opencode_review_lock.sh" # PR validation only: a new head cancels only an older run of this workflow # for the same repository and pull request. @@ -114,7 +149,9 @@ permissions: jobs: agent_review_runtime_quality: name: agent-review-runtime-quality - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] timeout-minutes: 25 env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true @@ -136,7 +173,9 @@ jobs: with: python-version: "3.14" cache: pip - cache-dependency-path: requirements-opencode-review-ci-hashes.txt + cache-dependency-path: | + requirements-opencode-review-ci-hashes.txt + requirements-noema-document-ci-hashes.txt - name: Select affected contract suites id: affected_suites @@ -153,6 +192,7 @@ jobs: review_repair_suite=false commercial_readiness_suite=false exact_artifact_suite=false + ghas_suite=false while IFS= read -r changed_path; do case "$changed_path" in @@ -164,6 +204,14 @@ jobs: review_repair_suite=true commercial_readiness_suite=true exact_artifact_suite=true + ghas_suite=true + ;; + scripts/ci/codeql_ghas_configuration_identity.py|\ + tests/test_codeql_ghas_configuration_identity.py|\ + tests/test_codeql_ghas_analyses_pagination.py|\ + tests/test_github_api_url_boundary.py|\ + tests/test_agent_review_runtime_quality_consolidation.py) + ghas_suite=true ;; tests/test_pr_review_autofix_nvidia_nim_contract.py) opencode_suite=true @@ -179,12 +227,27 @@ jobs: tests/test_noema_two_phase_handoff.py|\ tests/test_noema_refreshed_app_identity.py|\ tests/test_noema_token_lifetime_stale_run_contract.py|\ + scripts/ci/noema_review_document.py|\ + scripts/ci/noema_hwp_mcp_reader.mjs|\ + scripts/ci/noema-document-reader/package.json|\ + scripts/ci/noema-document-reader/package-lock.json|\ + tests/test_noema_document_review_context.py|\ docs/doctoring/noema-review-token-lifetime.md) noema_suite=true ;; .github/workflows/opencode-review-dispatch.yml|\ scripts/ci/ensure_rust_llvm19.sh|\ tests/test_opencode_rust_coverage_toolchain_contract.py|\ + scripts/ci/materialize_public_review_source.py|\ + tests/test_materialize_public_review_source.py|\ + tests/test_opencode_public_source_transport.py|\ + tests/test_rust_coverage_timeout_not_measured.py|\ + scripts/ci/resolve_base_rust_toolchain.py|\ + tests/test_resolve_base_rust_toolchain.py|\ + scripts/ci/place_maturin_extension.py|\ + tests/test_place_maturin_extension.py|\ + scripts/ci/materialize_base_javascript_packages.py|\ + tests/test_javascript_materializer_docstrings.py|\ docs/doctoring/opencode-rust-coverage-runtime-boundary.md) opencode_suite=true ;; @@ -192,20 +255,29 @@ jobs: docs/doctoring/strix-legal-git-paths.md|\ docs/doctoring/strix-model-behavior-error.md|\ docs/doctoring/strix-quality-timeout-fixtures.md|\ + docs/doctoring/strix-evidence-binding-2159-2168.md|\ scripts/ci/strix_quick_gate.sh|\ + scripts/ci/strix_evidence_binding.py|\ scripts/ci/test_strix_quick_gate.sh|\ tests/test_docs_only_pr_runner_admission.py|\ tests/test_strix_changed_path_policy.py|\ + tests/test_strix_evidence_binding.py|\ tests/test_strix_model_behavior_error.py|\ tests/test_strix_nvidia_nim_not_found_fallback.py|\ tests/test_strix_workflow_dependency_hashes.py|\ tests/test_strix_quality_timeout_fixture_budget.py) strix_suite=true ;; - requirements-opencode-review-ci-hashes.txt) + requirements-opencode-review-ci.txt|\ + requirements-opencode-review-ci-hashes.txt|\ + requirements-noema-document-ci.txt|\ + scripts/ci/compile_opencode_review_lock.sh) noema_suite=true opencode_suite=true ;; + requirements-noema-document-ci-hashes.txt) + noema_suite=true + ;; .github/workflows/pr-review-merge-scheduler.yml) queue_suite=true review_repair_suite=true @@ -225,10 +297,19 @@ jobs: scripts/ci/zdr_policy.py|\ scripts/ci/contextual_orchestrator_review_policy.py|\ scripts/ci/contextual_orchestrator_review_launcher.py|\ + scripts/ci/personal_review_route.py|\ scripts/ci/contextual_orchestrator_review_sidecar.sh|\ tests/test_zdr_policy.py|\ tests/test_contextual_orchestrator_review_policy.py|\ + tests/test_contextual_orchestrator_review_launcher.py|\ + tests/test_review_auto_route.py|\ + tests/test_personal_review_route.py|\ + tests/test_tokyo_review_consumer.py|\ + tests/test_review_auto_cost_admission.py|\ + tests/test_review_ai_app_independence.py|\ + tests/test_review_preflight_concurrency.py|\ tests/test_contextual_orchestrator_review_sidecar_contract.py|\ + tests/test_sidecar_disk_free_guard.py|\ tests/test_hourly_review_repair_callers.py|\ tests/test_github_hourly_conflict_repair.py|\ tests/test_hourly_scheduler_runtime_budget.py|\ @@ -296,6 +377,7 @@ jobs: echo "review_repair=$review_repair_suite" echo "commercial_readiness=$commercial_readiness_suite" echo "exact_artifact=$exact_artifact_suite" + echo "ghas=$ghas_suite" } >>"$GITHUB_OUTPUT" - name: Install exact hash-verified base dependencies @@ -321,7 +403,13 @@ jobs: if: steps.affected_suites.outputs.noema == 'true' || steps.affected_suites.outputs.opencode == 'true' || steps.affected_suites.outputs.review_repair == 'true' || steps.affected_suites.outputs.exact_artifact == 'true' run: >- python -m pip install --disable-pip-version-check --require-hashes - -r requirements-opencode-review-ci-hashes.txt + -r requirements-opencode-review-ci-hashes.txt -r requirements-noema-document-ci-hashes.txt + + - name: Install exact Noema document dependencies + if: steps.affected_suites.outputs.noema == 'true' + run: >- + python -m pip install --disable-pip-version-check --require-hashes --no-deps + -r requirements-noema-document-ci-hashes.txt - name: Verify Noema token-lifetime contracts if: steps.affected_suites.outputs.noema == 'true' @@ -331,20 +419,38 @@ jobs: tests/test_noema_reviewer_token_lifetime.py \ tests/test_noema_two_phase_handoff.py \ tests/test_noema_refreshed_app_identity.py \ - tests/test_noema_token_lifetime_stale_run_contract.py + tests/test_noema_token_lifetime_stale_run_contract.py \ + tests/test_noema_document_review_context.py python -m compileall -q \ .github/actions/noema-review/two_phase.py \ tests/test_noema_reviewer_token_lifetime.py \ tests/test_noema_two_phase_handoff.py \ tests/test_noema_refreshed_app_identity.py \ - tests/test_noema_token_lifetime_stale_run_contract.py + tests/test_noema_token_lifetime_stale_run_contract.py \ + tests/test_noema_document_review_context.py - name: Verify OpenCode Rust coverage toolchain contract if: steps.affected_suites.outputs.opencode == 'true' run: | set -euo pipefail - python -m pytest -q tests/test_opencode_rust_coverage_toolchain_contract.py - python -m compileall -q tests/test_opencode_rust_coverage_toolchain_contract.py + python -m pytest -q tests/test_opencode_rust_coverage_toolchain_contract.py tests/test_rust_coverage_timeout_not_measured.py tests/test_resolve_base_rust_toolchain.py tests/test_place_maturin_extension.py + python -m compileall -q tests/test_opencode_rust_coverage_toolchain_contract.py tests/test_resolve_base_rust_toolchain.py tests/test_place_maturin_extension.py + + - name: Verify public coverage source transport + if: steps.affected_suites.outputs.opencode == 'true' + run: | + set -euo pipefail + python -m pytest -q \ + --cov=scripts.ci.materialize_public_review_source --cov-branch --cov-fail-under=100 \ + tests/test_materialize_public_review_source.py tests/test_opencode_public_source_transport.py + python -m compileall -q scripts/ci/materialize_public_review_source.py + + - name: Verify JavaScript materializer documentation contract + if: steps.affected_suites.outputs.opencode == 'true' + run: | + set -euo pipefail + python -m pytest -q tests/test_javascript_materializer_docstrings.py + python -m compileall -q scripts/ci/materialize_base_javascript_packages.py tests/test_javascript_materializer_docstrings.py - name: Verify exact-head path policy and syntax if: steps.affected_suites.outputs.strix == 'true' @@ -357,13 +463,16 @@ jobs: python -m pytest -q \ tests/test_docs_only_pr_runner_admission.py \ tests/test_strix_changed_path_policy.py \ + tests/test_strix_evidence_binding.py \ tests/test_strix_model_behavior_error.py \ tests/test_strix_nvidia_nim_not_found_fallback.py \ tests/test_strix_workflow_dependency_hashes.py \ tests/test_strix_quality_timeout_fixture_budget.py bash scripts/ci/test_strix_quick_gate.sh python -m compileall -q \ + scripts/ci/strix_evidence_binding.py \ tests/test_strix_changed_path_policy.py \ + tests/test_strix_evidence_binding.py \ tests/test_strix_model_behavior_error.py \ tests/test_strix_nvidia_nim_not_found_fallback.py \ tests/test_strix_workflow_dependency_hashes.py \ @@ -386,8 +495,43 @@ jobs: --cov=scripts.ci.pr_review_autofix_context \ --cov=scripts.ci.zdr_policy \ --cov=scripts.ci.contextual_orchestrator_review_policy \ + --cov=scripts.ci.contextual_orchestrator_review_launcher \ --cov-branch \ - --cov-fail-under=100 + --cov-fail-under=100 \ + tests/test_pr_review_conflict_scope.py \ + tests/test_zdr_policy.py \ + tests/test_contextual_orchestrator_review_policy.py \ + tests/test_contextual_orchestrator_review_launcher.py \ + tests/test_review_auto_route.py \ + tests/test_personal_review_route.py \ + tests/test_tokyo_review_consumer.py \ + tests/test_review_auto_cost_admission.py \ + tests/test_review_ai_app_independence.py \ + tests/test_review_preflight_concurrency.py \ + tests/test_contextual_orchestrator_review_sidecar_contract.py \ + tests/test_sidecar_disk_free_guard.py \ + tests/test_hourly_review_repair_callers.py \ + tests/test_github_hourly_conflict_repair.py \ + tests/test_hourly_scheduler_runtime_budget.py \ + tests/test_pr_review_conflict_scope_control_files.py \ + tests/test_hourly_autofix_context_quality_gate.py \ + tests/test_pr_review_conflict_scope_git_executable.py \ + tests/test_pr_review_conflict_scope_ignored_paths.py \ + tests/test_pr_review_conflict_scope_symlink_targets.py \ + tests/test_pr_review_fix_hourly_contract.py \ + tests/test_pr_review_fix_scheduler.py \ + tests/test_pr_review_fix_scheduler_source_pin.py \ + tests/test_pr_review_autofix_context_head_binding.py \ + tests/test_pr_review_autofix_nvidia_nim_contract.py \ + tests/test_pr_review_autofix_writer_security_contract.py \ + tests/test_pr_review_autofix_context_failed_checks.py \ + tests/test_pr_review_autofix_context_import_fallback.py \ + tests/test_contextual_orchestrator_free_credential_admission.py \ + tests/test_contextual_orchestrator_bytez_catalog_integration.py \ + tests/test_contextual_orchestrator_review_live_discovery_contract.py \ + tests/test_contextual_orchestrator_review_runtime_preflight.py \ + tests/test_repository_branch_coverage_review_schedulers.py \ + tests/test_repository_branch_coverage_reporting_edges.py python -m interrogate --fail-under 100 \ scripts/ci/pr_review_conflict_scope.py \ scripts/ci/pr_review_autofix_context.py \ @@ -403,7 +547,15 @@ jobs: scripts/ci/contextual_orchestrator_review_launcher.py \ tests/test_zdr_policy.py \ tests/test_contextual_orchestrator_review_policy.py \ + tests/test_contextual_orchestrator_review_launcher.py \ + tests/test_review_auto_route.py \ + tests/test_personal_review_route.py \ + tests/test_tokyo_review_consumer.py \ + tests/test_review_auto_cost_admission.py \ + tests/test_review_ai_app_independence.py \ + tests/test_review_preflight_concurrency.py \ tests/test_contextual_orchestrator_review_sidecar_contract.py \ + tests/test_sidecar_disk_free_guard.py \ tests/test_hourly_review_repair_callers.py \ tests/test_github_hourly_conflict_repair.py \ tests/test_hourly_scheduler_runtime_budget.py \ @@ -425,7 +577,8 @@ jobs: run: | python -m coverage run \ --branch \ - -m pytest --import-mode=importlib tests/test_organization_commercial_readiness_loop*.py -q + -m pytest --import-mode=importlib \ + tests/test_organization_commercial_readiness_loop*.py -q python -m coverage report \ --include='scripts/ci/organization_commercial_readiness_loop.py' \ --show-missing \ @@ -481,6 +634,21 @@ jobs: tests/test_verify_exact_artifact_sbom_handoff.py \ tests/test_exact_artifact_quality_single_runner.py + - name: Verify GHAS identity, pagination, and API boundary contracts + if: steps.affected_suites.outputs.ghas == 'true' + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + set -euo pipefail + python -m pytest -q \ + tests/test_codeql_ghas_configuration_identity.py \ + tests/test_codeql_ghas_analyses_pagination.py \ + tests/test_github_api_url_boundary.py + python -m compileall -q \ + scripts/ci/codeql_ghas_configuration_identity.py \ + tests/test_codeql_ghas_configuration_identity.py \ + tests/test_codeql_ghas_analyses_pagination.py \ + tests/test_github_api_url_boundary.py + - name: Verify consolidated workflow contract run: | set -euo pipefail diff --git a/.github/workflows/audit-central-ruleset.yml b/.github/workflows/audit-central-ruleset.yml index bf24e36c7c..e735ae1923 100644 --- a/.github/workflows/audit-central-ruleset.yml +++ b/.github/workflows/audit-central-ruleset.yml @@ -25,7 +25,9 @@ permissions: jobs: audit: name: Audit organization ruleset 18156473 - runs-on: ubuntu-latest + runs-on: + group: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} env: GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }} steps: @@ -105,6 +107,18 @@ jobs: python3 scripts/ci/audit_central_required_workflows.py --stacked "$stacked_ruleset_json" - name: Audit organization CodeQL coverage + # Runs even when the ruleset step above failed. Those two audits share a + # job but not a subject: the ruleset step exits 1 on owner-configured + # governance drift, and on 2026-09-06 it did exactly that ("exactly two + # approving reviews are not required", "last-push approval protection is + # disabled"), which silently took this CodeQL coverage detector down with + # it -- every run since 2026-09-04 failed there and never reached this + # step. This step builds its own repository list into its own temp file + # and the step above exports nothing to GITHUB_ENV or GITHUB_OUTPUT, so + # it has no data dependency to lose. The job still fails overall; what + # changes is that a coverage gap is reported instead of hidden behind an + # unrelated failure. + if: always() env: ORG_LOGIN: ContextualWisdomLab ORG_WIDE_CREDENTIAL_AVAILABLE: ${{ secrets.PR_REVIEW_MERGE_TOKEN != '' || secrets.OPENCODE_APPROVE_TOKEN != '' }} @@ -165,13 +179,21 @@ jobs: printf '[]\n' >"$coverage_json" while IFS=$'\t' read -r repository archived; do default_setup_state=null + # `state` alone is not coverage: a repository can report + # "configured" with an empty `languages` list, which scans nothing + # and produces no analyses (measured 2026-09-07 on life-os, aFIPC + # and inkspan). Collect both fields so the audit can tell those + # apart from a setup that actually covers a language. + default_setup_languages=null if [ "$archived" != "true" ]; then - default_setup_state_json="$RUNNER_TEMP/codeql-default-setup-${repository//[^A-Za-z0-9_.-]/_}.json" - if gh api "repos/${ORG_LOGIN}/${repository}/code-scanning/default-setup" --jq .state \ - >"$default_setup_state_json" 2>/dev/null; then - default_setup_state=$(jq -R '.' "$default_setup_state_json") + default_setup_json="$RUNNER_TEMP/codeql-default-setup-${repository//[^A-Za-z0-9_.-]/_}.json" + if gh api "repos/${ORG_LOGIN}/${repository}/code-scanning/default-setup" \ + >"$default_setup_json" 2>/dev/null; then + default_setup_state=$(jq '.state // null' "$default_setup_json") + default_setup_languages=$(jq '.languages // []' "$default_setup_json") else default_setup_state=null + default_setup_languages=null fi fi @@ -187,12 +209,13 @@ jobs: fi fi - echo "CODEQL_COVERAGE repository=${repository} archived=${archived} default_setup_state=${default_setup_state} latest_codeql_analysis=${latest_codeql_analysis}" + echo "CODEQL_COVERAGE repository=${repository} archived=${archived} default_setup_state=${default_setup_state} default_setup_languages=${default_setup_languages} latest_codeql_analysis=${latest_codeql_analysis}" jq --arg name "$repository" \ --argjson archived "$archived" \ --argjson default_setup_state "$default_setup_state" \ + --argjson default_setup_languages "$default_setup_languages" \ --argjson latest_codeql_analysis "$latest_codeql_analysis" \ - '. + [{name: $name, archived: $archived, default_setup_state: $default_setup_state, latest_codeql_analysis: $latest_codeql_analysis}]' \ + '. + [{name: $name, archived: $archived, default_setup_state: $default_setup_state, default_setup_languages: $default_setup_languages, latest_codeql_analysis: $latest_codeql_analysis}]' \ "$coverage_json" >"${coverage_json}.next" mv "${coverage_json}.next" "$coverage_json" done < <(jq -r '.[] | [.name, (.archived | tostring)] | @tsv' "$repositories_json") diff --git a/.github/workflows/cloudflare-dns.yml b/.github/workflows/cloudflare-dns.yml index 991202f4e9..6b23e3b053 100644 --- a/.github/workflows/cloudflare-dns.yml +++ b/.github/workflows/cloudflare-dns.yml @@ -44,7 +44,9 @@ permissions: jobs: reconcile: name: Reconcile zones (${{ github.event.client_payload.mode || 'dry-run' }}) - runs-on: ubuntu-latest + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] steps: - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 diff --git a/.github/workflows/codeql-pr.yml b/.github/workflows/codeql-pr.yml index cb07ad2fab..d8957ad247 100644 --- a/.github/workflows/codeql-pr.yml +++ b/.github/workflows/codeql-pr.yml @@ -2,15 +2,18 @@ # refuses to admit it, 0/43+ across every sampled repository # (docs/doctoring/codeql-pr-required-workflow-always-fails.md). This file # stays required-workflow-safe by never calling codeql-action itself: it -# detects languages, dispatches the actual scan via repository_dispatch to -# codeql-scan-dispatch.yml (which runs natively, unrestricted, in -# ContextualWisdomLab/.github). The shard then fails intentionally to release -# its runner; the handler publishes codeql-dispatch/ and reruns only -# that exact failed job. On rerun the shard reads the terminal status once. -# Design: +# detects languages, fails each analyze-head shard pending to release its +# runner, then one coordinator POSTs repository_dispatch to +# codeql-scan-dispatch.yml (native, unrestricted, in +# ContextualWisdomLab/.github) with the remaining language matrix. The +# handler publishes a base/run/source-bound codeql-dispatch receipt and reruns +# only that exact failed job. On rerun the shard reads the terminal status once. Design: # docs/adr/0025-codeql-required-workflow-dispatch-architecture.md. The # merge-preview scan (analyze-merge) is required nowhere (PR #1766) and was # dropped, not migrated. +# Only the trusted main workflow uses the control group. PR-authored workflow +# revisions retain hosted execution; organization group restrictions also enforce +# the exact main path. Heavy scans stay on the dedicated CodeQL runner. name: CodeQL PR on: @@ -55,8 +58,12 @@ permissions: jobs: detect-languages: name: Detect CodeQL languages - if: github.event.action != 'closed' - runs-on: ubuntu-24.04 + # Draft PRs get no runner; ready_for_review re-runs this on the same head. + if: (github.event.action != 'closed') && (github.event.pull_request.draft != true || github.event.action == 'converted_to_draft' || github.event.action == 'closed') + runs-on: + group: ${{ (fromJSON(github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/codeql-pr.yml@refs/heads/main' && '{"group":"CWL central control","labels":["self-hosted","linux","x64"]}' || '{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (fromJSON(github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/codeql-pr.yml@refs/heads/main' && '{"group":"CWL central control","labels":["self-hosted","linux","x64"]}' || '{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} + timeout-minutes: 5 permissions: contents: read pull-requests: read @@ -73,28 +80,35 @@ jobs: - name: Build language matrix id: detect run: | + scannable=false matrix='[]' if [ -d .github/workflows ]; then + scannable=true matrix=$(echo "$matrix" | jq -c '. + [{"language":"actions","build-mode":"none"}]') fi if find . -type f \( -name '*.js' -o -name '*.jsx' -o -name '*.ts' -o -name '*.tsx' \) \ -not -path './.git/*' -print -quit | grep -q .; then + scannable=true matrix=$(echo "$matrix" | jq -c '. + [{"language":"javascript-typescript","build-mode":"none"}]') fi if find . -type f -name '*.py' -not -path './.git/*' -print -quit | grep -q .; then + scannable=true matrix=$(echo "$matrix" | jq -c '. + [{"language":"python","build-mode":"none"}]') fi if find . -type f \( -name '*.java' -o -name '*.kt' -o -name '*.kts' \) \ -not -path './.git/*' -print -quit | grep -q .; then + scannable=true matrix=$(echo "$matrix" | jq -c '. + [{"language":"java-kotlin","build-mode":"none"}]') fi if [ "$(echo "$matrix" | jq 'length')" -eq 0 ]; then + # ponytail: placeholder shard so the required check name still expands; not a scan matrix='[{"language":"actions","build-mode":"none"}]' fi { echo 'matrix<> "$GITHUB_OUTPUT" - name: Classify changed paths @@ -104,6 +118,7 @@ jobs: REPO: ${{ github.event.pull_request.base.repo.full_name || github.repository }} PR: ${{ github.event.pull_request.number }} EXPECTED_FILES: ${{ github.event.pull_request.changed_files }} + SCANNABLE: ${{ steps.detect.outputs.scannable }} shell: bash run: | set -uo pipefail @@ -131,6 +146,9 @@ jobs: echo "::notice::changed-scope could not read a complete PR file list; scanning everything." fi fi + if [ "${SCANNABLE}" != "true" ]; then + code=false + fi echo "code=${code}" >> "$GITHUB_OUTPUT" echo "changed-scope code=${code}" @@ -148,62 +166,204 @@ jobs: # dependency exactly; the only case where it's genuinely skipped is a # closed PR, where this job being implicitly skipped too is fine because # closed PRs need no required check. - runs-on: ubuntu-24.04 + runs-on: + group: ${{ (fromJSON(github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/codeql-pr.yml@refs/heads/main' && '{"group":"CWL central control","labels":["self-hosted","linux","x64"]}' || '{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (fromJSON(github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/codeql-pr.yml@refs/heads/main' && '{"group":"CWL central control","labels":["self-hosted","linux","x64"]}' || '{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} + # Verdict reads have exceeded five minutes; retain the ten-minute control budget. + timeout-minutes: 10 permissions: + actions: read contents: read id-token: write + pull-requests: read + statuses: read strategy: fail-fast: false matrix: ${{ fromJSON(needs.detect-languages.outputs.matrix) }} steps: - - name: Request current-head CodeQL scan dispatch - # Each shard dispatches only its own language and passes its exact - # run/job identity. The shard intentionally fails after dispatch so - # its runner is released; the trusted handler later reruns that one - # failed job after publishing a terminal current-head verdict. + - name: Set up Python for pinned GitHub CLI provisioning + if: needs.detect-languages.outputs.code == 'true' + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.13" + + - name: Provision pinned job-local GitHub CLI + if: needs.detect-languages.outputs.code == 'true' + shell: bash + run: | + set -euo pipefail + python3 - <<'PY' + import hashlib + import os + from pathlib import Path + import platform + import shutil + import subprocess + import sys + import tarfile + import tempfile + import urllib.request + + version = "2.102.0" + archive_sha256 = "bb766f710eef8ede859c18578c72c327597cd4c8a85b06001b1f3843c6019386" + binary_sha256 = "7469124f706944133d6a169691dd1c6c3511b12e85878d255e044e2948df4c9b" + url = "https://github.com/cli/cli/releases/download/v2.102.0/gh_2.102.0_linux_amd64.tar.gz" + if sys.platform != "linux" or platform.machine() not in ("x86_64", "amd64"): + raise RuntimeError("pinned GitHub CLI requires Linux x64") + parent = Path(os.environ["RUNNER_TEMP"]) + if not parent.is_absolute(): + raise RuntimeError("RUNNER_TEMP must be absolute") + for ancestor in (parent, *parent.parents): + if ancestor.is_symlink() or not ancestor.is_dir(): + raise RuntimeError("runner temporary ancestry must be real directories") + root = Path(tempfile.mkdtemp(prefix="cwl-gh-", dir=parent)) + os.chmod(root, 0o700) + try: + archive = root / "gh.tar.gz" + with urllib.request.urlopen(url, timeout=30) as response, archive.open("xb") as output: + total = 0 + digest = hashlib.sha256() + while True: + chunk = response.read(1024 * 1024) + if not chunk: + break + total += len(chunk) + if total > 20 * 1024 * 1024: + raise RuntimeError("GitHub CLI archive exceeds bound") + digest.update(chunk) + output.write(chunk) + if digest.hexdigest() != archive_sha256: + raise RuntimeError("GitHub CLI archive checksum mismatch") + expected_member = f"gh_{version}_linux_amd64/bin/gh" + with tarfile.open(archive, "r:gz") as source: + members = source.getmembers() + selected = [member for member in members if member.name == expected_member] + if len(selected) != 1 or not selected[0].isfile(): + raise RuntimeError("GitHub CLI archive must contain one regular binary") + member = selected[0] + if not 0 < member.size <= 64 * 1024 * 1024: + raise RuntimeError("GitHub CLI binary size exceeds bound") + stream = source.extractfile(member) + if stream is None: + raise RuntimeError("GitHub CLI binary could not be read") + with stream: + content = stream.read(member.size + 1) + if len(content) != member.size or hashlib.sha256(content).hexdigest() != binary_sha256: + raise RuntimeError("GitHub CLI binary checksum mismatch") + # Copy this single pinned regular member; never extract archive paths or links. + bindir = root / "bin" + bindir.mkdir(mode=0o700) + executable = bindir / "gh" + with executable.open("xb") as output: + output.write(content) + executable.chmod(0o700) + result = subprocess.run([str(executable), "--version"], check=True, + capture_output=True, text=True, timeout=10) + if not result.stdout.splitlines() or not result.stdout.splitlines()[0].startswith(f"gh version {version} "): + raise RuntimeError("GitHub CLI version mismatch") + archive.unlink() + with open(os.environ["GITHUB_PATH"], "a", encoding="utf-8") as output: + output.write(str(bindir) + "\n") + with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as output: + output.write("CWL_GH_BIN=" + str(executable) + "\n") + info = root.stat() + output.write("CWL_GH_ROOT=" + str(root) + "\n") + output.write("CWL_GH_ROOT_DEVICE=" + str(info.st_dev) + "\n") + output.write("CWL_GH_ROOT_INODE=" + str(info.st_ino) + "\n") + print("Installed checksum-verified job-local GitHub CLI " + version) + except BaseException: + shutil.rmtree(root) + raise + PY + + - name: Verify pinned job-local GitHub CLI selection + if: needs.detect-languages.outputs.code == 'true' + shell: bash + run: | + set -euo pipefail + if [ ! -f "$CWL_GH_BIN" ] || [ -L "$CWL_GH_BIN" ] || [ ! -x "$CWL_GH_BIN" ]; then + echo "::error::Job-local GitHub CLI executable is missing or linked." + exit 1 + fi + if [ "$(command -v gh)" != "$CWL_GH_BIN" ]; then + echo "::error::GitHub CLI PATH selection differs from the pinned job-local binary." + exit 1 + fi + printf '%s %s\n' '7469124f706944133d6a169691dd1c6c3511b12e85878d255e044e2948df4c9b' "$CWL_GH_BIN" | sha256sum --check --status + version_output="$(gh --version)" + case "$version_output" in + 'gh version 2.102.0 '*) printf '%s\n' "GitHub CLI 2.102.0 path and checksum verified." ;; + *) echo "::error::GitHub CLI selected an unsupported version."; exit 1 ;; + esac + + - name: Read current-head CodeQL dispatch verdict + # Shards never dispatch. They re-check the live head, consume an + # authenticated base/run/source-bound CodeQL verdict when one exists, + # and otherwise fail pending so the runner is released. One + # coordinator job POSTs the remaining language matrix after every + # shard has a job id. id: dispatch if: needs.detect-languages.outputs.code == 'true' env: GH_TOKEN: ${{ github.token }} - OIDC_AUDIENCE: opencode-github-action - OPENCODE_API_BASE_URL: https://api.opencode.ai TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.repository }} PR_NUMBER: ${{ github.event.pull_request.number }} - PR_BASE_REF: ${{ github.event.pull_request.base.ref }} - PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} - PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} LANGUAGE: ${{ matrix.language }} - BUILD_MODE: ${{ matrix.build-mode }} RUN_ATTEMPT: ${{ github.run_attempt }} REQUIRED_RUN_ID: ${{ github.run_id }} - REQUIRED_JOB_ID: ${{ job.check_run_id }} run: | set -euo pipefail live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')" + live_base="$(printf '%s' "$live_pr" | jq -r '.base.sha // empty')" + live_merge="$(printf '%s' "$live_pr" | jq -r '.merge_commit_sha // empty')" live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')" - if [ -z "$live_head" ] || [ -z "$live_state" ]; then + if ! [[ "$PR_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ && "$live_head" =~ ^[0-9a-fA-F]{40}$ ]] || [[ "$live_state" != "open" && "$live_state" != "closed" ]]; then echo "::error::Could not validate live pull request state before CodeQL dispatch." exit 1 fi if [ "$live_state" = "closed" ]; then echo "PR is closed on the live exact head; a current-head CodeQL scan is not requested." + echo "verdict=obsolete" >>"$GITHUB_OUTPUT" exit 0 fi if [ "${live_head,,}" != "${PR_HEAD_SHA,,}" ]; then + # A lagging API read or diverged history must not retire the current scan. + comparison="$(gh api "repos/${TARGET_REPOSITORY}/compare/${PR_HEAD_SHA}...${live_head}")" + if ! printf '%s' "$comparison" | jq -e ' + .status == "ahead" and .behind_by == 0 and + ((.ahead_by | type) == "number") and .ahead_by >= 1 + ' >/dev/null; then + echo "::error::Live head does not prove this CodeQL shard was superseded." + exit 1 + fi echo "Pull request head moved on the live open PR; a fresh dispatch will fire for the current head." + echo "verdict=obsolete" >>"$GITHUB_OUTPUT" exit 0 fi + if ! [[ "$live_base" =~ ^[0-9a-fA-F]{40}$ ]] || + ! [[ "$live_merge" =~ ^[0-9a-fA-F]{40}$ ]]; then + echo "::error::Could not validate live pull request base/source SHA before CodeQL verdict read." + exit 1 + fi + if ! [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]]; then + echo "::error::CodeQL shard requires a canonical current run id." + exit 1 + fi statuses="$(gh api "repos/${TARGET_REPOSITORY}/commits/${PR_HEAD_SHA}/statuses")" - verdict_state="$(printf '%s' "$statuses" | jq -r --arg ctx "codeql-dispatch/${LANGUAGE}" ' + expected_context="codeql-dispatch/${LANGUAGE}/${live_base}" + expected_description="cwl1;h=${PR_HEAD_SHA};w=codeql-scan-dispatch;r=${REQUIRED_RUN_ID};s=${live_merge}" + verdict_state="$(printf '%s' "$statuses" | jq -r --arg ctx "$expected_context" --arg description "$expected_description" ' [ .[] | select(.context == $ctx) + | select(.description == $description) | select( (.creator.login // "" | ascii_downcase) as $creator | $creator == "opencode-agent" or $creator == "opencode-agent[bot]" + or $creator == "cwl-noema-review" or $creator == "cwl-noema-review[bot]" ) ] | first // {} | .state // empty @@ -215,47 +375,73 @@ jobs: exit 0 ;; esac - if [ "$RUN_ATTEMPT" != "1" ]; then - echo "::error::Exact CodeQL job was rerun without an authenticated terminal verdict." - exit 1 - fi - if ! [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || - ! [[ "$REQUIRED_JOB_ID" =~ ^[1-9][0-9]*$ ]]; then - echo "::error::CodeQL dispatch requires canonical current run and job ids." - exit 1 - fi - if [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then - echo "::error::CodeQL scan dispatch requires GitHub OIDC." + expected_title="CodeQL Scan Dispatch ${TARGET_REPOSITORY}#${PR_NUMBER}@${PR_HEAD_SHA}/${live_base}/${REQUIRED_RUN_ID}/${live_merge}" + expected_job="CodeQL dispatch scan (${LANGUAGE})" + # A dispatch bound to this required run cannot predate its creation. + required_created_at="$(gh api "repos/${TARGET_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}" --jq .created_at)" + if ! [[ "$required_created_at" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ ]]; then + echo "::error::Could not validate required run creation time before CodeQL verdict lookup." exit 1 fi - separator='&' - [[ "$ACTIONS_ID_TOKEN_REQUEST_URL" == *\?* ]] || separator='?' - oidc_token="$(curl -fsS -H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" "${ACTIONS_ID_TOKEN_REQUEST_URL}${separator}audience=${OIDC_AUDIENCE}" | jq -r '.value // empty')" - if [ -z "$oidc_token" ]; then - echo "::error::CodeQL scan dispatch could not obtain its OIDC token." - exit 1 + runs_json="$(gh api --method GET --paginate -f per_page=100 -f event=repository_dispatch -f created=">=${required_created_at}" "repos/ContextualWisdomLab/.github/actions/workflows/codeql-scan-dispatch.yml/runs" | jq -s .)" + run_id="$(printf '%s' "$runs_json" | jq -r --arg title "$expected_title" --arg path ".github/workflows/codeql-scan-dispatch.yml" ' + [ + .[] | .workflow_runs[] + | select(.path == $path) + | select(.event == "repository_dispatch") + | select(.status == "completed") + | select(.display_title == $title or .name == $title) + ] + | first + | .id // empty + ')" + if [[ "$run_id" =~ ^[1-9][0-9]*$ ]]; then + jobs_json="$(gh api --paginate "repos/ContextualWisdomLab/.github/actions/runs/${run_id}/jobs" | jq -s .)" + dispatch_job="$(printf '%s' "$jobs_json" | jq -c --arg name "$expected_job" ' + [.[] | .jobs[] | select(.name == $name)] + | if length == 1 then .[0] else empty end + ')" + if [ -n "$dispatch_job" ]; then + gate_conclusion="$(printf '%s' "$dispatch_job" | jq -r ' + (.steps[]? | select(.name == "Enforce CodeQL Medium+ SARIF gate") | .conclusion) // empty + ')" + ghas_identity_conclusion="$(printf '%s' "$dispatch_job" | jq -r ' + (.steps[]? | select(.name == "Verify GHAS base/head CodeQL configuration identity") | .conclusion) // empty + ')" + sarif_upload_conclusion="$(printf '%s' "$dispatch_job" | jq -r ' + (.steps[]? | select(.name == "Preserve CodeQL SARIF evidence") | .conclusion) // empty + ')" + case "$gate_conclusion" in + success) + if [ "$ghas_identity_conclusion" = "success" ] && + [ "$sarif_upload_conclusion" = "success" ]; then + echo "verdict=success" >>"$GITHUB_OUTPUT" + echo "Found completed CodeQL dispatch proof for ${LANGUAGE}: gate, GHAS identity, and SARIF evidence succeeded." + exit 0 + fi + ;; + failure|cancelled|skipped) + echo "verdict=failure" >>"$GITHUB_OUTPUT" + echo "Found completed CodeQL dispatch scan gate for ${LANGUAGE}: failure." + exit 0 + ;; + esac + job_conclusion="$(printf '%s' "$dispatch_job" | jq -r '.conclusion // empty')" + case "$job_conclusion" in + success) + echo "verdict=success" >>"$GITHUB_OUTPUT" + echo "Found completed CodeQL dispatch scan job for ${LANGUAGE}: success." + exit 0 + ;; + esac + fi fi - app_token="$(curl -fsS -X POST -H "Authorization: Bearer ${oidc_token}" "${OPENCODE_API_BASE_URL}/exchange_github_app_token" | jq -r '.token // empty')" - if [ -z "$app_token" ]; then - echo "::error::CodeQL scan dispatch could not obtain its repository-scoped app token." + + if [ "$RUN_ATTEMPT" != "1" ]; then + echo "::error::Exact CodeQL job was rerun without an authenticated terminal verdict; GHAS identity and preserved SARIF are required for authenticated terminal proof." exit 1 fi - echo "::add-mask::$app_token" - jq -cn \ - --arg target_repository "$TARGET_REPOSITORY" \ - --arg pr_number "$PR_NUMBER" \ - --arg pr_base_ref "$PR_BASE_REF" \ - --arg pr_base_sha "$PR_BASE_SHA" \ - --arg pr_head_ref "$PR_HEAD_REF" \ - --arg pr_head_sha "$PR_HEAD_SHA" \ - --arg language "$LANGUAGE" \ - --arg build_mode "$BUILD_MODE" \ - --arg required_run_id "$REQUIRED_RUN_ID" \ - --arg required_job_id "$REQUIRED_JOB_ID" \ - --arg required_language "$LANGUAGE" \ - '{event_type:"codeql-scan",client_payload:{target_repository:$target_repository,pr_number:$pr_number,pr_base_ref:$pr_base_ref,pr_base_sha:$pr_base_sha,pr_head_ref:$pr_head_ref,pr_head_sha:$pr_head_sha,matrix:[{language:$language,"build-mode":$build_mode}],required_run_id:$required_run_id,required_job_id:$required_job_id,required_language:$required_language}}' | - GH_TOKEN="$app_token" gh api -X POST repos/ContextualWisdomLab/.github/dispatches --input - echo "verdict=pending" >>"$GITHUB_OUTPUT" - name: Release runner or enforce current-head CodeQL verdict @@ -271,6 +457,9 @@ jobs: exit 1 fi case "$VERDICT_STATE" in + obsolete) + echo "Closed or superseded PR shard; no scan verdict is asserted." + ;; success) echo "Current-head CodeQL dispatch verdict for ${LANGUAGE}: success." ;; @@ -287,3 +476,354 @@ jobs: exit 1 ;; esac + + - name: Clean up this job's pinned GitHub CLI + if: always() && env.CWL_GH_ROOT != '' + shell: bash + run: | + set -euo pipefail + python3 - <<'PY' + import os + from pathlib import Path + import re + import shutil + import stat + + root = Path(os.environ["CWL_GH_ROOT"]) + parent = Path(os.environ["RUNNER_TEMP"]) + if (not root.is_absolute() or root.parent != parent + or not re.fullmatch(r"cwl-gh-[A-Za-z0-9_-]+", root.name) + or any(ord(c) < 32 for c in str(root))): + raise RuntimeError("refusing unrelated GitHub CLI cleanup path") + for ancestor in (parent, *parent.parents): + if ancestor.is_symlink() or not ancestor.is_dir(): + raise RuntimeError("refusing changed GitHub CLI temporary ancestry") + try: + info = root.lstat() + except FileNotFoundError: + print("Owned GitHub CLI directory is already absent.") + else: + if (not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid() + or info.st_dev != int(os.environ["CWL_GH_ROOT_DEVICE"]) + or info.st_ino != int(os.environ["CWL_GH_ROOT_INODE"])): + raise RuntimeError("refusing changed GitHub CLI directory identity") + # No glob or shared-temp sweep: delete only this job's original directory. + shutil.rmtree(root) + print("Removed this job's checksum-verified GitHub CLI directory.") + PY + + dispatch-current-head: + name: Dispatch current-head CodeQL scan + needs: [detect-languages, analyze-head] + if: >- + always() + && github.event.action != 'closed' + && github.event.pull_request.state != 'closed' + && needs.detect-languages.result == 'success' + && needs.detect-languages.outputs.code == 'true' + runs-on: + group: ${{ (fromJSON(github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/codeql-pr.yml@refs/heads/main' && '{"group":"CWL central control","labels":["self-hosted","linux","x64"]}' || '{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (fromJSON(github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/codeql-pr.yml@refs/heads/main' && '{"group":"CWL central control","labels":["self-hosted","linux","x64"]}' || '{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} + timeout-minutes: 5 + permissions: + contents: read + id-token: write + actions: read + pull-requests: read + statuses: read + steps: + - name: Set up Python for pinned GitHub CLI provisioning + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.13" + + - name: Provision pinned job-local GitHub CLI + shell: bash + run: | + set -euo pipefail + python3 - <<'PY' + import hashlib + import os + from pathlib import Path + import platform + import shutil + import subprocess + import sys + import tarfile + import tempfile + import urllib.request + + version = "2.102.0" + archive_sha256 = "bb766f710eef8ede859c18578c72c327597cd4c8a85b06001b1f3843c6019386" + binary_sha256 = "7469124f706944133d6a169691dd1c6c3511b12e85878d255e044e2948df4c9b" + url = "https://github.com/cli/cli/releases/download/v2.102.0/gh_2.102.0_linux_amd64.tar.gz" + if sys.platform != "linux" or platform.machine() not in ("x86_64", "amd64"): + raise RuntimeError("pinned GitHub CLI requires Linux x64") + parent = Path(os.environ["RUNNER_TEMP"]) + if not parent.is_absolute(): + raise RuntimeError("RUNNER_TEMP must be absolute") + for ancestor in (parent, *parent.parents): + if ancestor.is_symlink() or not ancestor.is_dir(): + raise RuntimeError("runner temporary ancestry must be real directories") + root = Path(tempfile.mkdtemp(prefix="cwl-gh-", dir=parent)) + os.chmod(root, 0o700) + try: + archive = root / "gh.tar.gz" + with urllib.request.urlopen(url, timeout=30) as response, archive.open("xb") as output: + total = 0 + digest = hashlib.sha256() + while True: + chunk = response.read(1024 * 1024) + if not chunk: + break + total += len(chunk) + if total > 20 * 1024 * 1024: + raise RuntimeError("GitHub CLI archive exceeds bound") + digest.update(chunk) + output.write(chunk) + if digest.hexdigest() != archive_sha256: + raise RuntimeError("GitHub CLI archive checksum mismatch") + expected_member = f"gh_{version}_linux_amd64/bin/gh" + with tarfile.open(archive, "r:gz") as source: + members = source.getmembers() + selected = [member for member in members if member.name == expected_member] + if len(selected) != 1 or not selected[0].isfile(): + raise RuntimeError("GitHub CLI archive must contain one regular binary") + member = selected[0] + if not 0 < member.size <= 64 * 1024 * 1024: + raise RuntimeError("GitHub CLI binary size exceeds bound") + stream = source.extractfile(member) + if stream is None: + raise RuntimeError("GitHub CLI binary could not be read") + with stream: + content = stream.read(member.size + 1) + if len(content) != member.size or hashlib.sha256(content).hexdigest() != binary_sha256: + raise RuntimeError("GitHub CLI binary checksum mismatch") + # Copy this single pinned regular member; never extract archive paths or links. + bindir = root / "bin" + bindir.mkdir(mode=0o700) + executable = bindir / "gh" + with executable.open("xb") as output: + output.write(content) + executable.chmod(0o700) + result = subprocess.run([str(executable), "--version"], check=True, + capture_output=True, text=True, timeout=10) + if not result.stdout.splitlines() or not result.stdout.splitlines()[0].startswith(f"gh version {version} "): + raise RuntimeError("GitHub CLI version mismatch") + archive.unlink() + with open(os.environ["GITHUB_PATH"], "a", encoding="utf-8") as output: + output.write(str(bindir) + "\n") + with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as output: + output.write("CWL_GH_BIN=" + str(executable) + "\n") + info = root.stat() + output.write("CWL_GH_ROOT=" + str(root) + "\n") + output.write("CWL_GH_ROOT_DEVICE=" + str(info.st_dev) + "\n") + output.write("CWL_GH_ROOT_INODE=" + str(info.st_ino) + "\n") + print("Installed checksum-verified job-local GitHub CLI " + version) + except BaseException: + shutil.rmtree(root) + raise + PY + + - name: Verify pinned job-local GitHub CLI selection + shell: bash + run: | + set -euo pipefail + if [ ! -f "$CWL_GH_BIN" ] || [ -L "$CWL_GH_BIN" ] || [ ! -x "$CWL_GH_BIN" ]; then + echo "::error::Job-local GitHub CLI executable is missing or linked." + exit 1 + fi + if [ "$(command -v gh)" != "$CWL_GH_BIN" ]; then + echo "::error::GitHub CLI PATH selection differs from the pinned job-local binary." + exit 1 + fi + printf '%s %s\n' '7469124f706944133d6a169691dd1c6c3511b12e85878d255e044e2948df4c9b' "$CWL_GH_BIN" | sha256sum --check --status + version_output="$(gh --version)" + case "$version_output" in + 'gh version 2.102.0 '*) printf '%s\n' "GitHub CLI 2.102.0 path and checksum verified." ;; + *) echo "::error::GitHub CLI selected an unsupported version."; exit 1 ;; + esac + + - name: Dispatch current-head CodeQL scan + env: + GH_TOKEN: ${{ github.token }} + OIDC_AUDIENCE: opencode-github-action + OPENCODE_API_BASE_URL: https://api.opencode.ai + TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_BASE_REF: ${{ github.event.pull_request.base.ref }} + PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} + PR_HEAD_REF: ${{ github.event.pull_request.head.ref }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + REQUIRED_RUN_ID: ${{ github.run_id }} + MATRIX: ${{ needs.detect-languages.outputs.matrix }} + run: | + set -euo pipefail + live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" + live_head="$(printf '%s' "$live_pr" | jq -r '.head.sha // empty')" + live_base="$(printf '%s' "$live_pr" | jq -r '.base.sha // empty')" + live_merge="$(printf '%s' "$live_pr" | jq -r '.merge_commit_sha // empty')" + live_base_ref="$(printf '%s' "$live_pr" | jq -r '.base.ref // empty')" + live_head_ref="$(printf '%s' "$live_pr" | jq -r '.head.ref // empty')" + live_state="$(printf '%s' "$live_pr" | jq -r 'if (.state | type) == "string" then .state else empty end')" + if [ -z "$live_head" ] || [ -z "$live_state" ]; then + echo "::error::Could not validate live pull request state before CodeQL dispatch." + exit 1 + fi + if [ "$live_state" = "closed" ]; then + echo "PR is closed on the live exact head; a current-head CodeQL scan is not requested." + exit 0 + fi + if [ "${live_head,,}" != "${PR_HEAD_SHA,,}" ]; then + echo "Pull request head moved on the live open PR; a fresh dispatch will fire for the current head." + exit 0 + fi + if ! [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]]; then + echo "::error::CodeQL dispatch requires a canonical current run id." + exit 1 + fi + if ! [[ "$live_base" =~ ^[0-9a-fA-F]{40}$ ]] || + ! [[ "$live_merge" =~ ^[0-9a-fA-F]{40}$ ]] || + [ -z "$live_base_ref" ] || [ -z "$live_head_ref" ]; then + echo "::error::Could not validate live pull request base/source identity before CodeQL dispatch." + exit 1 + fi + + include_json="$(printf '%s' "$MATRIX" | jq -c '.include // empty' 2>/dev/null || true)" + if [ -z "$include_json" ] || + [ "$(printf '%s' "$include_json" | jq 'type == "array" and length >= 1')" != "true" ]; then + echo "::error::CodeQL coordinator received an empty or malformed language matrix." + exit 1 + fi + + jobs_json="$( + gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}/jobs" --jq '.jobs[]' | + jq -s '{jobs:.}' + )" + required_jobs='[]' + while IFS= read -r entry; do + language="$(printf '%s' "$entry" | jq -r '.language // empty')" + expected_name="CodeQL compatibility analysis (${language})" + job_id="$(printf '%s' "$jobs_json" | jq -r --arg name "$expected_name" ' + [.jobs[]? | select(.name == $name) | .id] + | if length == 1 then .[0] | tostring else empty end + ')" + if ! [[ "$job_id" =~ ^[1-9][0-9]*$ ]]; then + echo "::error::CodeQL coordinator missing current-head job id for ${language}." + exit 1 + fi + required_jobs="$( + jq -c --arg language "$language" --argjson job_id "$job_id" \ + '. + [{language:$language,job_id:$job_id}]' <<<"$required_jobs" + )" + done < <(printf '%s' "$include_json" | jq -c '.[]') + + statuses="$(gh api "repos/${TARGET_REPOSITORY}/commits/${PR_HEAD_SHA}/statuses")" + pending_matrix='[]' + while IFS= read -r entry; do + language="$(printf '%s' "$entry" | jq -r '.language // empty')" + expected_context="codeql-dispatch/${language}/${live_base}" + expected_description="cwl1;h=${PR_HEAD_SHA};w=codeql-scan-dispatch;r=${REQUIRED_RUN_ID};s=${live_merge}" + verdict_state="$(printf '%s' "$statuses" | jq -r --arg ctx "$expected_context" --arg description "$expected_description" ' + [ + .[] + | select(.context == $ctx) + | select(.description == $description) + | select( + (.creator.login // "" | ascii_downcase) as $creator + | $creator == "opencode-agent" or $creator == "opencode-agent[bot]" + or $creator == "cwl-noema-review" or $creator == "cwl-noema-review[bot]" + ) + ] + | first // {} | .state // empty + ')" + case "$verdict_state" in + success|failure|error) + echo "Found authenticated current-head CodeQL verdict for ${language}: ${verdict_state}." + ;; + *) + pending_matrix="$(jq -c --argjson entry "$entry" '. + [$entry]' <<<"$pending_matrix")" + ;; + esac + done < <(printf '%s' "$include_json" | jq -c '.[]') + + if [ "$(printf '%s' "$pending_matrix" | jq 'length')" -eq 0 ]; then + echo "All detected CodeQL languages already have authenticated terminal verdicts; skipping dispatch." + exit 0 + fi + + required_jobs="$( + jq -nc --argjson pending "$pending_matrix" --argjson jobs "$required_jobs" ' + ($pending | map(.language)) as $langs + | [$jobs[] | select(.language as $l | $langs | index($l) != null)] + ' + )" + if [ "$(printf '%s' "$required_jobs" | jq 'length')" != "$(printf '%s' "$pending_matrix" | jq 'length')" ]; then + echo "::error::CodeQL coordinator could not bind a job id to every pending language." + exit 1 + fi + + if [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then + echo "::error::CodeQL scan dispatch requires GitHub OIDC." + exit 1 + fi + separator='&' + [[ "$ACTIONS_ID_TOKEN_REQUEST_URL" == *\?* ]] || separator='?' + oidc_token="$(curl -fsS -H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" "${ACTIONS_ID_TOKEN_REQUEST_URL}${separator}audience=${OIDC_AUDIENCE}" | jq -r '.value // empty')" + if [ -z "$oidc_token" ]; then + echo "::error::CodeQL scan dispatch could not obtain its OIDC token." + exit 1 + fi + app_token="$(curl -fsS -X POST -H "Authorization: Bearer ${oidc_token}" "${OPENCODE_API_BASE_URL}/exchange_github_app_token" | jq -r '.token // empty')" + if [ -z "$app_token" ]; then + echo "::error::CodeQL scan dispatch could not obtain its repository-scoped app token." + exit 1 + fi + echo "::add-mask::$app_token" + jq -cn \ + --arg target_repository "$TARGET_REPOSITORY" \ + --arg pr_number "$PR_NUMBER" \ + --arg pr_base_ref "$live_base_ref" \ + --arg pr_base_sha "$live_base" \ + --arg pr_head_ref "$live_head_ref" \ + --arg pr_head_sha "$live_head" \ + --argjson matrix "$pending_matrix" \ + --arg required_run_id "$REQUIRED_RUN_ID" \ + --argjson required_jobs "$required_jobs" \ + --arg producer_source_sha "$live_merge" \ + '{event_type:"codeql-scan-v2",client_payload:{target_repository:$target_repository,pr_number:$pr_number,pr_base_ref:$pr_base_ref,pr_base_sha:$pr_base_sha,pr_head:{schema:"1",ref:$pr_head_ref,sha:$pr_head_sha},producer_source_sha:$producer_source_sha,matrix:$matrix,required_run_id:$required_run_id,required_jobs:$required_jobs}}' | + GH_TOKEN="$app_token" gh api -X POST repos/ContextualWisdomLab/.github/dispatches --input - + - name: Clean up this job's pinned GitHub CLI + if: always() && env.CWL_GH_ROOT != '' + shell: bash + run: | + set -euo pipefail + python3 - <<'PY' + import os + from pathlib import Path + import re + import shutil + import stat + + root = Path(os.environ["CWL_GH_ROOT"]) + parent = Path(os.environ["RUNNER_TEMP"]) + if (not root.is_absolute() or root.parent != parent + or not re.fullmatch(r"cwl-gh-[A-Za-z0-9_-]+", root.name) + or any(ord(c) < 32 for c in str(root))): + raise RuntimeError("refusing unrelated GitHub CLI cleanup path") + for ancestor in (parent, *parent.parents): + if ancestor.is_symlink() or not ancestor.is_dir(): + raise RuntimeError("refusing changed GitHub CLI temporary ancestry") + try: + info = root.lstat() + except FileNotFoundError: + print("Owned GitHub CLI directory is already absent.") + else: + if (not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid() + or info.st_dev != int(os.environ["CWL_GH_ROOT_DEVICE"]) + or info.st_ino != int(os.environ["CWL_GH_ROOT_INODE"])): + raise RuntimeError("refusing changed GitHub CLI directory identity") + # No glob or shared-temp sweep: delete only this job's original directory. + shutil.rmtree(root) + print("Removed this job's checksum-verified GitHub CLI directory.") + PY diff --git a/.github/workflows/codeql-scan-dispatch.yml b/.github/workflows/codeql-scan-dispatch.yml index 1ad28f3086..04656e5b34 100644 --- a/.github/workflows/codeql-scan-dispatch.yml +++ b/.github/workflows/codeql-scan-dispatch.yml @@ -12,22 +12,30 @@ # Exercise this handler end-to-end by POSTing a real repository_dispatch # event instead -- that always runs the default-branch version. name: CodeQL Scan Dispatch +# LEGACY_V1_REMOVAL_CONDITION: remove codeql-scan:legacy-v1 only after the +# protected v2 producer has landed and every in-flight v1 required run has +# reached a terminal conclusion. Both protocols share this protected handler. run-name: >- CodeQL Scan Dispatch ${{ github.event.client_payload.target_repository || github.repository }}#${{ github.event.client_payload.pr_number || 'event' }}@${{ - github.event.client_payload.pr_head_sha || github.sha }} + github.event.client_payload.pr_head.sha || github.event.client_payload.pr_head_sha || github.sha }}/${{ + github.event.action == 'codeql-scan-v2' && + format('{0}/{1}/{2}', github.event.client_payload.pr_base_sha || 'none', + github.event.client_payload.required_run_id || github.run_id, + github.event.client_payload.producer_source_sha || 'missing-source') || + format('{0}/{1}', github.event.client_payload.pr_base_sha || 'none', + github.event.client_payload.required_run_id || github.run_id) }} on: repository_dispatch: - types: [codeql-scan] + types: [codeql-scan, codeql-scan-v2] concurrency: group: >- codeql-scan-dispatch-${{ github.event.client_payload.target_repository || github.repository }}-${{ - github.event.client_payload.pr_number || github.run_id }}-${{ - github.event.client_payload.required_language || 'unknown-language' }} + github.event.client_payload.pr_number || github.run_id }} cancel-in-progress: true permissions: @@ -36,7 +44,9 @@ permissions: jobs: validate-dispatch: name: validate-dispatch - runs-on: ubuntu-24.04 + runs-on: + group: CWL central CodeQL + labels: [self-hosted, linux, x64] timeout-minutes: 8 permissions: contents: read @@ -50,8 +60,11 @@ jobs: head_sha: ${{ steps.validate.outputs.head_sha }} matrix: ${{ steps.validate.outputs.matrix }} required_run_id: ${{ steps.validate.outputs.required_run_id }} - required_job_id: ${{ steps.validate.outputs.required_job_id }} - required_language: ${{ steps.validate.outputs.required_language }} + required_jobs: ${{ steps.validate.outputs.required_jobs }} + rerun_mode: ${{ steps.validate.outputs.rerun_mode }} + rerun_schema: ${{ steps.validate.outputs.rerun_schema }} + producer_source_sha: ${{ steps.validate.outputs.producer_source_sha }} + dispatch_protocol: ${{ steps.validate.outputs.dispatch_protocol }} steps: - name: Exchange OpenCode app token for target repository metadata reads id: metadata_read_app_token @@ -140,46 +153,209 @@ jobs: DISPATCH_ACTOR: ${{ github.triggering_actor }} DISPATCH_SENDER: ${{ github.event.sender.login || '' }} ALLOWED_DISPATCH_ACTOR: ${{ vars.OPENCODE_REPOSITORY_DISPATCH_ACTOR }} + DISPATCH_PROTOCOL: ${{ github.event.action }} TARGET_REPOSITORY: ${{ github.event.client_payload.target_repository }} PR_NUMBER: ${{ github.event.client_payload.pr_number }} SUPPLIED_BASE_REF: ${{ github.event.client_payload.pr_base_ref || '' }} SUPPLIED_BASE_SHA: ${{ github.event.client_payload.pr_base_sha || '' }} - SUPPLIED_HEAD_REF: ${{ github.event.client_payload.pr_head_ref || '' }} - SUPPLIED_HEAD_SHA: ${{ github.event.client_payload.pr_head_sha || '' }} - SUPPLIED_MATRIX: ${{ github.event.client_payload.matrix || '' }} + SUPPLIED_HEAD_ENVELOPE: ${{ toJSON(github.event.client_payload.pr_head) }} + SUPPLIED_HEAD_SCHEMA: ${{ github.event.client_payload.pr_head.schema || '' }} + SUPPLIED_HEAD_REF: ${{ github.event.client_payload.pr_head.ref || github.event.client_payload.pr_head_ref || '' }} + SUPPLIED_HEAD_SHA: ${{ github.event.client_payload.pr_head.sha || github.event.client_payload.pr_head_sha || '' }} + SUPPLIED_LEGACY_HEAD_REF: ${{ github.event.client_payload.pr_head_ref || '' }} + SUPPLIED_LEGACY_HEAD_SHA: ${{ github.event.client_payload.pr_head_sha || '' }} + SUPPLIED_PRODUCER_SOURCE_SHA: ${{ github.event.client_payload.producer_source_sha || '' }} + SUPPLIED_MATRIX: ${{ toJSON(github.event.client_payload.matrix) }} SUPPLIED_REQUIRED_RUN_ID: ${{ github.event.client_payload.required_run_id || '' }} + SUPPLIED_REQUIRED_JOBS: ${{ toJSON(github.event.client_payload.required_jobs) }} + SUPPLIED_RERUN_MODE: ${{ github.event.client_payload.rerun_mode || '' }} + SUPPLIED_RERUN_REQUEST: ${{ toJSON(github.event.client_payload.rerun_request) }} + # Pre-#2008 payloads still send scalar required_job_id + + # required_language with a one-shard matrix. Synthesize + # required_jobs from those only when the array is empty. SUPPLIED_REQUIRED_JOB_ID: ${{ github.event.client_payload.required_job_id || '' }} SUPPLIED_REQUIRED_LANGUAGE: ${{ github.event.client_payload.required_language || '' }} run: | set -euo pipefail - if [ -z "$ALLOWED_DISPATCH_ACTOR" ] || - [ "$DISPATCH_ACTOR" != "$ALLOWED_DISPATCH_ACTOR" ] || - [ "$DISPATCH_SENDER" != "$ALLOWED_DISPATCH_ACTOR" ]; then - printf '::error::repository_dispatch authorization rejected actor=%s sender=%s because both must match the configured scheduler identity.\n' "${DISPATCH_ACTOR:-}" "${DISPATCH_SENDER:-}" + # ALLOWED_DISPATCH_ACTOR is a comma-separated allowlist shared with + # opencode-review-dispatch.yml and pr-review-fix-scheduler.yml; all + # three parse it the same way. Actor AND sender must both equal the + # SAME listed identity, and an empty allowlist admits nothing. + actor_allowed=0 + IFS=',' read -r -a allowed_dispatch_actors <<<"$ALLOWED_DISPATCH_ACTOR" + for allowed_actor in "${allowed_dispatch_actors[@]}"; do + allowed_actor="${allowed_actor//[[:space:]]/}" + if [ -n "$allowed_actor" ] && + [ "$DISPATCH_ACTOR" = "$allowed_actor" ] && + [ "$DISPATCH_SENDER" = "$allowed_actor" ]; then + actor_allowed=1 + break + fi + done + if [ "$actor_allowed" -ne 1 ]; then + printf '::error::repository_dispatch authorization rejected actor=%s sender=%s because both must match one configured scheduler identity.\n' "${DISPATCH_ACTOR:-}" "${DISPATCH_SENDER:-}" exit 1 fi printf 'Authorized repository_dispatch actor=%s sender=%s target=%s.\n' "$DISPATCH_ACTOR" "$DISPATCH_SENDER" "$TARGET_REPOSITORY" + case "$DISPATCH_PROTOCOL" in + codeql-scan) + dispatch_protocol=legacy-v1 + if [ "$SUPPLIED_HEAD_ENVELOPE" != "null" ] || + [ -n "$SUPPLIED_PRODUCER_SOURCE_SHA" ] || + { [ -n "$SUPPLIED_RERUN_REQUEST" ] && [ "$SUPPLIED_RERUN_REQUEST" != "null" ]; } || + [ -n "$SUPPLIED_RERUN_MODE" ]; then + echo "::error::Legacy CodeQL dispatch rejected v2-only identity fields." + exit 1 + fi + ;; + codeql-scan-v2) + dispatch_protocol=v2 + if [ "$SUPPLIED_HEAD_ENVELOPE" = "null" ]; then + echo "::error::CodeQL v2 dispatch requires the versioned pr_head envelope." + exit 1 + fi + ;; + *) + echo "::error::CodeQL dispatch protocol is unsupported." + exit 1 + ;; + esac + + if [ "$SUPPLIED_HEAD_ENVELOPE" != "null" ]; then + if [ "$(printf '%s' "$SUPPLIED_HEAD_ENVELOPE" | jq -r ' + type == "object" + and ((.ref | type) == "string") + and ((.sha | type) == "string") + ' 2>/dev/null || true)" != "true" ]; then + printf '::error::repository_dispatch supplied invalid pr_head envelope; ref and sha must be strings.\n' + exit 1 + fi + envelope_schema_type="$(printf '%s' "$SUPPLIED_HEAD_ENVELOPE" | jq -r '.schema | type')" + if [ "$envelope_schema_type" = "null" ]; then + printf '::error::repository_dispatch supplied unsupported pr_head schema=.\n' + exit 1 + fi + if [ "$envelope_schema_type" != "string" ]; then + printf '::error::repository_dispatch supplied invalid pr_head envelope; schema must be a string.\n' + exit 1 + fi + envelope_schema="$(printf '%s' "$SUPPLIED_HEAD_ENVELOPE" | jq -r '.schema')" + envelope_ref="$(printf '%s' "$SUPPLIED_HEAD_ENVELOPE" | jq -r '.ref')" + envelope_sha="$(printf '%s' "$SUPPLIED_HEAD_ENVELOPE" | jq -r '.sha')" + if [ "$envelope_schema" != "1" ]; then + printf '::error::repository_dispatch supplied unsupported pr_head schema=%s.\n' "$envelope_schema" + exit 1 + fi + if [ "$SUPPLIED_HEAD_SCHEMA" != "$envelope_schema" ] || + [ "$SUPPLIED_HEAD_REF" != "$envelope_ref" ] || + [ "$SUPPLIED_HEAD_SHA" != "$envelope_sha" ]; then + printf '::error::repository_dispatch pr_head envelope disagrees with extracted workflow inputs.\n' + exit 1 + fi + if { [ -n "$SUPPLIED_LEGACY_HEAD_REF" ] || [ -n "$SUPPLIED_LEGACY_HEAD_SHA" ]; } && + { [ "$SUPPLIED_LEGACY_HEAD_REF" != "$envelope_ref" ] || + [ "$SUPPLIED_LEGACY_HEAD_SHA" != "$envelope_sha" ]; }; then + printf '::error::repository_dispatch rejected conflicting nested and legacy pr_head identity.\n' + exit 1 + fi + elif [ -n "$SUPPLIED_HEAD_SCHEMA" ]; then + printf '::error::repository_dispatch supplied unsupported pr_head schema=%s.\n' "$SUPPLIED_HEAD_SCHEMA" + exit 1 + fi + if ! [[ "$TARGET_REPOSITORY" =~ ^ContextualWisdomLab/[A-Za-z0-9_.-]+$ ]] || ! [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]]; then printf '::error::PR metadata validation rejected a target outside ContextualWisdomLab or an invalid pull request number. target=%s pr=%s\n' "${TARGET_REPOSITORY:-}" "${PR_NUMBER:-}" exit 1 fi + if [ "$dispatch_protocol" = v2 ] && + ! [[ "$SUPPLIED_PRODUCER_SOURCE_SHA" =~ ^[0-9a-fA-F]{40}$ ]]; then + echo "::error::CodeQL producer source is missing or malformed." + exit 1 + fi matrix_json="$(printf '%s' "$SUPPLIED_MATRIX" | jq -c '.' 2>/dev/null || true)" + jobs_json="$(printf '%s' "$SUPPLIED_REQUIRED_JOBS" | jq -c '.' 2>/dev/null || true)" + rerun_request_json="$(printf '%s' "$SUPPLIED_RERUN_REQUEST" | jq -c '.' 2>/dev/null || true)" if [ -z "$matrix_json" ] || - [ "$(printf '%s' "$matrix_json" | jq 'type == "array" and length == 1')" != "true" ] || - [ "$(printf '%s' "$matrix_json" | jq '[.[] | select((.language | type == "string") and (.language | test("^[a-z0-9-]+$")) and (."build-mode" | type == "string"))] | length == ($ARGS.positional[0] | tonumber)' --args "$(printf '%s' "$matrix_json" | jq 'length')")" != "true" ]; then - printf '::error::CodeQL scan dispatch matrix must contain exactly one valid language/build-mode shard. matrix=%s\n' "${SUPPLIED_MATRIX:-}" + [ "$(printf '%s' "$matrix_json" | jq 'type == "array" and length >= 1')" != "true" ] || + [ "$(printf '%s' "$matrix_json" | jq '[.[] | select((.language | type == "string") and (.language | test("^[a-z0-9-]+$")) and (."build-mode" | type == "string"))] | length == ($ARGS.positional[0] | tonumber)' --args "$(printf '%s' "$matrix_json" | jq 'length')")" != "true" ] || + [ "$(printf '%s' "$matrix_json" | jq '(map(.language) | unique | length) == (map(.language) | length)')" != "true" ]; then + printf '::error::CodeQL scan dispatch matrix must contain at least one valid language/build-mode shard with unique languages. matrix=%s\n' "${SUPPLIED_MATRIX:-}" exit 1 fi - matrix_language="$(printf '%s' "$matrix_json" | jq -r '.[0].language // empty')" - if ! [[ "$SUPPLIED_REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || - ! [[ "$SUPPLIED_REQUIRED_JOB_ID" =~ ^[1-9][0-9]*$ ]] || - [ "$SUPPLIED_REQUIRED_LANGUAGE" != "$matrix_language" ]; then - printf '::error::CodeQL wake identity is missing, non-canonical, or does not match the dispatched language.\n' + rerun_mode="${SUPPLIED_RERUN_MODE:-failed}" + rerun_schema="legacy-0" + if [ "$rerun_mode" != "failed" ] && [ "$rerun_mode" != "all" ]; then + printf '::error::CodeQL rerun mode is invalid.\n' + exit 1 + fi + if [ -n "$rerun_request_json" ] && [ "$rerun_request_json" != "null" ]; then + if [ -n "$jobs_json" ] && [ "$(printf '%s' "$jobs_json" | jq '(. != null) and (. != [])')" = "true" ] || + [ -n "$SUPPLIED_RERUN_MODE" ] || [ -n "$SUPPLIED_REQUIRED_JOB_ID" ] || + [ -n "$SUPPLIED_REQUIRED_LANGUAGE" ]; then + printf '::error::CodeQL dispatch rejected conflicting legacy and nested rerun envelopes.\n' + exit 1 + fi + rerun_schema_type="$(printf '%s' "$rerun_request_json" | jq -r '.schema | type')" + if [ "$rerun_schema_type" = "null" ]; then + printf '::error::unsupported CodeQL rerun schema=.\n' + exit 1 + fi + if [ "$rerun_schema_type" != "string" ]; then + printf '::error::CodeQL rerun schema must be a string.\n' + exit 1 + fi + rerun_schema="$(printf '%s' "$rerun_request_json" | jq -r '.schema')" + if [ "$rerun_schema" != "1" ]; then + printf '::error::unsupported CodeQL rerun schema=%s.\n' "$rerun_schema" + exit 1 + fi + if [ "$(printf '%s' "$rerun_request_json" | jq ' + type == "object" + and ((keys | sort) == ["mode", "required_jobs", "schema"]) + and (.mode == "failed" or .mode == "all") + and (.required_jobs | type == "array") + ')" != "true" ]; then + printf '::error::CodeQL rerun mode or required job envelope is invalid.\n' + exit 1 + fi + rerun_mode="$(printf '%s' "$rerun_request_json" | jq -r '.mode')" + jobs_json="$(printf '%s' "$rerun_request_json" | jq -c '.required_jobs')" + fi + if [ -z "$jobs_json" ] || + [ "$(printf '%s' "$jobs_json" | jq '(. == null) or (. == [])')" = "true" ]; then + if [ "$(printf '%s' "$matrix_json" | jq 'type == "array" and length == 1')" = "true" ] && + [[ "$SUPPLIED_REQUIRED_JOB_ID" =~ ^[1-9][0-9]*$ ]] && + [ "$SUPPLIED_REQUIRED_LANGUAGE" = "$(printf '%s' "$matrix_json" | jq -r '.[0].language // empty')" ]; then + jobs_json="$(jq -nc --arg language "$SUPPLIED_REQUIRED_LANGUAGE" --arg job_id "$SUPPLIED_REQUIRED_JOB_ID" '[{language: $language, job_id: ($job_id | tonumber)}]')" + fi + fi + if [ -z "$jobs_json" ] || + [ "$(jq -n --argjson matrix "$matrix_json" --argjson jobs "$jobs_json" ' + ($jobs | type == "array") + and (($jobs | length) == ($matrix | length)) + and ($jobs | all( + (.language | type == "string") + and (.language | test("^[a-z0-9-]+$")) + and ( + ((.job_id | type == "number") and (.job_id == (.job_id | floor)) and (.job_id >= 1)) + or ((.job_id | type == "string") and (.job_id | test("^[1-9][0-9]*$"))) + ) + )) + and (($jobs | map(.language) | sort) == ($matrix | map(.language) | sort)) + and (($jobs | map(.language) | unique | length) == ($jobs | length)) + and (($jobs | map(.job_id | tostring) | unique | length) == ($jobs | length)) + ')" != "true" ]; then + printf '::error::CodeQL wake identity is missing, non-canonical, or does not match the dispatched languages one-to-one.\n' exit 1 fi + if ! [[ "$SUPPLIED_REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]]; then + printf '::error::CodeQL wake identity is missing, non-canonical, or does not match the dispatched languages one-to-one.\n' + exit 1 + fi + jobs_json="$(printf '%s' "$jobs_json" | jq -c 'map({language, job_id: (.job_id | tonumber)})')" pull_request_json="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" live_base_repository="$(jq -r '.base.repo.full_name // empty' <<<"$pull_request_json")" @@ -188,6 +364,7 @@ jobs: live_base_sha="$(jq -r '.base.sha // empty' <<<"$pull_request_json")" live_head_ref="$(jq -r '.head.ref // empty' <<<"$pull_request_json")" live_head_sha="$(jq -r '.head.sha // empty' <<<"$pull_request_json")" + live_merge_commit_sha="$(jq -r '.merge_commit_sha // empty' <<<"$pull_request_json")" live_state="$(jq -r '.state // empty' <<<"$pull_request_json")" if [ "$live_state" != "open" ] || @@ -210,6 +387,26 @@ jobs: printf '::error::repository_dispatch metadata does not match the live pull request: %s. supplied_base=%s/%s live_base=%s/%s supplied_head=%s/%s live_head=%s/%s\n' "$(IFS=,; printf '%s' "${mismatches[*]}")" "${SUPPLIED_BASE_REF:-}" "${SUPPLIED_BASE_SHA:-}" "$live_base_ref" "$live_base_sha" "${SUPPLIED_HEAD_REF:-}" "${SUPPLIED_HEAD_SHA:-}" "$live_head_ref" "$live_head_sha" exit 1 fi + if [ "$dispatch_protocol" = v2 ]; then + if ! [[ "$live_merge_commit_sha" =~ ^[0-9a-fA-F]{40}$ ]] || + [ "${SUPPLIED_PRODUCER_SOURCE_SHA,,}" != "${live_merge_commit_sha,,}" ]; then + echo "::error::CodeQL producer revision does not match the live pull request merge revision." + exit 1 + fi + producer_commit_json="$(gh api "repos/${TARGET_REPOSITORY}/git/commits/${SUPPLIED_PRODUCER_SOURCE_SHA}")" + if ! printf '%s' "$producer_commit_json" | jq -e \ + --arg source "${SUPPLIED_PRODUCER_SOURCE_SHA,,}" \ + --arg base "${live_base_sha,,}" \ + --arg head "${live_head_sha,,}" ' + ((.sha // "" | ascii_downcase) == $source) + and ((.parents // []) | length == 2) + and ((.parents[0].sha // "" | ascii_downcase) == $base) + and ((.parents[1].sha // "" | ascii_downcase) == $head) + ' >/dev/null; then + echo "::error::CodeQL producer revision is not the exact live base/head merge." + exit 1 + fi + fi { printf 'target_repository=%s\n' "$TARGET_REPOSITORY" @@ -222,18 +419,25 @@ jobs: printf '%s\n' "$matrix_json" echo "EOF" printf 'required_run_id=%s\n' "$SUPPLIED_REQUIRED_RUN_ID" - printf 'required_job_id=%s\n' "$SUPPLIED_REQUIRED_JOB_ID" - printf 'required_language=%s\n' "$SUPPLIED_REQUIRED_LANGUAGE" + printf 'rerun_mode=%s\n' "$rerun_mode" + printf 'rerun_schema=%s\n' "$rerun_schema" + printf 'producer_source_sha=%s\n' "$SUPPLIED_PRODUCER_SOURCE_SHA" + printf 'dispatch_protocol=%s\n' "$dispatch_protocol" + echo "required_jobs<>"$GITHUB_OUTPUT" printf 'Validated current live metadata for %s#%s: base=%s/%s head=%s/%s.\n' "$TARGET_REPOSITORY" "$PR_NUMBER" "$live_base_ref" "$live_base_sha" "$live_head_ref" "$live_head_sha" scan: name: CodeQL dispatch scan (${{ matrix.language }}) needs: validate-dispatch - runs-on: ubuntu-24.04 + runs-on: + group: CWL central CodeQL + labels: [self-hosted, linux, x64] timeout-minutes: 30 permissions: - actions: write + actions: read contents: read security-events: read id-token: write @@ -315,6 +519,7 @@ jobs: } >>"$GITHUB_OUTPUT" - name: Re-validate live pull request metadata before privileged scan + id: live_metadata env: GH_TOKEN: ${{ steps.target_app_token.outputs.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }} TARGET_REPOSITORY: ${{ needs.validate-dispatch.outputs.target_repository }} @@ -340,7 +545,7 @@ jobs: exit 1 fi - - name: Fetch the pinned CodeQL SARIF gate script + - name: Fetch the pinned CodeQL SARIF gate and GHAS identity scripts env: GH_TOKEN: ${{ github.token }} WORKFLOW_SHA: ${{ github.workflow_sha }} @@ -349,20 +554,19 @@ jobs: gh api "repos/ContextualWisdomLab/.github/contents/scripts/ci/codeql_sarif_gate.py?ref=${WORKFLOW_SHA}" \ --jq .content | base64 --decode >"$RUNNER_TEMP/codeql_sarif_gate.py" python3 -c "import ast; ast.parse(open('$RUNNER_TEMP/codeql_sarif_gate.py').read())" + gh api "repos/ContextualWisdomLab/.github/contents/scripts/ci/codeql_ghas_configuration_identity.py?ref=${WORKFLOW_SHA}" \ + --jq .content | base64 --decode >"$RUNNER_TEMP/codeql_ghas_configuration_identity.py" + python3 -c "import ast; ast.parse(open('$RUNNER_TEMP/codeql_ghas_configuration_identity.py').read())" - name: Materialize pull request head for CodeQL scan - env: - GH_TOKEN: ${{ steps.target_app_token.outputs.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }} - TARGET_REPOSITORY: ${{ needs.validate-dispatch.outputs.target_repository }} - HEAD_SHA: ${{ needs.validate-dispatch.outputs.head_sha }} - run: | - set -euo pipefail - gh auth setup-git - git init -q . - git remote add origin "$GITHUB_SERVER_URL/$TARGET_REPOSITORY.git" - git fetch --no-tags --depth=1 origin "$HEAD_SHA" - git checkout --detach --quiet "$HEAD_SHA" - git cat-file -e "$HEAD_SHA^{commit}" + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: ${{ needs.validate-dispatch.outputs.target_repository }} + ref: ${{ needs.validate-dispatch.outputs.head_sha }} + token: ${{ steps.target_app_token.outputs.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }} + persist-credentials: false + clean: true + fetch-depth: 1 - name: Initialize CodeQL uses: github/codeql-action/init@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 @@ -383,32 +587,173 @@ jobs: id: gate run: python3 "$RUNNER_TEMP/codeql_sarif_gate.py" codeql-results-dispatch + - name: Detect optional Noema analysis-read credential + id: noema_analysis_config + if: always() && steps.live_metadata.outcome == 'success' + env: + TARGET_REPOSITORY: ${{ needs.validate-dispatch.outputs.target_repository }} + NOEMA_APP_CLIENT_ID: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID || '' }} + NOEMA_APP_PRIVATE_KEY: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY || '' }} + run: | + set -euo pipefail + if [ -n "$NOEMA_APP_CLIENT_ID" ] && [ -n "$NOEMA_APP_PRIVATE_KEY" ]; then + printf 'repository=%s\n' "${TARGET_REPOSITORY#*/}" >>"$GITHUB_OUTPUT" + echo "available=true" >>"$GITHUB_OUTPUT" + fi + + - name: Mint target-scoped Noema analysis-read token + id: noema_analysis_token + if: steps.gate.outcome == 'success' && steps.noema_analysis_config.outputs.available == 'true' + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID }} + private-key: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }} + owner: ContextualWisdomLab + repositories: ${{ steps.noema_analysis_config.outputs.repository }} + permission-security-events: read + + - name: Select target CodeQL analysis-read credential + id: ghas_analysis_token + if: steps.gate.outcome == 'success' + env: + TARGET_REPOSITORY: ${{ needs.validate-dispatch.outputs.target_repository }} + TARGET_APP_TOKEN: ${{ steps.target_app_token.outputs.token || '' }} + NOEMA_ANALYSIS_TOKEN: ${{ steps.noema_analysis_token.outputs.token || '' }} + PR_REVIEW_MERGE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || '' }} + OPENCODE_APPROVE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN || '' }} + WORKFLOW_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + + probe_analysis_read() { + token_label="$1" + token="$2" + if [ -z "$token" ]; then + return 1 + fi + if GH_TOKEN="$token" gh api \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + "repos/${TARGET_REPOSITORY}/code-scanning/analyses?per_page=1&tool_name=CodeQL" \ + >/dev/null 2>&1; then + echo "::add-mask::$token" + { + printf 'token=%s\n' "$token" + printf 'source=%s\n' "$token_label" + } >>"$GITHUB_OUTPUT" + echo "Selected ${token_label} after proving target CodeQL analysis-read access." + return 0 + fi + echo "::notice::${token_label} cannot read target CodeQL analyses; trying the next configured credential." + return 1 + } + + if probe_analysis_read "target-app-token" "$TARGET_APP_TOKEN" || + probe_analysis_read "pr-review-merge-token" "$PR_REVIEW_MERGE_TOKEN" || + probe_analysis_read "opencode-approve-token" "$OPENCODE_APPROVE_TOKEN" || + probe_analysis_read "github-token" "$WORKFLOW_TOKEN" || + probe_analysis_read "noema-analysis-token" "$NOEMA_ANALYSIS_TOKEN"; then + exit 0 + fi + + echo "::error::no configured credential can read target CodeQL analyses; GHAS configuration identity cannot be proven." + exit 1 + + - name: Verify GHAS base/head CodeQL configuration identity + id: ghas_configuration_identity + if: steps.gate.outcome == 'success' + env: + GH_TOKEN: ${{ steps.ghas_analysis_token.outputs.token }} + TARGET_REPOSITORY: ${{ needs.validate-dispatch.outputs.target_repository }} + PR_NUMBER: ${{ needs.validate-dispatch.outputs.pr_number }} + BASE_REF: ${{ needs.validate-dispatch.outputs.base_ref }} + BASE_SHA: ${{ needs.validate-dispatch.outputs.base_sha }} + HEAD_SHA: ${{ needs.validate-dispatch.outputs.head_sha }} + LANGUAGE: ${{ matrix.language }} + run: | + set -euo pipefail + # Default setup often lands a fast language before a slower one on the + # same PR head; GHAS can settle "configuration not found" for the + # slower base identity in that window (#2133). Bounded polling waits + # for the scanned language's exact base identity on this exact head + # before any terminal dispatch status is published. + base_ref="$BASE_REF" + case "$base_ref" in + refs/*) ;; + *) base_ref="refs/heads/${base_ref}" ;; + esac + head_ref="refs/pull/${PR_NUMBER}/head" + python3 "$RUNNER_TEMP/codeql_ghas_configuration_identity.py" \ + --repository "$TARGET_REPOSITORY" \ + --base-ref "$base_ref" \ + --base-sha "$BASE_SHA" \ + --head-ref "$head_ref" \ + --head-sha "$HEAD_SHA" \ + --language "$LANGUAGE" + - name: Preserve CodeQL SARIF evidence + id: sarif_upload if: always() && hashFiles('codeql-results-dispatch/**/*.sarif') != '' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: codeql-dispatch-${{ matrix.language }}-${{ github.run_id }}-${{ github.run_attempt }} path: codeql-results-dispatch + if-no-files-found: error retention-days: 7 + - name: Mint target-scoped Noema CodeQL status token + id: noema_status_token + if: always() && steps.noema_analysis_config.outputs.available == 'true' + continue-on-error: true + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID }} + private-key: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }} + owner: ContextualWisdomLab + repositories: ${{ steps.noema_analysis_config.outputs.repository }} + permission-statuses: write + - name: Publish CodeQL dispatch status id: publish_status - if: always() + if: always() && steps.live_metadata.outcome == 'success' env: + NOEMA_STATUS_TOKEN: ${{ steps.noema_status_token.outputs.token || '' }} TARGET_APP_STATUS_TOKEN: ${{ steps.target_app_token.outputs.token || '' }} GITHUB_STATUS_READ_TOKEN: ${{ github.token }} PR_REVIEW_MERGE_STATUS_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || '' }} OPENCODE_APPROVE_STATUS_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN || '' }} TARGET_REPOSITORY: ${{ needs.validate-dispatch.outputs.target_repository }} HEAD_SHA: ${{ needs.validate-dispatch.outputs.head_sha }} + BASE_SHA: ${{ needs.validate-dispatch.outputs.base_sha }} + REQUIRED_RUN_ID: ${{ needs.validate-dispatch.outputs.required_run_id }} + PRODUCER_SOURCE_SHA: ${{ needs.validate-dispatch.outputs.producer_source_sha }} + DISPATCH_PROTOCOL: ${{ needs.validate-dispatch.outputs.dispatch_protocol }} LANGUAGE: ${{ matrix.language }} GATE_OUTCOME: ${{ steps.gate.outcome }} + GHAS_IDENTITY_OUTCOME: ${{ steps.ghas_configuration_identity.outcome }} + SARIF_UPLOAD_OUTCOME: ${{ steps.sarif_upload.outcome }} run: | set -euo pipefail + if [ "${SARIF_UPLOAD_OUTCOME:-}" != "success" ]; then + echo "::error::CodeQL SARIF evidence was not preserved; terminal status publication and exact-run settlement are blocked." + exit 1 + fi case "$GATE_OUTCOME" in success) - state="success" - description="CodeQL dispatch scan passed (no unsuppressed Medium+ findings)" + case "${GHAS_IDENTITY_OUTCOME:-skipped}" in + success) + state="success" + description="CodeQL dispatch scan passed with continuous GHAS configuration identity" + ;; + failure) + state="failure" + description="CodeQL gate passed but GHAS base/head configuration identity is incomplete" + ;; + *) + state="error" + description="CodeQL gate passed without GHAS configuration identity proof (${GHAS_IDENTITY_OUTCOME:-unknown})" + ;; + esac ;; failure) state="failure" @@ -419,6 +764,20 @@ jobs: description="CodeQL dispatch scan did not produce a verdict (${GATE_OUTCOME:-unknown})" ;; esac + case "$DISPATCH_PROTOCOL" in + legacy-v1) + receipt_context="codeql-dispatch/${LANGUAGE}" + receipt_description="$description" + ;; + v2) + receipt_context="codeql-dispatch/${LANGUAGE}/${BASE_SHA}" + receipt_description="cwl1;h=${HEAD_SHA};w=codeql-scan-dispatch;r=${REQUIRED_RUN_ID};s=${PRODUCER_SOURCE_SHA}" + ;; + *) + echo "::error::CodeQL status publication rejected an unknown dispatch protocol." + exit 1 + ;; + esac post_status() { token_label="$1" @@ -430,13 +789,39 @@ jobs: status_error="$(mktemp)" if GH_TOKEN="$token" gh api -X POST "repos/${TARGET_REPOSITORY}/statuses/${HEAD_SHA}" \ -f state="$state" \ - -f context="codeql-dispatch/${LANGUAGE}" \ - -f description="$description" \ + -f context="$receipt_context" \ + -f description="$receipt_description" \ -f target_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" \ >"$status_response" 2>"$status_error"; then + actual_creator="$(jq -r '.creator.login // "" | ascii_downcase' "$status_response" 2>/dev/null || true)" + creator_trusted=false + case "$token_label" in + noema-status-token) + case "$actual_creator" in + cwl-noema-review|cwl-noema-review\[bot\]) creator_trusted=true ;; + esac + ;; + target-app-token|pr-review-merge-token|opencode-approve-token) + case "$actual_creator" in + opencode-agent|opencode-agent\[bot\]) creator_trusted=true ;; + esac + ;; + github-token) + if [ "${TARGET_REPOSITORY,,}" = "contextualwisdomlab/.github" ] && + [ "${GITHUB_REPOSITORY,,}" = "contextualwisdomlab/.github" ] && + [ "$actual_creator" = "github-actions[bot]" ]; then + creator_trusted=true + fi + ;; + esac + if [ "$creator_trusted" = true ]; then + rm -f "$status_response" "$status_error" + echo "Published CodeQL dispatch status to ${TARGET_REPOSITORY}@${HEAD_SHA} using ${token_label}." + return 0 + fi rm -f "$status_response" "$status_error" - echo "Published CodeQL dispatch status to ${TARGET_REPOSITORY}@${HEAD_SHA} using ${token_label}." - return 0 + echo "::notice::CodeQL dispatch status publish using ${token_label} returned unexpected creator=${actual_creator:-missing}; trying the next configured credential." + return 1 fi error_summary="$(head -n 1 "$status_error" | tr -d '\r' || true)" rm -f "$status_response" "$status_error" @@ -448,6 +833,9 @@ jobs: return 1 } + if post_status "noema-status-token" "${NOEMA_STATUS_TOKEN:-}"; then + exit 0 + fi if post_status "target-app-token" "$TARGET_APP_STATUS_TOKEN"; then exit 0 fi @@ -461,71 +849,331 @@ jobs: exit 0 fi + if [ "$GATE_OUTCOME" = "success" ]; then + echo "::notice::Could not publish the CodeQL dispatch status after all configured credentials failed. The exact completed scan and preserved SARIF artifact remain the authenticated fallback evidence." + exit 0 + fi + echo "::error::Could not publish the CodeQL dispatch status after all configured credentials failed; the exact required job will remain failed and will not be woken with stale or missing evidence." exit 1 - - name: Wake exact CodeQL required job - if: >- - always() - && steps.publish_status.outcome == 'success' - && needs.validate-dispatch.outputs.target_repository != '' - && needs.validate-dispatch.outputs.pr_number != '' - && needs.validate-dispatch.outputs.head_sha != '' - && github.event.client_payload.required_run_id != '' - && github.event.client_payload.required_job_id != '' + settle-required-run: + name: settle exact required run + needs: [validate-dispatch, scan] + if: >- + always() + && needs.validate-dispatch.result == 'success' + && needs.scan.result != 'cancelled' + && needs.scan.result != 'skipped' + runs-on: + group: CWL central CodeQL + labels: [self-hosted, linux, x64] + timeout-minutes: 8 + permissions: + actions: write + contents: read + id-token: write + steps: + - name: Exchange OpenCode app token for run settlement + id: target_app_token env: - GH_TOKEN: ${{ needs.validate-dispatch.outputs.target_repository == github.repository && github.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }} + OIDC_AUDIENCE: opencode-github-action + OPENCODE_API_BASE_URL: https://api.opencode.ai + run: | + set -euo pipefail + + mark_unavailable() { + echo "available=false" >>"$GITHUB_OUTPUT" + } + + if [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || + [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then + echo "OpenCode app token exchange unavailable: OIDC request environment is missing." + mark_unavailable + exit 0 + fi + + request_url="${ACTIONS_ID_TOKEN_REQUEST_URL}" + separator="&" + case "$request_url" in + *\?*) ;; + *) separator="?" ;; + esac + + if ! oidc_response="$( + curl -fsS \ + -H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \ + "${request_url}${separator}audience=${OIDC_AUDIENCE}" + )"; then + echo "OpenCode app token exchange unavailable: OIDC token request did not complete." + mark_unavailable + exit 0 + fi + + oidc_token="$(jq -r '.value // empty' <<<"$oidc_response")" + if [ -z "$oidc_token" ]; then + echo "OpenCode app token exchange unavailable: OIDC token response was empty." + mark_unavailable + exit 0 + fi + + if ! token_response="$( + curl -fsS \ + -X POST \ + -H "Authorization: Bearer ${oidc_token}" \ + "${OPENCODE_API_BASE_URL}/exchange_github_app_token" + )"; then + echo "OpenCode app token exchange unavailable: app token request did not complete." + mark_unavailable + exit 0 + fi + + app_token="$(jq -r '.token // empty' <<<"$token_response")" + if [ -z "$app_token" ]; then + echo "OpenCode app token exchange unavailable: app token response was empty." + mark_unavailable + exit 0 + fi + + echo "::add-mask::$app_token" + { + echo "available=true" + echo "token=$app_token" + } >>"$GITHUB_OUTPUT" + + - name: Resolve Noema settlement token configuration + id: noema_settlement_config + env: + NOEMA_APP_CLIENT_ID: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID || '' }} + NOEMA_APP_PRIVATE_KEY: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY || '' }} + TARGET_REPOSITORY: ${{ needs.validate-dispatch.outputs.target_repository }} + run: | + set -euo pipefail + if [ -n "$NOEMA_APP_CLIENT_ID" ] && [ -n "$NOEMA_APP_PRIVATE_KEY" ]; then + printf 'repository=%s\n' "${TARGET_REPOSITORY#*/}" >>"$GITHUB_OUTPUT" + echo "available=true" >>"$GITHUB_OUTPUT" + fi + + - name: Mint target-scoped Noema CodeQL settlement token + id: noema_settlement_token + if: steps.noema_settlement_config.outputs.available == 'true' + continue-on-error: true + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID }} + private-key: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }} + owner: ContextualWisdomLab + repositories: ${{ steps.noema_settlement_config.outputs.repository }} + permission-actions: write + + - name: Settle exact CodeQL required run + env: + NOEMA_WAKE_TOKEN: ${{ steps.noema_settlement_token.outputs.token || '' }} + TARGET_APP_WAKE_TOKEN: ${{ steps.target_app_token.outputs.token || '' }} + PR_REVIEW_MERGE_WAKE_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || '' }} + OPENCODE_APPROVE_WAKE_TOKEN: ${{ secrets.OPENCODE_APPROVE_TOKEN || '' }} + GITHUB_WAKE_TOKEN: ${{ needs.validate-dispatch.outputs.target_repository == github.repository && github.token || '' }} + HANDLER_READ_TOKEN: ${{ github.token }} TARGET_REPOSITORY: ${{ needs.validate-dispatch.outputs.target_repository }} PR_NUMBER: ${{ needs.validate-dispatch.outputs.pr_number }} + BASE_REF: ${{ needs.validate-dispatch.outputs.base_ref }} + BASE_SHA: ${{ needs.validate-dispatch.outputs.base_sha }} + HEAD_REF: ${{ needs.validate-dispatch.outputs.head_ref }} HEAD_SHA: ${{ needs.validate-dispatch.outputs.head_sha }} REQUIRED_RUN_ID: ${{ needs.validate-dispatch.outputs.required_run_id }} - REQUIRED_JOB_ID: ${{ needs.validate-dispatch.outputs.required_job_id }} - REQUIRED_LANGUAGE: ${{ needs.validate-dispatch.outputs.required_language }} - WAKE_TOKEN_SOURCE: ${{ needs.validate-dispatch.outputs.target_repository == github.repository && 'github-token' || secrets.PR_REVIEW_MERGE_TOKEN != '' && 'PR_REVIEW_MERGE_TOKEN' || secrets.OPENCODE_APPROVE_TOKEN != '' && 'OPENCODE_APPROVE_TOKEN' || 'unavailable' }} + REQUIRED_JOBS: ${{ needs.validate-dispatch.outputs.required_jobs }} + RERUN_MODE: ${{ needs.validate-dispatch.outputs.rerun_mode }} + RERUN_SCHEMA: ${{ needs.validate-dispatch.outputs.rerun_schema }} + MAX_CODEQL_RERUN_ATTEMPT: "48" + PRODUCER_SOURCE_SHA: ${{ needs.validate-dispatch.outputs.producer_source_sha }} run: | set -euo pipefail - if [ -z "${GH_TOKEN:-}" ] || [ "$WAKE_TOKEN_SOURCE" = "unavailable" ]; then - echo "::error::Actions-capable CodeQL wake credential is unavailable." + + run_api() { + token_label="$1" + token="$2" + shift 2 + if [ -z "$token" ]; then + return 1 + fi + api_response="" + if api_response="$(GH_TOKEN="$token" gh api "$@")"; then + printf '%s\n' "$api_response" + echo "::notice::CodeQL settlement API used ${token_label}." >&2 + return 0 + fi + echo "::notice::CodeQL settlement API using ${token_label} did not succeed." >&2 + return 1 + } + + github_api() { + run_api "noema-settlement-token" "${NOEMA_WAKE_TOKEN:-}" "$@" || + run_api "target-app-token" "$TARGET_APP_WAKE_TOKEN" "$@" || + run_api "pr-review-merge-token" "$PR_REVIEW_MERGE_WAKE_TOKEN" "$@" || + run_api "opencode-approve-token" "$OPENCODE_APPROVE_WAKE_TOKEN" "$@" || + run_api "github-token" "$GITHUB_WAKE_TOKEN" "$@" + } + + if ! pull="$(github_api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")"; then + echo "::error::CodeQL settlement could not read the current pull request." exit 1 fi - if ! [[ "$REQUIRED_RUN_ID" =~ ^[1-9][0-9]*$ ]] || - ! [[ "$REQUIRED_JOB_ID" =~ ^[1-9][0-9]*$ ]] || - ! [[ "$REQUIRED_LANGUAGE" =~ ^[a-z0-9-]+$ ]]; then - echo "::error::CodeQL wake identity is non-canonical." + if [ "$(printf '%s' "$pull" | jq -r '.state // empty')" != "open" ] || + [ "$(printf '%s' "$pull" | jq -r '.base.repo.full_name // empty')" != "$TARGET_REPOSITORY" ] || + [ "$(printf '%s' "$pull" | jq -r '.base.ref // empty')" != "$BASE_REF" ] || + [ "$(printf '%s' "$pull" | jq -r '.base.sha // empty')" != "$BASE_SHA" ] || + [ "$(printf '%s' "$pull" | jq -r '.head.repo.full_name // empty')" != "$TARGET_REPOSITORY" ] || + [ "$(printf '%s' "$pull" | jq -r '.head.ref // empty')" != "$HEAD_REF" ] || + [ "$(printf '%s' "$pull" | jq -r '.head.sha // empty')" != "$HEAD_SHA" ]; then + echo "::error::CodeQL settlement rejected a closed PR, changed base, or stale head." exit 1 fi - pull="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" - live_state="$(printf '%s' "$pull" | jq -r '.state // empty')" - live_head="$(printf '%s' "$pull" | jq -r '.head.sha // empty')" - if [ "$live_state" != "open" ] || [ "$live_head" != "$HEAD_SHA" ]; then - echo "::error::CodeQL wake rejected a closed PR or stale head." + if ! required_run="$(github_api "repos/${TARGET_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")"; then + echo "::error::CodeQL settlement could not read the required run." exit 1 fi - - run="$(gh api "repos/${TARGET_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}")" - run_identity="$(printf '%s' "$run" | jq -r --arg head "$HEAD_SHA" --argjson run_id "$REQUIRED_RUN_ID" ' + if [ "$(printf '%s' "$required_run" | jq -r --arg head "$HEAD_SHA" --argjson run_id "$REQUIRED_RUN_ID" ' select(.id == $run_id) | select(.event == "pull_request") | select(.path == ".github/workflows/codeql-pr.yml") | select(.head_sha == $head) - | .id // empty - ')" - expected_name="CodeQL compatibility analysis (${REQUIRED_LANGUAGE})" - job="$(gh api "repos/${TARGET_REPOSITORY}/actions/jobs/${REQUIRED_JOB_ID}")" - job_identity="$(printf '%s' "$job" | jq -r --arg head "$HEAD_SHA" --arg name "$expected_name" --argjson run_id "$REQUIRED_RUN_ID" --argjson job_id "$REQUIRED_JOB_ID" ' - select(.id == $job_id) - | select(.run_id == $run_id) - | select(.head_sha == $head) - | select(.name == $name) | select(.status == "completed" and .conclusion == "failure") + | select((.run_attempt | type) == "number") + | select(.run_attempt == (.run_attempt | floor) and .run_attempt >= 1) | .id // empty - ')" - if [ "$run_identity" != "$REQUIRED_RUN_ID" ] || - [ "$job_identity" != "$REQUIRED_JOB_ID" ]; then - echo "::error::CodeQL wake rejected missing or ambiguous exact run/job identity." + ')" != "$REQUIRED_RUN_ID" ]; then + echo "::error::CodeQL settlement rejected the required run identity." + exit 1 + fi + + required_run_attempt="$(printf '%s' "$required_run" | jq -r '.run_attempt')" + if ! [[ "$MAX_CODEQL_RERUN_ATTEMPT" =~ ^[1-9][0-9]*$ ]] || + [ "$required_run_attempt" -ge "$MAX_CODEQL_RERUN_ATTEMPT" ]; then + rerun_languages="$(printf '%s' "$REQUIRED_JOBS" | jq -r 'map(.language) | sort | join(",")')" + printf '::error::codeql_settlement phase=pre_mutation reason=rerun_budget_exhausted run_id=%s run_attempt=%s max_rerun_attempt=%s rerun_schema=%s languages=%s handler_run_id=%s handler_run_attempt=%s\n' \ + "$REQUIRED_RUN_ID" "$required_run_attempt" "$MAX_CODEQL_RERUN_ATTEMPT" \ + "$RERUN_SCHEMA" "$rerun_languages" "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" + exit 1 + fi + + if ! required_job_pages="$(github_api --paginate "repos/${TARGET_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}/jobs?per_page=100" | jq -s .)"; then + echo "::error::CodeQL settlement could not read the required jobs." + exit 1 + fi + required_job_list="$(printf '%s' "$required_job_pages" | jq -c '[.[] | .jobs[]?]')" + while IFS= read -r required_job; do + language="$(printf '%s' "$required_job" | jq -r '.language // empty')" + job_id="$(printf '%s' "$required_job" | jq -r '.job_id // empty')" + expected_name="CodeQL compatibility analysis (${language})" + match_count="$(printf '%s' "$required_job_list" | jq --arg language "$language" --arg name "$expected_name" --arg head "$HEAD_SHA" --argjson run_id "$REQUIRED_RUN_ID" --argjson job_id "$job_id" --arg mode "$RERUN_MODE" ' + [.[] | select( + .id == $job_id + and .run_id == $run_id + and .head_sha == $head + and .name == $name + and .status == "completed" + and ( + ($mode == "failed" and .conclusion == "failure") + or ($mode == "all" and (.conclusion == "success" or .conclusion == "failure")) + ) + )] | length + ')" + if [ "$match_count" -ne 1 ]; then + echo "::error::CodeQL settlement rejected missing or ambiguous exact job identity for ${language}." + exit 1 + fi + done < <(printf '%s' "$REQUIRED_JOBS" | jq -c '.[]') + + required_job_ids="$(printf '%s' "$REQUIRED_JOBS" | jq -c '[.[].job_id]')" + if [ "$RERUN_MODE" = "failed" ] && + [ "$(printf '%s' "$required_job_list" | jq --argjson required_ids "$required_job_ids" ' + [.[] | .id as $id | select(.status == "completed" and .conclusion == "failure" and ($required_ids | index($id) | not))] | length + ')" -ne 0 ]; then + echo "::error::CodeQL settlement rejected unrelated failed jobs outside the exact language map." + exit 1 + fi + + if ! handler_job_pages="$(GH_TOKEN="$HANDLER_READ_TOKEN" gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/jobs?per_page=100" | jq -s .)" || + ! handler_artifact_pages="$(GH_TOKEN="$HANDLER_READ_TOKEN" gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/artifacts?per_page=100" | jq -s .)"; then + echo "::error::CodeQL settlement could not read exact handler evidence." exit 1 fi + handler_jobs="$(printf '%s' "$handler_job_pages" | jq -c '[.[] | .jobs[]?]')" + handler_artifacts="$(printf '%s' "$handler_artifact_pages" | jq -c '[.[] | .artifacts[]?]')" + while IFS= read -r required_job; do + language="$(printf '%s' "$required_job" | jq -r '.language')" + expected_job_name="CodeQL dispatch scan (${language})" + expected_artifact_name="codeql-dispatch-${language}-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" + handler_job_count="$(printf '%s' "$handler_jobs" | jq --arg name "$expected_job_name" --argjson attempt "$GITHUB_RUN_ATTEMPT" ' + [.[] | select( + .name == $name + and .status == "completed" + and (.conclusion == "success" or .conclusion == "failure") + and .run_attempt == $attempt + and ([.steps[]? | select(.name == "Enforce CodeQL Medium+ SARIF gate" and (.conclusion == "success" or .conclusion == "failure"))] | length) == 1 + and ([.steps[]? | select(.name == "Preserve CodeQL SARIF evidence" and .conclusion == "success")] | length) == 1 + )] | length + ')" + handler_artifact_count="$(printf '%s' "$handler_artifacts" | jq --arg name "$expected_artifact_name" ' + [.[] | select(.name == $name and (.expired == false) and (.size_in_bytes > 0))] | length + ')" + if [ "$handler_job_count" -ne 1 ] || [ "$handler_artifact_count" -ne 1 ]; then + echo "::error::CodeQL settlement rejected incomplete handler gate or SARIF evidence for ${language}." + exit 1 + fi + clean_gate_count="$(printf '%s' "$handler_jobs" | jq --arg name "$expected_job_name" --argjson attempt "$GITHUB_RUN_ATTEMPT" ' + [.[] | select( + .name == $name + and .status == "completed" + and .run_attempt == $attempt + and ([.steps[]? | select(.name == "Enforce CodeQL Medium+ SARIF gate" and .conclusion == "success")] | length) == 1 + )] | length + ')" + ghas_identity_count="$(printf '%s' "$handler_jobs" | jq --arg name "$expected_job_name" --argjson attempt "$GITHUB_RUN_ATTEMPT" ' + [.[] | select( + .name == $name + and .status == "completed" + and .run_attempt == $attempt + and ([.steps[]? | select(.name == "Verify GHAS base/head CodeQL configuration identity" and .conclusion == "success")] | length) == 1 + )] | length + ')" + if [ "$clean_gate_count" -eq 1 ] && [ "$ghas_identity_count" -ne 1 ]; then + echo "::error::CodeQL settlement rejected missing GHAS configuration identity proof for ${language}." + exit 1 + fi + done < <(printf '%s' "$REQUIRED_JOBS" | jq -c '.[]') + + case "$RERUN_MODE" in + failed) rerun_endpoint="rerun-failed-jobs" ;; + all) rerun_endpoint="rerun" ;; + *) + echo "::error::CodeQL settlement rejected an unsupported rerun mode." + exit 1 + ;; + esac + + post_wake() { + token_label="$1" + token="$2" + if [ -z "$token" ]; then + return 1 + fi + if GH_TOKEN="$token" gh api -X POST "repos/${TARGET_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}/${rerun_endpoint}" >/dev/null; then + echo "Re-ran exact CodeQL required run ${REQUIRED_RUN_ID} mode=${RERUN_MODE} head=${HEAD_SHA} using ${token_label}." + return 0 + fi + echo "::notice::CodeQL settlement POST using ${token_label} did not succeed." + return 1 + } + + if post_wake "noema-settlement-token" "${NOEMA_WAKE_TOKEN:-}" || + post_wake "target-app-token" "$TARGET_APP_WAKE_TOKEN" || + post_wake "pr-review-merge-token" "$PR_REVIEW_MERGE_WAKE_TOKEN" || + post_wake "opencode-approve-token" "$OPENCODE_APPROVE_WAKE_TOKEN" || + post_wake "github-token" "$GITHUB_WAKE_TOKEN"; then + exit 0 + fi - gh api -X POST "repos/${TARGET_REPOSITORY}/actions/jobs/${REQUIRED_JOB_ID}/rerun" >/dev/null - echo "Re-ran exact failed CodeQL job ${REQUIRED_JOB_ID} for ${REQUIRED_LANGUAGE} on ${HEAD_SHA}." + echo "::error::CodeQL settlement could not enqueue verified run-wide recovery." + exit 1 diff --git a/.github/workflows/company-default-head-trust-gate.yml b/.github/workflows/company-default-head-trust-gate.yml new file mode 100644 index 0000000000..08745e92d0 --- /dev/null +++ b/.github/workflows/company-default-head-trust-gate.yml @@ -0,0 +1,64 @@ +name: Reusable company default-head trust gate + +on: + workflow_call: + inputs: + target_runner_label: + required: true + type: string + trusted_ref_sha: + required: false + type: string + default: "" + trusted_ref_ack: + required: false + type: string + default: "" + outputs: + runner_matrix: + value: ${{ jobs.validate.outputs.runner_matrix }} + +permissions: + contents: read + +jobs: + validate: + runs-on: + group: CWL CI isolated + labels: [self-hosted, Linux, X64, cwlab-ci-isolated] + timeout-minutes: 5 + outputs: + runner_matrix: ${{ steps.runner.outputs.runner_matrix }} + steps: + - id: runner + shell: bash + env: + TARGET: ${{ inputs.target_runner_label }} + run: | + case "$TARGET" in + all) matrix='["itxllmpgw01","itxllmpgw02","xtrmsales-llmgw1","xtrmsales-llmgw2"]' ;; + itxllmpgw01|itxllmpgw02|itxllmpgw03|itxllmpgw04|xtrmsales-llmgw1|xtrmsales-llmgw2|llm-gateway-dev|itx-llm-operation-1|itx-llm-operation-2) + matrix="[\"$TARGET\"]" + ;; + *) echo "unsupported target_runner_label=$TARGET" >&2; exit 2 ;; + esac + echo "runner_matrix=$matrix" >> "$GITHUB_OUTPUT" + - shell: bash + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch || 'main' }} + REF_NAME: ${{ github.ref_name }} + SHA: ${{ github.sha }} + GH_TOKEN: ${{ github.token }} + REPOSITORY: ${{ github.repository }} + run: | + set -Eeuo pipefail + if [ "$REF_NAME" != "$DEFAULT_BRANCH" ]; then + echo "self-hosted failure diagnostics require the protected default branch" >&2 + exit 2 + fi + current_sha="$(gh api "repos/$REPOSITORY/commits/$DEFAULT_BRANCH" --jq .sha)" + if [ "$SHA" != "$current_sha" ]; then + echo "refusing stale default-branch run sha=$SHA current_sha=$current_sha" >&2 + exit 2 + fi + echo "trusted_ref_status=current_default_head sha=$SHA" diff --git a/.github/workflows/company-settlement-validate.yml b/.github/workflows/company-settlement-validate.yml new file mode 100644 index 0000000000..2cd0498ba3 --- /dev/null +++ b/.github/workflows/company-settlement-validate.yml @@ -0,0 +1,56 @@ +# Source-only fixed-pool candidate. Caller-context group admission remains separate. +name: Company settlement validate +on: + workflow_call: {} +permissions: + contents: read +jobs: + validate: + runs-on: + group: CWL CI isolated + labels: [self-hosted, Linux, X64, cwlab-ci-isolated] + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + persist-credentials: false + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.13" + - uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 # v10.1.0 + - name: Install locked test dependencies + run: uv pip install --system -r requirements.txt -r requirements-dev.txt + - name: Start isolated PostgreSQL without a TCP listener + shell: bash + run: | + set -Eeuo pipefail + pg_bin="$(pg_config --bindir)" + test -x "$pg_bin/initdb" + test -x "$pg_bin/pg_ctl" + "$pg_bin/postgres" --version + data_dir="$RUNNER_TEMP/settlement-verify-postgres" + socket_dir="$RUNNER_TEMP/settlement-verify-socket" + mkdir -m 700 "$data_dir" "$socket_dir" + printf 'SETTLEMENT_VERIFY_PG_BIN=%s\n' "$pg_bin" >> "$GITHUB_ENV" + printf 'SETTLEMENT_VERIFY_PG_DATA=%s\n' "$data_dir" >> "$GITHUB_ENV" + "$pg_bin/initdb" -D "$data_dir" --no-locale --encoding=UTF8 \ + --auth-local=trust --auth-host=reject + "$pg_bin/pg_ctl" -D "$data_dir" \ + -o "-F -h '' -k $socket_dir" -l "$RUNNER_TEMP/settlement-verify-postgres.log" -w start + printf 'SETTLEMENT_VERIFY_TEST_DSN=host=%s dbname=postgres\n' "$socket_dir" >> "$GITHUB_ENV" + - name: Verify exact-head PgCat and lock regressions + env: + SETTLEMENT_VERIFY_REQUIRE_POSTGRES: "1" + run: >- + python -m pytest -q tests/test_issue119_pgcat_endpoint_resolver.py + tests/test_issue119_settlement_live_verify.py + tests/test_issue119_settlement_replica_routing.py + tests/test_issue119_settlement_review_regressions.py + tests/test_multi_instance_runtime_contract.py + - name: Stop isolated test database + if: always() + shell: bash + run: | + if test -f "${SETTLEMENT_VERIFY_PG_DATA:-}/postmaster.pid"; then + "$SETTLEMENT_VERIFY_PG_BIN/pg_ctl" -D "$SETTLEMENT_VERIFY_PG_DATA" -m fast -w stop + fi diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 1bb83c2baf..fc63256794 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -1,6 +1,6 @@ # Reusable Dependency Review (workflow_call), consolidating the near- -# identical dependency-review.yml files argos, mightyETL, newsdom-api, -# scopeweave, and naruon each carried independently. See +# identical dependency-review.yml files argos, mightyETL, naruon, newsdom-api, +# and scopeweave each carried independently. See # docs/adr/0024-dependency-review-reusable-workflow-consolidation.md and # docs/doctoring/dependency-review-reusable-workflow-consolidation.md for the # per-repo field audit behind these inputs. @@ -9,34 +9,41 @@ # calling repo's own thin workflow file -- a workflow_call target cannot also # be the thing GitHub triggers directly on pull_request. # -# Dependency Review requires GitHub Dependency Graph (and, on private repos -# without GitHub Advanced Security, it is unavailable regardless of a repo's -# own settings). scopeweave's original workflow already detected this -# dynamically via the dependency-graph compare API instead of assuming from -# public/private repository status (mightyETL's original approach, which is -# wrong for a private repo that does have GHAS). This reusable workflow -# adopts the dynamic detection as the common, more-correct behavior for -# every caller, so no per-repo public/private input is needed. +# Dependency Review requires a successful Dependency Graph comparison for the +# exact pull-request base/head. Repository visibility is not a sufficient +# capability signal, and HTTP 403/404 are not safe availability signals: +# GitHub can use those statuses for authorization/policy denials as well as +# unavailable resources. For a pull_request, only exact immutable base/head +# object IDs plus a valid owner/name repository identity may reach transport, +# and only HTTP 200 authorizes running the pinned action. Every non-200 +# comparison fails closed. Non-pull_request triggers may skip because they do +# not carry the exact PR base/head identity. +# +# Reusable-workflow permissions can only be maintained or reduced through the +# call chain. Every thin caller therefore must declare at least `contents: +# read` and `pull-requests: read`; this workflow cannot elevate a caller token +# that omitted those scopes. # # Example caller (.github/workflows/dependency-review.yml in a product repo). -# Pin `uses:` to this file's exact commit SHA, not @main: an unpinned mutable -# ref would run an unreviewed central change against every PR check in the -# calling repo (Devin flagged this on the first four callers; fixed in all of -# them). If the calling repo's branch protection requires a status check -# literally named after the old standalone job, converting to `uses:` here -# will rename the published check to " / dependency-review" and -# silently break that required check -- update the branch protection's -# required-check name to match before or immediately after merging a caller. +# Pin `uses:` to the exact protected-main commit SHA carrying this workflow, +# never `@main`: a mutable central ref could run an unreviewed workflow change +# against the caller's PR checks. If branch protection requires the old +# standalone job name, note that reusable workflow adoption publishes the +# combined check name ` / dependency-review`; update the required +# check name to the exact published context without weakening the gate. # # name: Dependency Review # on: # pull_request: +# permissions: +# contents: read +# pull-requests: read # concurrency: # group: dependency-review-${{ github.event.pull_request.number || github.ref }} # cancel-in-progress: true # jobs: # dependency-review: -# uses: ContextualWisdomLab/.github/.github/workflows/dependency-review.yml@ +# uses: ContextualWisdomLab/.github/.github/workflows/dependency-review.yml@ # with: # fail_on_severity: high # allow_ghsas: "GHSA-69w3-r845-3855" @@ -86,7 +93,9 @@ permissions: jobs: dependency-review: - runs-on: ubuntu-latest + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] env: # Opts every JS action this job runs (checkout, dependency-review-action) # into the Node 24 actions runtime ahead of GitHub's default cutover, @@ -121,30 +130,43 @@ jobs: exit 0 fi + git_object_id='^[0-9a-f]{40}([0-9a-f]{24})?$' + repository_identity='^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$' + if ! [[ "${BASE_SHA}" =~ $git_object_id ]] || ! [[ "${HEAD_SHA}" =~ $git_object_id ]]; then + echo "::error::Dependency review evidence unavailable: exact 40- or 64-character hexadecimal base and head revisions are required before any compare request. Named refs are not evidence. Verify the pull-request event SHAs, then rerun. Failing closed." + exit 1 + fi + if ! [[ "${REPOSITORY}" =~ $repository_identity ]]; then + echo "::error::Dependency review evidence unavailable: owner/name repository identity is required before any compare request. Verify the pull-request repository, then rerun. Failing closed." + exit 1 + fi + repository_owner="${REPOSITORY%%/*}" + repository_name="${REPOSITORY#*/}" + if [ "${repository_owner}" = "." ] || [ "${repository_owner}" = ".." ] || [ "${repository_name}" = "." ] || [ "${repository_name}" = ".." ]; then + echo "::error::Dependency review evidence unavailable: owner/name repository identity is required before any compare request. Dot or parent-directory path components are not evidence. Verify the pull-request repository, then rerun. Failing closed." + exit 1 + fi + api_url="${GITHUB_API_URL:-https://api.github.com}" response_file="$(mktemp)" + curl_exit=0 status="$( curl -fsS -o "$response_file" -w '%{http_code}' \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer ${GH_TOKEN}" \ -H "X-GitHub-Api-Version: 2022-11-28" \ "${api_url}/repos/${REPOSITORY}/dependency-graph/compare/${BASE_SHA}...${HEAD_SHA}" \ - || true - )" + )" || curl_exit=$? - if [ "$status" = "200" ]; then + if [ "$curl_exit" -eq 0 ] && [ "$status" = "200" ]; then echo "available=true" >>"$GITHUB_OUTPUT" exit 0 fi - if [ "$status" = "403" ] || [ "$status" = "404" ]; then - echo "::warning::Dependency graph compare returned HTTP ${status} for ${REPOSITORY}; skipping the dependency-review hard gate (GitHub Dependency Graph, or GitHub Advanced Security on a private repository, is unavailable)." - echo "available=false" >>"$GITHUB_OUTPUT" - exit 0 + echo "::error::Dependency graph comparison failed with curl exit ${curl_exit} and HTTP ${status:-unavailable}. For a pull_request, transport failures and non-200 responses are ambiguous between feature availability and authorization/policy/transport failure, so Dependency Review fails closed instead of being skipped." + if [ -s "$response_file" ]; then + cat "$response_file" fi - - echo "::error::Dependency graph availability check failed with HTTP ${status}. This is not a 'graph unavailable' response (403/404) -- treating it as a genuine failure instead of silently skipping the security gate." - cat "$response_file" exit 1 - name: Dependency review @@ -155,9 +177,3 @@ jobs: fail-on-severity: ${{ inputs.fail_on_severity }} allow-ghsas: ${{ inputs.allow_ghsas }} comment-summary-in-pr: ${{ inputs.comment_summary_in_pr }} - - - name: Dependency graph unavailable note - if: steps.dependency_graph.outputs.available != 'true' && github.event_name == 'pull_request' - run: | - echo "Dependency Review requires GitHub Dependency Graph to be enabled for this repository (and, on private repositories, GitHub Advanced Security)." - echo "Other required dependency-vulnerability gates (OSV-Scanner, Scorecard) remain the blocking coverage until Dependency Graph is available here." diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml index f86b614022..8097a8c1aa 100644 --- a/.github/workflows/deploy-pages.yml +++ b/.github/workflows/deploy-pages.yml @@ -45,7 +45,9 @@ permissions: jobs: deploy_pages: name: Deploy ${{ inputs.project_name }} - runs-on: ubuntu-latest + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] steps: - name: Checkout caller repo uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 diff --git a/.github/workflows/exact-artifact-sbom-attestation.yml b/.github/workflows/exact-artifact-sbom-attestation.yml index b038c5478e..1e280ecb4f 100644 --- a/.github/workflows/exact-artifact-sbom-attestation.yml +++ b/.github/workflows/exact-artifact-sbom-attestation.yml @@ -64,7 +64,9 @@ env: jobs: verify-evidence-artifact: name: Verify inert sealed evidence - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] timeout-minutes: 15 permissions: actions: read @@ -78,19 +80,39 @@ jobs: - name: Materialize immutable trusted verifier uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: - # job.workflow_repository/workflow_sha are not real Actions context - # properties (actionlint-flagged); this always resolved to an empty - # repository/ref, silently defaulting checkout away from the pinned - # trusted verifier source. ContextualWisdomLab/.github is this - # workflow's own repository; github.workflow_sha is the real, - # documented property for its pinned commit. + # Independently reviewed helper snapshot, not caller/called workflow SHA. repository: ContextualWisdomLab/.github - ref: ${{ github.workflow_sha }} + # Reviewed helper revision; intentionally distinct from workflow revision. + ref: 00c6551183cca101cfc97c43656a17cc2491c1b4 path: trusted-intake persist-credentials: false - sparse-checkout: scripts/ci/verify_exact_artifact_sbom_handoff.py + sparse-checkout: | + scripts/ci/ + requirements-strix-ci-hashes.txt sparse-checkout-cone-mode: false + - name: Verify fixed helper checkout identity + env: + HELPER_ROOT: trusted-intake + CALLER_WORKFLOW_SHA: ${{ github.workflow_sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + expected=00c6551183cca101cfc97c43656a17cc2491c1b4 + test "$(git -C "$HELPER_ROOT" rev-parse HEAD)" = "$expected" + origin="$(git -C "$HELPER_ROOT" remote get-url origin)" + case "$origin" in + https://github.com/ContextualWisdomLab/.github|https://github.com/ContextualWisdomLab/.github.git) ;; + *) echo "Foreign helper repository" >&2; exit 1 ;; + esac + test "$(git -C "$HELPER_ROOT" rev-parse HEAD:scripts/ci)" = bf26d3eefdb71fe79b855d941ffb46eb432b2f76 + test "$(git -C "$HELPER_ROOT" rev-parse HEAD:requirements-strix-ci-hashes.txt)" = 9e705850b5ce53c7fe836bc3df3a18771151e3f6 + git -C "$HELPER_ROOT" diff --exit-code HEAD -- scripts/ci requirements-strix-ci-hashes.txt + test -f "$HELPER_ROOT/scripts/ci/release_dependency_gate.py" + test -f "$HELPER_ROOT/scripts/ci/verify_exact_artifact_sbom_handoff.py" + test -f "$HELPER_ROOT/requirements-strix-ci-hashes.txt" + printf 'helper_repository=ContextualWisdomLab/.github helper_sha=%s caller_workflow_sha=%s\n' "$expected" "$CALLER_WORKFLOW_SHA" + + - name: Verify immutable same-run artifact metadata env: GH_TOKEN: ${{ github.token }} @@ -160,7 +182,9 @@ jobs: attest-exact-artifacts: name: Attest exact wheel and sdist SBOMs needs: verify-evidence-artifact - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] timeout-minutes: 20 permissions: actions: read @@ -177,19 +201,39 @@ jobs: - name: Materialize immutable trusted verifier uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: - # job.workflow_repository/workflow_sha are not real Actions context - # properties (actionlint-flagged); this always resolved to an empty - # repository/ref, silently defaulting checkout away from the pinned - # trusted verifier source. ContextualWisdomLab/.github is this - # workflow's own repository; github.workflow_sha is the real, - # documented property for its pinned commit. + # Independently reviewed helper snapshot, not caller/called workflow SHA. repository: ContextualWisdomLab/.github - ref: ${{ github.workflow_sha }} + # Reviewed helper revision; intentionally distinct from workflow revision. + ref: 00c6551183cca101cfc97c43656a17cc2491c1b4 path: trusted-signer persist-credentials: false - sparse-checkout: scripts/ci/verify_exact_artifact_sbom_handoff.py + sparse-checkout: | + scripts/ci/ + requirements-strix-ci-hashes.txt sparse-checkout-cone-mode: false + - name: Verify fixed helper checkout identity + env: + HELPER_ROOT: trusted-signer + CALLER_WORKFLOW_SHA: ${{ github.workflow_sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + expected=00c6551183cca101cfc97c43656a17cc2491c1b4 + test "$(git -C "$HELPER_ROOT" rev-parse HEAD)" = "$expected" + origin="$(git -C "$HELPER_ROOT" remote get-url origin)" + case "$origin" in + https://github.com/ContextualWisdomLab/.github|https://github.com/ContextualWisdomLab/.github.git) ;; + *) echo "Foreign helper repository" >&2; exit 1 ;; + esac + test "$(git -C "$HELPER_ROOT" rev-parse HEAD:scripts/ci)" = bf26d3eefdb71fe79b855d941ffb46eb432b2f76 + test "$(git -C "$HELPER_ROOT" rev-parse HEAD:requirements-strix-ci-hashes.txt)" = 9e705850b5ce53c7fe836bc3df3a18771151e3f6 + git -C "$HELPER_ROOT" diff --exit-code HEAD -- scripts/ci requirements-strix-ci-hashes.txt + test -f "$HELPER_ROOT/scripts/ci/release_dependency_gate.py" + test -f "$HELPER_ROOT/scripts/ci/verify_exact_artifact_sbom_handoff.py" + test -f "$HELPER_ROOT/requirements-strix-ci-hashes.txt" + printf 'helper_repository=ContextualWisdomLab/.github helper_sha=%s caller_workflow_sha=%s\n' "$expected" "$CALLER_WORKFLOW_SHA" + + - name: Verify immutable same-run artifact metadata env: GH_TOKEN: ${{ github.token }} @@ -275,9 +319,7 @@ jobs: - name: Verify online and prepare offline bundles env: GH_TOKEN: ${{ github.token }} - # job.workflow_repository is not a real Actions context property - # (actionlint-flagged); ContextualWisdomLab/.github is this workflow's - # own repository, matching the pinned checkout above. + # Signer repository is fixed independently of the caller identity. SIGNER_REPOSITORY: ContextualWisdomLab/.github PREDICATE_TYPE: ${{ inputs.predicate_type }} SOURCE_REPOSITORY: ${{ inputs.source_repository }} @@ -334,13 +376,13 @@ jobs: EOF { printf '\n## Exact signed identity\n\n' - printf -- '- Source repository: `%s`\n' "$SOURCE_REPOSITORY" - printf -- '- Source SHA: `%s`\n' "$SOURCE_SHA" - printf -- '- Signer repository: `%s`\n' "$SIGNER_REPOSITORY" - printf -- '- Signer workflow: `%s`\n' "$signer_workflow" - printf -- '- Predicate type: `%s`\n' "$PREDICATE_TYPE" - printf -- '- Wheel: `%s`\n' "$WHEEL_FILENAME" - printf -- '- Source distribution: `%s`\n' "$SDIST_FILENAME" + printf -- "- Source repository: \`%s\`\n" "$SOURCE_REPOSITORY" + printf -- "- Source SHA: \`%s\`\n" "$SOURCE_SHA" + printf -- "- Signer repository: \`%s\`\n" "$SIGNER_REPOSITORY" + printf -- "- Signer workflow: \`%s\`\n" "$signer_workflow" + printf -- "- Predicate type: \`%s\`\n" "$PREDICATE_TYPE" + printf -- "- Wheel: \`%s\`\n" "$WHEEL_FILENAME" + printf -- "- Source distribution: \`%s\`\n" "$SDIST_FILENAME" cat <> offline-attestation-evidence/README.md ( cd offline-attestation-evidence - LC_ALL=C find . -maxdepth 1 -type f ! -name SHA256SUMS -printf '%f\n' \ - | LC_ALL=C sort \ + evidence_names="$(LC_ALL=C find . -maxdepth 1 -type f ! -name SHA256SUMS -printf '%f\n' | LC_ALL=C sort)" + printf '%s\n' "$evidence_names" \ | while IFS= read -r evidence_file; do + [ -n "$evidence_file" ] || continue sha256sum "$evidence_file" done > SHA256SUMS ) @@ -400,4 +443,4 @@ jobs: name: exact-artifact-sbom-offline-verification path: offline-attestation-evidence if-no-files-found: error - retention-days: 90 \ No newline at end of file + retention-days: 90 diff --git a/.github/workflows/exact-head-coverage-quality-gate.yml b/.github/workflows/exact-head-coverage-quality-gate.yml index 6b957571d2..f0670a4b50 100644 --- a/.github/workflows/exact-head-coverage-quality-gate.yml +++ b/.github/workflows/exact-head-coverage-quality-gate.yml @@ -37,7 +37,9 @@ permissions: jobs: quality-gate: - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] timeout-minutes: ${{ inputs.timeout_minutes }} steps: - name: Checkout exact source revision diff --git a/.github/workflows/fast-mlsirm-cflite_pr.yml b/.github/workflows/fast-mlsirm-cflite_pr.yml new file mode 100644 index 0000000000..47d9e16f97 --- /dev/null +++ b/.github/workflows/fast-mlsirm-cflite_pr.yml @@ -0,0 +1,89 @@ +name: ClusterFuzzLite PR fuzzing + +on: + workflow_call: + +permissions: + contents: read + +jobs: + PR: + if: ${{ !github.event.pull_request.draft && github.event.action != 'closed' }} + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + permissions: + contents: read + security-events: write + concurrency: + group: central-fast-mlsirm-cflite-${{ github.workflow }}-${{ github.repository }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + strategy: + fail-fast: false + matrix: + sanitizer: [address] + steps: + - name: Prepare ClusterFuzzLite output directory + run: | + mkdir -p "$GITHUB_WORKSPACE/build-out" + chmod -R a+rwx "$GITHUB_WORKSPACE/build-out" + echo "Prepared ClusterFuzzLite build-out for non-root fuzzbuilder writes" + ls -ld "$GITHUB_WORKSPACE" "$GITHUB_WORKSPACE/build-out" + - name: Build fuzzers (${{ matrix.sanitizer }}) + id: build + uses: google/clusterfuzzlite/actions/build_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 + with: + language: rust + github-token: ${{ secrets.GITHUB_TOKEN }} + sanitizer: ${{ matrix.sanitizer }} + - name: Run fuzzers (${{ matrix.sanitizer }}) + id: run + uses: google/clusterfuzzlite/actions/run_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fuzz-seconds: 120 + mode: code-change + output-sarif: true + sanitizer: ${{ matrix.sanitizer }} + - name: Restore runner access to ClusterFuzzLite workspace + if: ${{ always() }} + run: | + # Docker creates root-owned storage/work directories. Restore directory + # access for the next checkout; retain evidence and file ownership. + sudo python3 - "$GITHUB_WORKSPACE" "$(id -u)" "$(id -g)" <<'PY' + import os + import stat + import sys + + root, uid, gid = sys.argv[1], int(sys.argv[2]), int(sys.argv[3]) + if uid <= 0 or gid <= 0 or root == '/' or not os.path.isabs(root) or os.path.realpath(root) != root: + raise SystemExit("Unsafe ClusterFuzzLite workspace identity") + flags = os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW + identity = os.stat(root, follow_symlinks=False) + directory = os.open(root, flags) + try: + opened = os.fstat(directory) + if (identity.st_dev, identity.st_ino, identity.st_uid) != (opened.st_dev, opened.st_ino, uid): + raise SystemExit("ClusterFuzzLite workspace changed during cleanup") + device = opened.st_dev + + def restore(fd): + for name in os.listdir(fd): + entry = os.stat(name, dir_fd=fd, follow_symlinks=False) + if entry.st_dev != device or stat.S_ISLNK(entry.st_mode): + continue + if stat.S_ISDIR(entry.st_mode): + child = os.open(name, flags, dir_fd=fd) + try: + opened_child = os.fstat(child) + if (opened_child.st_dev, opened_child.st_ino) == (entry.st_dev, entry.st_ino): + restore(child) + if os.fstat(child).st_uid == 0: + os.fchown(child, uid, gid) + finally: + os.close(child) + + restore(directory) + finally: + os.close(directory) + PY diff --git a/.github/workflows/fast-mlsirm-ci.yml b/.github/workflows/fast-mlsirm-ci.yml new file mode 100644 index 0000000000..6a851d9833 --- /dev/null +++ b/.github/workflows/fast-mlsirm-ci.yml @@ -0,0 +1,324 @@ +name: CI + +on: + workflow_call: + +permissions: + contents: read + +concurrency: + # Keep one active CI run per PR while preserving independent push runs. A + # synchronized head cancels its queued/running predecessor instead of + # multiplying the hosted-runner backlog with stale evidence. + group: central-fast-mlsirm-ci-${{ github.workflow }}-${{ github.repository }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + # Matrix legs report as "python (3.12)" / "python (3.14)". Branch protection + # still requires the exact check context name `python`, so a thin aggregate + # job below re-exports matrix success under that name. + python-matrix: + if: ${{ github.event_name != 'pull_request' || (!github.event.pull_request.draft && github.event.action != 'closed') }} + name: python (${{ matrix.python-version }}) + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + strategy: + fail-fast: false + matrix: + python-version: ["3.12", "3.14"] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: ${{ matrix.python-version }} + # Rust toolchain is required so the maturin build compiles the primary + # numeric core (fast_mlsirm._core) during the editable install; the + # Rust<->NumPy parity gate then exercises the Rust path. + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 + with: + toolchain: 1.97.1 + - run: python -m pip install --require-hashes -r requirements/ci.txt + - run: python -m pip install --no-deps --no-build-isolation -e . + - name: Verify Rust core is the resolved default backend + run: python -c "import fast_mlsirm._core as c; from fast_mlsirm.backend import resolve_backend; from fast_mlsirm import FitConfig; assert hasattr(c, 'neg_loglik_and_grad'); assert resolve_backend(FitConfig().backend) == 'rust'; print('rust primary OK')" + - run: pytest + + python: + name: python + needs: python-matrix + if: ${{ always() && (github.event_name != 'pull_request' || (!github.event.pull_request.draft && github.event.action != 'closed')) }} + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + steps: + - name: Require every CPython matrix leg + run: | + echo "python-matrix result=${{ needs.python-matrix.result }}" + test "${{ needs.python-matrix.result }}" = "success" + + + rust: + if: ${{ github.event_name != 'pull_request' || (!github.event.pull_request.draft && github.event.action != 'closed') }} + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: "3.12" + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 + with: + toolchain: 1.97.1 + # Ordinary Rust tests include the deterministic equation and bounded + # recovery-contract sentinels. Exhaustive ignored studies run in the + # scheduled Statistical Studies workflow rather than on every PR. + # --locked fails the job when a Cargo.lock would have to change to build. + # crates/fast-mlsirm-py is excluded from the root workspace and carries its + # own lock, so without this a dependency added there resolves fresh at build + # time and the committed lock the security gate scans silently falls behind + # the graph that actually ships. + - run: cargo test --locked --workspace + - run: cargo test --locked --manifest-path crates/fast-mlsirm-py/Cargo.toml + + gpu-smoke: + if: ${{ github.event_name != 'pull_request' || (!github.event.pull_request.draft && github.event.action != 'closed') }} + # Exact GPU and study-precision nodes below own every corresponding + # non-execution allowlist entry. The full paper-design recovery study + # remains in the scheduled/manual workflow. + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 240 + env: + WGPU_BACKEND: vulkan + XDG_RUNTIME_DIR: /tmp/fast-mlsirm-xdg-runtime + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: "3.12" + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 + with: + toolchain: 1.97.1 + - name: Install software Vulkan adapter + run: | + # The hosted runner's mirror+file registry can keep selecting a + # black-holed Azure mirror even after apt falls back for InRelease. + # Use a job-local source file pinned to canonical Ubuntu archives so + # package payloads do not re-enter that broken mirror selection path. + . /etc/os-release + test -n "${VERSION_CODENAME:-}" + cat > /tmp/fast-mlsirm-ubuntu.sources <> "$GITHUB_ENV" + - name: Prove Vulkan compute adapter availability + run: vulkaninfo --summary + - run: python -m pip install --require-hashes -r requirements/ci.txt + - run: python -m pip install --no-deps --no-build-isolation -e . + - name: Run exact GPU and study-precision evidence without a skip + env: + STAGE5_HIGH_Q: "1" + STAGE5_HIGH_Q_BOOTSTRAP_REPS: "2" + run: | + pytest -q --junitxml=gpu-junit.xml \ + tests/test_marginal_parity.py::test_marginal_gpu_agrees_with_cpu_loosely \ + tests/test_bifactor_gpu_high_q.py::test_bifactor_gpu_parity_q121 \ + tests/test_bifactor_gpu_high_q.py::test_bifactor_gpu_parity_q241 \ + tests/test_bifactor_gpu_high_q.py::test_bifactor_gpu_parity_q481 \ + tests/test_bifactor_gpu_high_q.py::test_bifactor_gpu_parity_q241_wide_items_metal_workgroups \ + tests/test_bifactor_gpu_high_q.py::test_bifactor_cpu_q121_vs_q241_agree \ + tests/test_bifactor_bootstrap_benchmark.py::test_joint_bootstrap_cpu_vs_gpu_wall_time_q121 + - name: Reject skipped GPU evidence + run: | + python - <<'PY' + from pathlib import Path + from xml.etree import ElementTree + + required = { + ("tests.test_marginal_parity", "test_marginal_gpu_agrees_with_cpu_loosely"), + ("tests.test_bifactor_gpu_high_q", "test_bifactor_gpu_parity_q121"), + ("tests.test_bifactor_gpu_high_q", "test_bifactor_gpu_parity_q241"), + ("tests.test_bifactor_gpu_high_q", "test_bifactor_gpu_parity_q481"), + ("tests.test_bifactor_gpu_high_q", "test_bifactor_gpu_parity_q241_wide_items_metal_workgroups"), + ("tests.test_bifactor_gpu_high_q", "test_bifactor_cpu_q121_vs_q241_agree"), + ("tests.test_bifactor_bootstrap_benchmark", "test_joint_bootstrap_cpu_vs_gpu_wall_time_q121"), + } + + def count(node, field): + value = node.attrib.get(field, "") + if not value.isascii() or not value.isdecimal(): + raise SystemExit(f"GPU evidence has invalid {field} count") + return int(value) + + root = ElementTree.parse(Path("gpu-junit.xml")).getroot() + if root.tag == "testsuite": + suites = [root] + elif root.tag == "testsuites" and all(child.tag == "testsuite" for child in root): + suites = list(root) + else: + raise SystemExit("GPU evidence has unsupported JUnit layout") + if not suites: + raise SystemExit("GPU evidence did not execute a test") + identities = [] + for suite in suites: + if any(child.tag not in {"testcase", "properties", "system-out", "system-err"} for child in suite): + raise SystemExit("GPU evidence has unsupported suite children") + cases = list(suite.findall("testcase")) + if list(suite.iter("testcase")) != cases: + raise SystemExit("GPU evidence has nested testcase rows") + if count(suite, "tests") != len(cases): + raise SystemExit("GPU evidence header/testcase count mismatch") + for field, tag in (("failures", "failure"), ("errors", "error"), ("skipped", "skipped")): + actual = sum(len(list(case.iter(tag))) for case in cases) + if count(suite, field) != actual or actual != 0: + raise SystemExit(f"GPU evidence contained invalid or nonzero {field}") + identities.extend((case.get("classname"), case.get("name")) for case in cases) + if len(identities) != len(required) or set(identities) != required: + raise SystemExit("GPU evidence requires each exact selector once, without extras") + if root.tag == "testsuites": + for field in ("tests", "failures", "errors", "skipped"): + if field in root.attrib: + expected = len(identities) if field == "tests" else 0 + if count(root, field) != expected: + raise SystemExit("GPU evidence aggregate header count mismatch") + PY + + fuzz: + if: ${{ github.event_name != 'pull_request' || (!github.event.pull_request.draft && github.event.action != 'closed') }} + # Short, bounded coverage-guided fuzzing so PR cost stays low. The + # property-based Hypothesis tests already run in the `python` job (via the + # dev extra); this job adds the Atheris coverage-guided harnesses with a + # ~60s-per-target budget. Longer campaigns can be run nightly if desired. + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: "3.12" + # pip is version-pinned so the toolchain used to resolve the fuzz extras + # is reproducible (OpenSSF Scorecard pinned-dependencies hardening). + - run: python -m pip install --upgrade "pip==26.1.2" + # The property checks below use pytest from the dev extra; fuzz alone + # provides Atheris and Hypothesis, but not the pytest command. + # Editable/local installs cannot use --require-hashes. + - run: python -m pip install -e '.[dev,fuzz]' + - name: Run Hypothesis input properties without a skip + run: pytest tests/test_fuzz_properties.py -q --junitxml=fuzz-properties-junit.xml + - name: Reject skipped Hypothesis evidence + run: | + python - <<'PY' + from pathlib import Path + from xml.etree import ElementTree + + root = ElementTree.parse(Path("fuzz-properties-junit.xml")).getroot() + suites = [root] if root.tag == "testsuite" else list(root.findall("testsuite")) + tests = sum(int(suite.attrib.get("tests", "0")) for suite in suites) + skipped = sum(int(suite.attrib.get("skipped", "0")) for suite in suites) + if tests < 1: + raise SystemExit("Hypothesis evidence did not execute a test") + if skipped != 0: + raise SystemExit( + f"Hypothesis evidence contained {skipped} skipped test(s)" + ) + PY + - name: Fuzz load_factor_csv (CSV parser) + run: | + python fuzz/atheris/fuzz_load_factor_csv.py \ + -max_total_time=60 -timeout=25 fuzz/corpus/load_factor_csv + - name: Fuzz render_diagnostics_report (JSON -> HTML) + run: | + python fuzz/atheris/fuzz_render_report.py \ + -max_total_time=60 -timeout=25 fuzz/corpus/render_report + - name: Fuzz config validators + run: | + python fuzz/atheris/fuzz_config.py \ + -max_total_time=60 -timeout=25 fuzz/corpus/config + + package: + if: ${{ github.event_name != 'pull_request' || (!github.event.pull_request.draft && github.event.action != 'closed') }} + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: "3.12" + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 + with: + toolchain: 1.97.1 + - run: python -m pip install --require-hashes -r requirements/package.txt + - run: python -m build --no-isolation + - run: python -m pip install --no-index --no-deps --force-reinstall dist/*.whl + - run: python -c "import fast_mlsirm, fast_mlsirm._core as core; print(fast_mlsirm.__version__, hasattr(core, 'neg_loglik_and_grad'))" + - name: Run release acceptance smoke + run: | + python scripts/release_acceptance.py --out release-acceptance --require-rust --persons 12 --dims 1 --items-per-dim 2 --latent-dim 1 --latent-dims 1,2 --folds 2 --max-iter 1 --n-restarts 1 + python - <<'PY' + import json + + with open("release-acceptance/acceptance_summary.json", encoding="utf-8") as fh: + summary = json.load(fh) + assert summary["status"] == "ok" + PY + - name: Run enterprise sales readiness gate + run: | + python scripts/sales_readiness.py \ + --acceptance release-acceptance/acceptance_summary.json \ + --dist dist \ + --require-rust \ + --require-20b-product \ + --check-import \ + --out release-acceptance/sales_readiness_manifest.json + python - <<'PY' + import json + + with open("release-acceptance/sales_readiness_manifest.json", encoding="utf-8") as fh: + manifest = json.load(fh) + assert manifest["status"] == "ok" + PY diff --git a/.github/workflows/fast-mlsirm-codeql.yml b/.github/workflows/fast-mlsirm-codeql.yml new file mode 100644 index 0000000000..18800abe22 --- /dev/null +++ b/.github/workflows/fast-mlsirm-codeql.yml @@ -0,0 +1,58 @@ +name: CodeQL + +on: + workflow_call: + +concurrency: + group: central-fast-mlsirm-codeql-${{ github.repository }}-${{ github.event.pull_request.number || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + +jobs: + analyze-actions: + # Branch protection requires a deterministic Actions-language CodeQL check + # on PRs. Default setup is disabled; this reusable workflow uploads SARIF. + name: Analyze (actions) + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + permissions: + contents: read + security-events: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd + with: + languages: actions + - uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd + with: + output: codeql-results/actions + - name: Keep CodeQL diagnostics local to the job + run: test -s codeql-results/actions/actions.sarif + + analyze-python: + # Python analysis and code-scanning upload require an explicit operator run. + if: github.event_name == 'workflow_dispatch' + name: Analyze (python) + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + permissions: + contents: read + security-events: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd + with: + languages: python + - uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd + with: + output: codeql-results/python + - name: Keep CodeQL diagnostics local to the job + run: test -s codeql-results/python/python.sarif diff --git a/.github/workflows/fast-mlsirm-hourly-pr-governance.yml b/.github/workflows/fast-mlsirm-hourly-pr-governance.yml new file mode 100644 index 0000000000..0188e2dd52 --- /dev/null +++ b/.github/workflows/fast-mlsirm-hourly-pr-governance.yml @@ -0,0 +1,163 @@ +name: Hourly PR Queue Governance + +on: + workflow_call: + +permissions: + actions: read + contents: read + pull-requests: read + +concurrency: + group: central-fast-mlsirm-hourly-pr-governance-hourly-pr-governance-${{ github.repository }} + cancel-in-progress: true + +jobs: + governance-evidence: + name: Hourly PR queue governance evidence + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 10 + steps: + - name: Check out reviewed source + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: "3.12" + - name: Verify the workflow contract without third-party dependencies + run: | + python - <<'PY' + import runpy + + namespace = runpy.run_path("tests/test_hourly_pr_governance_workflow.py") + test_names = [ + name + for name in sorted(namespace) + if name.startswith("test_hourly_governance_workflow_") + ] + if not test_names: + raise RuntimeError("no hourly governance contract tests were discovered") + for name in test_names: + namespace[name]() + PY + - name: Build live PR queue governance evidence + id: governance + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + is_retryable_github_failure() { + python - <<'PY' + import json + from pathlib import Path + import re + + path = Path("hourly-pr-queue-governance/pr_queue_governance_manifest.json") + if not path.is_file(): + print("false") + raise SystemExit + try: + manifest = json.loads(path.read_text(encoding="utf-8")) + except (OSError, UnicodeError, json.JSONDecodeError): + print("false") + raise SystemExit + + status_failed = manifest.get("status") == "failed" + if not status_failed: + print("false") + raise SystemExit + + failed_checks = manifest.get("failed_checks") + retryable_failed_check_names = {"github:snapshot", "github:base_sha"} + if ( + not isinstance(failed_checks, list) + or not failed_checks + or not all( + isinstance(check, dict) and check.get("ok") is False + for check in failed_checks + ) + ): + print("false") + raise SystemExit + failed_check_names = { + str(check.get("name", "")) for check in failed_checks + } + only_transient_snapshot_checks_failed = ( + bool(failed_check_names) + and failed_check_names.issubset(retryable_failed_check_names) + ) + + github = manifest.get("github", {}) + errors = github.get("errors", []) if isinstance(github, dict) else [] + retry_status = re.compile(r"\bHTTP (?:502|503|504)\b", re.IGNORECASE) + retryable = ( + only_transient_snapshot_checks_failed + and bool(errors) + and all( + isinstance(error, dict) + and type(error.get("returncode")) is int + and error.get("returncode") != 0 + and isinstance(error.get("stderr"), str) + and retry_status.search(error.get("stderr", "")) is not None + for error in errors + ) + ) + print("true" if retryable else "false") + PY + } + + max_attempts=3 + attempt=1 + while true; do + rm -rf hourly-pr-queue-governance + mkdir -p hourly-pr-queue-governance + + snapshot_rc=0 + python scripts/capture_pr_queue_snapshot.py \ + --repo ContextualWisdomLab/fast-mlsirm \ + --out hourly-pr-queue-governance/github_snapshot.json \ + || snapshot_rc=$? + + builder_rc=0 + python scripts/build_pr_queue_governance.py \ + --repo ContextualWisdomLab/fast-mlsirm \ + --out hourly-pr-queue-governance \ + --offline-snapshot hourly-pr-queue-governance/github_snapshot.json \ + || builder_rc=$? + + if [[ "$snapshot_rc" -eq 0 && "$builder_rc" -eq 0 ]]; then + break + fi + + retryable="$(is_retryable_github_failure)" + if [[ "$retryable" != "true" || "$attempt" -ge "$max_attempts" ]]; then + exit 1 + fi + delay=$((10 * (2 ** (attempt - 1)))) + echo "::warning::Transient GitHub API failure on attempt ${attempt}; retrying in ${delay}s." + sleep "$delay" + attempt=$((attempt + 1)) + done + - name: Build live workflow registry drift evidence + env: + GH_TOKEN: ${{ github.token }} + run: | + python scripts/audit_workflow_registry.py \ + --repo ContextualWisdomLab/fast-mlsirm \ + --out hourly-pr-queue-governance/workflow_registry_audit.json + - name: Publish hourly governance evidence + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: hourly-pr-queue-governance-${{ github.run_id }} + path: | + hourly-pr-queue-governance/github_snapshot.json + hourly-pr-queue-governance/pr_queue_governance_manifest.json + hourly-pr-queue-governance/pr_queue_governance_report.html + hourly-pr-queue-governance/workflow_registry_audit.json + if-no-files-found: error + retention-days: 30 diff --git a/.github/workflows/fast-mlsirm-publish-pypi.yml b/.github/workflows/fast-mlsirm-publish-pypi.yml new file mode 100644 index 0000000000..325974b0c4 --- /dev/null +++ b/.github/workflows/fast-mlsirm-publish-pypi.yml @@ -0,0 +1,1988 @@ +name: Publish Package +on: + workflow_call: + inputs: + release_tag: + description: "Immutable release tag to publish (for example v0.9.0)" + required: true + type: string + release_commit: + description: "Reviewed release source commit (full lowercase SHA-1)" + required: true + type: string + control_plane_commit: + description: "Protected default-branch commit that selected this publication workflow" + required: true + type: string + +permissions: + contents: read + +concurrency: + group: central-fast-mlsirm-publish-pypi-publish-pypi-${{ inputs.release_tag }} + cancel-in-progress: false + +jobs: + verify-release: + name: verify release provenance + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 5 + outputs: + release_commit: ${{ steps.release-source.outputs.value }} + source_date_epoch: ${{ steps.source-date-epoch.outputs.value }} + steps: + - name: Verify publication control-plane identity + env: + CONTROL_PLANE_COMMIT: ${{ inputs.control_plane_commit }} + CONTROL_PLANE_SHA: ${{ github.sha }} + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + PUBLISH_REF: ${{ github.ref }} + run: | + set -euo pipefail + if ! printf '%s' "$CONTROL_PLANE_COMMIT" | grep -Eq '^[0-9a-f]{40}$'; then + echo "control_plane_commit must be a canonical 40-character lowercase SHA-1" >&2 + exit 1 + fi + expected_ref="refs/heads/$DEFAULT_BRANCH" + if [ "$PUBLISH_REF" != "$expected_ref" ]; then + echo "package publication must run from $expected_ref, not $PUBLISH_REF" >&2 + exit 1 + fi + if [ "$CONTROL_PLANE_SHA" != "$CONTROL_PLANE_COMMIT" ]; then + echo "publication control plane moved after release verification" >&2 + exit 1 + fi + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + fetch-tags: true + persist-credentials: false + - name: Validate and select release source ancestry + id: release-source + env: + CONTROL_PLANE_COMMIT: ${{ inputs.control_plane_commit }} + RELEASE_COMMIT: ${{ inputs.release_commit }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + if ! printf '%s' "$RELEASE_COMMIT" | grep -Eq '^[0-9a-f]{40}$'; then + echo "release_commit must be a canonical 40-character lowercase SHA-1" >&2 + exit 1 + fi + trusted_control_commit="$(git rev-parse HEAD)" + if [ "$trusted_control_commit" != "$CONTROL_PLANE_COMMIT" ]; then + echo "checked-out protected control plane does not match control_plane_commit" >&2 + exit 1 + fi + canonical_release_commit="$(git rev-parse --verify "$RELEASE_COMMIT^{commit}" 2>/dev/null)" || { + echo "release_commit is unavailable for ancestry verification" >&2 + exit 1 + } + if [ "$canonical_release_commit" != "$RELEASE_COMMIT" ]; then + echo "release_commit did not resolve to its canonical commit identity" >&2 + exit 1 + fi + if ! git merge-base --is-ancestor "$canonical_release_commit" "$trusted_control_commit"; then + echo "release commit must be an ancestor of the publication control plane" >&2 + exit 1 + fi + echo "value=$canonical_release_commit" >> "$GITHUB_OUTPUT" + git checkout --detach "$canonical_release_commit" + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: "3.12" + - name: Require release tag and source commit to match project version + env: + RELEASE_COMMIT: ${{ steps.release-source.outputs.value }} + RELEASE_TAG: ${{ inputs.release_tag }} + run: | + set -euo pipefail + checked_out_commit="$(git rev-parse HEAD)" + if [ "$checked_out_commit" != "$RELEASE_COMMIT" ]; then + echo "checked-out release source does not match release_commit" >&2 + exit 1 + fi + # R5a: the immutable tag is created only after release admission + # (create-tag-and-release). A missing tag is the normal first-run + # state; an existing tag (a retry) must already target release_commit. + if tag_commit="$(git rev-parse -q --verify "$RELEASE_TAG^{commit}" 2>/dev/null)"; then + if [ "$tag_commit" != "$RELEASE_COMMIT" ]; then + echo "release tag does not target release_commit" >&2 + exit 1 + fi + else + echo "release tag $RELEASE_TAG does not exist yet; it is created only after release admission" + fi + python - <<'PY' + import os + import tomllib + + with open("pyproject.toml", "rb") as stream: + project = tomllib.load(stream)["project"] + + expected = f"v{project['version']}" + actual = os.environ["RELEASE_TAG"] + if actual != expected: + raise SystemExit( + f"release tag does not match project version: expected {expected!r}, got {actual!r}" + ) + PY + - name: Require reviewed release license helpers + run: | + set -euo pipefail + for helper in tools/select_third_party_license.sh tools/verify_wheel_license.py; do + if [ ! -f "$helper" ] || [ -L "$helper" ]; then + echo "::error::Release source lacks the reviewed license helper: $helper" + exit 1 + fi + done + # Reproducible-build clock: the committer time of the exact release + # commit, never the wall clock. maturin stamps it into the embedded + # CycloneDX SBOM (timestamp/serialNumber) and archive metadata; without it + # two builds of the same commit produce different wheel bytes. + - name: Derive SOURCE_DATE_EPOCH from the release commit + id: source-date-epoch + env: + RELEASE_COMMIT: ${{ steps.release-source.outputs.value }} + run: | + set -euo pipefail + epoch="$(git log -1 --format=%ct "$RELEASE_COMMIT")" + if ! printf '%s' "$epoch" | grep -Eq '^[0-9]+$'; then + echo "could not derive SOURCE_DATE_EPOCH from release_commit" >&2 + exit 1 + fi + echo "value=$epoch" >> "$GITHUB_OUTPUT" + echo "SOURCE_DATE_EPOCH=$epoch (committer time of $RELEASE_COMMIT)" + + sdist: + name: sdist + needs: verify-release + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 15 + env: + SOURCE_DATE_EPOCH: ${{ needs.verify-release.outputs.source_date_epoch }} + CARGO_RELEASE_SHA: ${{ needs.verify-release.outputs.release_commit }} + CARGO_RELEASE_LEG: sdist + CARGO_BUILD_IMAGE: "" + CARGO_BUILD_PYTHON: python + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + fetch-tags: true + persist-credentials: false + - name: Validate release ancestry in this job + env: + CONTROL_PLANE_COMMIT: ${{ github.sha }} + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + if ! printf '%s' "$RELEASE_COMMIT" | grep -Eq '^[0-9a-f]{40}$'; then + echo "release_commit must be a canonical 40-character lowercase SHA-1" >&2 + exit 1 + fi + trusted_control_commit="$(git rev-parse HEAD)" + if [ "$trusted_control_commit" != "$CONTROL_PLANE_COMMIT" ]; then + echo "checked-out protected control plane does not match control_plane_commit" >&2 + exit 1 + fi + canonical_release_commit="$(git rev-parse --verify "$RELEASE_COMMIT^{commit}" 2>/dev/null)" || { + echo "release_commit is unavailable for ancestry verification" >&2 + exit 1 + } + if [ "$canonical_release_commit" != "$RELEASE_COMMIT" ]; then + echo "release_commit did not resolve to its canonical commit identity" >&2 + exit 1 + fi + if ! git merge-base --is-ancestor "$canonical_release_commit" "$trusted_control_commit"; then + echo "release commit must be an ancestor of the publication control plane" >&2 + exit 1 + fi + git checkout --detach "$canonical_release_commit" + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: "3.12" + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + path: trusted-control + persist-credentials: false + - name: Build sdist + uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b + with: + command: sdist + maturin-version: v1.15.0 + args: --out dist + - name: Capture first sdist build tools + env: + CARGO_BUILD_PASS: first + run: python trusted-control/scripts/ci/capture_release_build_scope.py + - name: Rebuild sdist for byte-reproducibility check + uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b + with: + command: sdist + maturin-version: v1.15.0 + args: --out dist-rebuild + - name: Capture second sdist build tools + env: + CARGO_BUILD_PASS: second + run: python trusted-control/scripts/ci/capture_release_build_scope.py + # Historical release tags predate the pyproject include workaround for + # maturin omitting LICENSE from mixed-layout sdists while still writing + # License-File metadata. Inject the checked-out LICENSE when missing so + # already-cut tags remain publishable without retagging. The rewrite pins + # the gzip header (mtime = SOURCE_DATE_EPOCH, no embedded file name) and + # the injected member's metadata so it is byte-reproducible. + - name: Ensure LICENSE is present in the sdist + run: | + set -euo pipefail + python - <<'PY' + from __future__ import annotations + + import gzip + import io + import os + import tarfile + from pathlib import Path + + epoch = os.environ.get("SOURCE_DATE_EPOCH", "") + if not epoch.isdigit(): + raise SystemExit(f"SOURCE_DATE_EPOCH is not set to a release-commit epoch: {epoch!r}") + license_path = Path("LICENSE") + if not license_path.is_file(): + raise SystemExit("checked-out release source is missing LICENSE") + license_bytes = license_path.read_bytes() + + for dist in (Path("dist"), Path("dist-rebuild")): + archives = sorted(dist.glob("*.tar.gz")) + if len(archives) != 1: + raise SystemExit(f"expected exactly one sdist archive in {dist}, found {archives!r}") + archive = archives[0] + + with tarfile.open(archive, "r:gz") as reader: + names = reader.getnames() + members = reader.getmembers() + file_members = { + member.name: reader.extractfile(member).read() + for member in members + if member.isfile() + } + + roots = {name.split("/", 1)[0] for name in names if name} + if len(roots) != 1: + raise SystemExit(f"sdist must have a single top-level directory, found {sorted(roots)!r}") + root = next(iter(roots)) + license_member = f"{root}/LICENSE" + if license_member in file_members: + if file_members[license_member] != license_bytes: + raise SystemExit("sdist LICENSE content does not match checked-out LICENSE") + print(f"{archive} already contains {license_member}") + continue + + rebuilt = archive.with_suffix(archive.suffix + ".rewritten") + with ( + tarfile.open(archive, "r:gz") as reader, + open(rebuilt, "wb") as raw, + gzip.GzipFile(filename="", mode="wb", fileobj=raw, mtime=int(epoch)) as compressed, + tarfile.open(fileobj=compressed, mode="w", format=tarfile.PAX_FORMAT) as writer, + ): + for member in members: + payload = reader.extractfile(member) if member.isfile() else None + writer.addfile(member, payload) + info = tarfile.TarInfo(name=license_member) + info.size = len(license_bytes) + info.mode = 0o644 + info.mtime = int(epoch) + writer.addfile(info, io.BytesIO(license_bytes)) + + rebuilt.replace(archive) + with tarfile.open(archive, "r:gz") as reader: + if license_member not in reader.getnames(): + raise SystemExit(f"failed to inject {license_member} into {archive}") + print(f"injected {license_member} into {archive}") + PY + - name: Compare double-build sdist digests and record them + shell: bash + env: + LEG: sdist + ARTIFACT_GLOB: "*.tar.gz" + CONTAINER_IMAGE: "" + run: | + set -euo pipefail + python - <<'PY' + import hashlib + import json + import os + import re + import subprocess + from pathlib import Path + + # Claim scope: a clean-target repeat build in the SAME runner and + # container environment. This is not independent-environment or + # independent-toolchain provenance. + leg = os.environ["LEG"] + pattern = os.environ["ARTIFACT_GLOB"] + + def digest(directory: str) -> tuple[str, str]: + found = sorted(Path(directory).glob(pattern)) + if len(found) != 1: + raise SystemExit(f"expected exactly one {pattern} in {directory}, found {found!r}") + return found[0].name, hashlib.sha256(found[0].read_bytes()).hexdigest() + + epoch = os.environ.get("SOURCE_DATE_EPOCH", "") + if not epoch.isdigit(): + raise SystemExit(f"SOURCE_DATE_EPOCH is not set to a release-commit epoch: {epoch!r}") + first = digest("dist") + second = digest("dist-rebuild") + if first != second: + raise SystemExit(f"{leg} is not byte-reproducible: {first!r} != {second!r}") + + # Provenance is bound to THIS invocation: Linux wheel legs build in + # the digest-pinned container passed to maturin-action, which must be + # the image present on the runner; every other row (macOS, Windows, + # sdist) ran without a container and records the runner identity. + # Unrelated cached images are never consulted. + container = os.environ.get("CONTAINER_IMAGE", "") + runner_os = os.environ.get("RUNNER_OS", "") + if runner_os == "Linux" and leg != "sdist": + if not re.fullmatch(r"[a-z0-9.\-]+(?:/[a-z0-9._\-]+)+@sha256:[0-9a-f]{64}", container): + raise SystemExit(f"{leg}: Linux wheel leg has no digest-pinned build container: {container!r}") + inspected = subprocess.run( + ["docker", "image", "inspect", "--format", "{{json .RepoDigests}}", container], + capture_output=True, text=True, + ) + if inspected.returncode != 0 or container not in json.loads(inspected.stdout or "[]"): + raise SystemExit(f"{leg}: pinned build container {container} is not present on this runner") + build_env = f"container:{container}" + else: + if container: + raise SystemExit(f"{leg}: ran without a container but carries container provenance {container!r}") + identity = [os.environ.get(key, "") for key in ("ImageOS", "ImageVersion", "RUNNER_OS", "RUNNER_ARCH")] + if not all(identity): + raise SystemExit(f"{leg}: missing runner identity (ImageOS/ImageVersion/RUNNER_OS/RUNNER_ARCH): {identity!r}") + build_env = "runner:" + "/".join(identity) + + name, sha = first + out = Path("repro-digest") + out.mkdir(exist_ok=True) + row = "\t".join([leg, "true", "clean-target-repeat-same-env", sha, second[1], name, build_env]) + (out / f"{leg}.tsv").write_text(row + "\n", encoding="utf-8") + print(row) + PY + - name: Capture finished sdist members from the build leg + env: + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + run: python trusted-control/scripts/ci/capture_release_bundle.py repro-digest/sdist.tsv dist + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: dist-sdist + path: dist + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: repro-rebuild-sdist + path: dist-rebuild + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: repro-digest-sdist + path: repro-digest + + publication-disabled: + name: publication disabled (release runner matrix unavailable) + needs: [sdist] + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 1 + steps: + - name: Fail closed while required release runners are unavailable + env: + RELEASE_TAG: ${{ inputs.release_tag }} + run: | + echo "::error::Release ${RELEASE_TAG} was not tagged, released, or published because the required macOS, Windows, and ARM self-hosted release legs are unavailable." + exit 1 + + wheels: + # Disabled until self-hosted macOS, Windows and ARM release legs are available. + # Keep the full release matrix contract; downstream publication remains skipped. + if: ${{ false }} + name: wheels (${{ matrix.target }}, py${{ matrix.python-version }}) + needs: [verify-release, sdist] + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 90 + env: + SOURCE_DATE_EPOCH: ${{ needs.verify-release.outputs.source_date_epoch }} + CARGO_RELEASE_SHA: ${{ needs.verify-release.outputs.release_commit }} + CARGO_RELEASE_LEG: ${{ matrix.target }}-py${{ matrix.python-version }} + CARGO_BUILD_IMAGE: ${{ matrix.container }} + CARGO_BUILD_PYTHON: ${{ matrix.interpreter }} + strategy: + fail-fast: false + matrix: + include: + - runner: ubuntu-latest + target: x86_64-unknown-linux-gnu + manylinux: "2014" + python-version: "3.12" + interpreter: python3.12 + container: "quay.io/pypa/manylinux2014_x86_64@sha256:2fa5f99f660f115f547d96b4edcadfc19f77a6de8807734bf0fc4924f81be01a" + - runner: ubuntu-latest + target: x86_64-unknown-linux-gnu + manylinux: "2014" + python-version: "3.13" + interpreter: python3.13 + container: "quay.io/pypa/manylinux2014_x86_64@sha256:2fa5f99f660f115f547d96b4edcadfc19f77a6de8807734bf0fc4924f81be01a" + - runner: ubuntu-latest + target: x86_64-unknown-linux-gnu + manylinux: "2014" + python-version: "3.14" + interpreter: python3.14 + container: "quay.io/pypa/manylinux2014_x86_64@sha256:2fa5f99f660f115f547d96b4edcadfc19f77a6de8807734bf0fc4924f81be01a" + - runner: ubuntu-24.04-arm + target: aarch64-unknown-linux-gnu + manylinux: "2014" + python-version: "3.12" + interpreter: python3.12 + container: "quay.io/pypa/manylinux2014_aarch64@sha256:47940daeb8e0fdcc7deefad334bfda3d8d12b447ba1ebd5411f9f106318eafdb" + - runner: ubuntu-24.04-arm + target: aarch64-unknown-linux-gnu + manylinux: "2014" + python-version: "3.13" + interpreter: python3.13 + container: "quay.io/pypa/manylinux2014_aarch64@sha256:47940daeb8e0fdcc7deefad334bfda3d8d12b447ba1ebd5411f9f106318eafdb" + - runner: ubuntu-24.04-arm + target: aarch64-unknown-linux-gnu + manylinux: "2014" + python-version: "3.14" + interpreter: python3.14 + container: "quay.io/pypa/manylinux2014_aarch64@sha256:47940daeb8e0fdcc7deefad334bfda3d8d12b447ba1ebd5411f9f106318eafdb" + - runner: macos-latest + target: universal2-apple-darwin + manylinux: "" + python-version: "3.12" + interpreter: python + container: "" + - runner: macos-latest + target: universal2-apple-darwin + manylinux: "" + python-version: "3.13" + interpreter: python + container: "" + - runner: macos-latest + target: universal2-apple-darwin + manylinux: "" + python-version: "3.14" + interpreter: python + container: "" + - runner: windows-latest + target: x86_64-pc-windows-msvc + manylinux: "" + python-version: "3.12" + interpreter: python + container: "" + - runner: windows-latest + target: x86_64-pc-windows-msvc + manylinux: "" + python-version: "3.13" + interpreter: python + container: "" + - runner: windows-latest + target: x86_64-pc-windows-msvc + manylinux: "" + python-version: "3.14" + interpreter: python + container: "" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + fetch-tags: true + persist-credentials: false + - name: Validate release ancestry in this job + env: + CONTROL_PLANE_COMMIT: ${{ github.sha }} + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + if ! printf '%s' "$RELEASE_COMMIT" | grep -Eq '^[0-9a-f]{40}$'; then + echo "release_commit must be a canonical 40-character lowercase SHA-1" >&2 + exit 1 + fi + trusted_control_commit="$(git rev-parse HEAD)" + if [ "$trusted_control_commit" != "$CONTROL_PLANE_COMMIT" ]; then + echo "checked-out protected control plane does not match control_plane_commit" >&2 + exit 1 + fi + canonical_release_commit="$(git rev-parse --verify "$RELEASE_COMMIT^{commit}" 2>/dev/null)" || { + echo "release_commit is unavailable for ancestry verification" >&2 + exit 1 + } + if [ "$canonical_release_commit" != "$RELEASE_COMMIT" ]; then + echo "release_commit did not resolve to its canonical commit identity" >&2 + exit 1 + fi + if ! git merge-base --is-ancestor "$canonical_release_commit" "$trusted_control_commit"; then + echo "release commit must be an ancestor of the publication control plane" >&2 + exit 1 + fi + git checkout --detach "$canonical_release_commit" + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: ${{ matrix.python-version }} + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + path: trusted-control + persist-credentials: false + # maturin-action forwards only allow-listed env prefixes (CARGO_, RUST, + # MATURIN_, ...) into the manylinux container, so SOURCE_DATE_EPOCH must + # be passed explicitly via docker-options for the Linux legs. Native + # macOS/Windows builds inherit it from the job env. + - name: Select reviewed license bundle for wheel target + shell: bash + run: bash tools/select_third_party_license.sh "${{ matrix.target }}" + - name: Build wheel + uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b + env: + CARGO_BUILD_PASS: first + with: + target: ${{ matrix.target }} + manylinux: ${{ matrix.manylinux || 'auto' }} + container: ${{ matrix.container }} + maturin-version: v1.15.0 + docker-options: -e SOURCE_DATE_EPOCH + before-script-linux: $CARGO_BUILD_PYTHON trusted-control/scripts/ci/capture_release_build_scope.py + args: --release --out dist -i ${{ matrix.interpreter }} + - name: Verify wheel license bytes + run: python tools/verify_wheel_license.py dist + - name: Capture first native-runner build graph + if: ${{ runner.os != 'Linux' }} + env: + CARGO_BUILD_PASS: first + run: python trusted-control/scripts/ci/capture_release_build_scope.py + # Clean-target second build (fresh cargo target dir) for EVERY leg. The + # reproducibility record fails closed on any publishable artifact + # without a matching byte-verified row. + - name: Rebuild wheel for byte-reproducibility check + uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b + env: + CARGO_BUILD_PASS: second + with: + target: ${{ matrix.target }} + manylinux: ${{ matrix.manylinux || 'auto' }} + container: ${{ matrix.container }} + maturin-version: v1.15.0 + docker-options: -e SOURCE_DATE_EPOCH + before-script-linux: $CARGO_BUILD_PYTHON trusted-control/scripts/ci/capture_release_build_scope.py + args: --release --out dist-rebuild --target-dir target-rebuild -i ${{ matrix.interpreter }} + - name: Capture second native-runner build graph + if: ${{ runner.os != 'Linux' }} + env: + CARGO_BUILD_PASS: second + run: python trusted-control/scripts/ci/capture_release_build_scope.py + - name: Compare double-build wheel digests and record them + shell: bash + env: + LEG: ${{ matrix.target }}-py${{ matrix.python-version }} + ARTIFACT_GLOB: "*.whl" + CONTAINER_IMAGE: ${{ matrix.container }} + run: | + set -euo pipefail + python - <<'PY' + import hashlib + import json + import os + import re + import subprocess + from pathlib import Path + + # Claim scope: a clean-target repeat build in the SAME runner and + # container environment. This is not independent-environment or + # independent-toolchain provenance. + leg = os.environ["LEG"] + pattern = os.environ["ARTIFACT_GLOB"] + + def digest(directory: str) -> tuple[str, str]: + found = sorted(Path(directory).glob(pattern)) + if len(found) != 1: + raise SystemExit(f"expected exactly one {pattern} in {directory}, found {found!r}") + return found[0].name, hashlib.sha256(found[0].read_bytes()).hexdigest() + + epoch = os.environ.get("SOURCE_DATE_EPOCH", "") + if not epoch.isdigit(): + raise SystemExit(f"SOURCE_DATE_EPOCH is not set to a release-commit epoch: {epoch!r}") + first = digest("dist") + second = digest("dist-rebuild") + if first != second: + raise SystemExit(f"{leg} is not byte-reproducible: {first!r} != {second!r}") + + # Provenance is bound to THIS invocation: Linux wheel legs build in + # the digest-pinned container passed to maturin-action, which must be + # the image present on the runner; every other row (macOS, Windows, + # sdist) ran without a container and records the runner identity. + # Unrelated cached images are never consulted. + container = os.environ.get("CONTAINER_IMAGE", "") + runner_os = os.environ.get("RUNNER_OS", "") + if runner_os == "Linux" and leg != "sdist": + if not re.fullmatch(r"[a-z0-9.\-]+(?:/[a-z0-9._\-]+)+@sha256:[0-9a-f]{64}", container): + raise SystemExit(f"{leg}: Linux wheel leg has no digest-pinned build container: {container!r}") + inspected = subprocess.run( + ["docker", "image", "inspect", "--format", "{{json .RepoDigests}}", container], + capture_output=True, text=True, + ) + if inspected.returncode != 0 or container not in json.loads(inspected.stdout or "[]"): + raise SystemExit(f"{leg}: pinned build container {container} is not present on this runner") + build_env = f"container:{container}" + else: + if container: + raise SystemExit(f"{leg}: ran without a container but carries container provenance {container!r}") + identity = [os.environ.get(key, "") for key in ("ImageOS", "ImageVersion", "RUNNER_OS", "RUNNER_ARCH")] + if not all(identity): + raise SystemExit(f"{leg}: missing runner identity (ImageOS/ImageVersion/RUNNER_OS/RUNNER_ARCH): {identity!r}") + build_env = "runner:" + "/".join(identity) + + name, sha = first + out = Path("repro-digest") + out.mkdir(exist_ok=True) + row = "\t".join([leg, "true", "clean-target-repeat-same-env", sha, second[1], name, build_env]) + (out / f"{leg}.tsv").write_text(row + "\n", encoding="utf-8") + print(row) + PY + - name: Capture finished wheel members from the build leg + shell: bash + env: + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + LEG: ${{ matrix.target }}-py${{ matrix.python-version }} + run: python trusted-control/scripts/ci/capture_release_bundle.py "repro-digest/$LEG.tsv" dist + - uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 + with: + version: "0.12.5" + enable-cache: false + - name: Capture isolated locked runtime and all optional dependencies + shell: bash + env: + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + LEG: ${{ matrix.target }}-py${{ matrix.python-version }} + run: | + set -euo pipefail + python trusted-control/scripts/ci/capture_release_runtime.py "repro-digest/$LEG.tsv" dist "$RUNNER_TEMP" + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + name: dist-sdist + path: sdist-input + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + name: repro-digest-sdist + path: sdist-proof + - name: Verify and unpack the same-run sdist + env: + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + run: python trusted-control/scripts/ci/release_sdist_consumer.py prepare sdist-proof/sdist.tsv sdist-input sdist-consumer + - name: Select reviewed license bundle for sdist consumer target + working-directory: sdist-consumer/source + shell: bash + run: bash tools/select_third_party_license.sh "${{ matrix.target }}" + - name: Build this wheel target from the verified sdist + uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b + with: + command: build + working-directory: sdist-consumer/source + target: ${{ matrix.target }} + manylinux: ${{ matrix.manylinux || 'auto' }} + container: ${{ matrix.container }} + maturin-version: v1.15.0 + docker-options: -e SOURCE_DATE_EPOCH + args: --release --out dist-consumer -i ${{ matrix.interpreter }} + - name: Verify sdist consumer wheel license bytes + working-directory: sdist-consumer/source + run: python tools/verify_wheel_license.py dist-consumer + - name: Capture target sdist consumer wheel + shell: bash + env: + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + LEG: ${{ matrix.target }}-py${{ matrix.python-version }} + run: python trusted-control/scripts/ci/release_sdist_consumer.py capture "repro-digest/$LEG.tsv" dist sdist-consumer repro-digest + - name: Install target sdist consumer wheel + shell: bash + env: + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + LEG: ${{ matrix.target }}-py${{ matrix.python-version }} + run: python trusted-control/scripts/ci/release_sdist_consumer.py install "repro-digest/$LEG.tsv" repro-digest "$RUNNER_TEMP" + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: dist-wheel-${{ matrix.target }}-py${{ matrix.python-version }} + path: dist + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: repro-rebuild-${{ matrix.target }}-py${{ matrix.python-version }} + path: dist-rebuild + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: repro-digest-${{ matrix.target }}-py${{ matrix.python-version }} + path: repro-digest + + macos-x86-runtime: + if: ${{ false }} + name: macOS x86 runtime ${{ matrix.python-version }} + needs: [verify-release, wheels] + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 20 + permissions: + contents: read + actions: read + strategy: + fail-fast: false + matrix: + python-version: ["3.12", "3.13", "3.14"] + env: + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + LEG: universal2-apple-darwin-py${{ matrix.python-version }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + fetch-tags: true + persist-credentials: false + - name: Validate release ancestry in this job + env: + CONTROL_PLANE_COMMIT: ${{ github.sha }} + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + if ! printf '%s' "$RELEASE_COMMIT" | grep -Eq '^[0-9a-f]{40}$'; then + echo "release_commit must be a canonical 40-character lowercase SHA-1" >&2 + exit 1 + fi + trusted_control_commit="$(git rev-parse HEAD)" + if [ "$trusted_control_commit" != "$CONTROL_PLANE_COMMIT" ]; then + echo "checked-out protected control plane does not match control_plane_commit" >&2 + exit 1 + fi + canonical_release_commit="$(git rev-parse --verify "$RELEASE_COMMIT^{commit}" 2>/dev/null)" || { + echo "release_commit is unavailable for ancestry verification" >&2 + exit 1 + } + if [ "$canonical_release_commit" != "$RELEASE_COMMIT" ]; then + echo "release_commit did not resolve to its canonical commit identity" >&2 + exit 1 + fi + if ! git merge-base --is-ancestor "$canonical_release_commit" "$trusted_control_commit"; then + echo "release commit must be an ancestor of the publication control plane" >&2 + exit 1 + fi + git checkout --detach "$canonical_release_commit" + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + path: trusted-control + persist-credentials: false + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: ${{ matrix.python-version }} + architecture: x64 + - uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 + with: + version: "0.12.5" + enable-cache: false + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + name: dist-wheel-${{ env.LEG }} + path: dist + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + name: repro-digest-${{ env.LEG }} + path: build-proof + - name: Capture x86_64 installed dependencies and imported wheel + run: | + set -euo pipefail + test "$(uname -m)" = x86_64 + test "$(python -c 'import platform; print(platform.machine())')" = x86_64 + mkdir runtime-proof + cp "build-proof/$LEG.tsv" "runtime-proof/$LEG.tsv" + python trusted-control/scripts/ci/capture_release_runtime.py "runtime-proof/$LEG.tsv" dist "$RUNNER_TEMP" + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: repro-macos-x86-${{ env.LEG }} + path: runtime-proof + + # Publication gate: release-assets and publish-pypi need this job, so a + # publishable artifact without a matching byte-verified row blocks both. + reproducibility-record: + name: reproducibility record + needs: [verify-release, sdist, wheels, macos-x86-runtime] + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 5 + permissions: + contents: read + actions: read + outputs: + distribution_set_artifact_id: ${{ steps.record-upload.outputs.artifact-id }} + distribution_set_artifact_digest: sha256:${{ steps.record-upload.outputs.artifact-digest }} + selected_wheel_filename: ${{ steps.bind-distributions.outputs.selected_wheel_filename }} + selected_sdist_filename: ${{ steps.bind-distributions.outputs.selected_sdist_filename }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + path: trusted-control + persist-credentials: false + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + fetch-tags: true + path: release-source + persist-credentials: false + - name: Validate release ancestry in this job + env: + CONTROL_PLANE_COMMIT: ${{ github.sha }} + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + working-directory: release-source + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + if ! printf '%s' "$RELEASE_COMMIT" | grep -Eq '^[0-9a-f]{40}$'; then + echo "release_commit must be a canonical 40-character lowercase SHA-1" >&2 + exit 1 + fi + trusted_control_commit="$(git rev-parse HEAD)" + if [ "$trusted_control_commit" != "$CONTROL_PLANE_COMMIT" ]; then + echo "checked-out protected control plane does not match control_plane_commit" >&2 + exit 1 + fi + canonical_release_commit="$(git rev-parse --verify "$RELEASE_COMMIT^{commit}" 2>/dev/null)" || { + echo "release_commit is unavailable for ancestry verification" >&2 + exit 1 + } + if [ "$canonical_release_commit" != "$RELEASE_COMMIT" ]; then + echo "release_commit did not resolve to its canonical commit identity" >&2 + exit 1 + fi + if ! git merge-base --is-ancestor "$canonical_release_commit" "$trusted_control_commit"; then + echo "release commit must be an ancestor of the publication control plane" >&2 + exit 1 + fi + git checkout --detach "$canonical_release_commit" + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + pattern: dist-* + path: dist + merge-multiple: true + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + pattern: repro-digest-* + path: repro-digest + merge-multiple: false + - name: Require a byte-verified row for every publishable artifact + env: + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + RELEASE_TAG: ${{ inputs.release_tag }} + SOURCE_DATE_EPOCH: ${{ needs.verify-release.outputs.source_date_epoch }} + run: | + set -euo pipefail + python3 - <<'PY' + import hashlib + import os + from pathlib import Path + + header = ["target", "byte_verified", "verification", "sha256", "rebuild_sha256", "file", "build_env"] + rows = {} + for path in sorted(Path("repro-digest").glob("repro-digest-*/*.tsv")): + for line in path.read_text(encoding="utf-8").splitlines(): + fields = line.split("\t") + if len(fields) != len(header): + raise SystemExit(f"malformed digest row in {path.name}: {line!r}") + row = dict(zip(header, fields)) + if row["file"] in rows: + raise SystemExit(f"duplicate digest row for {row['file']}") + rows[row["file"]] = row + + published = {p.name: hashlib.sha256(p.read_bytes()).hexdigest() for p in sorted(Path("dist").iterdir())} + if len(published) != 13: + raise SystemExit(f"expected 13 publishable artifacts (12 wheels + sdist), found {sorted(published)!r}") + failures = [] + for name, sha in published.items(): + row = rows.get(name) + if row is None: + failures.append(f"{name}: no byte-verification row") + elif row["byte_verified"] != "true" or row["sha256"] != sha or row["rebuild_sha256"] != sha: + failures.append(f"{name}: not byte-verified against the published bytes ({row!r}, published {sha})") + for name in sorted(set(rows) - set(published)): + failures.append(f"{name}: digest row without a publishable artifact") + if failures: + raise SystemExit("reproducibility gate failed:\n" + "\n".join(failures)) + + record = Path("reproducibility-record.tsv") + # Existing matrix rows retain the build environment. This job only + # binds declarations; its Ubuntu host is not a target closure oracle. + import importlib.util + import json + spec = importlib.util.spec_from_file_location("transport", "trusted-control/scripts/ci/release_artifact_transport.py") + transport = importlib.util.module_from_spec(spec) + spec.loader.exec_module(transport) + scope_records = [transport.scope_identity( + Path("dist") / row["file"], row["target"], Path("release-source"), + os.environ["RELEASE_COMMIT"], row["build_env"], + ) for row in rows.values()] + Path("release-scope-identities.json").write_text(json.dumps(scope_records, sort_keys=True) + "\n") + with record.open("w", encoding="utf-8") as stream: + stream.write( + f"# release {os.environ['RELEASE_TAG']} @ {os.environ['RELEASE_COMMIT']}, " + f"SOURCE_DATE_EPOCH={os.environ['SOURCE_DATE_EPOCH']}\n" + ) + stream.write("\t".join(header) + "\n") + for name in sorted(rows): + stream.write("\t".join(rows[name][key] for key in header) + "\n") + summary = ( + "## Release artifact digests\n\n" + f"Byte-verified: {len(rows)} of {len(published)} publishable artifacts; single-built only: 0.\n\n" + "Claim scope: every row is a clean-target repeat build in the SAME runner/container " + "environment (build_env column). This is NOT independent-environment or " + "independent-toolchain provenance.\n\n" + "```\n" + record.read_text(encoding="utf-8") + "```\n" + ) + with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as stream: + stream.write(summary) + print(record.read_text(encoding="utf-8")) + PY + - name: List distribution artifacts from this run + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + gh api --paginate "repos/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID/artifacts?per_page=100" \ + --jq '.artifacts[]' > run-artifacts.jsonl + gh api "repos/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID/attempts/$GITHUB_RUN_ATTEMPT" \ + > run-attempt.json + - name: Bind all verified distribution bytes to immutable artifact IDs + id: bind-distributions + env: + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + RELEASE_TAG: ${{ inputs.release_tag }} + EXPECTED_WHEEL_LEGS: "x86_64-unknown-linux-gnu-py3.12 x86_64-unknown-linux-gnu-py3.13 x86_64-unknown-linux-gnu-py3.14 aarch64-unknown-linux-gnu-py3.12 aarch64-unknown-linux-gnu-py3.13 aarch64-unknown-linux-gnu-py3.14 universal2-apple-darwin-py3.12 universal2-apple-darwin-py3.13 universal2-apple-darwin-py3.14 x86_64-pc-windows-msvc-py3.12 x86_64-pc-windows-msvc-py3.13 x86_64-pc-windows-msvc-py3.14" + run: | + set -euo pipefail + python3 - <<'PY' + import hashlib + import json + import os + import re + from datetime import datetime + from pathlib import Path + + legs = os.environ["EXPECTED_WHEEL_LEGS"].split() + expected_legs = set(legs) | {"sdist"} + if len(legs) != 12 or len(expected_legs) != 13: + raise SystemExit("distribution set requires 12 distinct wheel legs and one sdist") + lines = Path("reproducibility-record.tsv").read_text(encoding="utf-8").splitlines() + if len(lines) != 15 or not lines[0].startswith( + f"# release {os.environ['RELEASE_TAG']} @ {os.environ['RELEASE_COMMIT']}, SOURCE_DATE_EPOCH=" + ) or lines[1].split("\t") != ["target", "byte_verified", "verification", "sha256", "rebuild_sha256", "file", "build_env"]: + raise SystemExit("reproducibility record header or row count differs from this release") + rows = {} + for line in lines[2:]: + fields = line.split("\t") + if len(fields) != 7 or fields[0] in rows: + raise SystemExit("duplicate or malformed distribution row") + rows[fields[0]] = dict(zip(lines[1].split("\t"), fields)) + if set(rows) != expected_legs: + raise SystemExit("reproducibility record does not cover the exact distribution legs") + files = {row["file"] for row in rows.values()} + paths = list(Path("dist").iterdir()) + if (len(files) != 13 or len(paths) != 13 or files != {p.name for p in paths} + or any(p.is_symlink() or not p.is_file() for p in paths)): + raise SystemExit("distribution files differ from the record") + + seen = {} + for line in Path("run-artifacts.jsonl").read_text(encoding="utf-8").splitlines(): + artifact = json.loads(line) + name = artifact.get("name") + if name in seen: + raise SystemExit(f"duplicate same-run artifact name: {name}") + seen[name] = artifact + expected_names = {"dist-sdist"} | {f"dist-wheel-{leg}" for leg in legs} + if {name for name in seen if isinstance(name, str) and name.startswith("dist-")} != expected_names: + raise SystemExit("distribution artifact names differ from the exact 13-leg set") + run_id = int(os.environ["GITHUB_RUN_ID"]) + attempt = int(os.environ["GITHUB_RUN_ATTEMPT"]) + control_sha = os.environ["GITHUB_SHA"] + if (run_id < 1 or attempt < 1 + or not re.fullmatch(r"[0-9a-f]{40}", os.environ["RELEASE_COMMIT"]) + or not re.fullmatch(r"[0-9a-f]{40}", control_sha)): + raise SystemExit("invalid release execution identity") + run = json.loads(Path("run-attempt.json").read_text(encoding="utf-8")) + if (run.get("id") != run_id or run.get("run_attempt") != attempt + or run.get("head_sha") != control_sha): + raise SystemExit("workflow attempt does not match the release control plane") + def timestamp(value): + if not isinstance(value, str): + raise SystemExit("missing workflow or artifact timestamp") + parsed = datetime.fromisoformat(value.replace("Z", "+00:00")) + if parsed.tzinfo is None: + raise SystemExit("workflow or artifact timestamp has no timezone") + return parsed + attempt_started = timestamp(run.get("run_started_at")) + distributions = [] + ids = set() + for leg, row in sorted(rows.items()): + path = Path("dist") / row["file"] + with path.open("rb") as stream: + sha = hashlib.file_digest(stream, "sha256").hexdigest() + if (not re.fullmatch(r"[0-9a-f]{64}", row["sha256"]) + or row["byte_verified"] != "true" or row["rebuild_sha256"] != sha + or row["sha256"] != sha or path.name != row["file"]): + raise SystemExit(f"{leg}: distribution bytes are not reproducibility verified") + name = "dist-sdist" if leg == "sdist" else f"dist-wheel-{leg}" + artifact = seen[name] + artifact_id = artifact.get("id") + digest = artifact.get("digest") + if (type(artifact_id) is not int or artifact_id <= 0 or artifact_id in ids + or not isinstance(digest, str) or not re.fullmatch(r"sha256:[0-9a-f]{64}", digest) + or (artifact.get("workflow_run") or {}).get("id") != run_id + or (artifact.get("workflow_run") or {}).get("head_sha") != control_sha + or timestamp(artifact.get("created_at")) <= attempt_started + or artifact.get("expired") is not False): + raise SystemExit(f"{name}: missing or foreign immutable artifact identity") + ids.add(artifact_id) + distributions.append({"leg": leg, "file": row["file"], "sha256": sha, + "artifact_id": artifact_id, "artifact_name": name, + "artifact_digest": digest}) + scope_evidence = [] + expected_scope_names = {f"repro-digest-{leg}" for leg in expected_legs} + if {name for name in seen if name.startswith("repro-digest-")} != expected_scope_names: + raise SystemExit("scope evidence artifacts differ from the exact 13-leg set") + for leg in sorted(expected_legs): + name = f"repro-digest-{leg}" + artifact = seen[name] + artifact_id, digest = artifact.get("id"), artifact.get("digest") + if (type(artifact_id) is not int or artifact_id <= 0 or artifact_id in ids + or not isinstance(digest, str) or not re.fullmatch(r"sha256:[0-9a-f]{64}", digest) + or (artifact.get("workflow_run") or {}).get("id") != run_id + or (artifact.get("workflow_run") or {}).get("head_sha") != control_sha + or timestamp(artifact.get("created_at")) <= attempt_started + or artifact.get("expired") is not False): + raise SystemExit(f"{name}: missing or foreign immutable artifact identity") + ids.add(artifact_id) + scope_evidence.append({"leg": leg, "artifact_id": artifact_id, + "artifact_name": name, "artifact_digest": digest}) + manifest = {"schema_version": 1, "source_repository": os.environ["GITHUB_REPOSITORY"], + "source_sha": os.environ["RELEASE_COMMIT"], "control_sha": control_sha, "run_id": run_id, + "run_attempt": attempt, "distributions": distributions} + Path("release-gate-distribution-set.json").write_text( + json.dumps(manifest, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8" + ) + scope_set = {"schema_version": 1, "source_repository": os.environ["GITHUB_REPOSITORY"], + "source_sha": os.environ["RELEASE_COMMIT"], "control_sha": control_sha, + "run_id": run_id, "run_attempt": attempt, "evidence": scope_evidence} + Path("release-scope-evidence-set.json").write_text( + json.dumps(scope_set, sort_keys=True, separators=(",", ":")) + "\n", encoding="utf-8" + ) + with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output: + output.write(f"selected_wheel_filename={rows['x86_64-unknown-linux-gnu-py3.12']['file']}\n") + output.write(f"selected_sdist_filename={rows['sdist']['file']}\n") + PY + - id: record-upload + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: reproducibility-record + path: | + reproducibility-record.tsv + release-scope-identities.json + release-gate-distribution-set.json + release-scope-evidence-set.json + + dependency-gate: + name: complete dependency licence and Strix gate + needs: [verify-release, reproducibility-record] + uses: ContextualWisdomLab/.github/.github/workflows/release-dependency-license-strix-gate.yml@e249236e3f420c4e9c470f26925640dae59f0d93 + with: + source_repository: ${{ github.repository }} + source_sha: ${{ needs.verify-release.outputs.release_commit }} + ecosystems: python,cargo + python_lock_path: requirements/package.txt + cargo_manifest_path: crates/fast-mlsirm-py/Cargo.toml + cargo_dev_manifest_path: crates/mlsirm-core/Cargo.toml + distribution_set_artifact_id: ${{ needs.reproducibility-record.outputs.distribution_set_artifact_id }} + distribution_set_artifact_digest: ${{ needs.reproducibility-record.outputs.distribution_set_artifact_digest }} + wheel_filename: ${{ needs.reproducibility-record.outputs.selected_wheel_filename }} + sdist_filename: ${{ needs.reproducibility-record.outputs.selected_sdist_filename }} + # The pinned owner contract still declares direct provider credentials. + # Do not cross that legacy boundary: this release path remains fail-closed + # until an immutable gateway-token-only contract is released and pinned. + + # R5b/R5c: nothing is tagged, released or published until the exact thirteen + # files match both the reproducibility record and a successful same-run + # central licence/Strix verdict and verified target-specific scope evidence. + # This job creates only a manifest; the following jobs create release state. + release-admission: + name: release admission + needs: [verify-release, reproducibility-record, dependency-gate] + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 10 + permissions: + contents: read + actions: read + env: + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + RELEASE_TAG: ${{ inputs.release_tag }} + REPOSITORY: ${{ github.repository }} + RUN_ID: ${{ github.run_id }} + EXPECTED_WHEEL_LEGS: "x86_64-unknown-linux-gnu-py3.12 x86_64-unknown-linux-gnu-py3.13 x86_64-unknown-linux-gnu-py3.14 aarch64-unknown-linux-gnu-py3.12 aarch64-unknown-linux-gnu-py3.13 aarch64-unknown-linux-gnu-py3.14 universal2-apple-darwin-py3.12 universal2-apple-darwin-py3.13 universal2-apple-darwin-py3.14 x86_64-pc-windows-msvc-py3.12 x86_64-pc-windows-msvc-py3.13 x86_64-pc-windows-msvc-py3.14" + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + fetch-tags: true + path: release-source + persist-credentials: false + - name: Validate release ancestry in this job + env: + CONTROL_PLANE_COMMIT: ${{ github.sha }} + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + working-directory: release-source + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + if ! printf '%s' "$RELEASE_COMMIT" | grep -Eq '^[0-9a-f]{40}$'; then + echo "release_commit must be a canonical 40-character lowercase SHA-1" >&2 + exit 1 + fi + trusted_control_commit="$(git rev-parse HEAD)" + if [ "$trusted_control_commit" != "$CONTROL_PLANE_COMMIT" ]; then + echo "checked-out protected control plane does not match control_plane_commit" >&2 + exit 1 + fi + canonical_release_commit="$(git rev-parse --verify "$RELEASE_COMMIT^{commit}" 2>/dev/null)" || { + echo "release_commit is unavailable for ancestry verification" >&2 + exit 1 + } + if [ "$canonical_release_commit" != "$RELEASE_COMMIT" ]; then + echo "release_commit did not resolve to its canonical commit identity" >&2 + exit 1 + fi + if ! git merge-base --is-ancestor "$canonical_release_commit" "$trusted_control_commit"; then + echo "release commit must be an ancestor of the publication control plane" >&2 + exit 1 + fi + git checkout --detach "$canonical_release_commit" + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + path: trusted-control + persist-credentials: false + - name: List this run's artifacts + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + gh api --paginate "repos/$REPOSITORY/actions/runs/$RUN_ID/artifacts?per_page=100" \ + --jq '.artifacts[]' > run-artifacts.jsonl + gh api "repos/$REPOSITORY/actions/runs/$RUN_ID/attempts/$GITHUB_RUN_ATTEMPT" \ + > run-attempt.json + - name: Require the exact same-run artifact set + env: + RECORD_ID: ${{ needs.reproducibility-record.outputs.distribution_set_artifact_id }} + RECORD_DIGEST: ${{ needs.reproducibility-record.outputs.distribution_set_artifact_digest }} + VERDICT_ID: ${{ needs.dependency-gate.outputs.full_set_verdict_artifact_id }} + VERDICT_DIGEST: ${{ needs.dependency-gate.outputs.full_set_verdict_artifact_digest }} + VERDICT_NAME: ${{ format('{0}--full-set-verdict', needs.dependency-gate.outputs.evidence_artifact_name) }} + SEALED_NAME: ${{ needs.dependency-gate.outputs.evidence_artifact_name }} + SEALED_ID: ${{ needs.dependency-gate.outputs.evidence_artifact_id }} + SEALED_DIGEST: ${{ needs.dependency-gate.outputs.evidence_artifact_digest }} + run: | + set -euo pipefail + python3 - <<'PY' + import json + import os + import re + + # The central job's success authenticates the complete verdict; + # immutable IDs and digests bind its two evidence artifacts here. + legs = os.environ["EXPECTED_WHEEL_LEGS"].split() + if len(legs) != 12 or len(set(legs)) != 12: + raise SystemExit(f"expected 12 distinct wheel legs, got {legs!r}") + run_id = int(os.environ["RUN_ID"]) + seen: dict[str, dict] = {} + with open("run-artifacts.jsonl", encoding="utf-8") as stream: + for line in stream: + if not line.strip(): + continue + artifact = json.loads(line) + name = artifact.get("name", "") + if name in seen: + raise SystemExit(f"duplicate artifact name in this run: {name}") + seen[name] = artifact + distributions = {"dist-sdist"} | {f"dist-wheel-{leg}" for leg in legs} + scope_evidence = {"repro-digest-sdist"} | {f"repro-digest-{leg}" for leg in legs} + intel_evidence = {f"repro-macos-x86-universal2-apple-darwin-py{version}" + for version in ("3.12", "3.13", "3.14")} + evidence = {os.environ["VERDICT_NAME"], os.environ["SEALED_NAME"]} + failures = [] + selected = [] + ids = set() + missing_evidence = sorted(evidence - set(seen)) + if missing_evidence: + failures.append(f"central release evidence missing: {missing_evidence}") + for name in sorted(distributions | scope_evidence | intel_evidence | evidence | {"reproducibility-record"}): + artifact = seen.get(name) + if artifact is None: + failures.append(f"{name}: not uploaded by this run") + continue + if (artifact.get("workflow_run") or {}).get("id") != run_id: + failures.append(f"{name}: not produced by run {run_id}") + if artifact.get("expired") is not False: + failures.append(f"{name}: expired or expiry unknown") + if not re.fullmatch(r"sha256:[0-9a-f]{64}", artifact.get("digest") or ""): + failures.append(f"{name}: no sha256 artifact digest") + artifact_id = artifact.get("id") + if type(artifact_id) is not int or artifact_id <= 0 or artifact_id in ids: + failures.append(f"{name}: missing or duplicate immutable artifact ID") + else: + ids.add(artifact_id) + if name == "reproducibility-record" and ( + str(artifact_id) != os.environ["RECORD_ID"] + or artifact.get("digest") != os.environ["RECORD_DIGEST"] + ): + failures.append("reproducibility record differs from producer output") + if name == os.environ["VERDICT_NAME"] and ( + str(artifact_id) != os.environ["VERDICT_ID"] + or artifact.get("digest") != os.environ["VERDICT_DIGEST"] + ): + failures.append("full-set verdict differs from central gate output") + if name == os.environ["SEALED_NAME"] and ( + str(artifact_id) != os.environ["SEALED_ID"] + or artifact.get("digest") != os.environ["SEALED_DIGEST"] + ): + failures.append("sealed pair evidence differs from central gate output") + selected.append({"id": artifact_id, "name": name, "digest": artifact.get("digest")}) + unexpected = sorted( + name for name in seen + if name.startswith(("dist-", "repro-digest-", "repro-macos-x86-", "license-evidence-")) + and name not in distributions | scope_evidence | intel_evidence | evidence + ) + failures += [f"{name}: unexpected publishable or evidence artifact" for name in unexpected] + if failures: + raise SystemExit("release admission refused:\n" + "\n".join(failures)) + with open("selected-artifacts.json", "w", encoding="utf-8") as output: + json.dump(selected, output, sort_keys=True) + print(f"same-run artifacts: {len(distributions)} distributions and {len(evidence)} central evidence bundles") + PY + - name: Download selected immutable IDs and verify archive digests + env: + GH_TOKEN: ${{ github.token }} + run: python3 -I trusted-control/scripts/ci/release_artifact_transport.py "$REPOSITORY" + - uses: astral-sh/setup-uv@bec219d24cd3e171d82865faccec33120bb574f4 + with: + version: "0.12.5" + enable-cache: false + - name: Recompute the complete source dependency export + run: | + set -euo pipefail + uv export --project release-source --locked --offline --no-dev --no-emit-project --all-extras \ + --format requirements.txt --no-header --output-file "$RUNNER_TEMP/release-requirements.txt" + python3 - <<'PY' + import os + from pathlib import Path + + expected = (Path(os.environ["RUNNER_TEMP"]) / "release-requirements.txt").read_bytes() + for leg in os.environ["EXPECTED_WHEEL_LEGS"].split(): + captured = (Path("downloaded") / f"repro-digest-{leg}" / + f"{leg}.runtime-requirements.txt").read_bytes() + lines = captured.split(b"\n", 2) + if (len(lines) != 3 + or lines[0] != b"# This file was autogenerated by uv via the following command:" + or not lines[1].startswith(b"# uv export ") + or lines[2] != expected): + raise SystemExit(f"{leg}: runtime requirements differ from release source export") + if leg.startswith("universal2-apple-darwin-"): + intel = (Path("downloaded") / f"repro-macos-x86-{leg}" / + f"{leg}.runtime-requirements.txt").read_bytes().split(b"\n", 2) + if (len(intel) != 3 or intel[0] != lines[0] + or not intel[1].startswith(b"# uv export ") + or intel[2] != expected): + raise SystemExit(f"{leg}: Intel runtime requirements differ from release source export") + PY + - name: Provision hash-locked metadata parser + run: | + set -euo pipefail + uv venv "$RUNNER_TEMP/release-admission-venv" + uv pip install --python "$RUNNER_TEMP/release-admission-venv/bin/python" \ + --require-hashes -r trusted-control/requirements/package.txt + - name: Admit exactly the verified bytes of release_commit + env: + RECORD_ID: ${{ needs.reproducibility-record.outputs.distribution_set_artifact_id }} + RECORD_DIGEST: ${{ needs.reproducibility-record.outputs.distribution_set_artifact_digest }} + VERDICT_ID: ${{ needs.dependency-gate.outputs.full_set_verdict_artifact_id }} + VERDICT_DIGEST: ${{ needs.dependency-gate.outputs.full_set_verdict_artifact_digest }} + VERDICT_NAME: ${{ format('{0}--full-set-verdict', needs.dependency-gate.outputs.evidence_artifact_name) }} + run: | + set -euo pipefail + "$RUNNER_TEMP/release-admission-venv/bin/python" - <<'PY' + import hashlib + import importlib.util + import io + import json + import os + import re + import zipfile + from pathlib import Path + + spec = importlib.util.spec_from_file_location("transport", "trusted-control/scripts/ci/release_artifact_transport.py") + transport = importlib.util.module_from_spec(spec) + spec.loader.exec_module(transport) + selected = json.loads(Path("selected-artifacts.json").read_text()) + receipt = json.loads(Path("transport-receipt.json").read_text()) + transport.verify_materialized(selected, receipt, Path("downloaded")) + + def sha256(path: Path) -> str: + return transport.hash_file(path) + + legs = os.environ["EXPECTED_WHEEL_LEGS"].split() + commit, tag = os.environ["RELEASE_COMMIT"], os.environ["RELEASE_TAG"] + failures = [] + + # Source and same-release binding of the reproducibility record. + lines = Path("downloaded/reproducibility-record/reproducibility-record.tsv").read_text(encoding="utf-8").splitlines() + if not lines or not lines[0].startswith(f"# release {tag} @ {commit}, SOURCE_DATE_EPOCH="): + raise SystemExit(f"reproducibility record is not bound to {tag} @ {commit}: {lines[:1]!r}") + header = lines[1].split("\t") + rows = {} + filenames = set() + for line in lines[2:]: + if len(line.split("\t")) != len(header): + raise SystemExit("malformed reproducibility row") + row = dict(zip(header, line.split("\t"))) + if row["target"] in rows or row["file"] in filenames: + raise SystemExit("duplicate record target or filename") + filenames.add(row["file"]) + rows[row["target"]] = row + if set(rows) != set(legs) | {"sdist"}: + raise SystemExit(f"record legs {sorted(rows)!r} are not the 12 wheel legs + sdist") + if len(filenames) != 13: + raise SystemExit("expected 13 unique distribution filenames") + for leg in legs: + target, python = leg.rsplit("-py", 1) + cp = "cp" + python.replace(".", "") + platforms = { + "x86_64-unknown-linux-gnu": r"manylinux(?:2014|_2_[0-9]+)_x86_64", + "aarch64-unknown-linux-gnu": r"manylinux(?:2014|_2_[0-9]+)_aarch64", + "universal2-apple-darwin": r"macosx_[0-9]+_[0-9]+_(?:x86_64|arm64|universal2)", + "x86_64-pc-windows-msvc": r"win_amd64", + } + parts = rows[leg]["file"].removesuffix(".whl").rsplit("-", 3) + if (not rows[leg]["file"].endswith(".whl") or len(parts) != 4 + or parts[1:3] != [cp, cp] + or not all(re.fullmatch(platforms[target], p) for p in parts[3].split(".")) + or (target == "universal2-apple-darwin" + and not any(p.endswith("_universal2") for p in parts[3].split(".")))): + raise SystemExit(f"{leg}: wheel filename platform/ABI mismatch") + if not rows["sdist"]["file"].endswith(".tar.gz"): + raise SystemExit("sdist filename must end with .tar.gz") + for leg, row in rows.items(): + if row["byte_verified"] != "true" or row["sha256"] != row["rebuild_sha256"]: + failures.append(f"{leg}: record row is not byte-verified") + + # The 13 distribution files are exactly the recorded bytes. + dist = {} + for leg, row in rows.items(): + artifact_name = "dist-sdist" if leg == "sdist" else f"dist-wheel-{leg}" + members = list((Path("downloaded") / artifact_name).iterdir()) + if len(members) != 1 or members[0].name != row["file"]: + raise SystemExit(f"{leg}: distribution files differ from the record") + dist[members[0].name] = sha256(members[0]) + if dist[members[0].name] != row["sha256"]: + raise SystemExit(f"{leg}: distribution files differ from the record") + if leg != "sdist": + try: + with zipfile.ZipFile(members[0]) as archive: + metadata = [p for p in archive.namelist() if p.endswith(".dist-info/WHEEL")] + if len(metadata) != 1: + raise ValueError("expected one WHEEL metadata member") + with archive.open(metadata[0]) as wheel_metadata: + tags = [line[5:].rstrip("\r\n") for line in io.TextIOWrapper(wheel_metadata, encoding="utf-8") if line.startswith("Tag: ")] + parts = row["file"][:-4].rsplit("-", 3) + expected_tags = {f"{parts[1]}-{parts[2]}-{p}" for p in parts[3].split(".")} + if len(tags) != len(set(tags)) or set(tags) != expected_tags: + raise ValueError("WHEEL tags differ from filename ABI/platform") + except (OSError, ValueError, zipfile.BadZipFile, UnicodeError) as error: + raise SystemExit(f"{leg}: wheel metadata invalid ({error})") + recorded = {row["file"]: row["sha256"] for row in rows.values()} + if dist != recorded: + failures.append(f"distribution files differ from the record: files={sorted(dist)!r}") + if failures: + raise SystemExit("release admission refused:\n" + "\n".join(failures)) + + scope_set = json.loads(Path("downloaded/reproducibility-record/release-scope-evidence-set.json").read_text()) + scope_names = {f"repro-digest-{leg}" for leg in rows} + expected_scope_set = { + "schema_version": 1, "source_repository": os.environ["REPOSITORY"], + "source_sha": commit, "control_sha": os.environ["GITHUB_SHA"], + "run_id": int(os.environ["RUN_ID"]), "run_attempt": int(os.environ["GITHUB_RUN_ATTEMPT"]), + "evidence": [ + {"leg": leg, "artifact_id": item["id"], "artifact_name": name, + "artifact_digest": item["digest"]} + for leg in sorted(rows) + for name in [f"repro-digest-{leg}"] + for item in selected if item["name"] == name + ], + } + if (scope_set != expected_scope_set or len(scope_set.get("evidence", [])) != 13 + or {item["name"] for item in selected if item["name"].startswith("repro-digest-")} != scope_names): + raise SystemExit("scope evidence set differs from selected same-run artifacts") + def unique_object(pairs): + value = {} + for key, item in pairs: + if key in value: + raise ValueError(f"duplicate bundle inventory key: {key}") + value[key] = item + return value + runtime_records = [] + build_records = [] + build_graph_receipts = [] + for leg, row in rows.items(): + name = f"repro-digest-{leg}" + members = {path.name: path for path in (Path("downloaded") / name).iterdir()} + expected_members = {f"{leg}.tsv", f"{leg}.bundle.json", + f"{leg}.build-first.json", f"{leg}.build-second.json", + f"{leg}.build-python.zip"} + if leg != "sdist": + expected_members |= {f"{leg}.runtime.json", f"{leg}.runtime-requirements.txt", + f"{leg}.consumer.json", f"{leg}.consumer.whl"} + if (leg == "sdist" and set(members) != expected_members + or leg != "sdist" and not expected_members <= set(members)): + raise SystemExit(f"{leg}: scope evidence artifact members differ from build output") + if members[f"{leg}.tsv"].read_text(encoding="utf-8") != "\t".join(row[key] for key in header) + "\n": + raise SystemExit(f"{leg}: scope evidence row differs from reproducibility record") + artifact_name = "dist-sdist" if leg == "sdist" else f"dist-wheel-{leg}" + distribution = Path("downloaded") / artifact_name / row["file"] + expected_bundle = transport.bundle_inventory(distribution, leg, commit, row["build_env"]) + observed_bundle = json.loads(members[f"{leg}.bundle.json"].read_text(encoding="utf-8"), + object_pairs_hook=unique_object) + if observed_bundle != expected_bundle: + raise SystemExit(f"{leg}: build-leg bundle inventory differs from distribution bytes") + build_first = json.loads(members[f"{leg}.build-first.json"].read_text(encoding="utf-8"), + object_pairs_hook=unique_object) + build_second = json.loads(members[f"{leg}.build-second.json"].read_text(encoding="utf-8"), + object_pairs_hook=unique_object) + transport.verify_build_scope(build_first, build_second, row, Path("release-source"), + commit, members[f"{leg}.build-python.zip"]) + build_graph_receipts.append(build_first) + build_records.append({key: build_first[key] for key in + ("leg", "python_packages", "python_snapshot_sha256", + "build_env", "maturin_version", "maturin_binary_sha256")}) + if leg != "sdist": + runtime = json.loads(members[f"{leg}.runtime.json"].read_text(encoding="utf-8"), + object_pairs_hook=unique_object) + transport.verify_runtime_inventory( + runtime, members[f"{leg}.runtime-requirements.txt"], row, + Path("release-source"), commit, expected_bundle, distribution, members, + ) + consumer = json.loads(members[f"{leg}.consumer.json"].read_text(encoding="utf-8"), + object_pairs_hook=unique_object) + transport.verify_sdist_consumer( + consumer, members[f"{leg}.consumer.whl"], distribution, row, + rows["sdist"], commit, runtime, + ) + runtime_records.append(runtime) + + runtime_variants = [] + for version in ("3.12", "3.13", "3.14"): + leg = f"universal2-apple-darwin-py{version}" + row = rows[leg] + members = {path.name: path for path in + (Path("downloaded") / f"repro-macos-x86-{leg}").iterdir()} + if not {f"{leg}.tsv", f"{leg}.runtime.json", + f"{leg}.runtime-requirements.txt"} <= members.keys(): + raise SystemExit(f"{leg}: Intel runtime evidence is incomplete") + if members[f"{leg}.tsv"].read_text(encoding="utf-8") != "\t".join( + row[key] for key in header) + "\n": + raise SystemExit(f"{leg}: Intel runtime row differs from selected wheel") + distribution = Path("downloaded") / f"dist-wheel-{leg}" / row["file"] + bundle = transport.bundle_inventory(distribution, leg, commit, row["build_env"]) + runtime = json.loads(members[f"{leg}.runtime.json"].read_text(encoding="utf-8"), + object_pairs_hook=unique_object) + transport.verify_runtime_inventory( + runtime, members[f"{leg}.runtime-requirements.txt"], row, + Path("release-source"), commit, bundle, distribution, members, + runtime_only=True, + ) + runtime_variants.append(runtime) + + # The central job result is required by `needs`; its immutable verdict + # must independently match the record and all current-attempt artifacts. + verifier_spec = importlib.util.spec_from_file_location( + "full_verdict", "trusted-control/scripts/ci/verify_release_full_set_verdict.py" + ) + verifier = importlib.util.module_from_spec(verifier_spec) + verifier_spec.loader.exec_module(verifier) + verdict_name = os.environ["VERDICT_NAME"] + verdict_members = {path.name: path for path in (Path("downloaded") / verdict_name).iterdir()} + if set(verdict_members) != {"full-set-verdict.json", "gate-report.json", + "runtime-archive-license-report.json", "release-native-links.json"}: + raise SystemExit("central verdict artifact does not contain the exact dependency report") + verdict = json.loads(verdict_members["full-set-verdict.json"].read_text(encoding="utf-8"), + object_pairs_hook=unique_object) + report_bytes = verdict_members["gate-report.json"].read_bytes() + report = json.loads(report_bytes, object_pairs_hook=unique_object) + archive_report_bytes = verdict_members["runtime-archive-license-report.json"].read_bytes() + archive_report = json.loads(archive_report_bytes, object_pairs_hook=unique_object) + native_bytes = verdict_members["release-native-links.json"].read_bytes() + native = json.loads(native_bytes, object_pairs_hook=unique_object) + verifier.verify_full_set_verdict( + json.loads(Path("downloaded/reproducibility-record/release-gate-distribution-set.json").read_text()), + scope_set, + verdict, + [json.loads(line) for line in Path("run-artifacts.jsonl").read_text().splitlines()], + json.loads(Path("run-attempt.json").read_text()), + repository=os.environ["REPOSITORY"], source_sha=commit, + control_sha=os.environ["GITHUB_SHA"], run_id=int(os.environ["RUN_ID"]), + run_attempt=int(os.environ["GITHUB_RUN_ATTEMPT"]), + record_id=int(os.environ["RECORD_ID"]), record_digest=os.environ["RECORD_DIGEST"], + verdict_id=int(os.environ["VERDICT_ID"]), verdict_digest=os.environ["VERDICT_DIGEST"], + verdict_name=verdict_name, + ) + verifier.verify_runtime_dependency_coverage( + verdict, report, report_bytes, archive_report, archive_report_bytes, + runtime_records, runtime_variants, build_records, + repository=os.environ["REPOSITORY"], source_sha=commit, + ) + verifier.verify_native_link_inventory(verdict, native, native_bytes, + runtime_records, commit) + + # The same immutable-ID transport receipt covers this JSON alongside + # the TSV. Recompute its source/archive bindings before any admission. + scope_records = json.loads(Path("downloaded/reproducibility-record/release-scope-identities.json").read_text()) + distribution_paths = { + row["file"]: Path("downloaded") / ("dist-sdist" if leg == "sdist" else f"dist-wheel-{leg}") / row["file"] + for leg, row in rows.items() + } + transport.verify_scope_declarations(scope_records, rows, distribution_paths, Path("release-source"), commit) + for leg, row in rows.items(): + transport.verify_distribution_requirements(distribution_paths[row["file"]], leg, + Path("release-source"), commit) + transport.verify_bundled_license_notices( + transport.bundle_inventory(distribution_paths[row["file"]], leg, commit, row["build_env"]), + Path("release-source"), commit) + transport.verify_license_selection_report(report, Path("release-source"), commit) + transport.verify_cargo_graph_closure(build_graph_receipts, Path("release-source"), commit) + verifier.verify_cargo_dependency_coverage(report, build_graph_receipts, + source=Path("release-source"), source_sha=commit) + + # Source declarations cannot approve a closure. All collected scope + # receipts and the sealed licence/Strix set were verified above. + # Exclusive creation prevents a pre-existing file from being reused. + with Path("admitted-manifest.tsv").open("x", encoding="utf-8") as admitted: + for filename, digest in sorted(recorded.items()): + admitted.write(f"{digest} {filename}\n") + + PY + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: release-admission-manifest + path: admitted-manifest.tsv + + # R5a: the immutable tag and the GitHub release are created only here, after + # release admission. release-tag.yml verifies and dispatches but no longer + # creates either. A retry re-runs the whole build/record/admission chain; an + # existing tag or release is only resumed when it already targets + # release_commit, so no existing-tag path skips admission. + create-tag-and-release: + name: create tag and release after admission + needs: [verify-release, release-admission] + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 10 + permissions: + contents: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + fetch-tags: true + persist-credentials: false + - name: Validate release ancestry in this job + env: + CONTROL_PLANE_COMMIT: ${{ github.sha }} + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + if ! printf '%s' "$RELEASE_COMMIT" | grep -Eq '^[0-9a-f]{40}$'; then + echo "release_commit must be a canonical 40-character lowercase SHA-1" >&2 + exit 1 + fi + trusted_control_commit="$(git rev-parse HEAD)" + if [ "$trusted_control_commit" != "$CONTROL_PLANE_COMMIT" ]; then + echo "checked-out protected control plane does not match control_plane_commit" >&2 + exit 1 + fi + canonical_release_commit="$(git rev-parse --verify "$RELEASE_COMMIT^{commit}" 2>/dev/null)" || { + echo "release_commit is unavailable for ancestry verification" >&2 + exit 1 + } + if [ "$canonical_release_commit" != "$RELEASE_COMMIT" ]; then + echo "release_commit did not resolve to its canonical commit identity" >&2 + exit 1 + fi + if ! git merge-base --is-ancestor "$canonical_release_commit" "$trusted_control_commit"; then + echo "release commit must be an ancestor of the publication control plane" >&2 + exit 1 + fi + git checkout --detach "$canonical_release_commit" + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: "3.12" + - name: Derive the release version from the admitted tag + env: + RELEASE_TAG: ${{ inputs.release_tag }} + run: | + set -euo pipefail + if ! printf '%s' "$RELEASE_TAG" | grep -Eq '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$'; then + echo "release_tag must be v.., got $RELEASE_TAG" >&2 + exit 1 + fi + echo "RELEASE_VERSION=${RELEASE_TAG#v}" >> "$GITHUB_ENV" + - name: Fail closed on fragment to CHANGELOG aggregate drift + run: | + set -euo pipefail + python scripts/render_changelog_fragments.py --check CHANGELOG.md + - name: Extract the release notes from the CHANGELOG section + run: | + set -euo pipefail + awk -v header="## [$RELEASE_VERSION] - " ' + index($0, header) == 1 { capture = 1; next } + capture && /^## / { exit } + capture { print } + ' CHANGELOG.md > release_notes.md + if ! grep -q '[^[:space:]]' release_notes.md; then + echo "CHANGELOG section for $RELEASE_VERSION is empty" >&2 + exit 1 + fi + - name: Cap the release body below the GitHub release-body limit + run: | + set -euo pipefail + python3 - <<'PY' + import os + from pathlib import Path + + notes_path = Path("release_notes.md") + notes_text = notes_path.read_text(encoding="utf-8") + body_limit = 120_000 + if len(notes_text) > body_limit: + release_version = os.environ["RELEASE_VERSION"] + repository = os.environ["GITHUB_REPOSITORY"] + changelog_url = ( + f"https://github.com/{repository}/blob/" + f"v{release_version}/CHANGELOG.md" + ) + headings = list( + dict.fromkeys( + line + for line in notes_text.splitlines() + if line.startswith("#### ") + ) + ) + summary_lines = [ + "The complete notes for this release exceed GitHub's " + "release-body limit, so this body is a summary.", + "", + f"Full authoritative notes: the `[{release_version}]` " + f"section of [CHANGELOG.md]({changelog_url}).", + "", + "### Contents", + "", + ] + summary_lines += [ + "- " + line[len("#### "):] for line in headings + ] + capped_text = "\n".join(summary_lines) + "\n" + if len(capped_text) > body_limit: + capped_text = ( + capped_text[: body_limit - 60].rsplit("\n", 1)[0] + + "\n\n(contents list truncated)\n" + ) + notes_path.write_text(capped_text, encoding="utf-8") + PY + - name: Classify the tag and release state after admission + id: release_tag_state + env: + GH_TOKEN: ${{ github.token }} + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + run: | + set -euo pipefail + api_status() { + endpoint="$1" + response_file="$2" + curl --silent --show-error \ + --output "$response_file" \ + --write-out '%{http_code}' \ + --header "Accept: application/vnd.github+json" \ + --header "Authorization: Bearer $GH_TOKEN" \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/$endpoint" + } + response_file="$(mktemp)" + status="$(api_status "git/ref/tags/v$RELEASE_VERSION" "$response_file")" + case "$status" in + 404) + tag_exists=false + echo "resume_existing_tag=false" >> "$GITHUB_OUTPUT" + ;; + 200) + python3 - "$response_file" <<'PY' + import json + import os + import sys + from pathlib import Path + + response = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) + tag_object = response.get("object", {}) + actual_tag_sha = tag_object.get("sha") + if ( + tag_object.get("type") != "commit" + or actual_tag_sha != os.environ["RELEASE_COMMIT"] + ): + raise SystemExit( + "existing tag does not target the requested release commit" + ) + PY + tag_exists=true + echo "tag v$RELEASE_VERSION already targets release_commit; resuming publication for the existing immutable tag" + echo "resume_existing_tag=true" >> "$GITHUB_OUTPUT" + ;; + *) + cat "$response_file" >&2 + echo "GitHub API returned HTTP $status while checking tag v$RELEASE_VERSION" >&2 + exit 1 + ;; + esac + status="$(api_status "releases/tags/v$RELEASE_VERSION" "$response_file")" + case "$status" in + 404) + echo "resume_existing_release=false" >> "$GITHUB_OUTPUT" + ;; + 200) + if [ "$tag_exists" != "true" ]; then + echo "release v$RELEASE_VERSION exists without its verified tag; refusing to reuse it" >&2 + exit 1 + fi + echo "release v$RELEASE_VERSION already exists for the verified tag; resuming without recreating it" + echo "resume_existing_release=true" >> "$GITHUB_OUTPUT" + ;; + *) + cat "$response_file" >&2 + echo "GitHub API returned HTTP $status while checking release v$RELEASE_VERSION" >&2 + exit 1 + ;; + esac + rm -f "$response_file" + - name: Atomically create the immutable release tag + if: steps.release_tag_state.outputs.resume_existing_tag != 'true' + env: + GH_TOKEN: ${{ github.token }} + RELEASE_COMMIT: ${{ needs.verify-release.outputs.release_commit }} + run: | + set -euo pipefail + request_file="$(mktemp)" + response_file="$(mktemp)" + cleanup() { + rm -f "$request_file" "$response_file" + } + trap cleanup EXIT + + python3 - "$request_file" <<'PY' + import json + import os + import sys + from pathlib import Path + + Path(sys.argv[1]).write_text( + json.dumps( + { + "ref": f"refs/tags/v{os.environ['RELEASE_VERSION']}", + "sha": os.environ["RELEASE_COMMIT"], + } + ), + encoding="utf-8", + ) + PY + + status="$(curl --silent --show-error \ + --request POST \ + --output "$response_file" \ + --write-out '%{http_code}' \ + --header "Accept: application/vnd.github+json" \ + --header "Authorization: Bearer $GH_TOKEN" \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + --header "Content-Type: application/json" \ + --data-binary "@$request_file" \ + "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/git/refs")" + if [ "$status" != "201" ]; then + cat "$response_file" >&2 + echo "GitHub API returned HTTP $status while creating the release tag" >&2 + exit 1 + fi + + python3 - "$response_file" <<'PY' + import json + import os + import sys + from pathlib import Path + + response = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) + expected_ref = f"refs/tags/v{os.environ['RELEASE_VERSION']}" + actual_ref = response.get("ref") + actual_sha = response.get("object", {}).get("sha") + if actual_ref != expected_ref or actual_sha != os.environ["RELEASE_COMMIT"]: + raise SystemExit( + "GitHub returned an unexpected ref or commit for the release tag" + ) + PY + - name: Publish the GitHub release from the verified tag + if: steps.release_tag_state.outputs.resume_existing_release != 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + gh release create "v$RELEASE_VERSION" \ + --repo "$GITHUB_REPOSITORY" \ + --verify-tag \ + --title "v$RELEASE_VERSION" \ + --notes-file release_notes.md + + release-assets: + name: attach release assets + needs: [sdist, wheels, reproducibility-record, release-admission, create-tag-and-release] + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 10 + permissions: + contents: write + steps: + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + pattern: dist-* + path: dist + merge-multiple: true + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + name: release-admission-manifest + path: admission + - name: Require the admitted bytes + run: | + set -euo pipefail + (cd dist && sha256sum -- *) | sort -k2 > downloaded.tsv + sort -k2 admission/admitted-manifest.tsv > admitted.tsv + if ! diff -u admitted.tsv downloaded.tsv; then + echo "downloaded distributions are not the admitted bytes" >&2 + exit 1 + fi + - name: List built artifacts + run: ls -la dist + - name: Attach release assets + env: + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ inputs.release_tag }} + REPOSITORY: ${{ github.repository }} + run: | + set -euo pipefail + # Immutable GitHub Releases reject later asset uploads (HTTP 422). + # Skip cleanly so a PyPI-only republish of an already-cut release is + # not forced into an overall workflow FAILURE by an unrecoverable + # asset sink, while still failing closed for mutable releases. + immutable="$(gh api "repos/$REPOSITORY/releases/tags/$RELEASE_TAG" --jq '.immutable // false')" + if [ "$immutable" = "true" ]; then + echo "release $RELEASE_TAG is immutable; skipping GitHub asset upload" + exit 0 + fi + # Per-file upload: already-present assets also return HTTP 422 + # (name already_exists / immutable). Treat those as success so + # retries after a partial attach remain green; other failures fail closed. + shopt -s nullglob + assets=(dist/*) + if [ "${#assets[@]}" -eq 0 ]; then + echo "no dist artifacts to attach" >&2 + exit 1 + fi + for asset in "${assets[@]}"; do + upload_log="$(mktemp)" + set +e + gh release upload "$RELEASE_TAG" "$asset" --repo "$REPOSITORY" >"$upload_log" 2>&1 + status=$? + set -e + if [ "$status" -eq 0 ]; then + rm -f "$upload_log" + continue + fi + if grep -Eiq 'HTTP 422|already[_ ]exists|immutable|unprocessable' "$upload_log"; then + echo "skipping already-uploaded or immutable asset: $asset" + cat "$upload_log" + rm -f "$upload_log" + continue + fi + cat "$upload_log" >&2 + rm -f "$upload_log" + echo "failed to attach release asset: $asset" >&2 + exit 1 + done + + publish-pypi: + name: publish to PyPI + needs: [sdist, wheels, reproducibility-record, release-admission, create-tag-and-release] + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 15 + environment: pypi + permissions: + contents: read + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + ref: ${{ github.sha }} + persist-credentials: false + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + pattern: dist-* + path: dist + merge-multiple: true + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c + with: + name: release-admission-manifest + path: admission + - name: Require the admitted bytes + run: | + set -euo pipefail + (cd dist && sha256sum -- *) | sort -k2 > downloaded.tsv + sort -k2 admission/admitted-manifest.tsv > admitted.tsv + if ! diff -u admitted.tsv downloaded.tsv; then + echo "downloaded distributions are not the admitted bytes" >&2 + exit 1 + fi + - name: List built artifacts + run: ls -la dist + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 + with: + password: ${{ secrets.PIPY_TOKEN }} + attestations: false + # Partial publishes (wheels accepted, sdist rejected) must be + # retryable without failing on already-uploaded filenames. + skip-existing: true + - name: Verify published PyPI matrix against admission + run: python3 scripts/ci/verify_published_pypi.py admission/admitted-manifest.tsv '${{ inputs.release_tag }}' diff --git a/.github/workflows/fast-mlsirm-pypi-gap-guard.yml b/.github/workflows/fast-mlsirm-pypi-gap-guard.yml new file mode 100644 index 0000000000..cfcfb3a3a2 --- /dev/null +++ b/.github/workflows/fast-mlsirm-pypi-gap-guard.yml @@ -0,0 +1,168 @@ +name: PyPI Gap Guard + +# Idempotent recovery: when a GitHub Release exists for a canonical vX.Y.Z tag +# whose project version matches pyproject.toml at that tag, but the version is +# absent from PyPI, dispatch the local publish-pypi.yml control plane with the +# same provenance inputs as release-tag (release_tag, release_commit from the +# tag, control_plane_commit = default-branch HEAD). Never retargets tags. +# See docs/orchestration/release-publish-contract.md and ADR-0029. + +on: + workflow_call: + +permissions: + contents: read + +concurrency: + group: central-fast-mlsirm-pypi-gap-guard-pypi-gap-guard + cancel-in-progress: false + +jobs: + recover-missing-pypi: + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 15 + permissions: + contents: read + actions: write + steps: + - name: Checkout default-branch control plane + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + fetch-depth: 0 + fetch-tags: true + ref: ${{ github.event.repository.default_branch }} + + - name: Resolve control-plane commit and PyPI inventory + id: inventory + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + EVENT_NAME: ${{ github.event_name }} + GH_TOKEN: ${{ github.token }} + RELEASE_TAG_HINT: ${{ github.event.release.tag_name }} + run: | + set -euo pipefail + CONTROL_PLANE_COMMIT="$(git rev-parse HEAD)" + if ! printf '%s' "$CONTROL_PLANE_COMMIT" | grep -Eq '^[0-9a-f]{40}$'; then + echo "default-branch HEAD is unavailable for package publication dispatch" >&2 + exit 1 + fi + echo "control_plane_commit=$CONTROL_PLANE_COMMIT" >> "$GITHUB_OUTPUT" + + pypi_json="$(mktemp)" + cleanup() { + rm -f "$pypi_json" + } + trap cleanup EXIT + + # Prefer the public PyPI JSON API over GitHub API spam for package + # version inventory. + curl --silent --show-error --fail \ + --header "Accept: application/json" \ + --output "$pypi_json" \ + "https://pypi.org/pypi/fast-mlsirm/json" + + python3 - "$pypi_json" <<'PY' + import json + import os + import sys + from pathlib import Path + + payload = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) + releases = payload.get("releases") or {} + versions = sorted(str(name) for name in releases.keys()) + Path("pypi_versions.txt").write_text( + "\n".join(versions) + ("\n" if versions else ""), + encoding="utf-8", + ) + print(f"loaded {len(versions)} PyPI versions for fast-mlsirm") + PY + + candidate_tags=() + if [ "$EVENT_NAME" = "release" ] && [ -n "${RELEASE_TAG_HINT:-}" ]; then + candidate_tags+=("$RELEASE_TAG_HINT") + else + # gh release list is scoped to this repository and avoids a broad + # REST pagination loop for every org repo. + release_tags="$( + gh release list --repo "$GITHUB_REPOSITORY" --limit 100 \ + --json tagName \ + --jq '.[].tagName' + )" + while IFS= read -r tag; do + [ -n "$tag" ] || continue + candidate_tags+=("$tag") + done <<< "$release_tags" + fi + + : > gap_tags.txt + for tag in "${candidate_tags[@]}"; do + if ! printf '%s' "$tag" | grep -Eq '^v(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$'; then + echo "skipping non-canonical release tag: $tag" + continue + fi + version="${tag#v}" + if grep -Fxq "$version" pypi_versions.txt; then + echo "PyPI already has $version; skipping $tag" + continue + fi + printf '%s\n' "$tag" >> gap_tags.txt + done + + if [ ! -s gap_tags.txt ]; then + echo "no PyPI gaps detected" + echo "gap_count=0" >> "$GITHUB_OUTPUT" + else + echo "gap_count=$(wc -l < gap_tags.txt | tr -d ' ')" >> "$GITHUB_OUTPUT" + echo "detected gaps:" + cat gap_tags.txt + fi + + - name: Dispatch publish-pypi for each missing version + if: steps.inventory.outputs.gap_count != '0' + env: + GH_TOKEN: ${{ github.token }} + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + CONTROL_PLANE_COMMIT: ${{ steps.inventory.outputs.control_plane_commit }} + run: | + set -euo pipefail + while IFS= read -r release_tag; do + [ -n "$release_tag" ] || continue + if ! release_commit="$(git rev-parse "$release_tag^{commit}" 2>/dev/null)"; then + echo "release tag $release_tag is unavailable for provenance verification" >&2 + exit 1 + fi + if ! printf '%s' "$release_commit" | grep -Eq '^[0-9a-f]{40}$'; then + echo "release commit for $release_tag is not a canonical SHA-1" >&2 + exit 1 + fi + if ! git merge-base --is-ancestor "$release_commit" "$CONTROL_PLANE_COMMIT"; then + echo "release commit for $release_tag must be an ancestor of default-branch HEAD" >&2 + exit 1 + fi + + project_version="$( + git show "$release_commit:pyproject.toml" | python3 -c ' + import sys + import tomllib + + project = tomllib.loads(sys.stdin.read())["project"] + print(project["version"]) + ' + )" + expected_tag="v$project_version" + if [ "$release_tag" != "$expected_tag" ]; then + echo "tag $release_tag does not match pyproject version $project_version at $release_commit" >&2 + exit 1 + fi + + echo "dispatching publish-pypi.yml for $release_tag @ $release_commit (control plane $CONTROL_PLANE_COMMIT)" + gh workflow run publish-pypi.yml \ + --repo "$GITHUB_REPOSITORY" \ + --ref "$DEFAULT_BRANCH" \ + -f release_tag="$release_tag" \ + -f release_commit="$release_commit" \ + -f control_plane_commit="$CONTROL_PLANE_COMMIT" + done < gap_tags.txt diff --git a/.github/workflows/fast-mlsirm-release-tag.yml b/.github/workflows/fast-mlsirm-release-tag.yml new file mode 100644 index 0000000000..54c662b2d8 --- /dev/null +++ b/.github/workflows/fast-mlsirm-release-tag.yml @@ -0,0 +1,343 @@ +name: Release Tag +on: + workflow_call: + inputs: + release_version: + description: "Release version to publish (e.g. 0.2.0); must match pyproject.toml" + required: true + type: string + release_commit: + description: "Release source commit reviewed for this version (full lowercase SHA-1)" + required: true + type: string + +permissions: + contents: read + +concurrency: + group: central-fast-mlsirm-release-tag-release-tag + cancel-in-progress: false + +jobs: + publish-release-tag: + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 10 + permissions: + contents: read + actions: write + steps: + - name: Verify dispatch targets the default branch + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + DISPATCH_REF: ${{ github.ref }} + run: | + set -euo pipefail + expected_ref="refs/heads/$DEFAULT_BRANCH" + if [ "$DISPATCH_REF" != "$expected_ref" ]; then + echo "release dispatch must target $expected_ref, not $DISPATCH_REF" >&2 + exit 1 + fi + - name: Validate release source identity + env: + RELEASE_COMMIT: ${{ inputs.release_commit }} + run: | + set -euo pipefail + if ! printf '%s' "$RELEASE_COMMIT" | grep -Eq '^[0-9a-f]{40}$'; then + echo "release_commit must be a canonical 40-character lowercase SHA-1" >&2 + exit 1 + fi + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + fetch-depth: 0 + ref: ${{ github.sha }} + - name: Verify the release source is on the current default-branch lineage + env: + DISPATCH_SHA: ${{ github.sha }} + RELEASE_COMMIT: ${{ inputs.release_commit }} + run: | + set -euo pipefail + checked_out_commit="$(git rev-parse HEAD)" + if [ "$checked_out_commit" != "$DISPATCH_SHA" ]; then + echo "checked-out control plane does not match the default-branch dispatch" >&2 + exit 1 + fi + canonical_release_commit="$(git rev-parse --verify "$RELEASE_COMMIT^{commit}")" + if [ "$canonical_release_commit" != "$RELEASE_COMMIT" ]; then + echo "release_commit must identify a commit directly" >&2 + exit 1 + fi + if ! git merge-base --is-ancestor "$RELEASE_COMMIT" "$DISPATCH_SHA"; then + echo "release commit must be an ancestor of the default branch" >&2 + exit 1 + fi + git checkout --detach "$canonical_release_commit" + if [ "$(git rev-parse HEAD)" != "$canonical_release_commit" ]; then + echo "checked-out release source does not match release_commit" >&2 + exit 1 + fi + - name: Verify the requested version is the released source version + env: + RELEASE_VERSION: ${{ inputs.release_version }} + run: | + set -euo pipefail + python3 - <<'PY' + import os + import re + import tomllib + from pathlib import Path + + release_version = os.environ["RELEASE_VERSION"] + core_semver = re.compile( + r"(?:0|[1-9][0-9]*)\." + r"(?:0|[1-9][0-9]*)\." + r"(?:0|[1-9][0-9]*)\Z" + ) + if core_semver.fullmatch(release_version) is None: + raise SystemExit( + "release_version must be a canonical three-component semantic " + "version without leading zeros" + ) + + project = tomllib.loads( + Path("pyproject.toml").read_text(encoding="utf-8") + ) + project_version = project["project"]["version"] + if project_version != release_version: + raise SystemExit( + f"pyproject.toml version {project_version} does not match " + f"requested {release_version}" + ) + + header = f"## [{release_version}] - " + matches = [ + line + for line in Path("CHANGELOG.md").read_text( + encoding="utf-8" + ).splitlines() + if line.startswith(header) + ] + if len(matches) != 1: + raise SystemExit( + f"expected exactly one CHANGELOG section for {release_version}; " + f"found {len(matches)}" + ) + PY + - name: Verify the release source is the version-cut transition + env: + RELEASE_COMMIT: ${{ inputs.release_commit }} + RELEASE_VERSION: ${{ inputs.release_version }} + run: | + set -euo pipefail + if ! parent_commit="$(git rev-parse "$RELEASE_COMMIT^" 2>/dev/null)"; then + echo "release commit must have a verifiable first parent" >&2 + exit 1 + fi + python3 - "$parent_commit" <<'PY' + import os + import subprocess + import sys + import tomllib + + parent_commit = sys.argv[1] + release_version = os.environ["RELEASE_VERSION"] + + def parent_text(path: str) -> str: + result = subprocess.run( + ["git", "show", f"{parent_commit}:{path}"], + check=False, + capture_output=True, + text=True, + ) + if result.returncode != 0: + raise SystemExit( + f"release parent is missing required source file {path}" + ) + return result.stdout + + parent_project = tomllib.loads(parent_text("pyproject.toml")) + parent_version = parent_project["project"]["version"] + if parent_version == release_version: + raise SystemExit( + "parent project version already equals requested release version" + ) + + header = f"## [{release_version}] - " + parent_changelog = parent_text("CHANGELOG.md").splitlines() + if any(line.startswith(header) for line in parent_changelog): + raise SystemExit( + "parent CHANGELOG already contains requested release section" + ) + PY + - name: Fail closed on fragment to CHANGELOG aggregate drift + run: | + set -euo pipefail + python scripts/render_changelog_fragments.py --check CHANGELOG.md + - name: Extract the release notes from the CHANGELOG section + env: + RELEASE_VERSION: ${{ inputs.release_version }} + run: | + set -euo pipefail + awk -v header="## [$RELEASE_VERSION] - " ' + index($0, header) == 1 { capture = 1; next } + capture && /^## / { exit } + capture { print } + ' CHANGELOG.md > release_notes.md + if ! grep -q '[^[:space:]]' release_notes.md; then + echo "CHANGELOG section for $RELEASE_VERSION is empty" >&2 + exit 1 + fi + - name: Keep internal monetary targets out of the public release notes + # The CHANGELOG section becomes the GitHub release body, which is a + # public distribution surface like the PyPI description. Naming what an + # entry removed is legitimate; restating the internal figure itself is + # the leak. v0.11.4 shipped with the amount spelled out before this ran. + run: | + set -euo pipefail + if grep -nEi 'KRW[[:space:]]*[0-9][0-9,]{6,}|[0-9]+[[:space:]]*(billion|B)[[:space:]]+(product|deal|contract)' release_notes.md; then + echo "release notes name an internal monetary target; describe the gate without the figure" >&2 + exit 1 + fi + + - name: Cap the release body below the GitHub release-body limit + env: + RELEASE_VERSION: ${{ inputs.release_version }} + run: | + set -euo pipefail + python3 - <<'PY' + import os + from pathlib import Path + + notes_path = Path("release_notes.md") + notes_text = notes_path.read_text(encoding="utf-8") + body_limit = 120_000 + if len(notes_text) > body_limit: + release_version = os.environ["RELEASE_VERSION"] + repository = os.environ["GITHUB_REPOSITORY"] + changelog_url = ( + f"https://github.com/{repository}/blob/" + f"v{release_version}/CHANGELOG.md" + ) + headings = list( + dict.fromkeys( + line + for line in notes_text.splitlines() + if line.startswith("#### ") + ) + ) + summary_lines = [ + "The complete notes for this release exceed GitHub's " + "release-body limit, so this body is a summary.", + "", + f"Full authoritative notes: the `[{release_version}]` " + f"section of [CHANGELOG.md]({changelog_url}).", + "", + "### Contents", + "", + ] + summary_lines += [ + "- " + line[len("#### "):] for line in headings + ] + capped_text = "\n".join(summary_lines) + "\n" + if len(capped_text) > body_limit: + capped_text = ( + capped_text[: body_limit - 60].rsplit("\n", 1)[0] + + "\n\n(contents list truncated)\n" + ) + notes_path.write_text(capped_text, encoding="utf-8") + PY + - name: Verify the release is absent and classify the tag state + id: release_tag_state + env: + GH_TOKEN: ${{ github.token }} + RELEASE_COMMIT: ${{ inputs.release_commit }} + RELEASE_VERSION: ${{ inputs.release_version }} + run: | + set -euo pipefail + api_status() { + endpoint="$1" + response_file="$2" + curl --silent --show-error \ + --output "$response_file" \ + --write-out '%{http_code}' \ + --header "Accept: application/vnd.github+json" \ + --header "Authorization: Bearer $GH_TOKEN" \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/$endpoint" + } + response_file="$(mktemp)" + status="$(api_status "releases/tags/v$RELEASE_VERSION" "$response_file")" + case "$status" in + 404) + ;; + 200) + echo "release v$RELEASE_VERSION already exists; refusing to overwrite or reuse it" >&2 + exit 1 + ;; + *) + cat "$response_file" >&2 + echo "GitHub API returned HTTP $status while checking release v$RELEASE_VERSION" >&2 + exit 1 + ;; + esac + status="$(api_status "git/ref/tags/v$RELEASE_VERSION" "$response_file")" + case "$status" in + 404) + echo "resume_existing_tag=false" >> "$GITHUB_OUTPUT" + ;; + 200) + python3 - "$response_file" <<'PY' + import json + import os + import sys + from pathlib import Path + + response = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8")) + tag_object = response.get("object", {}) + actual_tag_sha = tag_object.get("sha") + if ( + tag_object.get("type") != "commit" + or actual_tag_sha != os.environ["RELEASE_COMMIT"] + ): + raise SystemExit( + "existing tag does not target the requested release commit" + ) + PY + echo "tag v$RELEASE_VERSION exists without a release; resuming publication for the existing immutable tag" + echo "resume_existing_tag=true" >> "$GITHUB_OUTPUT" + ;; + *) + cat "$response_file" >&2 + echo "GitHub API returned HTTP $status while checking tag v$RELEASE_VERSION" >&2 + exit 1 + ;; + esac + rm -f "$response_file" + # R5a: the tag and release are created by publish-pypi.yml only after + # release admission; this workflow verifies and dispatches. + - name: Dispatch package publication from the verified release source + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + GH_TOKEN: ${{ github.token }} + RELEASE_COMMIT: ${{ inputs.release_commit }} + RELEASE_VERSION: ${{ inputs.release_version }} + run: | + set -euo pipefail + git fetch --no-tags origin "$DEFAULT_BRANCH" + CONTROL_PLANE_COMMIT="$(git rev-parse "origin/$DEFAULT_BRANCH")" + if ! printf '%s' "$CONTROL_PLANE_COMMIT" | grep -Eq '^[0-9a-f]{40}$'; then + echo "default-branch HEAD is unavailable for package publication dispatch" >&2 + exit 1 + fi + if ! git merge-base --is-ancestor "$RELEASE_COMMIT" "$CONTROL_PLANE_COMMIT"; then + echo "release commit must be an ancestor of the current default branch" >&2 + exit 1 + fi + gh workflow run publish-pypi.yml \ + --repo "$GITHUB_REPOSITORY" \ + --ref "$DEFAULT_BRANCH" \ + -f release_tag="v$RELEASE_VERSION" \ + -f release_commit="$RELEASE_COMMIT" \ + -f control_plane_commit="$CONTROL_PLANE_COMMIT" diff --git a/.github/workflows/fast-mlsirm-statistical-studies.yml b/.github/workflows/fast-mlsirm-statistical-studies.yml new file mode 100644 index 0000000000..ff4650cfd4 --- /dev/null +++ b/.github/workflows/fast-mlsirm-statistical-studies.yml @@ -0,0 +1,170 @@ +name: Statistical Studies + +on: + workflow_call: + +permissions: + contents: read + +# Secret boundary: this evidence workflow intentionally consumes no secret +# inputs. If a future study needs credentials, pass them only through an +# explicitly reviewed `${{ secrets.NAME }}` value in `env`; never hardcode, +# interpolate into command arguments, or print a credential. + +concurrency: + group: central-fast-mlsirm-statistical-studies-statistical-studies-${{ github.ref }} + cancel-in-progress: false + +jobs: + rust-ignored: + name: Ignored Rust studies shard ${{ matrix.shard }} + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + # Large Monte Carlo recovery tests are real evidence, not a reason to + # silently discard a shard after the helper's default 30-minute bound. + timeout-minutes: 180 + env: + FAST_MLSIRM_STATISTICAL_TEST_TIMEOUT_SECONDS: "7200" + strategy: + fail-fast: false + max-parallel: 3 + matrix: + shard: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 + with: + python-version: "3.12" + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 + with: + toolchain: 1.97.1 + - name: Run target-qualified ignored Rust shard + run: >- + python scripts/run_ignored_rust_shard.py + --shard ${{ matrix.shard }} + --shards 12 + --skip mlsirm-core/test/literature_true_parameter_recovery::kang_jeon_2025_minimum_cell_recovers_true_parameters + --skip mlsirm-core/test/literature_true_parameter_recovery::gpu_recovery_matches_cpu_on_paper_design + --skip mlsirm-core/test/higher_order_mc_recovery::higher_order_dina_recovery_respects_monte_carlo_tolerance + --skip mlsirm-core/lib/mlsirm_core::grm::tests::mc_grm_recovery_500 + + rust-pyo3-ignored: + name: Ignored PyO3 studies + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 60 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 + with: + toolchain: 1.97.1 + - run: cargo test --locked --release --manifest-path crates/fast-mlsirm-py/Cargo.toml -- --ignored --test-threads=1 --nocapture + + rust-recovery: + name: Literature CPU recovery studies + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 120 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 + with: + toolchain: 1.97.1 + - name: Run Kang-Jeon paper-design recovery + run: | + set -euo pipefail + # cargo test --exact exits 0 when the filter matches no test, so + # require exactly one executed passing test (fail-closed, #1869). + cargo test --locked --release -p mlsirm-core \ + --test literature_true_parameter_recovery \ + kang_jeon_2025_minimum_cell_recovers_true_parameters \ + -- --ignored --exact --nocapture 2>&1 | tee kang-jeon-recovery.log + grep -q "test result: ok. 1 passed" kang-jeon-recovery.log + - name: Run higher-order DINA Monte Carlo recovery + run: | + set -euo pipefail + # cargo test --exact exits 0 when the filter matches no test, so + # require exactly one executed passing test (fail-closed, #1869). + cargo test --locked --release -p mlsirm-core \ + --test higher_order_mc_recovery \ + higher_order_dina_recovery_respects_monte_carlo_tolerance \ + -- --ignored --exact --nocapture 2>&1 | tee higher-order-dina-recovery.log + grep -q "test result: ok. 1 passed" higher-order-dina-recovery.log + + grm-recovery: + name: GRM 500-rep recovery study + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 120 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 + with: + toolchain: 1.97.1 + - name: Run multidimensional GRM Monte Carlo recovery + run: | + set -euo pipefail + cargo test --locked --release -p mlsirm-core --lib \ + grm::tests::mc_grm_recovery_500 \ + -- --ignored --exact --nocapture --test-threads=1 \ + 2>&1 | tee grm-recovery-study.log + # cargo test --exact exits 0 when the filter matches no test, so + # require exactly one executed passing test (fail-closed, #1869). + grep -q "test result: ok. 1 passed" grm-recovery-study.log + - name: Publish GRM recovery study log + if: always() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a + with: + name: grm-recovery-study-${{ github.run_id }} + path: grm-recovery-study.log + if-no-files-found: error + retention-days: 90 + + gpu-recovery: + name: Literature GPU recovery parity + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 90 + env: + WGPU_BACKEND: vulkan + XDG_RUNTIME_DIR: /tmp/fast-mlsirm-xdg-runtime + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 + with: + toolchain: 1.97.1 + - name: Install software Vulkan adapter + run: | + sudo apt-get update + sudo apt-get install --yes mesa-vulkan-drivers vulkan-tools + mkdir -p "$XDG_RUNTIME_DIR" + chmod 700 "$XDG_RUNTIME_DIR" + LVP_ICD="$(find /usr/share/vulkan/icd.d -maxdepth 1 -type f -name '*lvp*.json' -print -quit)" + test -n "$LVP_ICD" + echo "VK_ICD_FILENAMES=$LVP_ICD" >> "$GITHUB_ENV" + - run: vulkaninfo --summary + - name: Run paper-design GPU recovery parity + run: | + set -euo pipefail + # cargo test --exact exits 0 when the filter matches no test, so + # require exactly one executed passing test (fail-closed, #1869). + cargo test --locked --release -p mlsirm-core \ + --test literature_true_parameter_recovery \ + gpu_recovery_matches_cpu_on_paper_design \ + -- --ignored --exact --nocapture 2>&1 | tee gpu-recovery-parity.log + grep -q "test result: ok. 1 passed" gpu-recovery-parity.log diff --git a/.github/workflows/hourly-review-repair.yml b/.github/workflows/hourly-review-repair.yml index 0b45c7fd37..d34a8477d8 100644 --- a/.github/workflows/hourly-review-repair.yml +++ b/.github/workflows/hourly-review-repair.yml @@ -136,7 +136,9 @@ permissions: jobs: resolve-target: name: Resolve target(s) for ${{ github.event.schedule }} - runs-on: ubuntu-24.04 + runs-on: + group: CWL central control + labels: [self-hosted, linux, x64] outputs: targets: ${{ steps.lookup.outputs.targets }} steps: diff --git a/.github/workflows/k-csap-skills-quality.yml b/.github/workflows/k-csap-skills-quality.yml new file mode 100644 index 0000000000..a0915b5056 --- /dev/null +++ b/.github/workflows/k-csap-skills-quality.yml @@ -0,0 +1,176 @@ +name: K-CSAP Skills Quality + +on: + workflow_call: + +permissions: + contents: read + +# Coalesce superseded heads of the same PR before runner assignment. Non-PR runs +# use the unique run id so protected-branch runs never cancel each other. +concurrency: + group: k-csap-skills-quality-${{ github.repository }}-${{ github.event.pull_request.number && format('pr-{0}', github.event.pull_request.number) || format('run-{0}', github.run_id) }} + cancel-in-progress: true + +jobs: + quality: + name: k-csap-skills-quality + # This job gate is evaluated before runner assignment. A skipped job is not a quality verdict. + if: >- + github.repository == 'ContextualWisdomLab/k-csap-skills' && + ((github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository && + github.event.pull_request.state == 'open' && + github.event.pull_request.head.sha != '' && + github.event.pull_request.number > 0 && + github.ref == format('refs/pull/{0}/merge', github.event.pull_request.number)) || + ((github.event_name == 'push' || github.event_name == 'workflow_dispatch') && + github.ref == 'refs/heads/master')) + runs-on: [self-hosted, Linux, X64, k-csap-isolated] + timeout-minutes: 20 + steps: + - name: Admit only trusted K-CSAP source + id: admission + env: + TARGET_REPOSITORY: ${{ github.repository }} + EVENT_NAME: ${{ github.event_name }} + HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name || '' }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || '' }} + EVENT_SHA: ${{ github.sha }} + EVENT_REF: ${{ github.ref }} + PR_STATE: ${{ github.event.pull_request.state || '' }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + set -euo pipefail + if [ "$TARGET_REPOSITORY" != "ContextualWisdomLab/k-csap-skills" ]; then + echo "::error::K-CSAP quality rejected an unexpected caller repository." + exit 1 + fi + case "$EVENT_NAME" in + push|workflow_dispatch) + if [ "$EVENT_REF" != 'refs/heads/master' ]; then + echo "::error::K-CSAP quality only accepts master branch pushes or dispatches." + exit 1 + fi + EXPECTED_SHA="$EVENT_SHA" + ;; + pull_request) + if [ "$HEAD_REPOSITORY" != "$TARGET_REPOSITORY" ] || [ "$PR_STATE" != "open" ]; then + echo "::error::K-CSAP quality accepts only open same-repository pull requests." + exit 1 + fi + if ! [[ "$EVENT_REF" =~ ^refs/pull/[1-9][0-9]*/merge$ ]]; then + echo "::error::K-CSAP quality rejected a malformed pull request ref." + exit 1 + fi + EXPECTED_SHA="$PR_HEAD_SHA" + ;; + *) + echo "::error::K-CSAP quality rejected an unsupported event." + exit 1 + ;; + esac + if ! [[ "$EXPECTED_SHA" =~ ^[0-9a-f]{40}$ ]]; then + echo "::error::K-CSAP quality rejected a malformed source SHA." + exit 1 + fi + echo "expected_sha=$EXPECTED_SHA" >>"$GITHUB_OUTPUT" + echo "admitted=true" >>"$GITHUB_OUTPUT" + + - name: Prepare run-owned source directory + id: prepare + env: + RUN_ID: ${{ github.run_id }} + RUN_ATTEMPT: ${{ github.run_attempt }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + set -euo pipefail + if ! [[ "$RUN_ID" =~ ^[1-9][0-9]*$ ]] || ! [[ "$RUN_ATTEMPT" =~ ^[1-9][0-9]*$ ]]; then + echo "::error::Invalid run identity." + exit 1 + fi + if [ -L "${GITHUB_WORKSPACE:?}" ] || [ ! -d "$GITHUB_WORKSPACE" ]; then + echo "::error::Workspace root is not a regular directory." + exit 1 + fi + source_path="k-csap-quality.${RUN_ID}.${RUN_ATTEMPT}" + if [ -e "$GITHUB_WORKSPACE/$source_path" ] || [ -L "$GITHUB_WORKSPACE/$source_path" ]; then + echo "::error::Source path already exists; refusing stale source." + exit 1 + fi + umask 077 + mkdir -- "$GITHUB_WORKSPACE/$source_path" + echo "source_path=$source_path" >>"$GITHUB_OUTPUT" + + # PR exact head is checked; merge-candidate integration is a separate gate. + - name: Checkout exact caller source + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + with: + ref: ${{ steps.admission.outputs.expected_sha }} + path: ${{ steps.prepare.outputs.source_path }} + persist-credentials: false + clean: true + + - name: Prepare private setup action storage + working-directory: ${{ github.workspace }}/${{ steps.prepare.outputs.source_path }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + set -euo pipefail + test ! -e .ci-tools && test ! -L .ci-tools + umask 077 + mkdir -p .ci-tools/tmp .ci-tools/tool-cache + + - name: Install pinned uv action + uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6 + env: + RUNNER_TOOL_CACHE: ${{ github.workspace }}/${{ steps.prepare.outputs.source_path }}/.ci-tools/tool-cache + RUNNER_TEMP: ${{ github.workspace }}/${{ steps.prepare.outputs.source_path }}/.ci-tools/tmp + with: + version: '0.9.7' + checksum: b26fcc8dfa1c39b5a5613445af3be3eefda45d9a39359bee271eafe34913583e + enable-cache: false + restore-cache: false + save-cache: false + cache-local-path: ${{ github.workspace }}/${{ steps.prepare.outputs.source_path }}/.ci-tools/cache + tool-dir: ${{ github.workspace }}/${{ steps.prepare.outputs.source_path }}/.ci-tools/uv-tools + tool-bin-dir: ${{ github.workspace }}/${{ steps.prepare.outputs.source_path }}/.ci-tools/bin + + - name: Verify K-CSAP quality in a job-private environment + working-directory: ${{ github.workspace }}/${{ steps.prepare.outputs.source_path }} + env: + EXPECTED_SHA: ${{ steps.admission.outputs.expected_sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + set -euo pipefail + test -d .ci-tools/tmp && test ! -L .ci-tools/tmp + job_root="$(mktemp -d "$PWD/.ci-tools/tmp/k-csap-quality.XXXXXX")" + cleanup() { rm -rf -- "$job_root"; } + trap cleanup EXIT + unset PYTHONHOME PYTHONPATH + export UV_CACHE_DIR="$job_root/uv-cache" + export UV_PYTHON_INSTALL_DIR="$job_root/python" + export UV_PROJECT_ENVIRONMENT="$job_root/venv" + export UV_NO_CONFIG=1 + test "$(git rev-parse HEAD)" = "$EXPECTED_SHA" + uv python install 3.11.14 + uv sync --locked --dev --python 3.11.14 + uv run --python 3.11.14 ruff check . + uv run --python 3.11.14 pytest -q + uv run --python 3.11.14 python scripts/validate.py + # A clean checkout has no working-tree diff; check committed content. + git diff --check "$(git hash-object -t tree /dev/null)" HEAD + + - name: Clean run-owned source after any step result + if: always() + env: + SOURCE_PATH: ${{ steps.prepare.outputs.source_path }} + RUN_ID: ${{ github.run_id }} + RUN_ATTEMPT: ${{ github.run_attempt }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + set -euo pipefail + if [ -z "$SOURCE_PATH" ]; then exit 0; fi + if ! [[ "$RUN_ID" =~ ^[1-9][0-9]*$ ]] || ! [[ "$RUN_ATTEMPT" =~ ^[1-9][0-9]*$ ]]; then exit 1; fi + if [ "$SOURCE_PATH" != "k-csap-quality.${RUN_ID}.${RUN_ATTEMPT}" ]; then exit 1; fi + if [ -L "${GITHUB_WORKSPACE:?}" ] || [ ! -d "$GITHUB_WORKSPACE" ]; then exit 1; fi + rm -rf -- "${GITHUB_WORKSPACE:?}/$SOURCE_PATH" diff --git a/.github/workflows/law-ai-agent-ci.yml b/.github/workflows/law-ai-agent-ci.yml new file mode 100644 index 0000000000..d0c19baf53 --- /dev/null +++ b/.github/workflows/law-ai-agent-ci.yml @@ -0,0 +1,117 @@ +name: Law AI Agent central CI + +on: + workflow_call: + +permissions: + contents: read + +concurrency: + group: law-ci-${{ github.workflow }}-${{ github.repository }}-${{ github.event.pull_request.number || format('{0}-{1}', github.event_name, github.ref) }} + cancel-in-progress: true + +jobs: + quality: + runs-on: + group: CWL law CI + labels: [self-hosted, Linux, X64, law-ai-agent-ci] + timeout-minutes: 20 + strategy: + fail-fast: false + max-parallel: 1 + matrix: + python-version: ["3.12", "3.14"] + steps: + - name: Admit exact owned caller before checkout + id: admission + shell: bash + env: + CALLER_REPOSITORY: ${{ github.repository }} + CALLER_EVENT: ${{ github.event_name }} + CALLER_REF: ${{ github.ref }} + SOURCE_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + CENTRAL_REPOSITORY: ${{ job.workflow_repository }} + CENTRAL_REF: ${{ job.workflow_ref }} + CENTRAL_SHA: ${{ job.workflow_sha }} + run: | + set -euo pipefail + python3 - <<'PY' + """Fail closed before checking out consumer or central source.""" + import json + import os + import re + import sys + from pathlib import Path + e = os.environ + owned = "ContextualWisdomLab/law-ai-agent" + valid = e["CALLER_REPOSITORY"] == owned + valid &= e["CENTRAL_REPOSITORY"] == "ContextualWisdomLab/.github" + valid &= bool(re.fullmatch(r"[0-9a-f]{40}", e["SOURCE_SHA"])) + valid &= bool(re.fullmatch(r"[0-9a-f]{40}", e["CENTRAL_SHA"])) + valid &= e["CENTRAL_REF"] == ( + "ContextualWisdomLab/.github/.github/workflows/law-ai-agent-ci.yml@" + + e["CENTRAL_SHA"] + ) + payload = json.loads(Path(e["GITHUB_EVENT_PATH"]).read_text()) + event = e["CALLER_EVENT"] + if event == "pull_request": + pr = payload.get("pull_request", {}) + head, base = pr.get("head", {}), pr.get("base", {}) + valid &= head.get("repo", {}).get("full_name") == owned + valid &= base.get("repo", {}).get("full_name") == owned + valid &= head.get("sha") == e["SOURCE_SHA"] + valid &= base.get("ref") == "main" + elif event == "push": + valid &= e["CALLER_REF"] == "refs/heads/main" + valid &= payload.get("ref") == "refs/heads/main" + valid &= payload.get("after") == e["SOURCE_SHA"] + valid &= payload.get("deleted") is False + elif event == "workflow_dispatch": + valid &= e["CALLER_REF"] == "refs/heads/main" + else: + valid = False + if not valid: + sys.exit("law-ci: admission refused") + with open(e["GITHUB_OUTPUT"], "a", encoding="utf-8") as output: + output.write("source_sha=" + e["SOURCE_SHA"] + "\n") + PY + - name: Checkout admitted consumer head + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 verified commit + with: + repository: ContextualWisdomLab/law-ai-agent + ref: ${{ steps.admission.outputs.source_sha }} + path: source + persist-credentials: false + - name: Checkout this reusable workflow source + uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5 verified commit + with: + repository: ${{ job.workflow_repository }} + ref: ${{ job.workflow_sha }} + path: central + persist-credentials: false + - name: Verify both checkout identities + shell: bash + env: + SOURCE_SHA: ${{ steps.admission.outputs.source_sha }} + CENTRAL_SHA: ${{ job.workflow_sha }} + run: | + set -euo pipefail + test "$(git -C "$GITHUB_WORKSPACE/source" rev-parse HEAD)" = "$SOURCE_SHA" + test "$(git -C "$GITHUB_WORKSPACE/central" rev-parse HEAD)" = "$CENTRAL_SHA" + - name: Provision pinned uv and matrix Python + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7 dereferenced commit + with: + version: "0.12.5" + python-version: ${{ matrix.python-version }} + enable-cache: false + - name: Run isolated native database and artifact gates + shell: bash + env: + LAW_CI_SOURCE: ${{ github.workspace }}/source + LAW_CI_SOURCE_SHA: ${{ steps.admission.outputs.source_sha }} + LAW_CI_PYTHON_VERSION: ${{ matrix.python-version }} + run: bash "$GITHUB_WORKSPACE/central/scripts/ci/law_ai_agent_ci.sh" run + - name: Cleanup owned native cluster on cancellation + if: always() + shell: bash + run: bash "$GITHUB_WORKSPACE/central/scripts/ci/law_ai_agent_ci.sh" cleanup diff --git a/.github/workflows/noema-review.yml b/.github/workflows/noema-review.yml index 21ea967201..cb9d671694 100644 --- a/.github/workflows/noema-review.yml +++ b/.github/workflows/noema-review.yml @@ -41,21 +41,68 @@ jobs: && github.event.action != 'converted_to_draft' && github.event.pull_request.head.repo.full_name == github.repository ) - runs-on: ubuntu-24.04 + runs-on: + group: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/contextual-orchestrator' || github.repository == 'ContextualWisdomLab/fast-mlsirm' || github.repository == 'ContextualWisdomLab/cwl-telemetry' || github.repository == 'ContextualWisdomLab/naruon' || github.repository == 'ContextualWisdomLab/late-life-anxiety-reanalysis' || github.repository == 'ContextualWisdomLab/tokyo-travel-2026') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/contextual-orchestrator' || github.repository == 'ContextualWisdomLab/fast-mlsirm' || github.repository == 'ContextualWisdomLab/cwl-telemetry' || github.repository == 'ContextualWisdomLab/naruon' || github.repository == 'ContextualWisdomLab/late-life-anxiety-reanalysis' || github.repository == 'ContextualWisdomLab/tokyo-travel-2026') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} timeout-minutes: 5 outputs: admitted: ${{ steps.live_head.outputs.admitted }} + base_sha: ${{ steps.live_head.outputs.base_sha }} permissions: contents: read pull-requests: read env: - GH_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN || github.token }} TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.event.client_payload.target_repository || github.repository }} PR_NUMBER: ${{ github.event.pull_request.number || github.event.client_payload.pr_number || '' }} EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.client_payload.pr_head_sha || '' }} steps: + - name: Select native Noema credential for metadata reads + if: env.PR_NUMBER != '' + id: noema_metadata_credential + env: + METADATA_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || '' }} + NOEMA_GITHUB_APP_CLIENT_ID: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID || '' }} + NOEMA_GITHUB_APP_PRIVATE_KEY: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY || '' }} + run: | + set -euo pipefail + if ! [[ "$TARGET_REPOSITORY" =~ ^ContextualWisdomLab/[A-Za-z0-9_.-]+$ ]] || + ! [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || + ! [[ "$EXPECTED_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then + echo "::error::Noema metadata credential rejected malformed target PR/head metadata." + exit 1 + fi + echo "repository=${TARGET_REPOSITORY#*/}" >>"$GITHUB_OUTPUT" + if [ "$TARGET_REPOSITORY" = "ContextualWisdomLab/k-csap-skills" ]; then + if [ -z "${NOEMA_GITHUB_APP_CLIENT_ID:-}" ] || [ -z "${NOEMA_GITHUB_APP_PRIVATE_KEY:-}" ]; then + echo "::error::k-csap-skills Noema metadata requires the native GitHub App client ID and private key; credential fallback is forbidden." + exit 1 + fi + echo "source=github-app" >>"$GITHUB_OUTPUT" + elif [ -n "${METADATA_TOKEN:-}" ]; then + echo "source=pat" >>"$GITHUB_OUTPUT" + elif [ -n "${NOEMA_GITHUB_APP_CLIENT_ID:-}" ] && [ -n "${NOEMA_GITHUB_APP_PRIVATE_KEY:-}" ]; then + echo "source=github-app" >>"$GITHUB_OUTPUT" + else + echo "source=workflow" >>"$GITHUB_OUTPUT" + fi + + - name: Mint read-only native Noema GitHub App token + if: env.PR_NUMBER != '' && steps.noema_metadata_credential.outputs.source == 'github-app' + id: noema_metadata_app_token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID }} + private-key: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }} + owner: ContextualWisdomLab + repositories: ${{ steps.noema_metadata_credential.outputs.repository }} + permission-contents: read + permission-metadata: read + permission-pull-requests: read + - name: Admit only the exact live Noema head id: live_head + env: + GH_TOKEN: ${{ env.TARGET_REPOSITORY == 'ContextualWisdomLab/k-csap-skills' && steps.noema_metadata_app_token.outputs.token || (env.TARGET_REPOSITORY != 'ContextualWisdomLab/k-csap-skills' && (secrets.NOEMA_REVIEW_TOKEN || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || steps.noema_metadata_app_token.outputs.token || github.token)) || '' }} run: | set -euo pipefail echo "admitted=false" >>"$GITHUB_OUTPUT" @@ -72,14 +119,93 @@ jobs: echo "::notice::Noema admission retired a stale trigger before review queue entry." exit 0 fi + live_base="$(jq -r '.base.sha // empty' <<<"$live_pr")" + if ! [[ "$live_base" =~ ^[0-9a-f]{40}$ ]]; then + echo "::error::Noema admission could not bind the live base commit." + exit 1 + fi + echo "base_sha=$live_base" >>"$GITHUB_OUTPUT" echo "admitted=true" >>"$GITHUB_OUTPUT" echo "Exact live Noema head admitted for ${TARGET_REPOSITORY}#${PR_NUMBER}." + changed-scope: + name: Detect changed scope + # Same job-level docs/image-only gate as strix.yml, security-scan.yml, + # sast-semgrep.yml, and codeql-pr.yml (see + # docs/doctoring/required-workflow-path-filter-boundary.md): the org + # ruleset ignores every `on:` filter when it runs this workflow in + # another repository, so the doc/image-only decision has to be made in + # a job and consumed through `needs`, not the trigger. Noema review + # previously ran its full model-review chain for every PR event + # including docs/changelog-only diffs; this closes that gap using the + # identical classifier already used elsewhere. Fails OPEN: an + # unreadable, empty, or truncated file list reviews everything. Not + # gated on repository_dispatch's own admission below: a repository_dispatch + # retry carries no `github.event.pull_request`, so this job's own + # PR/REPO lookup naturally falls through to "scan everything" for that + # path, matching strix.yml's identical repository_dispatch behavior. + if: github.event_name != 'pull_request_target' || (github.event.action != 'closed' && github.event.action != 'converted_to_draft') + runs-on: + group: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/contextual-orchestrator' || github.repository == 'ContextualWisdomLab/fast-mlsirm' || github.repository == 'ContextualWisdomLab/cwl-telemetry' || github.repository == 'ContextualWisdomLab/naruon' || github.repository == 'ContextualWisdomLab/late-life-anxiety-reanalysis' || github.repository == 'ContextualWisdomLab/tokyo-travel-2026') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/contextual-orchestrator' || github.repository == 'ContextualWisdomLab/fast-mlsirm' || github.repository == 'ContextualWisdomLab/cwl-telemetry' || github.repository == 'ContextualWisdomLab/naruon' || github.repository == 'ContextualWisdomLab/late-life-anxiety-reanalysis' || github.repository == 'ContextualWisdomLab/tokyo-travel-2026') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} + timeout-minutes: 5 + permissions: + contents: read + pull-requests: read + outputs: + code: ${{ steps.scope.outputs.code }} + deps: ${{ steps.scope.outputs.deps }} + steps: + - name: Classify changed paths + id: scope + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.event.pull_request.base.repo.full_name || github.repository }} + PR: ${{ github.event.pull_request.number }} + EXPECTED_FILES: ${{ github.event.pull_request.changed_files }} + shell: bash + run: | + set -uo pipefail + code=true + deps=true + if [ -n "${PR}" ] && [ -n "${EXPECTED_FILES}" ]; then + changed="" + for attempt in 1 2 3; do + if changed="$(gh api --paginate "repos/${REPO}/pulls/${PR}/files?per_page=100" --jq '.[].filename')" && [ -n "$changed" ]; then + break + fi + changed="" + sleep $((attempt * 3)) + done + # GitHub caps /pulls/N/files at 3000 entries; a short list would hide + # source files behind a doc-only verdict, so require an exact count. + if [ -n "$changed" ] && [ "$(printf '%s\n' "$changed" | wc -l | tr -d ' ')" = "${EXPECTED_FILES}" ]; then + code=false + deps=false + while IFS= read -r changed_path; do + case "$changed_path" in + *.md|*.markdown|*.rst|*.png|*.jpg|*.jpeg|*.gif|*.webp|*.bmp|*.ico|LICENSE|LICENSE.txt|COPYING|COPYING.txt|NOTICE|NOTICE.txt|.github/ISSUE_TEMPLATE/*) ;; + *) code=true ;; + esac + case "$changed_path" in + requirements*.txt|*/requirements*.txt|pyproject.toml|*/pyproject.toml|uv.lock|*/uv.lock|pylock.*.toml|*/pylock.*.toml|package.json|*/package.json|package-lock.json|*/package-lock.json|pnpm-lock.yaml|*/pnpm-lock.yaml|yarn.lock|*/yarn.lock|Cargo.toml|*/Cargo.toml|Cargo.lock|*/Cargo.lock|go.mod|*/go.mod|go.sum|*/go.sum|pom.xml|*/pom.xml|build.gradle|*/build.gradle|build.gradle.kts|*/build.gradle.kts|DESCRIPTION|*/DESCRIPTION) deps=true ;; + esac + done <<<"$changed" + else + echo "::notice::changed-scope could not read a complete PR file list; scanning everything." + fi + fi + echo "code=${code}" >> "$GITHUB_OUTPUT" + echo "deps=${deps}" >> "$GITHUB_OUTPUT" + echo "changed-scope code=${code} deps=${deps}" + cancel-closed-pr-runs: if: >- github.event_name == 'pull_request_target' && (github.event.action == 'closed' || github.event.action == 'converted_to_draft') - runs-on: ubuntu-24.04 + runs-on: + group: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/contextual-orchestrator' || github.repository == 'ContextualWisdomLab/fast-mlsirm' || github.repository == 'ContextualWisdomLab/cwl-telemetry' || github.repository == 'ContextualWisdomLab/naruon' || github.repository == 'ContextualWisdomLab/late-life-anxiety-reanalysis' || github.repository == 'ContextualWisdomLab/tokyo-travel-2026') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/contextual-orchestrator' || github.repository == 'ContextualWisdomLab/fast-mlsirm' || github.repository == 'ContextualWisdomLab/cwl-telemetry' || github.repository == 'ContextualWisdomLab/naruon' || github.repository == 'ContextualWisdomLab/late-life-anxiety-reanalysis' || github.repository == 'ContextualWisdomLab/tokyo-travel-2026') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} # Bound this job well short of GitHub's 360-minute platform default. Its # only step is a single-repository, status-filtered gh api --paginate # list-and-cancel sweep (up to 3 passes x 5 statuses), no branch update @@ -256,8 +382,10 @@ jobs: noema-review: name: noema-review - needs: [admit-current-head] - runs-on: ubuntu-24.04 + needs: [admit-current-head, changed-scope] + runs-on: + group: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/contextual-orchestrator' || github.repository == 'ContextualWisdomLab/fast-mlsirm' || github.repository == 'ContextualWisdomLab/cwl-telemetry' || github.repository == 'ContextualWisdomLab/naruon' || github.repository == 'ContextualWisdomLab/late-life-anxiety-reanalysis' || github.repository == 'ContextualWisdomLab/tokyo-travel-2026') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main' && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/contextual-orchestrator' || github.repository == 'ContextualWisdomLab/fast-mlsirm' || github.repository == 'ContextualWisdomLab/cwl-telemetry' || github.repository == 'ContextualWisdomLab/naruon' || github.repository == 'ContextualWisdomLab/late-life-anxiety-reanalysis' || github.repository == 'ContextualWisdomLab/tokyo-travel-2026') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main' && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} # No job-level timeout-minutes here, deliberately. This job's "Prepare # Noema model verdict" step calls two_phase.py's call_llm synchronously # via the contextual-orchestrator gateway and blocks on the model's own @@ -291,10 +419,25 @@ jobs: contents: read id-token: write pull-requests: read + outputs: + transport_capacity_unavailable: ${{ steps.noema_prepare.outputs.transport_capacity_unavailable || steps.noema_sidecar_failure.outputs.transport_capacity_unavailable }} + transport_retry_eligible: ${{ steps.noema_prepare.outputs.transport_retry_eligible || steps.noema_sidecar_failure.outputs.transport_retry_eligible }} + transport_retry_delay_seconds: ${{ steps.noema_prepare.outputs.transport_retry_delay_seconds || steps.noema_sidecar_failure.outputs.transport_retry_delay_seconds }} + transport_retry_next_attempt: ${{ steps.noema_prepare.outputs.transport_retry_next_attempt || steps.noema_sidecar_failure.outputs.transport_retry_next_attempt }} + provider_attempt_count: ${{ steps.noema_prepare.outputs.provider_attempt_count || steps.noema_sidecar_failure.outputs.provider_attempt_count }} + transport_http_status: ${{ steps.noema_prepare.outputs.transport_http_status || steps.noema_sidecar_failure.outputs.transport_http_status }} env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.event.client_payload.target_repository || github.repository }} - PR_NUMBER: ${{ github.event.pull_request.number || github.event.client_payload.pr_number || '' }} + # Empty PR_NUMBER already means "skip the review body" below (see the + # next step); a docs/image-only diff reuses that exact same, already + # fully-tested skip path by clearing it here too instead of adding a + # second, separately-gated condition to every downstream step. Fails + # OPEN: changed-scope's own output defaults to 'true' (or is empty + # when that job itself was skipped for a non-pull_request_target + # event), so this only ever clears PR_NUMBER on a proven docs/image-only + # diff. + PR_NUMBER: ${{ (needs.changed-scope.outputs.code != 'false' && (github.event.pull_request.number || github.event.client_payload.pr_number)) || '' }} EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.client_payload.pr_head_sha || '' }} steps: - name: Skip events without pull request context @@ -364,6 +507,10 @@ jobs: trusted_archive="${RUNNER_TEMP}/trusted-noema-source.tar.gz" api_url="${GITHUB_API_URL:-https://api.github.com}" curl -fsSL \ + --retry 3 \ + --retry-all-errors \ + --retry-delay 1 \ + --retry-max-time 30 \ -H "Authorization: Bearer ${GH_TOKEN}" \ -H "Accept: application/vnd.github+json" \ -o "$trusted_archive" \ @@ -371,10 +518,53 @@ jobs: tar -xzf "$trusted_archive" -C "$GITHUB_WORKSPACE" --strip-components=1 test -f scripts/ci/noema_review_gate.py + - name: Select native Noema credential for metadata reads + if: env.PR_NUMBER != '' + id: noema_metadata_credential + env: + METADATA_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || '' }} + NOEMA_GITHUB_APP_CLIENT_ID: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID || '' }} + NOEMA_GITHUB_APP_PRIVATE_KEY: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY || '' }} + run: | + set -euo pipefail + if ! [[ "$TARGET_REPOSITORY" =~ ^ContextualWisdomLab/[A-Za-z0-9_.-]+$ ]] || + ! [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || + ! [[ "$EXPECTED_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then + echo "::error::Noema metadata credential rejected malformed target PR/head metadata." + exit 1 + fi + echo "repository=${TARGET_REPOSITORY#*/}" >>"$GITHUB_OUTPUT" + if [ "$TARGET_REPOSITORY" = "ContextualWisdomLab/k-csap-skills" ]; then + if [ -z "${NOEMA_GITHUB_APP_CLIENT_ID:-}" ] || [ -z "${NOEMA_GITHUB_APP_PRIVATE_KEY:-}" ]; then + echo "::error::k-csap-skills Noema metadata requires the native GitHub App client ID and private key; credential fallback is forbidden." + exit 1 + fi + echo "source=github-app" >>"$GITHUB_OUTPUT" + elif [ -n "${METADATA_TOKEN:-}" ]; then + echo "source=pat" >>"$GITHUB_OUTPUT" + elif [ -n "${NOEMA_GITHUB_APP_CLIENT_ID:-}" ] && [ -n "${NOEMA_GITHUB_APP_PRIVATE_KEY:-}" ]; then + echo "source=github-app" >>"$GITHUB_OUTPUT" + else + echo "source=workflow" >>"$GITHUB_OUTPUT" + fi + + - name: Mint read-only native Noema GitHub App token + if: env.PR_NUMBER != '' && steps.noema_metadata_credential.outputs.source == 'github-app' + id: noema_metadata_app_token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID }} + private-key: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }} + owner: ContextualWisdomLab + repositories: ${{ steps.noema_metadata_credential.outputs.repository }} + permission-contents: read + permission-metadata: read + permission-pull-requests: read + - name: Reject a stale trigger before credential or model setup if: env.PR_NUMBER != '' env: - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ env.TARGET_REPOSITORY == 'ContextualWisdomLab/k-csap-skills' && steps.noema_metadata_app_token.outputs.token || (env.TARGET_REPOSITORY != 'ContextualWisdomLab/k-csap-skills' && (secrets.NOEMA_REVIEW_TOKEN || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || steps.noema_metadata_app_token.outputs.token || github.token)) || '' }} run: | set -euo pipefail if [[ ! "$EXPECTED_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then @@ -472,7 +662,7 @@ jobs: NOEMA_GITHUB_APP_CLIENT_ID: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID || '' }} NOEMA_GITHUB_APP_PRIVATE_KEY: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY || '' }} TOKEN_EXCHANGE_URL: ${{ vars.NOEMA_TOKEN_EXCHANGE_URL || vars.NOEMA_EXCHANGE_URL || '' }} - NOEMA_REVIEW_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN }} + NOEMA_REVIEW_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN || '' }} run: | set -euo pipefail @@ -483,6 +673,15 @@ jobs: repository_name="${TARGET_REPOSITORY#*/}" echo "repository=$repository_name" >>"$GITHUB_OUTPUT" + if [ "$TARGET_REPOSITORY" = "ContextualWisdomLab/k-csap-skills" ]; then + if [ -z "${NOEMA_GITHUB_APP_CLIENT_ID:-}" ] || [ -z "${NOEMA_GITHUB_APP_PRIVATE_KEY:-}" ]; then + echo "::error::k-csap-skills Noema reviewer requires the native GitHub App client ID and private key; PAT/OIDC fallback is forbidden." + exit 1 + fi + echo "source=github-app" >>"$GITHUB_OUTPUT" + exit 0 + fi + if [ -n "${NOEMA_REVIEW_TOKEN:-}" ]; then echo "source=pat" >>"$GITHUB_OUTPUT" echo "::notice::Noema reviewer using the NOEMA_REVIEW_TOKEN secret fallback identity." @@ -513,14 +712,9 @@ jobs: private-key: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }} owner: ContextualWisdomLab repositories: ${{ steps.noema_credential.outputs.repository }} - permission-actions: read - permission-checks: read permission-contents: read permission-metadata: read - permission-pull-requests: write - permission-security-events: read - permission-statuses: read - permission-vulnerability-alerts: read + permission-pull-requests: read - name: Exchange Noema app token through OIDC if: env.PR_NUMBER != '' && steps.noema_credential.outputs.source == 'oidc' @@ -583,7 +777,7 @@ jobs: - name: Validate current pull request head if: env.PR_NUMBER != '' env: - GH_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN || steps.noema_github_app_token.outputs.token || steps.noema_oidc_token.outputs.token }} + GH_TOKEN: ${{ env.TARGET_REPOSITORY == 'ContextualWisdomLab/k-csap-skills' && steps.noema_github_app_token.outputs.token || (env.TARGET_REPOSITORY != 'ContextualWisdomLab/k-csap-skills' && (secrets.NOEMA_REVIEW_TOKEN || steps.noema_github_app_token.outputs.token || steps.noema_oidc_token.outputs.token)) || '' }} run: | set -euo pipefail if ! [[ "$EXPECTED_HEAD_SHA" =~ ^[0-9a-fA-F]{40}$ ]]; then @@ -603,7 +797,7 @@ jobs: if: env.PR_NUMBER != '' id: target_visibility env: - GH_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN || steps.noema_github_app_token.outputs.token || steps.noema_oidc_token.outputs.token }} + GH_TOKEN: ${{ env.TARGET_REPOSITORY == 'ContextualWisdomLab/k-csap-skills' && steps.noema_github_app_token.outputs.token || (env.TARGET_REPOSITORY != 'ContextualWisdomLab/k-csap-skills' && (secrets.NOEMA_REVIEW_TOKEN || steps.noema_github_app_token.outputs.token || steps.noema_oidc_token.outputs.token)) || '' }} run: | set -euo pipefail if [ -z "${GH_TOKEN:-}" ]; then @@ -613,7 +807,7 @@ jobs: visibility="" for target_visibility_attempt in 1 2 3 4 5 6; do if visibility="$( - gh api "/repos/${TARGET_REPOSITORY}" --jq '.visibility // (if .private then "private" else "public" end)' + gh api "/repos/${TARGET_REPOSITORY}" --jq '.visibility // (if .private == true then "private" elif .private == false then "public" else "" end)' )"; then break fi @@ -637,27 +831,193 @@ jobs: ;; esac - - name: Provision contextual-orchestrator review sidecar + - name: Check live pull request draft state before sidecar provisioning + # two_phase.py's verdict preparation already reads the live PR and + # skips a draft ("PR is draft; Noema verdict preparation skipped."), + # but only after the 10-13 minute contextual-orchestrator sidecar + # provisioning below has held a runner (e.g. newsdom-api job + # 108077744310, .github job 106665379126). This moves that same + # runtime decision ahead of provisioning; it deliberately reads the + # live PR instead of the event payload's draft flag, because the + # organization ruleset runs this workflow in other repositories only + # on opened/synchronize/reopened, so the event snapshot is not the + # authority. Fails OPEN: an unreadable or malformed live PR yields + # live_draft=false and keeps today's full review path, where + # two_phase.py still performs its own draft check. A draft conclusion + # skips the model steps, leaves noema_prepare's outputs unset (the + # same "publication skipped" state a draft produced before), and the + # job still succeeds. if: env.PR_NUMBER != '' + id: live_draft + env: + GH_TOKEN: ${{ env.TARGET_REPOSITORY == 'ContextualWisdomLab/k-csap-skills' && steps.noema_github_app_token.outputs.token || (env.TARGET_REPOSITORY != 'ContextualWisdomLab/k-csap-skills' && (secrets.NOEMA_REVIEW_TOKEN || steps.noema_github_app_token.outputs.token || steps.noema_oidc_token.outputs.token)) || '' }} + run: | + set -euo pipefail + if [ -z "${GH_TOKEN:-}" ]; then + echo "::error::Noema reviewer credential is missing for live draft check." + exit 1 + fi + live_draft=false + if pull_request_json="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}" 2>/tmp/noema-live-draft-error)"; then + if jq -e -s 'length == 1 and (.[0] | type == "object" and .draft == true)' <<<"$pull_request_json" >/dev/null 2>&1; then + live_draft=true + fi + else + echo "::warning::Noema could not read the live pull request draft state; continuing with the model review." + sed 's/^/ /' /tmp/noema-live-draft-error >&2 || true + fi + echo "live_draft=${live_draft}" >>"$GITHUB_OUTPUT" + if [ "$live_draft" = "true" ]; then + echo "::notice::PR is draft; Noema model review skipped before sidecar provisioning." + fi + + - name: Provision pinned Node.js for Noema document review + if: env.PR_NUMBER != '' && steps.live_draft.outputs.live_draft != 'true' + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "22.23.3" + + - name: Set up lock-compatible sidecar Python + if: env.PR_NUMBER != '' && steps.live_draft.outputs.live_draft != 'true' + id: sidecar_python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + update-environment: false + + - name: Admit existing personal LiteLLM auto route + id: personal_route + if: env.PR_NUMBER != '' && steps.live_draft.outputs.live_draft != 'true' env: + LLM_GATEWAY_API_KEY: ${{ secrets.LLM_GATEWAY_API_KEY }} + PERSONAL_REVIEW_ZERO_COST_ATTESTED: ${{ vars.PERSONAL_REVIEW_ZERO_COST_ATTESTED || 'false' }} + PERSONAL_REVIEW_PUBLIC_AUTO_AUTHORIZED: ${{ vars.PERSONAL_REVIEW_PUBLIC_AUTO_AUTHORIZED || 'false' }} + PERSONAL_REVIEW_ZDR_ATTESTED: ${{ vars.PERSONAL_REVIEW_ZDR_ATTESTED || 'false' }} + PRIVATE_TARGET: ${{ steps.target_visibility.outputs.require_zdr }} + run: | + set -euo pipefail + if [ -z "${LLM_GATEWAY_API_KEY:-}" ]; then + echo '::error::STOP: existing personal review Secret is unavailable.' + exit 1 + fi + python3 -m scripts.ci.personal_review_route \ + --zero-cost "$PERSONAL_REVIEW_ZERO_COST_ATTESTED" \ + --public-auto-authorized "$PERSONAL_REVIEW_PUBLIC_AUTO_AUTHORIZED" \ + --zero-retention "$PERSONAL_REVIEW_ZDR_ATTESTED" \ + --private-target "$PRIVATE_TARGET" + echo 'selected=true' >>"$GITHUB_OUTPUT" + + - name: Initialize current Noema sidecar evidence + id: noema_evidence + if: env.PR_NUMBER != '' && steps.live_draft.outputs.live_draft != 'true' && steps.personal_route.outputs.selected != 'true' + run: | + set -euo pipefail + # Separate setup from the producer: unsafe paths fail before its outcome + # can admit an upload. Clear only these two files, before early startup. + python3 -I <<'PY' + import os + import stat + from pathlib import Path + + workspace_text = os.environ["GITHUB_WORKSPACE"] + workspace = Path(workspace_text) + if ( + not workspace.is_absolute() + or ".." in workspace.parts + or workspace.resolve(strict=True) != workspace + or not workspace.is_dir() + ): + raise SystemExit("Noema evidence workspace must be an unaliased directory") + evidence = workspace / "strix_runs" + if evidence.is_symlink() or (evidence.exists() and not evidence.is_dir()): + raise SystemExit("Noema evidence parent must be a non-symlink directory") + owned = ( + evidence / "contextual-orchestrator-preflight.json", + evidence / "contextual-orchestrator-sidecar.stderr.log", + ) + # Validate both before deleting either; reject dangling links, special + # files and hardlink aliases as well as nonregular evidence paths. + for path in owned: + try: + metadata = path.lstat() + except FileNotFoundError: + continue + if not stat.S_ISREG(metadata.st_mode) or metadata.st_nlink != 1: + raise SystemExit("Noema evidence must be regular, single-link files") + evidence.mkdir(mode=0o700, exist_ok=True) + for path in owned: + path.unlink(missing_ok=True) + PY + + - name: Provision contextual-orchestrator review sidecar + id: noema_sidecar + if: env.PR_NUMBER != '' && steps.live_draft.outputs.live_draft != 'true' && steps.personal_route.outputs.selected != 'true' + env: + SIDECAR_PYTHON: ${{ steps.sidecar_python.outputs.python-path }} BYTEZ_API_KEY: ${{ secrets.BYTEZ_API_KEY }} NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: ${{ steps.target_visibility.outputs.require_zdr }} + CONTEXTUAL_ORCHESTRATOR_POOL: auto run: | set -euo pipefail + test ! -L "$GITHUB_WORKSPACE/strix_runs" + rm -f "$GITHUB_WORKSPACE/strix_runs/contextual-orchestrator-preflight.json" bash "$GITHUB_WORKSPACE/scripts/ci/contextual_orchestrator_review_sidecar.sh" + - name: Classify sidecar provider-capacity failure + id: noema_sidecar_failure + if: failure() && steps.noema_sidecar.outcome == 'failure' + env: + NOEMA_TRANSPORT_RETRY_ATTEMPT: ${{ toJSON(github.event.client_payload.transport_retry_attempt) }} + run: | + python3 "$GITHUB_WORKSPACE/scripts/ci/noema_preflight_capacity.py" \ + --expected-head "$EXPECTED_HEAD_SHA" \ + --preflight-report "$GITHUB_WORKSPACE/strix_runs/contextual-orchestrator-preflight.json" + + - name: Provision local reviewed HWP document reader + if: env.PR_NUMBER != '' && steps.live_draft.outputs.live_draft != 'true' + env: + NPM_CONFIG_IGNORE_SCRIPTS: "true" + run: | + set -euo pipefail + node_major="$(node -p 'process.versions.node.split(".")[0]')" + case "$node_major" in + 20|22) ;; + *) + echo "::error::Noema HWP reader requires Node.js 20 or 22; found ${node_major:-missing}." + exit 1 + ;; + esac + python3 -m pip install --quiet --require-hashes --no-deps \ + -r "$GITHUB_WORKSPACE/requirements-noema-document-ci-hashes.txt" + reader_root="${RUNNER_TEMP}/noema-document-reader" + rm -rf "$reader_root" + mkdir -p "$reader_root" + cp "$GITHUB_WORKSPACE/scripts/ci/noema-document-reader/package.json" \ + "$GITHUB_WORKSPACE/scripts/ci/noema-document-reader/package-lock.json" \ + "$reader_root/" + ( + cd "$reader_root" + npm ci --ignore-scripts --omit=dev --no-audit --no-fund + ) + echo "NOEMA_HWP_MCP_SOURCE=$reader_root/node_modules/hwp-mcp" >>"$GITHUB_ENV" + - name: Prepare Noema model verdict - if: env.PR_NUMBER != '' + if: env.PR_NUMBER != '' && steps.live_draft.outputs.live_draft != 'true' id: noema_prepare env: - GH_TOKEN: ${{ secrets.NOEMA_REVIEW_TOKEN || steps.noema_github_app_token.outputs.token || steps.noema_oidc_token.outputs.token }} + GH_TOKEN: ${{ env.TARGET_REPOSITORY == 'ContextualWisdomLab/k-csap-skills' && steps.noema_github_app_token.outputs.token || (env.TARGET_REPOSITORY != 'ContextualWisdomLab/k-csap-skills' && (secrets.NOEMA_REVIEW_TOKEN || steps.noema_github_app_token.outputs.token || steps.noema_oidc_token.outputs.token)) || '' }} NOEMA_REVIEW_TOKEN_SOURCE: ${{ steps.noema_credential.outputs.source == 'pat' && 'noema-review-pat' || steps.noema_credential.outputs.source == 'github-app' && 'noema-review-github-app' || 'noema-review-app-oidc' }} NOEMA_REVIEW_ACTOR: ${{ steps.noema_github_app_token.outputs['app-slug'] && format('{0}[bot]', steps.noema_github_app_token.outputs['app-slug']) || '' }} NOEMA_REVIEW_INSTALLATION_ID: ${{ steps.noema_github_app_token.outputs['installation-id'] }} + NOEMA_TRANSPORT_RETRY_ATTEMPT: ${{ toJSON(github.event.client_payload.transport_retry_attempt) }} + LLM_GATEWAY_API_KEY: ${{ secrets.LLM_GATEWAY_API_KEY }} + PERSONAL_ROUTE_SELECTED: ${{ steps.personal_route.outputs.selected }} + NOEMA_GITHUB_APP_CLIENT_ID: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID }} + NOEMA_GITHUB_APP_PRIVATE_KEY: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }} run: | set -euo pipefail if [ -z "${PR_NUMBER:-}" ]; then @@ -669,18 +1029,28 @@ jobs: echo "::error::Noema reviewer credential selection succeeded but no token was minted; review cannot prepare a verdict." exit 1 fi + if [ "${PERSONAL_ROUTE_SELECTED:-}" = 'true' ]; then + test -n "${LLM_GATEWAY_API_KEY:-}" + export NOEMA_LLM_API_URL="https://litellm.poinnetworks.net/v1/chat/completions" + export NOEMA_LLM_MODEL="auto" + export NOEMA_LLM_API_KEY="$LLM_GATEWAY_API_KEY" + unset NOEMA_LLM_VIA_ORCHESTRATOR + else if [ -z "${CONTEXTUAL_ORCHESTRATOR_BASE_URL:-}" ] || [ -z "${CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE:-}" ]; then echo "::error::contextual-orchestrator review sidecar must be provisioned before Noema LLM review." exit 1 fi source "$GITHUB_WORKSPACE/scripts/ci/load_contextual_orchestrator_token.sh" export NOEMA_LLM_API_URL="${CONTEXTUAL_ORCHESTRATOR_BASE_URL%/}/v1/chat/completions" - export NOEMA_LLM_MODEL="orchestrator/free" + export NOEMA_LLM_MODEL="orchestrator/auto" export NOEMA_LLM_API_KEY="${CONTEXTUAL_ORCHESTRATOR_TOKEN}" export NOEMA_LLM_VIA_ORCHESTRATOR=1 + fi verdict_file="${RUNNER_TEMP}/noema-verdict-envelope.json" - rm -f "$verdict_file" - python3 "$GITHUB_WORKSPACE/.github/actions/noema-review/two_phase.py" --repo "$TARGET_REPOSITORY" --pr-number "$PR_NUMBER" --expected-head "$EXPECTED_HEAD_SHA" --prepare-verdict-file "$verdict_file" + test ! -e "$verdict_file" + python3 "$GITHUB_WORKSPACE/.github/actions/noema-review/two_phase.py" \ + --repo "$TARGET_REPOSITORY" --pr-number "$PR_NUMBER" \ + --expected-head "$EXPECTED_HEAD_SHA" --prepare-verdict-file "$verdict_file" if [ -f "$verdict_file" ]; then echo "prepared=true" >>"$GITHUB_OUTPUT" else @@ -688,6 +1058,33 @@ jobs: echo "::notice::Noema model phase produced no publishable envelope; publication is skipped." fi + - name: Upload contextual-orchestrator sidecar evidence + # A live Draft never provisions the sidecar. Retain only current producer + # terminal diagnostics; absent/skipped outcomes cannot publish stale files. + # Non-Draft upload failures still fail the required review job. + if: always() && env.PR_NUMBER != '' && steps.live_draft.outputs.live_draft != 'true' && (steps.noema_sidecar.outcome == 'success' || steps.noema_sidecar.outcome == 'failure' || steps.noema_sidecar.outcome == 'cancelled') + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: noema-sidecar-evidence + path: | + strix_runs/contextual-orchestrator-sidecar.stderr.log + strix_runs/contextual-orchestrator-preflight.json + if-no-files-found: ignore + retention-days: 5 + + - name: Refresh read-only Noema GitHub App metadata token after model + if: env.PR_NUMBER != '' && steps.noema_prepare.outputs.prepared == 'true' && steps.noema_credential.outputs.source == 'github-app' + id: noema_publication_metadata_token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + with: + client-id: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID }} + private-key: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }} + owner: ContextualWisdomLab + repositories: ${{ steps.noema_credential.outputs.repository }} + permission-contents: read + permission-metadata: read + permission-pull-requests: read + - name: Refresh repository-scoped Noema GitHub App token for publication if: env.PR_NUMBER != '' && steps.noema_prepare.outputs.prepared == 'true' && steps.noema_credential.outputs.source == 'github-app' id: noema_github_app_publication_token @@ -697,22 +1094,19 @@ jobs: private-key: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }} owner: ContextualWisdomLab repositories: ${{ steps.noema_credential.outputs.repository }} - permission-actions: read - permission-checks: read permission-contents: read permission-metadata: read permission-pull-requests: write - permission-security-events: read - permission-statuses: read - permission-vulnerability-alerts: read - name: Publish prepared Noema verdict on the exact live head if: env.PR_NUMBER != '' && steps.noema_prepare.outputs.prepared == 'true' env: - GH_TOKEN: ${{ steps.noema_credential.outputs.source == 'pat' && secrets.NOEMA_REVIEW_TOKEN || steps.noema_credential.outputs.source == 'github-app' && steps.noema_github_app_publication_token.outputs.token || steps.noema_credential.outputs.source == 'oidc' && steps.noema_oidc_token.outputs.token || '' }} + GH_TOKEN: ${{ env.TARGET_REPOSITORY == 'ContextualWisdomLab/k-csap-skills' && steps.noema_github_app_publication_token.outputs.token || (env.TARGET_REPOSITORY != 'ContextualWisdomLab/k-csap-skills' && (steps.noema_credential.outputs.source == 'pat' && secrets.NOEMA_REVIEW_TOKEN || steps.noema_credential.outputs.source == 'github-app' && steps.noema_github_app_publication_token.outputs.token || steps.noema_credential.outputs.source == 'oidc' && steps.noema_oidc_token.outputs.token || '')) || '' }} NOEMA_REVIEW_TOKEN_SOURCE: ${{ steps.noema_credential.outputs.source == 'pat' && 'noema-review-pat' || steps.noema_credential.outputs.source == 'github-app' && 'noema-review-github-app-refresh' || steps.noema_credential.outputs.source == 'oidc' && 'noema-review-app-oidc' || '' }} NOEMA_REVIEW_ACTOR: ${{ steps.noema_github_app_publication_token.outputs['app-slug'] && format('{0}[bot]', steps.noema_github_app_publication_token.outputs['app-slug']) || '' }} NOEMA_REVIEW_INSTALLATION_ID: ${{ steps.noema_github_app_publication_token.outputs['installation-id'] }} + NOEMA_GITHUB_APP_CLIENT_ID: ${{ vars.NOEMA_GITHUB_APP_CLIENT_ID }} + NOEMA_GITHUB_APP_PRIVATE_KEY: ${{ secrets.NOEMA_GITHUB_APP_PRIVATE_KEY }} run: | set -euo pipefail if [ -z "${GH_TOKEN:-}" ]; then @@ -724,4 +1118,83 @@ jobs: echo "::error::Noema prepared-verdict output claimed success but its private envelope is missing." exit 1 fi - python3 "$GITHUB_WORKSPACE/.github/actions/noema-review/two_phase.py" --repo "$TARGET_REPOSITORY" --pr-number "$PR_NUMBER" --expected-head "$EXPECTED_HEAD_SHA" --publish-verdict-file "$verdict_file" + python3 "$GITHUB_WORKSPACE/.github/actions/noema-review/two_phase.py" \ + --repo "$TARGET_REPOSITORY" --pr-number "$PR_NUMBER" \ + --expected-head "$EXPECTED_HEAD_SHA" --publish-verdict-file "$verdict_file" + + continue-noema-transport: + needs: [admit-current-head, noema-review] + if: >- + always() + && needs.admit-current-head.outputs.admitted == 'true' + && needs.noema-review.result == 'failure' + && needs.noema-review.outputs.transport_capacity_unavailable == 'true' + && needs.noema-review.outputs.transport_retry_eligible == 'true' + runs-on: + group: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/contextual-orchestrator' || github.repository == 'ContextualWisdomLab/fast-mlsirm' || github.repository == 'ContextualWisdomLab/cwl-telemetry' || github.repository == 'ContextualWisdomLab/naruon' || github.repository == 'ContextualWisdomLab/late-life-anxiety-reanalysis' || github.repository == 'ContextualWisdomLab/tokyo-travel-2026') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/contextual-orchestrator' || github.repository == 'ContextualWisdomLab/fast-mlsirm' || github.repository == 'ContextualWisdomLab/cwl-telemetry' || github.repository == 'ContextualWisdomLab/naruon' || github.repository == 'ContextualWisdomLab/late-life-anxiety-reanalysis' || github.repository == 'ContextualWisdomLab/tokyo-travel-2026') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} + timeout-minutes: 10 + permissions: + contents: write + pull-requests: read + env: + # Consumer required workflows need the existing central dispatch credential. + # The central handler can use its repository-scoped token as fallback. + GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || github.token }} + TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.event.client_payload.target_repository || github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event.client_payload.pr_number || '' }} + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.client_payload.pr_head_sha || '' }} + EXPECTED_BASE_SHA: ${{ needs.admit-current-head.outputs.base_sha }} + DELAY_SECONDS: ${{ needs.noema-review.outputs.transport_retry_delay_seconds }} + NEXT_ATTEMPT: ${{ needs.noema-review.outputs.transport_retry_next_attempt }} + PROVIDER_ATTEMPT_COUNT: ${{ needs.noema-review.outputs.provider_attempt_count }} + TRANSPORT_HTTP_STATUS: ${{ needs.noema-review.outputs.transport_http_status }} + steps: + - name: Schedule bounded Noema transport re-dispatch + run: | + set -euo pipefail + if { [ "$GITHUB_REPOSITORY" != "ContextualWisdomLab/.github" ] && + [ "$TARGET_REPOSITORY" != "$GITHUB_REPOSITORY" ]; } || + ! [[ "$TARGET_REPOSITORY" =~ ^ContextualWisdomLab/[A-Za-z0-9_.-]+$ ]] || + ! [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || + ! [[ "$EXPECTED_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]] || + ! [[ "$EXPECTED_BASE_SHA" =~ ^[0-9a-f]{40}$ ]]; then + echo "::error::Noema transport re-dispatch rejected an unrelated origin or malformed PR identity." + exit 1 + fi + if ! [[ "$DELAY_SECONDS" =~ ^[1-9][0-9]*$ ]] || [ "$DELAY_SECONDS" -gt 300 ] || + ! [[ "$NEXT_ATTEMPT" =~ ^[12]$ ]]; then + echo "::error::Noema transport re-dispatch refused an unbounded delay or attempt." + exit 1 + fi + echo "::notice::Noema provider capacity unavailable (http_status=${TRANSPORT_HTTP_STATUS:-unknown}, provider_attempt_count=${PROVIDER_ATTEMPT_COUNT:-unknown}); waiting ${DELAY_SECONDS}s before same-head continuation re-dispatch ${NEXT_ATTEMPT}." + sleep "$DELAY_SECONDS" + live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" + live_head="$(jq -r '.head.sha // empty' <<<"$live_pr")" + live_head_repo="$(jq -r '.head.repo.full_name // empty' <<<"$live_pr")" + live_base="$(jq -r '.base.sha // empty' <<<"$live_pr")" + live_base_repo="$(jq -r '.base.repo.full_name // empty' <<<"$live_pr")" + live_state="$(jq -r '.state // empty' <<<"$live_pr")" + if [ "$live_head" != "$EXPECTED_HEAD_SHA" ] || + [ "$live_head_repo" != "$TARGET_REPOSITORY" ] || + [ "$live_base" != "$EXPECTED_BASE_SHA" ] || + [ "$live_base_repo" != "$TARGET_REPOSITORY" ] || + [ "$live_state" != "open" ]; then + echo "::notice::Noema transport re-dispatch retired because the live PR head or base moved or closed." + exit 0 + fi + jq -n \ + --arg target_repository "$TARGET_REPOSITORY" \ + --argjson pr_number "$PR_NUMBER" \ + --arg pr_head_sha "$EXPECTED_HEAD_SHA" \ + --argjson transport_retry_attempt "$NEXT_ATTEMPT" \ + '{ + event_type: "noema-review", + client_payload: { + target_repository: $target_repository, + pr_number: $pr_number, + pr_head_sha: $pr_head_sha, + transport_retry_attempt: $transport_retry_attempt + } + }' | gh api -X POST "repos/ContextualWisdomLab/.github/dispatches" --input - + echo "::notice::Scheduled Noema transport continuation re-dispatch for ${TARGET_REPOSITORY}#${PR_NUMBER} at ${EXPECTED_HEAD_SHA} (attempt ${NEXT_ATTEMPT})." diff --git a/.github/workflows/opencode-review-coalesce-tick.yml b/.github/workflows/opencode-review-coalesce-tick.yml new file mode 100644 index 0000000000..b676654592 --- /dev/null +++ b/.github/workflows/opencode-review-coalesce-tick.yml @@ -0,0 +1,137 @@ +name: OpenCode Review Coalesce Tick + +# Push-burst coalescing. The required review workflows dispatch a full +# multi-hour OpenCode review chain on every `synchronize` push, even when +# several pushes land within seconds of each other -- measured across 4 org +# repositories (419 consecutive-push gaps): density roughly halves right at +# 300s, the clearest inflection point in an otherwise continuous +# distribution. See docs/doctoring/actions-capacity-root-cause-20260917.md. +# +# This tick is the only thing that dispatches a synchronize-triggered +# OpenCode review once coalescing is turned on -- see +# scripts/ci/pr_review_merge_scheduler_core.py's coalesce_enabled() and +# head_stable_for_seconds(), which gate dispatch_opencode_review() itself. +# The job below is skipped (no runner) unless the +# `OPENCODE_REVIEW_COALESCE_ENABLED` repository variable is "true": merging +# this file changes nothing by default. The gate is job-scoped on purpose: +# a step-scoped gate (#2232) forced an inert job onto the org runner queue +# and run 35219385415 sat `queued` for 3h+ with the flag still false +# (docs/doctoring/coalesce-tick-inert-runner-queue-20260917.md). A job-level +# `if:` still produces a completed/`skipped` run record (live: 35191169833 +# finished in 1s) without competing for the plan concurrent-job ceiling. +# When the flag is on, multi-hour admission delay is covered by the +# scheduler fail-open in recent_coalesce_tick_completed() (#2233). +# +# GitHub's required-workflow ruleset only propagates pull_request_target-family +# events to sibling repositories, never `schedule:` (confirmed live, +# docs/doctoring/required-workflow-path-filter-boundary.md) -- a per-repo +# cron committed only here would fire solely for this repository's own PRs. +# This job instead lists every open PR across the organization from this one +# repository in a single run (org-wide GraphQL search), then re-invokes the +# existing, unmodified per-repo scheduler script once per repository that has +# an open PR -- reusing 100% of its existing same-head dedup, admission +# budget, live-head revalidation, and now the coalescing gate itself, instead +# of duplicating any of that logic here. + +on: + schedule: + - cron: "*/5 * * * *" + +concurrency: + # Non-stacking: at most one tick runs at a time, and at most one more + # waits behind it (GitHub Actions concurrency queues, it does not stack + # unboundedly). cancel-in-progress stays false so a tick already in the + # middle of dispatching is never cut off mid-repository. + group: opencode-review-coalesce-tick + cancel-in-progress: false + +permissions: + contents: read + +jobs: + coalesce-tick: + # Skip before runner admission when coalescing is off. Do not move this + # gate back to step scope: that reintroduces multi-hour queue wait for + # an inert echo under the org concurrent-job ceiling. + if: vars.OPENCODE_REVIEW_COALESCE_ENABLED == 'true' && github.repository == 'ContextualWisdomLab/.github' && github.event_name == 'schedule' && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review-coalesce-tick.yml@refs/heads/main' + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 4 + permissions: + actions: write + checks: read + contents: write + id-token: write + pull-requests: write + statuses: read + env: + GH_TOKEN: ${{ github.token }} + SCHEDULER_REQUIRED_WORKFLOW_REPOSITORY: ContextualWisdomLab/.github + SCHEDULER_ALLOW_CROSS_REPO_REPOSITORY_DISPATCH: ${{ (secrets.PR_REVIEW_MERGE_TOKEN != '' || secrets.OPENCODE_APPROVE_TOKEN != '') && 'true' || 'false' }} + OPENCODE_REVIEW_COALESCE_ENABLED: "true" + OPENCODE_REVIEW_COALESCE_WINDOW_SECONDS: ${{ vars.OPENCODE_REVIEW_COALESCE_WINDOW_SECONDS || '300' }} + steps: + - name: Checkout scheduler scripts + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + sparse-checkout: | + scripts/ci + sparse-checkout-cone-mode: false + + - name: List organization repositories with open pull requests + id: repos + run: | + set -euo pipefail + repos_file="${RUNNER_TEMP}/coalesce-repos.txt" + : >"$repos_file" + cursor="" + for _page in 1 2 3 4 5 6 7 8 9 10; do + cursor_arg=() + if [ -n "$cursor" ]; then + cursor_arg=(-f "cursor=$cursor") + fi + response="$(gh api graphql -f query=" + query(\$cursor: String) { + search(query: \"org:ContextualWisdomLab is:pr is:open draft:false\", type: ISSUE, first: 100, after: \$cursor) { + nodes { ... on PullRequest { repository { nameWithOwner } } } + pageInfo { hasNextPage endCursor } + } + }" "${cursor_arg[@]}" 2>/dev/null || echo '{}')" + printf '%s' "$response" | jq -r '.data.search.nodes[]?.repository.nameWithOwner // empty' >>"$repos_file" + has_next="$(printf '%s' "$response" | jq -r '.data.search.pageInfo.hasNextPage // false')" + [ "$has_next" = "true" ] || break + cursor="$(printf '%s' "$response" | jq -r '.data.search.pageInfo.endCursor')" + done + sort -u "$repos_file" -o "$repos_file" + echo "count=$(wc -l <"$repos_file" | tr -d ' ')" >>"$GITHUB_OUTPUT" + cat "$repos_file" + + - name: Dispatch a coalesced OpenCode review for each stabilized head + env: + REPOS_FILE: ${{ runner.temp }}/coalesce-repos.txt + run: | + set -euo pipefail + if [ ! -s "$REPOS_FILE" ]; then + echo "No open pull requests found org-wide; nothing to coalesce this tick." + exit 0 + fi + while IFS= read -r repo; do + [ -n "$repo" ] || continue + default_branch="$(gh api "repos/${repo}" --jq '.default_branch' 2>/dev/null || echo main)" + # Scoped to review dispatch only: this tick's job is coalescing, + # not merge scheduling or branch freshness, which stay owned by + # the regular per-push/per-review scheduler invocations. + python3 scripts/ci/pr_review_merge_scheduler.py \ + --repo "$repo" \ + --base-branch "$default_branch" \ + --review-workflow "Required OpenCode Review" \ + --review-dispatch-limit -1 \ + --branch-update-limit 0 \ + --no-enable-auto-merge \ + --merge-mode disabled \ + --no-update-branches \ + --trigger-reviews \ + || echo "::warning::Coalesce tick pass failed for ${repo}; continuing with remaining repositories." + done <"$REPOS_FILE" diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index ade10b37c4..cb3f10e0f6 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -11,19 +11,58 @@ on: repository_dispatch: types: [opencode-review] +concurrency: + # Workflow-level admission, for the same reason strix.yml, noema-review.yml and + # opencode-review.yml carry theirs at this level: a job-level group is never + # evaluated while the whole run waits behind the organization job ceiling, so + # superseded dispatches for one pull request coalesce only after each of them + # has already been allocated a runner. Measured on 2026-09-06: of the five + # dispatch runs that passed `validate-pr-metadata`, four were rejected hours + # later by `opencode-review`'s privileged metadata check because the head had + # moved while they queued (runs 34002473295, 34010256951, 34015973300, + # 34016922761) -- each after `coverage-source-tree` and `coverage-evidence` + # had run. Cancelling the superseded run at creation returns that slot instead + # of spending it to discover the review's subject no longer exists. + # + # The key is the target pull request, matching the job-level group below and + # codeql-scan-dispatch.yml's workflow-level group; `github.run_id` keeps runs + # without a payload in their own groups rather than colliding. + group: >- + opencode-review-dispatch-${{ + github.event.client_payload.target_repository || github.repository }}-${{ + github.event.client_payload.pr_number || github.run_id }} + cancel-in-progress: true + permissions: contents: read jobs: validate-pr-metadata: name: validate-pr-metadata + # Folded together with the former coverage-source-tree job (2026-09-17): + # both jobs only ever exchanged the OpenCode app token for READ-scoped + # data (target-repository metadata, then the PR merge tree) and neither + # executes untrusted PR-head content or holds a write-capable token -- + # they sit on the same side of the trust boundary that keeps + # coverage-evidence (untrusted test/build execution, `actions: read` + # only) and opencode-review-target (privileged review-publication + # writes) isolated. Folding them removes one of the three needs:-chained + # job-to-job runner-queue re-entries this workflow used to pay under + # saturation; see docs/doctoring/actions-capacity-root-cause-20260917.md + # for the measurement (run 34931908846: 21 minutes of job execution + # inside a 13h57m run, ~97.5% of which was queue wait between exactly + # these job boundaries). if: github.event_name == 'repository_dispatch' - runs-on: ubuntu-24.04 - timeout-minutes: 8 + runs-on: + group: CWL central OpenCode + labels: [self-hosted, linux, x64] + timeout-minutes: 20 permissions: contents: read pull-requests: read id-token: write + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true outputs: target_repository: ${{ steps.validate.outputs.target_repository }} pr_number: ${{ steps.validate.outputs.pr_number }} @@ -124,10 +163,26 @@ jobs: run: | set -euo pipefail if [ "$EVENT_NAME" = "repository_dispatch" ]; then - if [ -z "$ALLOWED_DISPATCH_ACTOR" ] || - [ "$DISPATCH_ACTOR" != "$ALLOWED_DISPATCH_ACTOR" ] || - [ "$DISPATCH_SENDER" != "$ALLOWED_DISPATCH_ACTOR" ]; then - printf '::error::repository_dispatch authorization rejected actor=%s sender=%s because both must match the configured scheduler identity.\n' "${DISPATCH_ACTOR:-}" "${DISPATCH_SENDER:-}" + # More than one trusted identity dispatches this workflow: + # opencode-review.yml sends through the OpenCode GitHub App + # (opencode-agent[bot]) while pr-review-merge-scheduler.yml sends + # with its own token chain. Accept a comma-separated allowlist, + # parsed exactly like ALLOWED_DISPATCH_TARGETS below. The actor + # AND the sender must both equal the SAME allowlisted identity; + # an empty allowlist admits nothing. + actor_allowed=0 + IFS=',' read -r -a allowed_dispatch_actors <<<"$ALLOWED_DISPATCH_ACTOR" + for allowed_actor in "${allowed_dispatch_actors[@]}"; do + allowed_actor="${allowed_actor//[[:space:]]/}" + if [ -n "$allowed_actor" ] && + [ "$DISPATCH_ACTOR" = "$allowed_actor" ] && + [ "$DISPATCH_SENDER" = "$allowed_actor" ]; then + actor_allowed=1 + break + fi + done + if [ "$actor_allowed" -ne 1 ]; then + printf '::error::repository_dispatch authorization rejected actor=%s sender=%s because both must match one configured scheduler identity.\n' "${DISPATCH_ACTOR:-}" "${DISPATCH_SENDER:-}" exit 1 fi @@ -203,26 +258,45 @@ jobs: } >>"$GITHUB_OUTPUT" printf 'Validated current live metadata for %s#%s: base=%s/%s head=%s/%s.\n' "$TARGET_REPOSITORY" "$PR_NUMBER" "$live_base_ref" "$live_base_sha" "$live_head_ref" "$live_head_sha" - coverage-source-tree: - name: coverage-source-tree - needs: [validate-pr-metadata] - if: >- - needs.validate-pr-metadata.result == 'success' - && github.event_name == 'repository_dispatch' - runs-on: ubuntu-24.04 - timeout-minutes: 12 - permissions: - contents: read - id-token: write - env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - steps: + - name: Refuse private source on a public artifact destination + env: + SOURCE_REPOSITORY_PRIVATE: ${{ steps.validate.outputs.is_private }} + ARTIFACT_REPOSITORY: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + # Artifact readers follow the receiving repository's current ACL. + # Do not use the dispatch event's potentially stale visibility. + case "$SOURCE_REPOSITORY_PRIVATE" in + false) ;; + true) + if ! [[ "$ARTIFACT_REPOSITORY" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]]; then + echo '::error::Receiving artifact repository identity is invalid.' + exit 1 + fi + if ! artifact_metadata="$(gh api "repos/${ARTIFACT_REPOSITORY}" 2>/dev/null)"; then + echo '::error::Receiving artifact repository visibility could not be read.' + exit 1 + fi + if ! jq -se --arg repository "$ARTIFACT_REPOSITORY" ' + length == 1 and (.[0] | + type == "object" and .full_name == $repository and + (.private | type == "boolean") and .private == true) + ' >/dev/null 2>&1 <<<"$artifact_metadata"; then + echo '::error::Private coverage source requires a currently private artifact destination; public or unknown destinations are refused.' + exit 1 + fi + ;; + *) echo '::error::Coverage source visibility is unknown.'; exit 1 ;; + esac + - name: Exchange OpenCode app token for target repository coverage reads id: coverage_read_app_token if: >- github.event_name == 'repository_dispatch' - && needs.validate-pr-metadata.outputs.target_repository != '' - && needs.validate-pr-metadata.outputs.target_repository != github.repository + && steps.validate.outputs.target_repository != '' + && steps.validate.outputs.target_repository != github.repository + && steps.validate.outputs.is_private == 'true' env: OIDC_AUDIENCE: opencode-github-action OPENCODE_API_BASE_URL: https://api.opencode.ai @@ -288,12 +362,13 @@ jobs: } >>"$GITHUB_OUTPUT" - name: Materialize pull request merge tree for coverage measurement + if: steps.validate.outputs.is_private == 'true' env: GH_TOKEN: ${{ steps.coverage_read_app_token.outputs.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }} - TARGET_REPOSITORY: ${{ needs.validate-pr-metadata.outputs.target_repository }} - PR_NUMBER: ${{ needs.validate-pr-metadata.outputs.pr_number }} - PR_BASE_SHA: ${{ needs.validate-pr-metadata.outputs.base_sha }} - PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }} + TARGET_REPOSITORY: ${{ steps.validate.outputs.target_repository }} + PR_NUMBER: ${{ steps.validate.outputs.pr_number }} + PR_BASE_SHA: ${{ steps.validate.outputs.base_sha }} + PR_HEAD_SHA: ${{ steps.validate.outputs.head_sha }} COVERAGE_SOURCE_WORKDIR: ${{ runner.temp }}/opencode-coverage-source COVERAGE_SOURCE_ARCHIVE: ${{ runner.temp }}/opencode-coverage-source.tar run: | @@ -341,7 +416,41 @@ jobs: git -C "$COVERAGE_SOURCE_WORKDIR" status --short tar -cf "$COVERAGE_SOURCE_ARCHIVE" -C "$COVERAGE_SOURCE_WORKDIR" . + - name: Recheck private artifact destination before upload + if: steps.validate.outputs.is_private == 'true' + env: + SOURCE_REPOSITORY_PRIVATE: ${{ steps.validate.outputs.is_private }} + ARTIFACT_REPOSITORY: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + # Artifact readers follow the receiving repository's current ACL. + # Do not use the dispatch event's potentially stale visibility. + case "$SOURCE_REPOSITORY_PRIVATE" in + false) ;; + true) + if ! [[ "$ARTIFACT_REPOSITORY" =~ ^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$ ]]; then + echo '::error::Receiving artifact repository identity is invalid.' + exit 1 + fi + if ! artifact_metadata="$(gh api "repos/${ARTIFACT_REPOSITORY}" 2>/dev/null)"; then + echo '::error::Receiving artifact repository visibility could not be read.' + exit 1 + fi + if ! jq -se --arg repository "$ARTIFACT_REPOSITORY" ' + length == 1 and (.[0] | + type == "object" and .full_name == $repository and + (.private | type == "boolean") and .private == true) + ' >/dev/null 2>&1 <<<"$artifact_metadata"; then + echo '::error::Private coverage source requires a currently private artifact destination; public or unknown destinations are refused.' + exit 1 + fi + ;; + *) echo '::error::Coverage source visibility is unknown.'; exit 1 ;; + esac + - name: Upload materialized pull request merge tree + if: steps.validate.outputs.is_private == 'true' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: opencode-coverage-source @@ -351,60 +460,50 @@ jobs: coverage-evidence: name: coverage-evidence - needs: [validate-pr-metadata, coverage-source-tree] + needs: [validate-pr-metadata] if: >- - always() - && needs.validate-pr-metadata.result == 'success' - && needs.coverage-source-tree.result != 'cancelled' + needs.validate-pr-metadata.result == 'success' && github.event_name == 'repository_dispatch' - runs-on: ubuntu-24.04 + # Pull-request merge-tree tests run only on the clean-per-job isolated pool. + # Keep metadata and review publication on their separate trusted jobs. + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] timeout-minutes: 300 permissions: - # The PR tree arrives through a same-run artifact. No repository-content, - # identity, secret, or write token is available to untrusted tests. + # Private source uses a same-run artifact; public source uses tokenless Git. + # No repository-content, identity, secret or write grant is added to tests. actions: read outputs: coverage_summary: ${{ steps.measure.outputs.coverage_summary }} env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true steps: + - name: Require explicit coverage source visibility + env: + IS_PRIVATE: ${{ needs.validate-pr-metadata.outputs.is_private }} + run: | + set -euo pipefail + case "$IS_PRIVATE" in + true|false) ;; + *) echo '::error::Coverage source visibility is unknown.'; exit 1 ;; + esac + - name: Resolve trusted OpenCode source ref id: trusted_source env: - JOB_CONTEXT_JSON: ${{ toJSON(job) }} - GITHUB_CONTEXT_JSON: ${{ toJSON(github) }} + TRUSTED_WORKFLOW_SHA: ${{ github.workflow_sha }} + TRUSTED_WORKFLOW_REF: ${{ github.workflow_ref }} run: | set -euo pipefail - python3 <<'PY' >>"$GITHUB_OUTPUT" - import json + python3 -I - <<'PY' >>"$GITHUB_OUTPUT" import os import re - import sys - - try: - job_context = json.loads(os.environ.get("JOB_CONTEXT_JSON") or "{}") - github_context = json.loads(os.environ.get("GITHUB_CONTEXT_JSON") or "{}") - except json.JSONDecodeError as exc: - print(f"::error::Could not parse GitHub workflow context JSON: {exc}", file=sys.stderr) - raise SystemExit(1) - - trusted_ref = str( - job_context.get("workflow_sha") or github_context.get("workflow_sha") or "" - ).strip() - workflow_ref = str( - job_context.get("workflow_ref") or github_context.get("workflow_ref") or "" - ).strip() - - if not trusted_ref: - trusted_ref = "main" - prefix = "ContextualWisdomLab/.github/.github/workflows/opencode-review-dispatch.yml@" - if workflow_ref.startswith(prefix): - trusted_ref = workflow_ref.split("@", 1)[1] - - if not re.fullmatch(r"[0-9a-fA-F]{40}|refs/[^\s]+|[A-Za-z0-9._/-]+", trusted_ref): - print("::error::Trusted OpenCode workflow ref resolved to an invalid value.", file=sys.stderr) - raise SystemExit(1) - + trusted_ref = os.environ.get("TRUSTED_WORKFLOW_SHA", "") + workflow_ref = os.environ.get("TRUSTED_WORKFLOW_REF", "") + prefix = "ContextualWisdomLab/.github/.github/workflows/opencode-review-dispatch.yml@" + if not re.fullmatch(r"[0-9a-f]{40}", trusted_ref) or not workflow_ref.startswith(prefix): + raise SystemExit("Trusted OpenCode workflow requires an immutable central SHA.") print(f"ref={trusted_ref}") PY @@ -413,33 +512,111 @@ jobs: TRUSTED_SOURCE_REF: ${{ steps.trusted_source.outputs.ref }} run: | set -euo pipefail - git init "$GITHUB_WORKSPACE" - git -C "$GITHUB_WORKSPACE" remote add trusted-source https://github.com/ContextualWisdomLab/.github.git - git -C "$GITHUB_WORKSPACE" fetch --depth=1 --no-tags trusted-source "$TRUSTED_SOURCE_REF" - git -C "$GITHUB_WORKSPACE" checkout --detach FETCH_HEAD - printf 'Materialized trusted coverage contract at %s from validated ref %s.\n' \ - "$(git -C "$GITHUB_WORKSPACE" rev-parse HEAD)" "$TRUSTED_SOURCE_REF" - - - name: Report coverage source materialization failure - if: needs.coverage-source-tree.result != 'success' + # This directory is exclusively owned by the isolated job. + if [ -z "${GITHUB_WORKSPACE:-}" ] || [ -z "${RUNNER_WORKSPACE:-}" ] || + [ -L "$GITHUB_WORKSPACE" ] || [ "$RUNNER_WORKSPACE" = / ] || + [ "$(realpath "$GITHUB_WORKSPACE")" != "$(pwd -P)" ] || + [ "$(dirname "$(realpath "$GITHUB_WORKSPACE")")" != "$(realpath "$RUNNER_WORKSPACE")" ]; then + echo "::error::Coverage workspace is outside the current runner job directory." + exit 1 + fi + find "$GITHUB_WORKSPACE" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} + + python3 -I - <<'PY' + import hashlib + import os + from pathlib import Path + import re + import stat + import subprocess + import tempfile + + workspace = Path(os.environ["GITHUB_WORKSPACE"]) + source = os.environ["TRUSTED_SOURCE_REF"] + if not re.fullmatch(r"[0-9a-f]{40}", source): + raise SystemExit("Trusted source SHA is invalid.") + root = Path(os.environ["RUNNER_TEMP"]) + for node in (root, *root.parents): + if not stat.S_ISDIR(node.lstat().st_mode): + raise SystemExit("Trusted bootstrap temporary root is unsafe.") + with tempfile.TemporaryDirectory(prefix="coverage-bootstrap-", dir=root) as home: + environment = { + "PATH": "/usr/bin:/bin", "HOME": home, "XDG_CONFIG_HOME": home, + "TMPDIR": home, "LC_ALL": "C", "GIT_CONFIG_NOSYSTEM": "1", + "GIT_CONFIG_GLOBAL": "/dev/null", "GIT_CONFIG_SYSTEM": "/dev/null", + "GIT_ATTR_NOSYSTEM": "1", "GIT_TERMINAL_PROMPT": "0", + "GIT_NO_REPLACE_OBJECTS": "1", + } + def git(*args): + options = ( + "credential.helper=", "http.extraHeader=", "http.proxy=", + "http.followRedirects=false", "http.sslVerify=true", + "core.hooksPath=/dev/null", "core.attributesFile=/dev/null", + "core.fsmonitor=false", "protocol.allow=never", + "protocol.https.allow=always", "core.autocrlf=false", + "submodule.recurse=false", "gc.auto=0", + ) + argv = ["/usr/bin/git"] + for value in options: + argv.extend(("-c", value)) + result = subprocess.run( + [*argv, *args], cwd=workspace, env=environment, + stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, check=False, shell=False, timeout=180, + ) + if result.returncode: + raise SystemExit("Trusted coverage bootstrap Git failed.") + return result.stdout + git("init", "-q", "--template=", ".") + git("fetch", "--depth=1", "--no-tags", "--no-recurse-submodules", + "https://github.com/ContextualWisdomLab/.github.git", source) + if git("cat-file", "-t", source).strip() != b"commit": + raise SystemExit("Trusted source is not a commit.") + git("checkout", "--detach", source) + if git("rev-parse", "HEAD").strip().decode() != source: + raise SystemExit("Trusted source checkout differs from workflow SHA.") + relative = "scripts/ci/materialize_public_review_source.py" + helper = workspace / relative + for node in (workspace / "scripts", workspace / "scripts/ci"): + if not stat.S_ISDIR(node.lstat().st_mode): + raise SystemExit("Trusted helper parent is unsafe.") + if not stat.S_ISREG(helper.lstat().st_mode): + raise SystemExit("Trusted helper is not a regular file.") + if hashlib.sha256(helper.read_bytes()).digest() != hashlib.sha256(git("show", source + ":" + relative)).digest(): + raise SystemExit("Trusted helper differs from pinned source.") + print("Materialized immutable trusted coverage contract without Git credentials.") + PY + + - name: Materialize public pull request merge tree without artifacts + if: needs.validate-pr-metadata.outputs.is_private == 'false' + env: + TARGET_REPOSITORY: ${{ needs.validate-pr-metadata.outputs.target_repository }} + PR_BASE_SHA: ${{ needs.validate-pr-metadata.outputs.base_sha }} + PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }} + IS_PRIVATE: ${{ needs.validate-pr-metadata.outputs.is_private }} + COVERAGE_SOURCE_WORKDIR: ${{ runner.temp }}/opencode-coverage-${{ github.run_id }}-${{ github.run_attempt }} run: | - echo "::error::Coverage source tree could not be materialized; see the coverage-source-tree job log for the exact target repository, base SHA, head SHA, and fetch or merge failure." - exit 1 + set -euo pipefail + python3 -I "$GITHUB_WORKSPACE/scripts/ci/materialize_public_review_source.py" \ + --repository "$TARGET_REPOSITORY" --base-sha "$PR_BASE_SHA" \ + --head-sha "$PR_HEAD_SHA" --is-private "$IS_PRIVATE" + test -d "$COVERAGE_SOURCE_WORKDIR/.git" - name: Download materialized pull request merge tree + if: needs.validate-pr-metadata.outputs.is_private == 'true' uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: opencode-coverage-source path: ${{ runner.temp }}/opencode-coverage-artifact - name: Prepare pull request merge tree for coverage measurement + if: needs.validate-pr-metadata.outputs.is_private == 'true' env: COVERAGE_SOURCE_ARCHIVE: ${{ runner.temp }}/opencode-coverage-artifact/opencode-coverage-source.tar - COVERAGE_SOURCE_WORKDIR: ${{ runner.temp }}/pr-head + COVERAGE_SOURCE_WORKDIR: ${{ runner.temp }}/opencode-coverage-${{ github.run_id }}-${{ github.run_attempt }} run: | set -euo pipefail - rm -rf "$COVERAGE_SOURCE_WORKDIR" - mkdir -p "$COVERAGE_SOURCE_WORKDIR" + # Each attempt owns a fresh tree; never delete another job's checkout. + mkdir "$COVERAGE_SOURCE_WORKDIR" # The archive contains pull-request-controlled paths. Validate every # member before extraction so a symlink, hardlink, device, FIFO, or # traversal path cannot redirect a later trusted host-side parser. @@ -489,7 +666,7 @@ jobs: env: PR_BASE_SHA: ${{ needs.validate-pr-metadata.outputs.base_sha }} PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }} - COVERAGE_SOURCE_WORKDIR: ${{ runner.temp }}/pr-head + COVERAGE_SOURCE_WORKDIR: ${{ runner.temp }}/opencode-coverage-${{ github.run_id }}-${{ github.run_attempt }} # Dependency resolution may consume wheels/packages, but PR-defined # install/build hooks are never executed implicitly. UV_NO_BUILD: "1" @@ -524,7 +701,7 @@ jobs: - name: Enforce changed-file syntax gate env: PR_BASE_SHA: ${{ needs.validate-pr-metadata.outputs.base_sha }} - COVERAGE_SOURCE_WORKDIR: ${{ runner.temp }}/pr-head + COVERAGE_SOURCE_WORKDIR: ${{ runner.temp }}/opencode-coverage-${{ github.run_id }}-${{ github.run_attempt }} run: | set -euo pipefail # Deterministic per-file syntax check on the PR's changed files. The @@ -551,12 +728,57 @@ jobs: exit 1 fi + - name: Reclaim stale coverage images + # One OpenCode job runs per runner at a time, so coverage images older + # than two hours belong to finished or abandoned runs. Runs dispatched + # before per-run cleanup existed left them behind and filled the + # self-hosted guest disk. Failure here never blocks measurement. + run: | + set -uo pipefail + reclaim() { + docker image ls --filter reference=opencode-coverage-tools --filter until=2h --format '{{.ID}}' | + sort -u | while read -r image; do docker image rm -f "$image" || return 1; done && + docker image prune -f && + docker builder prune -f --filter until=24h + } + if ! reclaim; then + echo "::warning::Could not reclaim stale coverage images; continuing." + fi + # The sandbox writes workspaces as root, so the runner's own temp + # cleanup cannot remove them; sweep ones older than two hours that + # a cancelled or pre-cleanup run left behind, never this run's. + if [ -d "${RUNNER_TEMP:-}" ]; then + # Only run-numbered directories; shared ones (artifact, source, + # sandbox-result, tool-build) are never swept. + if ! find "$RUNNER_TEMP" -mindepth 1 -maxdepth 1 -type d \ + \( -name 'opencode-coverage-[0-9]*-[0-9]*' -o -name 'opencode-coverage-tool-build-[0-9]*-[0-9]*' \) \ + -mmin +120 ! -name "opencode-coverage-${GITHUB_RUN_ID}-*" \ + ! -name "opencode-coverage-tool-build-${GITHUB_RUN_ID}-*" -print0 | + xargs -0 -r sudo rm -rf --; then + echo "::warning::Could not reclaim stale coverage workspaces; continuing." + fi + fi + + - name: Expose runner Rust toolchain + # Self-hosted runners keep rustup's cargo in ~/.cargo/bin, which the + # runner service does not put on PATH. Hosted images already have it. + run: | + set -euo pipefail + if command -v cargo >/dev/null 2>&1; then + exit 0 + fi + if [ -x "$HOME/.cargo/bin/cargo" ]; then + echo "$HOME/.cargo/bin" >>"$GITHUB_PATH" + exit 0 + fi + echo "::warning::cargo is not installed on this runner; Rust coverage will report a missing toolchain." + - name: Measure test and docstring evidence id: measure env: PR_BASE_SHA: ${{ needs.validate-pr-metadata.outputs.base_sha }} PR_HEAD_SHA: ${{ needs.validate-pr-metadata.outputs.head_sha }} - COVERAGE_SOURCE_WORKDIR: ${{ runner.temp }}/pr-head + COVERAGE_SOURCE_WORKDIR: ${{ runner.temp }}/opencode-coverage-${{ github.run_id }}-${{ github.run_attempt }} # Apply wheel-only resolution in the same step that consumes # pull-request dependency metadata. A value on an earlier step does # not cross the GitHub Actions step boundary. @@ -599,24 +821,52 @@ jobs: # PR-head source, credentials, lifecycle execution, or runner # command files. coverage_tool_image="opencode-coverage-tools:${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" - coverage_build_dir="${RUNNER_TEMP}/opencode-coverage-tool-build" + coverage_build_dir="${RUNNER_TEMP}/opencode-coverage-tool-build-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" trusted_ci_requirements="${GITHUB_WORKSPACE}/requirements-opencode-review-ci-hashes.txt" + trusted_noema_document_requirements="${GITHUB_WORKSPACE}/requirements-noema-document-ci-hashes.txt" trusted_base_python_installer="${GITHUB_WORKSPACE}/scripts/ci/install_base_python_locks.py" + trusted_vcs_import_root_resolver="${GITHUB_WORKSPACE}/scripts/ci/resolve_opencode_base_vcs_import_root.sh" if [ ! -f "$trusted_ci_requirements" ] || [ -L "$trusted_ci_requirements" ]; then echo "::error::Trusted coverage requirements must be a regular non-symlink file." exit 1 fi + if [ ! -f "$trusted_noema_document_requirements" ] || [ -L "$trusted_noema_document_requirements" ]; then + echo "::error::Trusted Noema document requirements must be a regular non-symlink file." + exit 1 + fi if [ ! -f "$trusted_base_python_installer" ] || [ -L "$trusted_base_python_installer" ]; then echo "::error::Trusted base Python lock installer must be a regular non-symlink file." exit 1 fi + if [ ! -f "$trusted_vcs_import_root_resolver" ] || [ -L "$trusted_vcs_import_root_resolver" ]; then + echo "::error::Trusted VCS import-root resolver must be a regular non-symlink file." + exit 1 + fi sudo rm -rf "$coverage_build_dir" mkdir -p "$coverage_build_dir" chmod 0700 "$coverage_build_dir" install -m 0644 "$trusted_ci_requirements" \ "$coverage_build_dir/requirements-opencode-review-ci-hashes.txt" + install -m 0644 "$trusted_noema_document_requirements" \ + "$coverage_build_dir/requirements-noema-document-ci-hashes.txt" install -m 0755 "$trusted_base_python_installer" \ "$coverage_build_dir/install-base-python-locks.py" + install -m 0755 "$trusted_vcs_import_root_resolver" \ + "$coverage_build_dir/resolve-opencode-base-vcs-import-root.sh" + trusted_cargo_fixture="${GITHUB_WORKSPACE}/tests/fixtures/coverage-cargo" + if [ -L "$trusted_cargo_fixture" ] || [ -L "$trusted_cargo_fixture/src" ]; then + echo "::error::Trusted Cargo coverage fixture directories must not be symlinks." + exit 1 + fi + for fixture_file in Cargo.toml Cargo.lock src/lib.rs; do + if [ ! -f "$trusted_cargo_fixture/$fixture_file" ] || + [ -L "$trusted_cargo_fixture/$fixture_file" ]; then + echo "::error::Trusted Cargo coverage fixture is missing or not a regular file." + exit 1 + fi + install -D -m 0644 "$trusted_cargo_fixture/$fixture_file" \ + "$coverage_build_dir/coverage-cargo-fixtures/$fixture_file" + done python_change_files="${RUNNER_TEMP}/opencode-python-change-files" if ! git -C "$COVERAGE_SOURCE_WORKDIR" diff \ --name-only --diff-filter=ACMRTUXBD -z "$PR_BASE_SHA" HEAD \ @@ -630,7 +880,7 @@ jobs: changed_basename="${changed_path##*/}" case "$changed_basename" in *.py|pyproject.toml|uv.lock|poetry.lock|Pipfile|Pipfile.lock|\ - setup.py|setup.cfg|tox.ini|pytest.ini|.pytest.ini|\ + setup.cfg|tox.ini|pytest.ini|.pytest.ini|\ requirements.lock|requirements*.txt|requirements*.in|\ environment.yml|environment.yaml) python_coverage_required=1 @@ -668,6 +918,38 @@ jobs: --base-sha "$PR_BASE_SHA" \ --head-sha "$PR_HEAD_SHA" \ --output-dir "$coverage_build_dir/base-javascript-packages" + # Vendors the base commit's Cargo dependency closure so `cargo llvm-cov` and any + # PyO3/maturin extension a Python test suite imports can build offline inside the + # `--network=none` sandbox below. Confirmed live on fast-mlsirm PRs #1868-#1892: with + # no vendored crates, `cargo llvm-cov` failed on `index.crates.io` DNS resolution and + # the generic Python coverage path failed at collection with `ImportError: cannot + # import name '_core'`, both surfacing as an indistinguishable "Coverage gate: failure" + # even when the pull request itself introduced no regression. + rust_lock_args=() + rust_change_files="${RUNNER_TEMP}/opencode-rust-change-files" + if ! git -C "$COVERAGE_SOURCE_WORKDIR" diff --no-renames --name-only -z \ + "$PR_BASE_SHA" "$PR_HEAD_SHA" >"$rust_change_files"; then + echo "::error::Could not classify exact-head Cargo changes." >&2 + exit 1 + fi + rust_lock_changed=0 + rust_manifest_changed=0 + while IFS= read -r -d '' changed_path; do + case "${changed_path##*/}" in + Cargo.lock) rust_lock_changed=1 ;; + Cargo.toml) rust_manifest_changed=1 ;; + esac + done <"$rust_change_files" + if [ "$rust_lock_changed" -eq 1 ] && [ "$rust_manifest_changed" -eq 0 ]; then + # The trusted materializer still rejects non-base pins and changed graphs. + rust_lock_args=(--head-sha "$PR_HEAD_SHA") + fi + python3 -I "$GITHUB_WORKSPACE/scripts/ci/materialize_base_rust_dependencies.py" \ + --repo-root "$COVERAGE_SOURCE_WORKDIR" \ + --base-sha "$PR_BASE_SHA" \ + --output-dir "$coverage_build_dir/base-rust-dependencies" \ + "${rust_lock_args[@]}" \ + --vendor-dir-for-config /opt/base-rust-dependencies/vendor cat >"$coverage_build_dir/Dockerfile" <<'DOCKERFILE' FROM docker.io/library/python:3.14-slim@sha256:b877e50bd90de10af8d82c57a022fc2e0dc731c5320d762a27986facfc3355c1 ENV DEBIAN_FRONTEND=noninteractive @@ -693,6 +975,11 @@ jobs: vulkan-tools \ xz-utils \ && rm -rf /var/lib/apt/lists/* + COPY coverage-cargo-fixtures /tmp/coverage-cargo-fixtures + RUN CARGO_HOME=/opt/coverage-cargo-home cargo fetch --locked \ + --manifest-path /tmp/coverage-cargo-fixtures/Cargo.toml \ + && rm -rf /tmp/coverage-cargo-fixtures \ + && chmod -R a+rX /opt/coverage-cargo-home ENV LLVM_COV=/usr/bin/llvm-cov-19 ENV LLVM_PROFDATA=/usr/bin/llvm-profdata-19 ENV COREPACK_HOME=/opt/corepack @@ -712,6 +999,27 @@ jobs: && tar -xzf /tmp/cargo-llvm-cov.tar.gz -C /usr/local/bin cargo-llvm-cov \ && chmod 0755 /usr/local/bin/cargo-llvm-cov \ && rm -f /tmp/cargo-llvm-cov.tar.gz + ARG OPENCODE_RUST_TOOLCHAIN= + RUN set -eu; \ + [ -n "$OPENCODE_RUST_TOOLCHAIN" ] || exit 0; \ + printf '%s\n' "$OPENCODE_RUST_TOOLCHAIN" | grep -Eqx '1\.[0-9]{1,3}\.[0-9]{1,3}'; \ + curl --proto '=https' --tlsv1.2 -fsSLo /tmp/rustup-init \ + https://static.rust-lang.org/rustup/archive/1.29.1/x86_64-unknown-linux-gnu/rustup-init; \ + echo 'dda7234360b7f578ca8b0ddcb80145646fa61a67c1720a5abc7051b35c9fcb71 /tmp/rustup-init' | sha256sum -c -; \ + chmod 0755 /tmp/rustup-init; \ + RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/rustup/cargo /tmp/rustup-init -y --no-modify-path \ + --profile minimal --default-toolchain "$OPENCODE_RUST_TOOLCHAIN" --component llvm-tools-preview; \ + rm -f /tmp/rustup-init; \ + toolchain="/usr/local/rustup/toolchains/${OPENCODE_RUST_TOOLCHAIN}-x86_64-unknown-linux-gnu"; \ + ln -s "$toolchain/bin/cargo" /usr/local/bin/cargo; \ + ln -s "$toolchain/bin/rustc" /usr/local/bin/rustc; \ + mkdir -p /usr/local/libexec/opencode-rust; \ + ln -s "$toolchain/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-cov" /usr/local/libexec/opencode-rust/llvm-cov; \ + ln -s "$toolchain/lib/rustlib/x86_64-unknown-linux-gnu/bin/llvm-profdata" /usr/local/libexec/opencode-rust/llvm-profdata; \ + chmod -R a+rX /usr/local/rustup; \ + test "$(/usr/local/bin/rustc --version | cut -d ' ' -f 2)" = "$OPENCODE_RUST_TOOLCHAIN"; \ + /usr/local/libexec/opencode-rust/llvm-cov --version >/dev/null; \ + /usr/local/libexec/opencode-rust/llvm-profdata --version >/dev/null COPY base-javascript-packages /tmp/base-javascript-packages RUN set -eu; \ mkdir -p /opt/corepack /opt/javascript-package-locks /opt/npm-cache /opt/pnpm-store; \ @@ -757,15 +1065,16 @@ jobs: corepack npm cache verify --cache /opt/npm-cache; \ chmod -R a+rX /opt/corepack /opt/npm-cache /opt/pnpm-store; \ rm -rf /tmp/base-javascript-packages - COPY requirements-opencode-review-ci-hashes.txt /tmp/requirements-opencode-review-ci-hashes.txt + COPY requirements-opencode-review-ci-hashes.txt requirements-noema-document-ci-hashes.txt /tmp/ RUN python3 -m pip install \ --break-system-packages \ --disable-pip-version-check \ --require-hashes \ --only-binary=:all: \ - -r /tmp/requirements-opencode-review-ci-hashes.txt \ - && rm -f /tmp/requirements-opencode-review-ci-hashes.txt + -r /tmp/requirements-opencode-review-ci-hashes.txt -r /tmp/requirements-noema-document-ci-hashes.txt \ + && rm -f /tmp/requirements-opencode-review-ci-hashes.txt /tmp/requirements-noema-document-ci-hashes.txt COPY base-python-requirements /tmp/base-python-requirements + COPY resolve-opencode-base-vcs-import-root.sh /usr/local/libexec/resolve-opencode-base-vcs-import-root.sh RUN set -eu; \ mkdir -p /opt/base-vcs-dependencies; \ site_packages="$(python3 -c 'import site; print(site.getsitepackages()[0])')"; \ @@ -773,6 +1082,8 @@ jobs: : >"$path_file"; \ dependency_index=0; \ dependency_list=/tmp/base-vcs-dependencies.tsv; \ + resolver=/usr/local/libexec/resolve-opencode-base-vcs-import-root.sh; \ + test -x "$resolver"; \ jq -r '.[] | [.import_name, .repository, .commit] | @tsv' \ /tmp/base-python-requirements/vcs-manifest.json >"$dependency_list"; \ while IFS="$(printf '\t')" read -r import_name repository commit; do \ @@ -786,81 +1097,73 @@ jobs: git -C "$destination" checkout --quiet --detach FETCH_HEAD; \ test "$(git -C "$destination" rev-parse HEAD)" = "$commit"; \ rm -rf -- "$destination/.git"; \ - import_root=''; \ - python_root=''; \ - candidate_count=0; \ - for candidate in \ - "$destination/src/$import_name" \ - "$destination/src/$import_name.py" \ - "$destination/$import_name" \ - "$destination/$import_name.py"; do \ - if [ -e "$candidate" ] || [ -L "$candidate" ]; then \ - import_root="$candidate"; \ - candidate_count=$((candidate_count + 1)); \ - fi; \ - done; \ - if [ "$candidate_count" -ne 1 ]; then \ - printf 'locked VCS source %s has a missing or ambiguous import root for %s\n' \ - "$repository" "$import_name" >&2; \ - exit 1; \ - fi; \ - if [ -L "$import_root" ] \ - || { [ -d "$import_root" ] \ - && { [ ! -f "$import_root/__init__.py" ] \ - || [ -L "$import_root/__init__.py" ]; }; }; then \ - printf 'locked VCS source %s has a namespace or linked import root for %s\n' \ - "$repository" "$import_name" >&2; \ - exit 1; \ - fi; \ - if find "$destination" -type l -print -quit | grep -q .; then \ - printf 'locked VCS source %s contains a symbolic-link layout\n' \ - "$repository" >&2; \ - exit 1; \ - fi; \ - if find "$destination" -type f \ - \( -name '*.so' -o -name '*.pyd' -o -name '*.dll' -o -name '*.dylib' \) \ - -print -quit | grep -q .; then \ - printf 'locked VCS source %s contains a compiled extension\n' \ - "$repository" >&2; \ - exit 1; \ - fi; \ - if find "$destination" -type d \ - \( -name '*.dist-info' -o -name '*.egg-info' \) \ - -print -quit | grep -q .; then \ - printf 'locked VCS source %s contains installed distribution metadata\n' \ - "$repository" >&2; \ - exit 1; \ - fi; \ - case "$import_root" in \ - "$destination/src/"*) python_root="$destination/src" ;; \ - *) python_root="$destination" ;; \ - esac; \ + python_root="$("$resolver" "$destination" "$import_name" "$repository")"; \ printf '%s\n' "$python_root" >>"$path_file"; \ dependency_index=$((dependency_index + 1)); \ done <"$dependency_list"; \ - rm -f -- "$dependency_list"; \ + rm -f -- "$dependency_list" "$resolver"; \ chmod -R a+rX /opt/base-vcs-dependencies "$path_file" COPY install-base-python-locks.py /usr/local/libexec/install-base-python-locks.py RUN python3 -I /usr/local/libexec/install-base-python-locks.py \ --requirements-root /tmp/base-python-requirements \ && rm -rf /tmp/base-python-requirements \ && rm -f /usr/local/libexec/install-base-python-locks.py + COPY base-rust-dependencies /opt/base-rust-dependencies + # Crate archives keep modes such as 0640 through COPY, so make the base + # Rust dependency tree readable by the unprivileged coverage sandbox UID. + RUN chmod -R a+rX /opt/base-rust-dependencies DOCKERFILE - if ! docker build --pull --no-cache --network=default \ - --tag "$coverage_tool_image" \ - --file "$coverage_build_dir/Dockerfile" \ - "$coverage_build_dir"; then + # Debian's rustc lags the MSRV of pinned repositories (fast-mlsirm pins + # 1.97.1; Debian ships 1.85), so a base-pinned repository gets that exact + # release installed at build time. Unpinned repositories keep Debian's. + base_rust_toolchain="$(python3 -I "$GITHUB_WORKSPACE/scripts/ci/resolve_base_rust_toolchain.py" \ + --repo-root "$COVERAGE_SOURCE_WORKDIR" --base-sha "$PR_BASE_SHA")" + build_coverage_tool_image() { + docker build --pull --no-cache --network=default \ + --build-arg "OPENCODE_RUST_TOOLCHAIN=$1" \ + --tag "$coverage_tool_image" \ + --file "$coverage_build_dir/Dockerfile" \ + "$coverage_build_dir" + } + coverage_llvm_cov=/usr/bin/llvm-cov-19 + coverage_llvm_profdata=/usr/bin/llvm-profdata-19 + if [ -n "$base_rust_toolchain" ] && build_coverage_tool_image "$base_rust_toolchain"; then + coverage_llvm_cov=/usr/local/libexec/opencode-rust/llvm-cov + coverage_llvm_profdata=/usr/local/libexec/opencode-rust/llvm-profdata + elif [ -n "$base_rust_toolchain" ]; then + # Rollback: a failed toolchain layer must not cost the repository its + # previous Debian-toolchain image. + echo "::warning::Rust ${base_rust_toolchain} layer failed; rebuilding with the Debian toolchain." + if ! build_coverage_tool_image ""; then + echo "::error::Trusted coverage tool image build failed before PR execution." + exit 1 + fi + elif ! build_coverage_tool_image ""; then echo "::error::Trusted coverage tool image build failed before PR execution." exit 1 fi sudo rm -rf "$coverage_build_dir" + # Use the daemon's capacity, not the runner host's CPU count. + # Keep four CPUs as an upper limit without rejecting smaller guests. + if ! coverage_daemon_cpus="$(docker info --format '{{.NCPU}}')"; then + echo '::error::Coverage Docker daemon CPU capacity is unavailable.' + exit 1 + fi + if [[ ! "$coverage_daemon_cpus" =~ ^[1-9][0-9]*$ ]]; then + echo '::error::Coverage Docker daemon CPU capacity is invalid.' + exit 1 + fi + case "$coverage_daemon_cpus" in + 1|2|3) coverage_sandbox_cpus="$coverage_daemon_cpus" ;; + *) coverage_sandbox_cpus=4 ;; + esac sandbox_status=0 docker run --rm --init --network=none \ --name "opencode-coverage-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" \ --pids-limit 2048 \ --memory 14g \ - --cpus 4 \ + --cpus "$coverage_sandbox_cpus" \ --security-opt no-new-privileges:true \ --cap-drop ALL \ --cap-add CHOWN \ @@ -887,8 +1190,8 @@ jobs: --env RUNNER_TEMP=/secure-output \ --env GITHUB_OUTPUT=/secure-output/github-output \ --env GITHUB_STEP_SUMMARY=/secure-output/step-summary \ - --env LLVM_COV=/usr/bin/llvm-cov-19 \ - --env LLVM_PROFDATA=/usr/bin/llvm-profdata-19 \ + --env LLVM_COV="$coverage_llvm_cov" \ + --env LLVM_PROFDATA="$coverage_llvm_profdata" \ "$coverage_tool_image" \ /bin/bash /trusted-measure-step.sh || sandbox_status=$? @@ -927,8 +1230,23 @@ jobs: chown -R root:root /work/.git chmod -R go-w /work/.git fi + rm -rf -- /work/.opencode-sandbox-home mkdir -p "$RUNNER_TEMP" /work/.opencode-sandbox-home /work/.opencode-sandbox-cache chown "$OPENCODE_SANDBOX_UID:$OPENCODE_SANDBOX_GID" /work/.opencode-sandbox-home /work/.opencode-sandbox-cache + # `run_and_capture`/`run_and_capture_advisory` below pin CARGO_HOME to + # /work/.opencode-sandbox-home/.cargo, which lives on the mutable /work bind mount, not + # the read-only image -- so the baked offline vendor config from + # /opt/base-rust-dependencies (see materialize_base_rust_dependencies.py) has to be + # copied there explicitly rather than set as an image ENV default. + mkdir -p /work/.opencode-sandbox-home/.cargo + cp -a /opt/coverage-cargo-home/. /work/.opencode-sandbox-home/.cargo/ + if [ -s /opt/base-rust-dependencies/cargo-config.toml ]; then + install -m 0644 /opt/base-rust-dependencies/cargo-config.toml \ + /work/.opencode-sandbox-home/.cargo/config.toml + fi + printf '\n[net]\noffline = true\n' >>/work/.opencode-sandbox-home/.cargo/config.toml + chmod 0444 /work/.opencode-sandbox-home/.cargo/config.toml + chown -R "$OPENCODE_SANDBOX_UID:$OPENCODE_SANDBOX_GID" /work/.opencode-sandbox-home/.cargo chmod 0700 "$RUNNER_TEMP" : >"$GITHUB_OUTPUT" chmod 0600 "$GITHUB_OUTPUT" @@ -939,6 +1257,8 @@ jobs: summary_file="${RUNNER_TEMP}/coverage-evidence.md" summary_output_file="${RUNNER_TEMP}/coverage-evidence-output.md" failures=0 + not_measured=0 + coverage_timeout_not_measured=0 r_peer_check_required=0 append() { @@ -946,9 +1266,11 @@ jobs: } append_command() { - printf '$ ' >>"$summary_file" - printf '%q ' "$@" >>"$summary_file" - printf '\n' >>"$summary_file" + { + printf '$ ' + printf '%q ' "$@" + printf '\n' + } >>"$summary_file" } emit_captured_log() { @@ -967,6 +1289,22 @@ jobs: tail -n 180 "$log_file" >>"$summary_file" } + # fast-mlsirm's Rust suite runs for tens of minutes under llvm-cov, past + # the 900 s per-command cap. Only for Rust coverage, a timeout (exit 124) + # is disclosed as not measured instead of failing the gate; test + # failures, kills (137) and every other command still fail. + record_command_result() { + if [ "$1" -eq 124 ] && [ "$coverage_timeout_not_measured" = 1 ]; then + append "- Result: NOT MEASURED (timed out after 900 s; disclosed to the reviewer, not counted as a failure)" + not_measured=$((not_measured + 1)) + elif [ "$1" -ne 0 ]; then + append "- Result: FAIL (exit ${1})" + failures=$((failures + 1)) + else + append "- Result: PASS" + fi + } + run_and_capture() { local label="$1" shift @@ -1008,12 +1346,7 @@ jobs: emit_captured_log "$log_file" append '```' append "" - if [ "$rc" -ne 0 ]; then - append "- Result: FAIL (exit ${rc})" - failures=$((failures + 1)) - else - append "- Result: PASS" - fi + record_command_result "$rc" append "" rm -f "$log_file" } @@ -1207,10 +1540,38 @@ jobs: --workflow-dir "$workflow_dir" } + project_is_maturin_project() { + local pyproject="${1}/pyproject.toml" + [ -f "$pyproject" ] || return 1 + grep -Eq '^\s*build-backend\s*=\s*"maturin' "$pyproject" + } + + # Builds the PyO3/maturin extension module (e.g. `fast_mlsirm._core`) fully offline + # before pytest runs, using the Cargo vendor config baked in by + # materialize_base_rust_dependencies.py (see the /work/.opencode-sandbox-home/.cargo + # setup above). Without this, coverage collection fails with `ImportError: cannot + # import name '_core'` because nothing else in the sandbox ever builds the compiled + # extension. CARGO_BUILD_JOBS=1 keeps the offline build within the sandbox's memory + # budget. + build_maturin_extension_if_needed() { + local project_dir="$1" + project_is_maturin_project "$project_dir" || return 0 + run_and_capture "Offline PyO3/maturin extension build (${project_dir})" \ + env CARGO_NET_OFFLINE=true CARGO_BUILD_JOBS=1 \ + bash -c "set -eu + cd \"\$1\" + dist_dir=\"\$(mktemp -d)\" + python3 -m maturin build --offline --release -o \"\$dist_dir\" + python3 -m pip install --user --no-index --no-deps --force-reinstall \"\$dist_dir\"/*.whl + python3 \"\$2\" \"\$dist_dir\"/*.whl . + rm -rf \"\$dist_dir\"" bash "$project_dir" "${GITHUB_WORKSPACE}/scripts/ci/place_maturin_extension.py" + } + run_python_test_coverage() { local measured_projects=0 while IFS= read -r project_dir; do measured_projects=1 + build_maturin_extension_if_needed "$project_dir" configured_commands_json="$(configured_python_ci_test_commands "$project_dir")" if [ -n "$configured_commands_json" ]; then while IFS= read -r configured_command_json; do @@ -1222,14 +1583,14 @@ jobs: done <<<"$configured_commands_json" else run_and_capture "Python coverage with missing-line report (${project_dir})" \ - bash -c 'cd "$1" && PYTHONPATH="$([ -d src ] && printf src:. || printf .)" python3 -m coverage run -m pytest tests && python3 -m coverage report --show-missing' bash "$project_dir" + bash -c "cd \"\$1\" && PYTHONPATH=\"\$([ -d src ] && printf src:. || printf .)\" python3 -m coverage run -m pytest tests && python3 -m coverage report --show-missing" bash "$project_dir" fi done < <(tracked_python_projects_with_tests) if [ "$measured_projects" -eq 0 ]; then if has_tracked_files '*.py'; then run_and_capture "Python coverage with missing-line report" \ - bash -c 'PYTHONPATH="$([ -d src ] && printf src:. || printf .)" python3 -m coverage run -m pytest && python3 -m coverage report --show-missing' + bash -c "PYTHONPATH=\"\$([ -d src ] && printf src:. || printf .)\" python3 -m coverage run -m pytest && python3 -m coverage report --show-missing" elif python3 -I -c 'import pytest_cov' >/dev/null 2>&1; then run_and_capture "Python pytest-cov coverage" python3 -m pytest --cov=. --cov-report=term-missing else @@ -1380,7 +1741,7 @@ jobs: if [ -f "${project_dir}/tests/test_docstrings.py" ]; then measured_projects=1 run_and_capture "Python docstring coverage (${project_dir})" \ - bash -c 'cd "$1" && PYTHONPATH="$([ -d src ] && printf src:. || printf .)" python3 -m pytest tests/test_docstrings.py' bash "$project_dir" + bash -c "cd \"\$1\" && PYTHONPATH=\"\$([ -d src ] && printf src:. || printf .)\" python3 -m pytest tests/test_docstrings.py" bash "$project_dir" fi done < <(tracked_python_projects_with_tests) [ "$measured_projects" -eq 1 ] @@ -1748,21 +2109,21 @@ jobs: if [ -n "$package_name" ] && jq -e '.workspaces // empty' package.json >/dev/null 2>&1; then run_and_capture "Tauri frontendDist build (${package_dir})" corepack npm run build --workspace "$package_name" else - run_and_capture "Tauri frontendDist build (${package_dir})" bash -c 'cd "$1" && corepack npm run build' bash "$package_dir" + run_and_capture "Tauri frontendDist build (${package_dir})" bash -c "cd \"\$1\" && corepack npm run build" bash "$package_dir" fi ;; pnpm) if [ -n "$package_name" ] && [ -f pnpm-workspace.yaml ]; then run_and_capture "Tauri frontendDist build (${package_dir})" corepack pnpm --filter "$package_name" run build else - run_and_capture "Tauri frontendDist build (${package_dir})" bash -c 'cd "$1" && corepack pnpm run build' bash "$package_dir" + run_and_capture "Tauri frontendDist build (${package_dir})" bash -c "cd \"\$1\" && corepack pnpm run build" bash "$package_dir" fi ;; yarn) if [ -n "$package_name" ] && jq -e '.workspaces // empty' package.json >/dev/null 2>&1; then run_and_capture "Tauri frontendDist build (${package_dir})" yarn workspace "$package_name" build else - run_and_capture "Tauri frontendDist build (${package_dir})" bash -c 'cd "$1" && yarn build' bash "$package_dir" + run_and_capture "Tauri frontendDist build (${package_dir})" bash -c "cd \"\$1\" && yarn build" bash "$package_dir" fi ;; esac @@ -1878,8 +2239,13 @@ jobs: # coverage command still runs and reports any uncovered GPU lines # exactly as before, so Rust repositories without GPU code are # unaffected and no gate is weakened. - if ls /usr/share/vulkan/icd.d/lvp_icd*.json >/dev/null 2>&1; then - lvp_icd="$(ls /usr/share/vulkan/icd.d/lvp_icd*.json | head -n1)" + local lvp_icd="" candidate_icd + for candidate_icd in /usr/share/vulkan/icd.d/lvp_icd*.json; do + [ -f "$candidate_icd" ] || continue + lvp_icd="$candidate_icd" + break + done + if [ -n "$lvp_icd" ]; then export VK_ICD_FILENAMES="$lvp_icd" export VK_DRIVER_FILES="$lvp_icd" export WGPU_BACKEND=vulkan @@ -1925,13 +2291,17 @@ jobs: } ensure_rust_toolchain() { - if [ "${LLVM_COV:-}" != "/usr/bin/llvm-cov-19" ] || \ - [ "${LLVM_PROFDATA:-}" != "/usr/bin/llvm-profdata-19" ] || \ + case "${LLVM_COV:-}:${LLVM_PROFDATA:-}" in + /usr/bin/llvm-cov-19:/usr/bin/llvm-profdata-19) llvm_pair_reviewed=1 ;; + /usr/local/libexec/opencode-rust/llvm-cov:/usr/local/libexec/opencode-rust/llvm-profdata) llvm_pair_reviewed=1 ;; + *) llvm_pair_reviewed=0 ;; + esac + if [ "$llvm_pair_reviewed" != 1 ] || \ ! test -x "$LLVM_COV" || ! test -x "$LLVM_PROFDATA"; then append "### Rust coverage toolchain" append "" append "- Result: FAIL" - append "- Reason: the networkless coverage runtime did not preserve the reviewed LLVM 19 tool paths." + append "- Reason: the networkless coverage runtime did not preserve the reviewed LLVM tool paths (Debian LLVM 19 or the base-pinned Rust toolchain)." append "- Fix: rebuild the trusted coverage image and preserve the exact LLVM bindings at the Docker boundary." append "" failures=$((failures + 1)) @@ -2009,6 +2379,7 @@ jobs: else manifests="$(rust_coverage_manifests)" if [ -n "$manifests" ]; then + coverage_timeout_not_measured=1 while IFS= read -r manifest; do local threshold if ! threshold="$(rust_coverage_fail_under_lines "$manifest")"; then @@ -2041,6 +2412,7 @@ jobs: cargo llvm-cov --manifest-path "$manifest" --all-features --fail-under-lines "$threshold" --show-missing-lines fi done <<<"$manifests" + coverage_timeout_not_measured=0 else append "### Rust test coverage" append "" @@ -2223,6 +2595,9 @@ jobs: append "## Coverage Decision" append "" + if [ "$not_measured" -ne 0 ]; then + append "- Not measured: ${not_measured} Rust coverage command(s) exceeded the 900 s per-command cap; line coverage for that code is unproven, not failed." + fi if [ "$failures" -eq 0 ]; then append "- Result: PASS" if [ "$measured_any" -eq 0 ]; then @@ -2282,6 +2657,21 @@ jobs: exit 1 fi + - name: Clean up coverage runner resources + if: always() + env: + COVERAGE_SOURCE_WORKDIR: ${{ runner.temp }}/opencode-coverage-${{ github.run_id }}-${{ github.run_attempt }} + COVERAGE_BUILD_DIR: ${{ runner.temp }}/opencode-coverage-tool-build-${{ github.run_id }}-${{ github.run_attempt }} + run: | + set -euo pipefail + # The sandbox writes as uid 65532; the runner cannot remove its files. + sudo rm -rf -- "$COVERAGE_SOURCE_WORKDIR" + sudo rm -rf -- "$COVERAGE_BUILD_DIR" + coverage_tool_image="opencode-coverage-tools:${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" + if docker image inspect "$coverage_tool_image" >/dev/null 2>&1; then + docker image rm "$coverage_tool_image" + fi + opencode-review-target: name: opencode-review needs: [validate-pr-metadata, coverage-evidence] @@ -2296,7 +2686,9 @@ jobs: needs.validate-pr-metadata.outputs.target_repository }}-${{ needs.validate-pr-metadata.outputs.pr_number || github.run_id }} cancel-in-progress: true - runs-on: ubuntu-24.04 + runs-on: + group: CWL central OpenCode + labels: [self-hosted, linux, x64] # Coverage and current-head evidence are prepared before the model pool. # A single legitimate review may need a full hour. The enclosing job must # contain the 12-minute evidence step, 205-minute provider-pool step, the @@ -2413,14 +2805,45 @@ jobs: printf 'Validated exact-head OpenCode review source for %s#%s (%s).\n' \ "$GH_REPOSITORY" "$PR_NUMBER" "$head_repository" + - name: Set up lock-compatible sidecar Python + id: sidecar_python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + update-environment: false + + - name: Admit existing personal LiteLLM auto route + id: personal_route + env: + LLM_GATEWAY_API_KEY: ${{ secrets.LLM_GATEWAY_API_KEY }} + PERSONAL_REVIEW_ZERO_COST_ATTESTED: ${{ vars.PERSONAL_REVIEW_ZERO_COST_ATTESTED || 'false' }} + PERSONAL_REVIEW_PUBLIC_AUTO_AUTHORIZED: ${{ vars.PERSONAL_REVIEW_PUBLIC_AUTO_AUTHORIZED || 'false' }} + PERSONAL_REVIEW_ZDR_ATTESTED: ${{ vars.PERSONAL_REVIEW_ZDR_ATTESTED || 'false' }} + PRIVATE_TARGET: ${{ needs.validate-pr-metadata.outputs.is_private }} + run: | + set -euo pipefail + if [ -z "${LLM_GATEWAY_API_KEY:-}" ]; then + echo '::error::STOP: existing personal review Secret is unavailable.' + exit 1 + fi + python3 -m scripts.ci.personal_review_route \ + --zero-cost "$PERSONAL_REVIEW_ZERO_COST_ATTESTED" \ + --public-auto-authorized "$PERSONAL_REVIEW_PUBLIC_AUTO_AUTHORIZED" \ + --zero-retention "$PERSONAL_REVIEW_ZDR_ATTESTED" \ + --private-target "$PRIVATE_TARGET" + echo 'selected=true' >>"$GITHUB_OUTPUT" + - name: Provision contextual-orchestrator review sidecar + if: steps.personal_route.outputs.selected != 'true' env: + SIDECAR_PYTHON: ${{ steps.sidecar_python.outputs.python-path }} BYTEZ_API_KEY: ${{ secrets.BYTEZ_API_KEY }} NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: ${{ needs.validate-pr-metadata.outputs.is_private }} + CONTEXTUAL_ORCHESTRATOR_POOL: auto run: | set -euo pipefail bash "$GITHUB_WORKSPACE/scripts/ci/contextual_orchestrator_review_sidecar.sh" @@ -2529,6 +2952,9 @@ jobs: git cat-file -e "${PR_BASE_SHA}^{commit}" git cat-file -e "${PR_HEAD_SHA}^{commit}" rm -rf "$OPENCODE_SOURCE_WORKDIR" + # This checkout outlives jobs on self-hosted runners; drop registrations + # whose directories runner temp cleanup already removed. + git worktree prune git worktree add --detach "$OPENCODE_SOURCE_WORKDIR" "$PR_HEAD_SHA" git -C "$OPENCODE_SOURCE_WORKDIR" status --short @@ -3049,13 +3475,13 @@ jobs: language_signal="Match changed prose" fi - printf -- '- Preferred review language: `%s`\n' "$language_signal" + printf -- "- Preferred review language: \`%s\`\\n" "$language_signal" printf -- '- Rule: write human-readable review prose in the preferred language; keep file paths, identifiers, logs, quoted source, error text, and protocol literals unchanged.\n' - printf -- '- PR title: `%s`\n' "$(printf '%s' "$title" | tr '\r\n`' ' ' | cut -c 1-240)" + printf -- "- PR title: \`%s\`\\n" "$(printf '%s' "$title" | tr '\r\n`' ' ' | cut -c 1-240)" if [ -n "$body" ]; then - printf -- '- PR body excerpt: `%s`\n' "$(printf '%s' "$body" | tr '\r\n`' ' ' | cut -c 1-360)" + printf -- "- PR body excerpt: \`%s\`\\n" "$(printf '%s' "$body" | tr '\r\n`' ' ' | cut -c 1-360)" else - printf -- '- PR body excerpt: `[empty]`\n' + printf -- "- PR body excerpt: \`[empty]\`\\n" fi } @@ -3334,7 +3760,7 @@ jobs: shift if ! git -C "$OPENCODE_SOURCE_WORKDIR" diff "$@"; then - printf 'Unable to collect %s from `%s` to `%s`; continue review from available changed-file evidence and direct file inspection.\n' "$description" "$PR_MERGE_BASE" "$PR_HEAD_SHA" + printf "Unable to collect %s from \`%s\` to \`%s\`; continue review from available changed-file evidence and direct file inspection.\\n" "$description" "$PR_MERGE_BASE" "$PR_HEAD_SHA" fi } @@ -3344,13 +3770,13 @@ jobs: printf -- "- Base SHA: \`%s\`\n" "$PR_BASE_SHA" printf -- "- Head SHA: \`%s\`\n\n" "$PR_HEAD_SHA" if ! PR_MERGE_BASE="$(git -C "$OPENCODE_SOURCE_WORKDIR" merge-base "$PR_BASE_SHA" "$PR_HEAD_SHA")"; then - printf 'Merge-base discovery failed for `%s` and `%s`; falling back to base SHA for bounded diff evidence.\n\n' "$PR_BASE_SHA" "$PR_HEAD_SHA" + printf "Merge-base discovery failed for \`%s\` and \`%s\`; falling back to base SHA for bounded diff evidence.\\n\\n" "$PR_BASE_SHA" "$PR_HEAD_SHA" PR_MERGE_BASE="$PR_BASE_SHA" fi printf -- "- Merge base SHA: \`%s\`\n\n" "$PR_MERGE_BASE" printf '## Current-head authority order\n\n' printf 'Treat current-head sections in this file as authoritative for this run: Other unresolved review thread evidence, Failed GitHub Check evidence, Coverage execution evidence, Changed files, and Focused changed hunks.\n' - printf 'All PR reviews and comments evidence is historical context only and may contain stale bot conclusions. Do not infer active failed checks, unresolved threads, or missing changed files from those comments unless current-head evidence corroborates the same claim for Head SHA `%s`.\n\n' "$PR_HEAD_SHA" + printf "All PR reviews and comments evidence is historical context only and may contain stale bot conclusions. Do not infer active failed checks, unresolved threads, or missing changed files from those comments unless current-head evidence corroborates the same claim for Head SHA \`%s\`.\\n\\n" "$PR_HEAD_SHA" if ! git -C "$OPENCODE_SOURCE_WORKDIR" diff --name-only --find-renames "$PR_MERGE_BASE" "$PR_HEAD_SHA" | awk 'NF > 0 && $0 !~ /^\// && $0 !~ /(^|\/)\.\.($|\/)/ { print }' >"$OPENCODE_CHANGED_FILES_FILE"; then printf 'Changed-file discovery failed; downstream review must inspect the PR head directly.\n\n' @@ -3506,6 +3932,11 @@ jobs: - name: Prepare isolated OpenCode review workspace env: + PERSONAL_ROUTE_SELECTED: ${{ steps.personal_route.outputs.selected }} + PERSONAL_REVIEW_ZERO_COST_ATTESTED: ${{ vars.PERSONAL_REVIEW_ZERO_COST_ATTESTED || 'false' }} + PERSONAL_REVIEW_PUBLIC_AUTO_AUTHORIZED: ${{ vars.PERSONAL_REVIEW_PUBLIC_AUTO_AUTHORIZED || 'false' }} + PERSONAL_REVIEW_ZDR_ATTESTED: ${{ vars.PERSONAL_REVIEW_ZDR_ATTESTED || 'false' }} + PRIVATE_TARGET: ${{ needs.validate-pr-metadata.outputs.is_private }} OPENCODE_REVIEW_WORKDIR: ${{ runner.temp }}/opencode-review-project OPENCODE_EVIDENCE_FILE: ${{ runner.temp }}/opencode-review-evidence.md OPENCODE_FAILED_CHECK_EVIDENCE_FILE: ${{ runner.temp }}/opencode-failed-check-evidence.md @@ -3855,8 +4286,8 @@ jobs: jq -n '{ "$schema": "https://opencode.ai/config.json", - "model": "contextual-orchestrator/orchestrator/free", - "small_model": "contextual-orchestrator/orchestrator/free", + "model": "contextual-orchestrator/orchestrator/auto", + "small_model": "contextual-orchestrator/orchestrator/auto", "enabled_providers": ["contextual-orchestrator"], "lsp": false, "mcp": {}, @@ -3938,20 +4369,20 @@ jobs: gateway_config="${OPENCODE_REVIEW_WORKDIR}/opencode.gateway.jsonc" jq ' - .model = "contextual-orchestrator/orchestrator/free" | - .small_model = "contextual-orchestrator/orchestrator/free" | + .model = "contextual-orchestrator/orchestrator/auto" | + .small_model = "contextual-orchestrator/orchestrator/auto" | .enabled_providers = ["contextual-orchestrator"] | .provider = { "contextual-orchestrator": { "npm": "@ai-sdk/openai-compatible", "name": "Contextual Orchestrator", "options": { - "baseURL": "{env:CONTEXTUAL_ORCHESTRATOR_BASE_URL}", + "baseURL": "{env:CONTEXTUAL_ORCHESTRATOR_BASE_URL}/v1", "apiKey": "{env:CONTEXTUAL_ORCHESTRATOR_TOKEN}" }, "models": { - "orchestrator/free": { - "name": "Orchestrator Free (ZDR-first zero-cost pool)", + "orchestrator/auto": { + "name": "Orchestrator Auto (cost-fenced dynamic selection)", "tool_call": true, "reasoning": true, "options": { @@ -3973,11 +4404,19 @@ jobs: ' "${OPENCODE_REVIEW_WORKDIR}/opencode.jsonc" >"$gateway_config" mv -- "$gateway_config" "${OPENCODE_REVIEW_WORKDIR}/opencode.jsonc" - if ! grep -Fq 'contextual-orchestrator/orchestrator/free' "${OPENCODE_REVIEW_WORKDIR}/opencode.jsonc" \ + if ! grep -Fq 'contextual-orchestrator/orchestrator/auto' "${OPENCODE_REVIEW_WORKDIR}/opencode.jsonc" \ || ! grep -Fq 'CONTEXTUAL_ORCHESTRATOR_BASE_URL' "${OPENCODE_REVIEW_WORKDIR}/opencode.jsonc"; then echo '::error::Generated isolated opencode.jsonc is missing the contextual-orchestrator gateway route.' exit 1 fi + if [ "${PERSONAL_ROUTE_SELECTED:-}" = 'true' ]; then + python3 -m scripts.ci.personal_review_route \ + --zero-cost "$PERSONAL_REVIEW_ZERO_COST_ATTESTED" \ + --public-auto-authorized "$PERSONAL_REVIEW_PUBLIC_AUTO_AUTHORIZED" \ + --zero-retention "$PERSONAL_REVIEW_ZDR_ATTESTED" \ + --private-target "$PRIVATE_TARGET" \ + --opencode-config "${OPENCODE_REVIEW_WORKDIR}/opencode.jsonc" + fi printf 'Prepared isolated OpenCode review workspace: %s\n' "$OPENCODE_REVIEW_WORKDIR" - name: Run OpenCode PR Review model pool @@ -3985,10 +4424,12 @@ jobs: if: needs.coverage-evidence.result == 'success' continue-on-error: true env: + LLM_GATEWAY_API_KEY: ${{ secrets.LLM_GATEWAY_API_KEY }} + PERSONAL_ROUTE_SELECTED: ${{ steps.personal_route.outputs.selected }} SHARE: "false" NPM_CONFIG_IGNORE_SCRIPTS: "true" NO_COLOR: "1" - OPENCODE_MODEL_CANDIDATES: "contextual-orchestrator/orchestrator/free" + OPENCODE_MODEL_CANDIDATES: "contextual-orchestrator/orchestrator/auto" # One attempt per model, then fall through to the next model. Retrying # the SAME model 5x let a rate-limited/hung leader consume the whole # step, so the pool never reached a healthy fallback model. @@ -4026,7 +4467,12 @@ jobs: RUN_ATTEMPT: ${{ github.run_attempt }} run: | set -euo pipefail - source "$GITHUB_WORKSPACE/scripts/ci/load_contextual_orchestrator_token.sh" + if [ "${PERSONAL_ROUTE_SELECTED:-}" = 'true' ]; then + test -n "${LLM_GATEWAY_API_KEY:-}" + export OPENCODE_MODEL_CANDIDATES="personal-litellm/auto" + else + source "$GITHUB_WORKSPACE/scripts/ci/load_contextual_orchestrator_token.sh" + fi set +e bash "$GITHUB_WORKSPACE/scripts/ci/run_opencode_review_model_pool.sh" pool_status=$? @@ -4361,27 +4807,27 @@ jobs: "$@" } - self_check_filter=' + self_check_filter=" def self_check: - (.name // "") as $n - | ["opencode-review", "coverage-evidence", "coverage-source-tree", "required-workflow-bootstrap", "metadata-only gate evaluation", "scan-pr-queue"] | index($n); + (.name // \"\") as \$n + | [\"opencode-review\", \"coverage-evidence\", \"coverage-source-tree\", \"required-workflow-bootstrap\", \"metadata-only gate evaluation\", \"scan-pr-queue\"] | index(\$n); def latest_peer_checks: [ (.check_runs // [])[] | select(self_check | not) | . + { checkedAt: ( - if ((.started_at // "") != "") then .started_at - else (.completed_at // "") + if ((.started_at // \"\") != \"\") then .started_at + else (.completed_at // \"\") end ) } ] - | sort_by(.app.slug // "", .name // "", .checkedAt // "", .id // 0) - | group_by([.app.slug // "", .name // ""]) + | sort_by(.app.slug // \"\", .name // \"\", .checkedAt // \"\", .id // 0) + | group_by([.app.slug // \"\", .name // \"\"]) | map(last) | .[]; - ' + " check_runs_file="$(mktemp)" pending_checks_file="$(mktemp)" @@ -4584,6 +5030,8 @@ jobs: # extensions for image validation or package/GPU builds plus API and # publication overhead. env: + LLM_GATEWAY_API_KEY: ${{ secrets.LLM_GATEWAY_API_KEY }} + PERSONAL_ROUTE_SELECTED: ${{ steps.personal_route.outputs.selected }} GH_TOKEN: ${{ steps.opencode_app_token.outputs.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }} CHECK_LOOKUP_GH_TOKEN: ${{ github.token }} # The OpenCode app installation token is exchanged from api.opencode.ai @@ -4606,7 +5054,7 @@ jobs: COVERAGE_EVIDENCE_SUMMARY: ${{ needs.coverage-evidence.outputs.coverage_summary || 'Coverage evidence job did not run or did not publish coverage evidence.' }} OPENCODE_REVIEW_WORKDIR: ${{ runner.temp }}/opencode-review-project OPENCODE_SOURCE_WORKDIR: ${{ runner.temp }}/opencode-pr-head - MODEL: contextual-orchestrator/orchestrator/free + MODEL: contextual-orchestrator/orchestrator/auto USE_GITHUB_TOKEN: "true" NPM_CONFIG_IGNORE_SCRIPTS: "true" NO_COLOR: "1" @@ -4640,6 +5088,10 @@ jobs: OPENCODE_EXPORT_TIMEOUT_SECONDS: "60" run: | set -euo pipefail + if [ "${PERSONAL_ROUTE_SELECTED:-}" = 'true' ]; then + test -n "${LLM_GATEWAY_API_KEY:-}" + export MODEL="personal-litellm/auto" + fi quoted_coverage="${COVERAGE_EVIDENCE_RESULT:-}" COVERAGE_EVIDENCE_RESULT="$(python3 scripts/ci/opencode_coverage_identity.py \ --repo "$GH_REPOSITORY" \ @@ -5028,7 +5480,7 @@ jobs: if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then { printf '## OpenCode %s review body\n\n' "$event" - printf -- '- Head SHA: `%s`\n' "$HEAD_SHA" + printf -- "- Head SHA: \`%s\`\\n" "$HEAD_SHA" printf -- '- Workflow run: %s\n' "$RUN_ID" printf -- '- Workflow attempt: %s\n\n' "$RUN_ATTEMPT" printf '%s\n' "$body" @@ -5255,6 +5707,8 @@ jobs: publish_fallback_diff_review() { local body_file event body_file="$(mktemp)" + # Infrastructure failure is not a source-backed product verdict. + # COMMENT preserves diagnostics while coverage and receipt gates fail closed. event="COMMENT" python3 scripts/ci/opencode_review_surfaces.py build-fallback-review \ --changed-files-file "${OPENCODE_CHANGED_FILES_FILE}" \ @@ -5266,9 +5720,7 @@ jobs: >"$body_file" printf '\n%s\n\n%s\n' "## Review outcome" "Coverage is a gate, not the review. This body reviews the changed product files." >>"$body_file" create_pull_review "$event" "$(cat "$body_file")" - # create_pull_review COMMENT rewrites the status comment to Gate - # result: COMMENT. Restore the coverage gate so a miss never looks - # finished; next action stays "fix coverage evidence, then rerun". + # Retain the coverage blocker independently of the diagnostic review. request_changes_for_coverage_evidence_failure rm -f "$body_file" } @@ -5382,7 +5834,7 @@ jobs: printf '## Summary\n\n' printf '%s\n\n' "$summary" printf '## Adversarial validation\n\n' - printf '```json\n%s\n```\n\n' "$adversarial_evidence" + printf "\`\`\`json\\n%s\\n\`\`\`\\n\\n" "$adversarial_evidence" printf -- '- Result: REQUEST_CHANGES\n' printf -- '- Reason: %s\n\n' "$reason" printf -- "- Head SHA: \`%s\`\n" "$HEAD_SHA" @@ -5598,6 +6050,24 @@ jobs: fi finding_index=$((finding_index + 1)) + + # The gate emits a second token for the class it can tell apart: + # STRIX_SANDBOX_UNAVAILABLE means Strix's own sandbox container + # never reached its Caido proxy, so the run died before the + # gateway served anything. Reporting that as "the gateway or its + # provider pool was unavailable" sends the reader to the wrong + # component -- the misattribution #1953 fixed in the gate itself, + # which survived here because this text was fixed for every + # STRIX_PROVIDER_UNAVAILABLE line. + if grep -q "STRIX_SANDBOX_UNAVAILABLE" "$strix_evidence_file"; then + printf '### %s. HIGH %s:%s - Strix sandbox bootstrap blocked current-head security evidence\n' "$finding_index" "$path" "$line" + printf -- '- Problem: Strix failed before producing vulnerability reports. The failed log reported STRIX_SANDBOX_UNAVAILABLE, which the gate emits when the run ended in Strix sandbox bootstrap after its bounded sandbox-specific retries.\n' + printf -- '- Root cause: Strix sandbox container did not reach its Caido proxy on 127.0.0.1, so the scan ended before any Vulnerability Report window was produced. This verdict names Strix sandbox, not the contextual-orchestrator gateway, and there is no application source line to patch from this evidence.\n' + printf -- '- Fix: Do not approve from this failed scan. Re-run Strix; the sandbox bootstrap is a startup race and the gate already retries it once. Do not change gateway or provider configuration on the strength of this finding.\n' + printf -- '- Regression test: Keep the gate emitting STRIX_SANDBOX_UNAVAILABLE for sandbox bootstrap failures and keep this consumer reading it, so a sandbox outage is never reported as a gateway outage.\n\n' + return 0 + fi + printf '### %s. HIGH %s:%s - Contextual-orchestrator provider availability blocked current-head security evidence\n' "$finding_index" "$path" "$line" printf -- '- Problem: Strix failed before producing vulnerability reports. The failed log reported LLM CONNECTION FAILED, RateLimitError or Too many requests, budget-limit output, gateway exhaustion, and Configured model and fallback models were unavailable.\n' printf -- '- Root cause: The contextual-orchestrator gateway or its discovered provider pool was unavailable for this run; no Strix Vulnerability Report window was produced, so there is no application source line to patch from this evidence.\n' @@ -5950,9 +6420,15 @@ jobs: printf 'Skipping publish-step failed-check OpenCode diagnosis for central review-process self-repair; using collected current-head failed-check logs/SARIF fallback so the publish step stays bounded.\n' >&2 return 1 fi - source "$GITHUB_WORKSPACE/scripts/ci/load_contextual_orchestrator_token.sh" - if [ -z "${CONTEXTUAL_ORCHESTRATOR_TOKEN:-}" ]; then - return 1 + if [ "${PERSONAL_ROUTE_SELECTED:-}" = 'true' ]; then + if [ -z "${LLM_GATEWAY_API_KEY:-}" ]; then + return 1 + fi + else + source "$GITHUB_WORKSPACE/scripts/ci/load_contextual_orchestrator_token.sh" + if [ -z "${CONTEXTUAL_ORCHESTRATOR_TOKEN:-}" ]; then + return 1 + fi fi if ! python3 "$GITHUB_WORKSPACE/scripts/ci/assert_opencode_reasoning_effort.py" \ --config "$OPENCODE_REVIEW_WORKDIR/opencode.jsonc" \ @@ -6179,33 +6655,33 @@ jobs: case "$mode" in failed) - jq_filter=' + jq_filter=" [.[].check_runs[]?] - | sort_by((.started_at // .completed_at // .created_at // ""), (.id // 0)) - | group_by(.name // "") + | sort_by((.started_at // .completed_at // .created_at // \"\"), (.id // 0)) + | group_by(.name // \"\") | map(last) | .[]? - | select(((.name // "" | ascii_downcase) as $n | ["opencode-review","coverage-evidence","metadata-only gate evaluation"] | index($n)) | not) - | select((.status // "") == "completed") - | select((.conclusion // "" | ascii_upcase) as $c | ["FAILURE","TIMED_OUT","ACTION_REQUIRED","CANCELLED","STARTUP_FAILURE"] | index($c)) - | select((.name // "") != "scan-pr-queue") - | select(((.conclusion // "" | ascii_downcase) == "cancelled" and ((.name // "") | contains("$" + "{{"))) | not) - | select(((.conclusion // "" | ascii_downcase) == "cancelled" and (.name // "") == "noema-review") | not) - | "- " + (if (.name // "") == "strix" then "Strix Security Scan/strix" else ((.name // "check") + " check run") end) + ": " + (.conclusion // "unknown") + (if (.details_url // .html_url // "") != "" then " (" + (.details_url // .html_url) + ")" else "" end) - ' + | select(((.name // \"\" | ascii_downcase) as \$n | [\"opencode-review\",\"coverage-evidence\",\"metadata-only gate evaluation\"] | index(\$n)) | not) + | select((.status // \"\") == \"completed\") + | select((.conclusion // \"\" | ascii_upcase) as \$c | [\"FAILURE\",\"TIMED_OUT\",\"ACTION_REQUIRED\",\"CANCELLED\",\"STARTUP_FAILURE\"] | index(\$c)) + | select((.name // \"\") != \"scan-pr-queue\") + | select(((.conclusion // \"\" | ascii_downcase) == \"cancelled\" and ((.name // \"\") | contains(\"\$\" + \"{{\"))) | not) + | select(((.conclusion // \"\" | ascii_downcase) == \"cancelled\" and (.name // \"\") == \"noema-review\") | not) + | \"- \" + (if (.name // \"\") == \"strix\" then \"Strix Security Scan/strix\" else ((.name // \"check\") + \" check run\") end) + \": \" + (.conclusion // \"unknown\") + (if (.details_url // .html_url // \"\") != \"\" then \" (\" + (.details_url // .html_url) + \")\" else \"\" end) + " ;; pending) - jq_filter=' + jq_filter=" [.[].check_runs[]?] - | sort_by((.started_at // .completed_at // .created_at // ""), (.id // 0)) - | group_by(.name // "") + | sort_by((.started_at // .completed_at // .created_at // \"\"), (.id // 0)) + | group_by(.name // \"\") | map(last) | .[]? - | select(((.name // "" | ascii_downcase) as $n | ["opencode-review","coverage-evidence","metadata-only gate evaluation"] | index($n)) | not) - | select((.name // "") != "scan-pr-queue") - | select((.status // "") != "completed") - | "- " + (if (.name // "") == "strix" then "Strix Security Scan/strix" else ((.name // "check") + " check run") end) + ": " + (.status // "unknown") + (if (.details_url // .html_url // "") != "" then " (" + (.details_url // .html_url) + ")" else "" end) - ' + | select(((.name // \"\" | ascii_downcase) as \$n | [\"opencode-review\",\"coverage-evidence\",\"metadata-only gate evaluation\"] | index(\$n)) | not) + | select((.name // \"\") != \"scan-pr-queue\") + | select((.status // \"\") != \"completed\") + | \"- \" + (if (.name // \"\") == \"strix\" then \"Strix Security Scan/strix\" else ((.name // \"check\") + \" check run\") end) + \": \" + (.status // \"unknown\") + (if (.details_url // .html_url // \"\") != \"\" then \" (\" + (.details_url // .html_url) + \")\" else \"\" end) + " ;; *) return 1 @@ -6263,10 +6739,10 @@ jobs: -f owner="$owner" \ -f name="$name" \ -F number="$PR_NUMBER" \ - -f query=' - query($owner:String!,$name:String!,$number:Int!) { - repository(owner:$owner,name:$name) { - pullRequest(number:$number) { + -f query=" + query(\$owner:String!,\$name:String!,\$number:Int!) { + repository(owner:\$owner,name:\$name) { + pullRequest(number:\$number) { statusCheckRollup { contexts(first: 100) { nodes { @@ -6291,7 +6767,7 @@ jobs: } } } - ' \ + " \ --jq ' (.data.repository.pullRequest.statusCheckRollup.contexts.nodes // []) | map( @@ -6416,7 +6892,7 @@ jobs: -f owner="$owner" \ -f name="$name" \ -F number="$PR_NUMBER" \ - -f query='query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){pullRequest(number:$number){id}}}' \ + -f query="query(\$owner:String!,\$name:String!,\$number:Int!){repository(owner:\$owner,name:\$name){pullRequest(number:\$number){id}}}" \ --jq '.data.repository.pullRequest.id // empty')"; then echo "GitHub Checks statusCheckRollup PR id lookup failed; falling back to current-head REST check-runs." >&2 pr_node_id="" @@ -6860,21 +7336,21 @@ jobs: -f "ref=refs/heads/${head_ref}" \ -f state=open \ -F per_page=100 \ - --jq ' + --jq " [ .[] | { number: (.number // 0), - rule: (.rule.id // .rule.name // "unknown"), - tool: (.tool.name // "code-scanning"), - severity: (.rule.security_severity_level // .rule.severity // "unknown"), - url: (.html_url // "") + rule: (.rule.id // .rule.name // \"unknown\"), + tool: (.tool.name // \"code-scanning\"), + severity: (.rule.security_severity_level // .rule.severity // \"unknown\"), + url: (.html_url // \"\") } - | select((.severity | ascii_downcase) as $s | ["medium","high","critical","warning","error"] | index($s)) + | select((.severity | ascii_downcase) as \$s | [\"medium\",\"high\",\"critical\",\"warning\",\"error\"] | index(\$s)) ] | .[] - | "- " + .tool + "/" + .rule + ": " + .severity + " alert #" + (.number | tostring) + (if .url != "" then " (" + .url + ")" else "" end) - ' >"$output_file" 2>"$lookup_error_file"; then + | \"- \" + .tool + \"/\" + .rule + \": \" + .severity + \" alert #\" + (.number | tostring) + (if .url != \"\" then \" (\" + .url + \")\" else \"\" end) + " >"$output_file" 2>"$lookup_error_file"; then sed 's/^/gh: /' "$lookup_error_file" >&2 || true rm -f "$lookup_error_file" return 1 @@ -6922,7 +7398,7 @@ jobs: printf 'OpenCode could not approve from deterministic current-head evidence because GitHub Checks have failed.\n\n' printf '## Findings\n\n' printf '### 1. HIGH Current-head GitHub Checks - Fix failed required checks before approval\n' - printf -- '- Problem: Failed same-head checks remain for `%s`.\n' "$HEAD_SHA" + printf -- "- Problem: Failed same-head checks remain for \`%s\`.\\n" "$HEAD_SHA" printf -- '- Root cause: The model-unavailable evidence fallback is allowed only when peer GitHub Checks are complete and clean.\n' printf -- '- Fix: Read and fix the failed check logs below, then rerun the current-head checks.\n' printf -- '- Regression test: Keep the model-unavailable fallback gated on an empty failed-check rollup.\n\n' @@ -6978,11 +7454,11 @@ jobs: if [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then { printf '## OpenCode required check satisfied by existing same-head approval\n\n' - printf -- '- Result: `EXISTING_CURRENT_HEAD_APPROVAL`\n' - printf -- '- Head SHA: `%s`\n' "$HEAD_SHA" + printf -- "- Result: \`EXISTING_CURRENT_HEAD_APPROVAL\`\\n" + printf -- "- Head SHA: \`%s\`\\n" "$HEAD_SHA" printf -- '- Workflow run: %s\n' "$RUN_ID" printf -- '- Workflow attempt: %s\n' "$RUN_ATTEMPT" - printf -- '- Model-pool outcome: `%s`\n' "${OPENCODE_MODEL_POOL_OUTCOME:-unknown}" + printf -- "- Model-pool outcome: \`%s\`\\n" "${OPENCODE_MODEL_POOL_OUTCOME:-unknown}" printf -- '- Reason: a prior real-model OpenCode APPROVED review with passed structured adversarial probes already targets this exact head, and the fallback rechecked coverage, peer checks, code-scanning alerts, and unresolved review threads before accepting it.\n' printf -- '- Review state: unchanged; no duplicate APPROVE review was posted from model-output-unavailable evidence.\n\n' } >>"$GITHUB_STEP_SUMMARY" @@ -7758,7 +8234,10 @@ jobs: main|master) project_flow="github-flow" ;; esac + # Publication does not grant merge authority. This follow-up only + # assesses the exact head; separately authorized scheduler runs own mutations. args=( + --dry-run --repo "$GH_REPOSITORY" --base-branch "$base_branch" --max-prs 1 @@ -7767,8 +8246,8 @@ jobs: --security-workflow "Strix Security Scan" --review-dispatch-limit 0 --no-trigger-reviews - --enable-auto-merge - --merge-mode direct_or_auto + --no-enable-auto-merge + --merge-mode disabled --no-update-branches ) if [ -n "${PR_NUMBER:-}" ]; then diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 19ea58003f..afd4649127 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -33,7 +33,24 @@ permissions: jobs: required-workflow-bootstrap: name: required-workflow-bootstrap - runs-on: ubuntu-24.04 + # Folded together with the former admit-current-head job (2026-09-17): + # both only ever read PR metadata via the default `github.token` (no + # untrusted PR-content execution, no elevated token), the same trust + # level, and admit-current-head was not itself a required branch- + # protection context (this job's name is, so it stays). Folding removes + # one needs:-chained job-to-job runner-queue re-entry; measured on + # .github PR #2183 run 35042040116: this exact boundary cost 4h46m of + # queue wait between two single-digit-second jobs. See + # docs/doctoring/actions-capacity-root-cause-20260917.md for the + # underlying measurement methodology. + runs-on: + group: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} + permissions: + contents: read + pull-requests: read + outputs: + admitted: ${{ steps.live_head.outputs.admitted }} steps: - name: Materialize the required review workflow run: >- @@ -137,6 +154,10 @@ jobs: api_url="${GITHUB_API_URL:-https://api.github.com}" mkdir -p "$trusted_source_dir" curl -fsSL \ + --retry 3 \ + --retry-all-errors \ + --retry-delay 1 \ + --retry-max-time 30 \ -H "Authorization: Bearer ${GH_TOKEN}" \ -H "Accept: application/vnd.github+json" \ -o "$trusted_archive" \ @@ -226,27 +247,27 @@ jobs: TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.repository }} PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number || 0 }} PULL_REQUEST_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + # The base branch's tip SHA at event time -- already-reviewed, + # already-merged state. Threaded through so the issue #2193 + # research/data artifact path declaration can be resolved only + # from here, never from the untrusted PR head; see + # `evaluate_pull_request`'s `base_ref` parameter. + PULL_REQUEST_BASE_SHA: ${{ github.event.pull_request.base.sha || '' }} EVENT_ACTION: ${{ github.event.action || 'unknown' }} run: | set -euo pipefail + base_ref_args=() + if [ -n "$PULL_REQUEST_BASE_SHA" ]; then + base_ref_args=(--base-ref "$PULL_REQUEST_BASE_SHA") + fi python3 .cwl-required-source/scripts/ci/pingora_edge_policy.py \ --repository "$TARGET_REPOSITORY" \ --pull-request "$PULL_REQUEST_NUMBER" \ --head-sha "$PULL_REQUEST_HEAD_SHA" \ --event-action "$EVENT_ACTION" \ - --api-url "https://api.github.com" + --api-url "https://api.github.com" \ + "${base_ref_args[@]}" - admit-current-head: - name: admit-current-head - needs: [required-workflow-bootstrap] - runs-on: ubuntu-24.04 - timeout-minutes: 5 - outputs: - admitted: ${{ steps.live_head.outputs.admitted }} - permissions: - contents: read - pull-requests: read - steps: - name: Admit only the exact live OpenCode head id: live_head env: @@ -276,11 +297,78 @@ jobs: echo "admitted=true" >>"$GITHUB_OUTPUT" echo "Exact live OpenCode head admitted for ${TARGET_REPOSITORY}#${PR_NUMBER}." + changed-scope: + name: Detect changed scope + # Same job-level docs/image-only gate as strix.yml, security-scan.yml, + # sast-semgrep.yml, and codeql-pr.yml (see + # docs/doctoring/required-workflow-path-filter-boundary.md): the org + # ruleset ignores every `on:` filter when it runs this workflow in + # another repository, so the doc/image-only decision has to be made in + # a job and consumed through `needs`, not the trigger. OpenCode review + # previously dispatched its full multi-hour coverage+model chain for + # every PR event including docs/changelog-only diffs; this closes that + # gap using the identical classifier. Fails OPEN: an unreadable, empty, + # or truncated file list reviews everything. + if: (github.event_name != 'pull_request_target' || (github.event.action != 'closed' && github.event.action != 'converted_to_draft')) && (github.event.pull_request.draft != true || github.event.action == 'converted_to_draft' || github.event.action == 'closed') + runs-on: + group: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} + timeout-minutes: 5 + permissions: + contents: read + pull-requests: read + outputs: + code: ${{ steps.scope.outputs.code }} + deps: ${{ steps.scope.outputs.deps }} + steps: + - name: Classify changed paths + id: scope + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.event.pull_request.base.repo.full_name || github.repository }} + PR: ${{ github.event.pull_request.number }} + EXPECTED_FILES: ${{ github.event.pull_request.changed_files }} + shell: bash + run: | + set -uo pipefail + code=true + deps=true + if [ -n "${PR}" ] && [ -n "${EXPECTED_FILES}" ]; then + # No retry loop here, unlike the identical classifier elsewhere + # (e.g. strix.yml): this required workflow is contract-tested to + # never retry or poll (tests/test_opencode_required_verdict_regression.py), + # so a single failed read falls straight through to the + # already-safe "scan everything" fallback below instead. + changed="$(gh api --paginate "repos/${REPO}/pulls/${PR}/files?per_page=100" --jq '.[].filename' || true)" + # GitHub caps /pulls/N/files at 3000 entries; a short list would hide + # source files behind a doc-only verdict, so require an exact count. + if [ -n "$changed" ] && [ "$(printf '%s\n' "$changed" | wc -l | tr -d ' ')" = "${EXPECTED_FILES}" ]; then + code=false + deps=false + while IFS= read -r changed_path; do + case "$changed_path" in + *.md|*.markdown|*.rst|*.png|*.jpg|*.jpeg|*.gif|*.webp|*.bmp|*.ico|LICENSE|LICENSE.txt|COPYING|COPYING.txt|NOTICE|NOTICE.txt|.github/ISSUE_TEMPLATE/*) ;; + *) code=true ;; + esac + case "$changed_path" in + requirements*.txt|*/requirements*.txt|pyproject.toml|*/pyproject.toml|uv.lock|*/uv.lock|pylock.*.toml|*/pylock.*.toml|package.json|*/package.json|package-lock.json|*/package-lock.json|pnpm-lock.yaml|*/pnpm-lock.yaml|yarn.lock|*/yarn.lock|Cargo.toml|*/Cargo.toml|Cargo.lock|*/Cargo.lock|go.mod|*/go.mod|go.sum|*/go.sum|pom.xml|*/pom.xml|build.gradle|*/build.gradle|build.gradle.kts|*/build.gradle.kts|DESCRIPTION|*/DESCRIPTION) deps=true ;; + esac + done <<<"$changed" + else + echo "::notice::changed-scope could not read a complete PR file list; scanning everything." + fi + fi + echo "code=${code}" >> "$GITHUB_OUTPUT" + echo "deps=${deps}" >> "$GITHUB_OUTPUT" + echo "changed-scope code=${code} deps=${deps}" + coverage-source-tree: name: coverage-source-tree - needs: [required-workflow-bootstrap, admit-current-head] - if: needs.admit-current-head.outputs.admitted == 'true' - runs-on: ubuntu-24.04 + needs: [required-workflow-bootstrap] + if: needs.required-workflow-bootstrap.outputs.admitted == 'true' + runs-on: + group: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} steps: - run: >- echo "PR-head source and coverage execution are delegated to the @@ -298,9 +386,11 @@ jobs: # `admit-current-head` directly lets the two run in parallel. The `if:` below # restates the admission gate this job previously inherited transitively # through coverage-source-tree, so an unadmitted head still skips it. - needs: [required-workflow-bootstrap, admit-current-head] - if: needs.admit-current-head.outputs.admitted == 'true' - runs-on: ubuntu-24.04 + needs: [required-workflow-bootstrap] + if: needs.required-workflow-bootstrap.outputs.admitted == 'true' + runs-on: + group: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} steps: - run: >- echo "This required-workflow job preserves the stable branch-protection @@ -317,16 +407,18 @@ jobs: # created until its `needs:` finish, so this link cost a further 12h13m of # queue wait on naruon#1528 (run 33581213805). Admission is still enforced # directly by this job's own `if:` below, not inherited through that edge. - needs: [admit-current-head] - if: needs.admit-current-head.outputs.admitted == 'true' - runs-on: ubuntu-24.04 + needs: [required-workflow-bootstrap, changed-scope] + if: needs.required-workflow-bootstrap.outputs.admitted == 'true' + runs-on: + group: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} permissions: contents: read pull-requests: read id-token: write steps: - name: Request current-head OpenCode review execution - if: github.event.action != 'closed' + if: github.event.action != 'closed' && needs.changed-scope.outputs.code != 'false' env: GH_TOKEN: ${{ github.token }} OIDC_AUDIENCE: opencode-github-action @@ -438,12 +530,18 @@ jobs: HEAD_SHA: ${{ github.event.pull_request.head.sha }} PR_ACTION: ${{ github.event.action }} PR_DRAFT: ${{ github.event.pull_request.draft }} + CHANGED_SCOPE_CODE: ${{ needs.changed-scope.outputs.code }} + WORKFLOW_SHA: ${{ github.workflow_sha }} run: | set -euo pipefail if [ "$PR_ACTION" = "closed" ]; then echo "PR closed; a current-head OpenCode verdict is not required." exit 0 fi + if [ "${CHANGED_SCOPE_CODE:-true}" = "false" ]; then + echo "PR contains no reviewable code changes (docs/image-only diff); a current-head OpenCode verdict is not required." + exit 0 + fi if [ -z "${PR_NUMBER:-}" ] || [ -z "${HEAD_SHA:-}" ]; then echo "::error::Missing PR number or head SHA; cannot verify a current-head OpenCode verdict." exit 1 @@ -475,35 +573,42 @@ jobs: if [ "$PR_DRAFT" = "true" ]; then echo "Event draft snapshot is stale; checking the verdict for the live ready PR." fi - reviews="$(gh api --paginate "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100")" - verdict="$(printf '%s\n' "$reviews" | jq -r -s --arg sha "$HEAD_SHA" ' - (add // []) - | [ - .[] - | select( - (.user.login // "" | ascii_downcase) as $user - | $user == "opencode-agent" or $user == "opencode-agent[bot]" - ) - | select((.commit_id // "" | ascii_downcase) == ($sha | ascii_downcase)) - | select(.state == "APPROVED" or .state == "CHANGES_REQUESTED") - ] - | (last // {}) as $review - | ($review.body // "" | ascii_downcase) as $body - | if $review.state == "CHANGES_REQUESTED" then - "CHANGES_REQUESTED" - elif $review.state == "APPROVED" - and ($body | contains("deterministic current-head evidence") | not) - and ($body | contains("deterministic fallback approval") | not) - and ($body | contains("model-unavailable evidence fallback") | not) - and ($body | contains("did not emit a usable current-head control block") | not) - and ($body | contains("scope: `unsupported`") | not) - and ($body | contains("model-pool outcome: `unknown`") | not) - then - "APPROVED" - else - empty - end - ')" + reviews_file="$(mktemp)" + helper="$(mktemp)" + trap 'rm -f "$reviews_file" "$helper"' EXIT + gh api --paginate --slurp "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}/reviews?per_page=100" >"$reviews_file" + gh api "repos/ContextualWisdomLab/.github/contents/scripts/ci/opencode_review_receipt_gate.py?ref=${WORKFLOW_SHA}" \ + --jq .content | base64 --decode >"$helper" + verdict="$(python3 - "$helper" "$reviews_file" "$HEAD_SHA" <<'PY' + import importlib.machinery + import importlib.util + import json + import sys + + helper_path, reviews_path, head_sha = sys.argv[1:] + loader = importlib.machinery.SourceFileLoader("trusted_opencode_receipt_gate", helper_path) + spec = importlib.util.spec_from_loader(loader.name, loader) + if spec is None or spec.loader is None: + raise RuntimeError("trusted OpenCode receipt helper could not be loaded") + gate = importlib.util.module_from_spec(spec) + spec.loader.exec_module(gate) + with open(reviews_path, encoding="utf-8") as stream: + pages = json.load(stream) + if not isinstance(pages, list) or not all( + isinstance(page, list) and all(isinstance(review, dict) for review in page) + for page in pages + ): + raise ValueError("formal review lookup returned malformed paginated JSON") + reviews = [review for page in pages for review in page + if str((review.get("user") or {}).get("login") or "").lower() + in {"opencode-agent", "opencode-agent[bot]"}] + receipt, reason = gate.evaluate_receipts(reviews, head_sha, is_draft=False) + if receipt is None: + print(f"No current-head formal OpenCode verdict: {reason}", file=sys.stderr) + raise SystemExit(0) + print(receipt["state"]) + PY + )" if [ -z "$verdict" ]; then echo "::error::No APPROVED or CHANGES_REQUESTED from opencode-agent on the current head. The dispatch workflow will rerun this failed job after publishing an authenticated exact-head verdict." exit 1 @@ -524,7 +629,9 @@ jobs: # head no longer matches the live one. The target job also revalidates the # live PR before dispatch and verdict admission. if: github.event_name == 'pull_request_target' && github.event.action == 'synchronize' - runs-on: ubuntu-24.04 + runs-on: + group: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} permissions: actions: write contents: read diff --git a/.github/workflows/organization-commercial-readiness-loop.yml b/.github/workflows/organization-commercial-readiness-loop.yml index fbe7efa434..fa1ef018ff 100644 --- a/.github/workflows/organization-commercial-readiness-loop.yml +++ b/.github/workflows/organization-commercial-readiness-loop.yml @@ -16,7 +16,9 @@ jobs: if: >- github.repository == 'ContextualWisdomLab/.github' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) - runs-on: ubuntu-24.04 + runs-on: + group: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} timeout-minutes: 25 permissions: contents: read diff --git a/.github/workflows/pg-erd-cloud-quality.yml b/.github/workflows/pg-erd-cloud-quality.yml new file mode 100644 index 0000000000..27512f864c --- /dev/null +++ b/.github/workflows/pg-erd-cloud-quality.yml @@ -0,0 +1,156 @@ +name: pg-erd-cloud quality + +# Fixed consumer only. Keep concurrency in the calling workflow, not here. +# Requires disposable isolated capacity and consumer repository access. +on: + workflow_call: + +permissions: + contents: read + +jobs: + backend: + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 30 + steps: + - name: Admit fixed caller before checkout + env: + CALLER_REPOSITORY: ${{ github.repository }} + CALLER_EVENT: ${{ github.event_name }} + PR_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} + PR_BASE_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name }} + CALLER_REF: ${{ github.ref }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + if [ "$CALLER_REPOSITORY" != "ContextualWisdomLab/pg-erd-cloud" ]; then + echo "Unsupported caller repository" >&2 + exit 1 + fi + case "$CALLER_EVENT" in + pull_request) + if [ "$PR_HEAD_REPOSITORY" != "$CALLER_REPOSITORY" ] || [ "$PR_BASE_REPOSITORY" != "$CALLER_REPOSITORY" ]; then + echo "Unsupported pull request source" >&2 + exit 1 + fi + ;; + push|workflow_dispatch) + if [ "$CALLER_REF" != "refs/heads/main" ]; then + echo "Unsupported caller ref" >&2 + exit 1 + fi + ;; + *) echo "Unsupported caller event" >&2; exit 1 ;; + esac + - name: Harden runner + uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Setup Python + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + with: + python-version: "3.10" + + - name: Verify backend in a job-private environment + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + job_root="$(mktemp -d "${RUNNER_TEMP:?}/pg-erd-backend.XXXXXXXX")" + trap 'rm -rf -- "$job_root"' EXIT + unset PYTHONHOME PYTHONPATH + export PIP_NO_CACHE_DIR=1 PIP_DISABLE_PIP_VERSION_CHECK=1 + export PIP_CONFIG_FILE=/dev/null + python -m venv "$job_root/venv" + export PATH="$job_root/venv/bin:$PATH" + cd backend + python -m pip install --require-hashes -r requirements-dev.lock + python -m pip install --require-hashes -r ../.github/requirements.lock + cd .. + python -m unittest discover -s .github/tests -v + python .github/scripts/validate_codeql_backfill.py + actionlint .github/workflows/*.yml + shellcheck .github/scripts/*.sh + cd backend + export PYTHONPATH=. + mypy app + pytest -q + + frontend: + # Persistent self-hosted hosts may inherit NODE_ENV=production. + env: + NODE_ENV: test + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 30 + steps: + - name: Admit fixed caller before checkout + env: + CALLER_REPOSITORY: ${{ github.repository }} + CALLER_EVENT: ${{ github.event_name }} + PR_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} + PR_BASE_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name }} + CALLER_REF: ${{ github.ref }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + if [ "$CALLER_REPOSITORY" != "ContextualWisdomLab/pg-erd-cloud" ]; then + echo "Unsupported caller repository" >&2 + exit 1 + fi + case "$CALLER_EVENT" in + pull_request) + if [ "$PR_HEAD_REPOSITORY" != "$CALLER_REPOSITORY" ] || [ "$PR_BASE_REPOSITORY" != "$CALLER_REPOSITORY" ]; then + echo "Unsupported pull request source" >&2 + exit 1 + fi + ;; + push|workflow_dispatch) + if [ "$CALLER_REF" != "refs/heads/main" ]; then + echo "Unsupported caller ref" >&2 + exit 1 + fi + ;; + *) echo "Unsupported caller event" >&2; exit 1 ;; + esac + - name: Harden runner + uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Setup Node + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: "26" + cache: "npm" + cache-dependency-path: frontend/package-lock.json + + - name: Install + working-directory: frontend + run: npm ci --include=dev + + - name: Typecheck + working-directory: frontend + run: npm run typecheck + + - name: Test + working-directory: frontend + run: npm run test + + - name: Build + working-directory: frontend + env: + NODE_ENV: production + run: npm run build diff --git a/.github/workflows/pr-auto-rebase.yml b/.github/workflows/pr-auto-rebase.yml index d25bb9c9f9..4992224926 100644 --- a/.github/workflows/pr-auto-rebase.yml +++ b/.github/workflows/pr-auto-rebase.yml @@ -68,7 +68,9 @@ permissions: jobs: auto-rebase: - runs-on: ubuntu-latest + runs-on: + group: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} permissions: contents: read # checkout only; force-push uses the app or PR-write token below pull-requests: write # read PR queue and manage labels diff --git a/.github/workflows/pr-review-autofix.yml b/.github/workflows/pr-review-autofix.yml index 1b7849a0c5..4528a3f014 100644 --- a/.github/workflows/pr-review-autofix.yml +++ b/.github/workflows/pr-review-autofix.yml @@ -22,7 +22,9 @@ permissions: jobs: autofix: - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] # No job-level timeout-minutes here, deliberately. This job's dominant # cost is `opencode run` (up to two invocations: the main autofix pass, # and a base-merge conflict-resolution pass) -- a job-level wall-clock @@ -263,8 +265,16 @@ jobs: python3 "$GITHUB_WORKSPACE/trusted-autofix-source/scripts/ci/pr_review_autofix_context.py" \ "${context_args[@]}" + - name: Set up lock-compatible sidecar Python + id: sidecar_python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + update-environment: false + - name: Provision contextual-orchestrator review sidecar env: + SIDECAR_PYTHON: ${{ steps.sidecar_python.outputs.python-path }} BYTEZ_API_KEY: ${{ secrets.BYTEZ_API_KEY }} NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} @@ -364,7 +374,7 @@ jobs: "npm": "@ai-sdk/openai-compatible", "name": "Contextual Orchestrator", "options": { - "baseURL": "{env:CONTEXTUAL_ORCHESTRATOR_BASE_URL}", + "baseURL": "{env:CONTEXTUAL_ORCHESTRATOR_BASE_URL}/v1", "apiKey": "{env:CONTEXTUAL_ORCHESTRATOR_TOKEN}" }, "models": { diff --git a/.github/workflows/pr-review-fix-scheduler.yml b/.github/workflows/pr-review-fix-scheduler.yml index dc9c7415ca..5543bbc2b9 100644 --- a/.github/workflows/pr-review-fix-scheduler.yml +++ b/.github/workflows/pr-review-fix-scheduler.yml @@ -89,7 +89,12 @@ permissions: jobs: dispatch-review-fixes: - runs-on: ubuntu-24.04 + # API reads and autofix dispatch only; no pull-request content is checked + # out. The central main caller uses the idle control pool instead of the + # saturated hosted queue; any other caller keeps hosted Ubuntu 24.04. + runs-on: + group: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/hourly-review-repair.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/hourly-review-repair.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} timeout-minutes: 35 env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true @@ -153,9 +158,22 @@ jobs: # Only the direct repository_dispatch surface needs sender binding; # cross-repository invocations still pass the configured allowlist. if [ "$EVENT_NAME" = "repository_dispatch" ]; then - if [ -z "$ALLOWED_DISPATCH_ACTOR" ] || - [ "$DISPATCH_ACTOR" != "$ALLOWED_DISPATCH_ACTOR" ] || - [ "$DISPATCH_SENDER" != "$ALLOWED_DISPATCH_ACTOR" ]; then + # ALLOWED_DISPATCH_ACTOR is a comma-separated allowlist shared with + # opencode-review-dispatch.yml and codeql-scan-dispatch.yml; all + # three parse it the same way. Actor AND sender must both equal the + # SAME listed identity, and an empty allowlist admits nothing. + actor_allowed=0 + IFS=',' read -r -a allowed_dispatch_actors <<<"$ALLOWED_DISPATCH_ACTOR" + for allowed_actor in "${allowed_dispatch_actors[@]}"; do + allowed_actor="${allowed_actor//[[:space:]]/}" + if [ -n "$allowed_actor" ] && + [ "$DISPATCH_ACTOR" = "$allowed_actor" ] && + [ "$DISPATCH_SENDER" = "$allowed_actor" ]; then + actor_allowed=1 + break + fi + done + if [ "$actor_allowed" -ne 1 ]; then echo "::error::Scheduler repository dispatch actor or sender is unauthorized." exit 1 fi diff --git a/.github/workflows/pr-review-merge-scheduler.yml b/.github/workflows/pr-review-merge-scheduler.yml index d32918cf45..7777f02bed 100644 --- a/.github/workflows/pr-review-merge-scheduler.yml +++ b/.github/workflows/pr-review-merge-scheduler.yml @@ -101,16 +101,23 @@ jobs: scan-pr-queue: # repository_dispatch review runs do not reliably carry pull_requests metadata. # Without this guard, one completed central review can wake a repo-wide scan. + # Draft PRs get no runner; ready_for_review re-runs this on the same head. if: >- ( - github.event_name != 'pull_request_target' || - github.event.action != 'closed' - ) && - ( - github.event_name != 'repository_dispatch' || - github.event.client_payload.org_sweep != true - ) - runs-on: ubuntu-24.04 + ( + github.event_name != 'pull_request_target' || + github.event.action != 'closed' + ) && + ( + github.event_name != 'repository_dispatch' || + github.event.client_payload.org_sweep != true + ) + ) && (github.event.pull_request.draft != true || github.event.action == 'converted_to_draft' || github.event.action == 'closed') + # The group admits only trusted central main workflows, including reusable + # callers. Keep admission/dispatch off pools occupied by model execution. + runs-on: + group: CWL central control + labels: [self-hosted, linux, x64] # Bound scan-pr-queue to a wall-clock ceiling well short of GitHub's # 360-minute platform default. This is a single-repository queue scan # (paginated GraphQL reads plus at most one review dispatch and one @@ -122,6 +129,7 @@ jobs: contents: write id-token: write pull-requests: write + statuses: read env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true GH_TOKEN: ${{ github.token }} @@ -346,6 +354,10 @@ jobs: trusted_archive="${RUNNER_TEMP}/trusted-scheduler-source.tar.gz" api_url="${GITHUB_API_URL:-https://api.github.com}" curl -fsSL \ + --retry 3 \ + --retry-all-errors \ + --retry-delay 1 \ + --retry-max-time 30 \ -H "Authorization: Bearer ${GH_TOKEN}" \ -H "Accept: application/vnd.github+json" \ -o "$trusted_archive" \ diff --git a/.github/workflows/psychometrics-commons-ci.yml b/.github/workflows/psychometrics-commons-ci.yml new file mode 100644 index 0000000000..d3303d88c5 --- /dev/null +++ b/.github/workflows/psychometrics-commons-ci.yml @@ -0,0 +1,323 @@ +name: psychometrics-commons Runtime CI + +on: + workflow_call: + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + RUSTFLAGS: -Dwarnings + RUSTDOCFLAGS: -Dwarnings + CARGO_LLVM_COV_VERSION: "0.8.6" + +jobs: + rust-quality: + name: Format, lint, test, and rustdoc + if: ${{ github.repository == 'ContextualWisdomLab/psychometrics-commons' && github.event_name == 'pull_request' && github.event.action != 'closed' && github.event.pull_request.state == 'open' && github.event.pull_request.number > 0 && startsWith(github.ref, 'refs/pull/') && endsWith(github.ref, '/merge') && github.event.pull_request.draft == false && github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.base.repo.full_name == github.repository && github.event.pull_request.base.ref == 'main' }} + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 15 + services: + postgres: + image: postgres:18-alpine@sha256:9a8afca54e7861fd90fab5fdf4c42477a6b1cb7d293595148e674e0a3181de15 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ci_${{ github.run_id }}_${{ github.run_attempt }} + POSTGRES_DB: psychometrics_commons_test + ports: + - 5432 + options: >- + --health-cmd "pg_isready -U postgres -d psychometrics_commons_test" + --health-interval 5s + --health-timeout 5s + --health-retries 10 + --health-start-period 5s + steps: + - name: Validate fixed caller and exact head before checkout + shell: bash + env: + TARGET_REPOSITORY: ${{ github.repository }} + SOURCE_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} + BASE_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name }} + BASE_REF: ${{ github.event.pull_request.base.ref }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_STATE: ${{ github.event.pull_request.state }} + PR_DRAFT: ${{ github.event.pull_request.draft }} + run: | + if [ "$TARGET_REPOSITORY" != 'ContextualWisdomLab/psychometrics-commons' ] || + [ "$GITHUB_EVENT_NAME" != pull_request ] || + [ "$SOURCE_REPOSITORY" != "$TARGET_REPOSITORY" ] || + [ "$BASE_REPOSITORY" != "$TARGET_REPOSITORY" ] || + [ "$BASE_REF" != main ] || + [ "$PR_STATE" != open ] || [ "$PR_DRAFT" != false ] || + [[ ! "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || + [ "$GITHUB_REF" != "refs/pull/$PR_NUMBER/merge" ] || + [[ ! "$HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then + echo 'Fixed caller or exact pull request head is invalid.' >&2 + exit 1 + fi + - name: Checkout exact head + uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 + with: + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + - name: Require exact checked-out head + env: + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD_SHA" + - name: Configure ephemeral PostgreSQL test connection + shell: bash + run: | + printf 'TEST_DATABASE_URL=host=localhost port=%s user=postgres password=ci_%s_%s dbname=psychometrics_commons_test\n' \ + "${{ job.services.postgres.ports['5432'] }}" "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" >> "$GITHUB_ENV" + - name: Install pinned Rust toolchain + run: rustup toolchain install 1.97.1 --profile minimal --component clippy --component rustfmt + - name: Check formatting + run: cargo +1.97.1 fmt --all -- --check + - name: Compile all targets + run: cargo +1.97.1 check --locked --all-targets + - name: Require committed Cargo lockfile + id: cargo_lock_gate + run: git diff --exit-code -- Cargo.lock + - name: Emit resolved Cargo lock evidence when the lock gate fails + if: failure() && steps.cargo_lock_gate.outcome == 'failure' + run: | + printf 'RESOLVED_CARGO_LOCK_BASE64=' + base64 -w0 Cargo.lock + printf '\n' + - name: Preserve resolved lockfile when the lock gate fails + if: failure() && steps.cargo_lock_gate.outcome == 'failure' + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f + with: + name: resolved-cargo-lock-${{ github.event.pull_request.head.sha }} + path: Cargo.lock + if-no-files-found: error + retention-days: 1 + - name: Run Clippy + run: cargo +1.97.1 clippy --all-targets -- -D warnings + - name: Run Rust tests + run: cargo +1.97.1 test --all-targets + - name: Run coverage-contract regression tests + run: python3 -m unittest discover -s tests -p 'test_*.py' -v + - name: Build warning-free documentation + run: cargo +1.97.1 doc --no-deps + + line-coverage: + name: Production line coverage + if: ${{ github.repository == 'ContextualWisdomLab/psychometrics-commons' && github.event_name == 'pull_request' && github.event.action != 'closed' && github.event.pull_request.state == 'open' && github.event.pull_request.number > 0 && startsWith(github.ref, 'refs/pull/') && endsWith(github.ref, '/merge') && github.event.pull_request.draft == false && github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.base.repo.full_name == github.repository && github.event.pull_request.base.ref == 'main' }} + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 15 + services: + postgres: + image: postgres:18-alpine@sha256:9a8afca54e7861fd90fab5fdf4c42477a6b1cb7d293595148e674e0a3181de15 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ci_${{ github.run_id }}_${{ github.run_attempt }} + POSTGRES_DB: psychometrics_commons_test + ports: + - 5432 + options: >- + --health-cmd "pg_isready -U postgres -d psychometrics_commons_test" + --health-interval 5s + --health-timeout 5s + --health-retries 10 + --health-start-period 5s + steps: + - name: Validate fixed caller and exact head before checkout + shell: bash + env: + TARGET_REPOSITORY: ${{ github.repository }} + SOURCE_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} + BASE_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name }} + BASE_REF: ${{ github.event.pull_request.base.ref }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_STATE: ${{ github.event.pull_request.state }} + PR_DRAFT: ${{ github.event.pull_request.draft }} + run: | + if [ "$TARGET_REPOSITORY" != 'ContextualWisdomLab/psychometrics-commons' ] || + [ "$GITHUB_EVENT_NAME" != pull_request ] || + [ "$SOURCE_REPOSITORY" != "$TARGET_REPOSITORY" ] || + [ "$BASE_REPOSITORY" != "$TARGET_REPOSITORY" ] || + [ "$BASE_REF" != main ] || + [ "$PR_STATE" != open ] || [ "$PR_DRAFT" != false ] || + [[ ! "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || + [ "$GITHUB_REF" != "refs/pull/$PR_NUMBER/merge" ] || + [[ ! "$HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then + echo 'Fixed caller or exact pull request head is invalid.' >&2 + exit 1 + fi + - name: Checkout exact head + uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 + with: + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + - name: Require exact checked-out head + env: + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD_SHA" + - name: Configure ephemeral PostgreSQL test connection + shell: bash + run: | + printf 'TEST_DATABASE_URL=host=localhost port=%s user=postgres password=ci_%s_%s dbname=psychometrics_commons_test\n' \ + "${{ job.services.postgres.ports['5432'] }}" "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" >> "$GITHUB_ENV" + - name: Install pinned Rust toolchain with LLVM tools + run: rustup toolchain install 1.97.1 --profile minimal --component llvm-tools-preview + - name: Install pinned cargo-llvm-cov + run: cargo +1.97.1 install cargo-llvm-cov --locked --version "$CARGO_LLVM_COV_VERSION" + - name: Verify cargo-llvm-cov version + run: cargo +1.97.1 llvm-cov --version | grep -F "$CARGO_LLVM_COV_VERSION" + - name: Generate line coverage + run: cargo +1.97.1 llvm-cov --json --summary-only --output-path coverage.json + - name: Enforce complete line coverage + run: python3 scripts/check_coverage.py coverage.json --kind lines + - name: Diagnose missing line coverage + if: failure() + run: | + python3 - <<'PY' + import json + from pathlib import Path + + payload = json.loads(Path("coverage.json").read_text(encoding="utf-8")) + entry = payload["data"][0] + kind = "lines" + for file_entry in entry.get("files", []): + summary = file_entry.get("summary", {}) + metric = summary.get(kind) + if isinstance(metric, dict) and metric.get("covered") != metric.get("count"): + print( + "INCOMPLETE_FILE " + f"{kind} {metric.get('covered')}/{metric.get('count')} " + f"{file_entry.get('filename')}" + ) + PY + cargo +1.97.1 llvm-cov report --lcov --output-path coverage-lines.lcov + python3 - <<'PY' + from pathlib import Path + + workspace = Path.cwd().resolve() + source = None + for raw_line in Path("coverage-lines.lcov").read_text(encoding="utf-8").splitlines(): + if raw_line.startswith("SF:"): + source_path = Path(raw_line[3:]).resolve() + try: + source = source_path.relative_to(workspace).as_posix() + except ValueError: + source = source_path.as_posix() + elif raw_line.startswith("DA:") and source is not None: + line, hits, *_ = raw_line[3:].split(",") + if hits == "0": + print(f"::error file={source},line={line}::uncovered production line") + PY + cargo +1.97.1 llvm-cov report --text --show-missing-lines --show-instantiations + + branch-coverage: + name: Production branch coverage + if: ${{ github.repository == 'ContextualWisdomLab/psychometrics-commons' && github.event_name == 'pull_request' && github.event.action != 'closed' && github.event.pull_request.state == 'open' && github.event.pull_request.number > 0 && startsWith(github.ref, 'refs/pull/') && endsWith(github.ref, '/merge') && github.event.pull_request.draft == false && github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.base.repo.full_name == github.repository && github.event.pull_request.base.ref == 'main' }} + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 15 + services: + postgres: + image: postgres:18-alpine@sha256:9a8afca54e7861fd90fab5fdf4c42477a6b1cb7d293595148e674e0a3181de15 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: ci_${{ github.run_id }}_${{ github.run_attempt }} + POSTGRES_DB: psychometrics_commons_test + ports: + - 5432 + options: >- + --health-cmd "pg_isready -U postgres -d psychometrics_commons_test" + --health-interval 5s + --health-timeout 5s + --health-retries 10 + --health-start-period 5s + steps: + - name: Validate fixed caller and exact head before checkout + shell: bash + env: + TARGET_REPOSITORY: ${{ github.repository }} + SOURCE_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }} + BASE_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name }} + BASE_REF: ${{ github.event.pull_request.base.ref }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_STATE: ${{ github.event.pull_request.state }} + PR_DRAFT: ${{ github.event.pull_request.draft }} + run: | + if [ "$TARGET_REPOSITORY" != 'ContextualWisdomLab/psychometrics-commons' ] || + [ "$GITHUB_EVENT_NAME" != pull_request ] || + [ "$SOURCE_REPOSITORY" != "$TARGET_REPOSITORY" ] || + [ "$BASE_REPOSITORY" != "$TARGET_REPOSITORY" ] || + [ "$BASE_REF" != main ] || + [ "$PR_STATE" != open ] || [ "$PR_DRAFT" != false ] || + [[ ! "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || + [ "$GITHUB_REF" != "refs/pull/$PR_NUMBER/merge" ] || + [[ ! "$HEAD_SHA" =~ ^[0-9a-f]{40}$ ]]; then + echo 'Fixed caller or exact pull request head is invalid.' >&2 + exit 1 + fi + - name: Checkout exact head + uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 + with: + ref: ${{ github.event.pull_request.head.sha }} + persist-credentials: false + - name: Require exact checked-out head + env: + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD_SHA" + - name: Configure ephemeral PostgreSQL test connection + shell: bash + run: | + printf 'TEST_DATABASE_URL=host=localhost port=%s user=postgres password=ci_%s_%s dbname=psychometrics_commons_test\n' \ + "${{ job.services.postgres.ports['5432'] }}" "$GITHUB_RUN_ID" "$GITHUB_RUN_ATTEMPT" >> "$GITHUB_ENV" + - name: Install pinned nightly with LLVM tools + run: rustup toolchain install nightly-2026-08-18 --profile minimal --component llvm-tools-preview + - name: Install pinned cargo-llvm-cov + run: cargo +nightly-2026-08-18 install cargo-llvm-cov --locked --version "$CARGO_LLVM_COV_VERSION" + - name: Verify cargo-llvm-cov version + run: cargo +nightly-2026-08-18 llvm-cov --version | grep -F "$CARGO_LLVM_COV_VERSION" + - name: Generate branch coverage + run: cargo +nightly-2026-08-18 llvm-cov --branch --json --summary-only --output-path coverage-branches.json + - name: Enforce complete branch coverage + run: python3 scripts/check_coverage.py coverage-branches.json --kind branches + - name: Diagnose missing branch coverage + if: failure() + run: | + python3 - <<'PY' + import json + from pathlib import Path + + payload = json.loads(Path("coverage-branches.json").read_text(encoding="utf-8")) + entry = payload["data"][0] + kind = "branches" + for file_entry in entry.get("files", []): + summary = file_entry.get("summary", {}) + metric = summary.get(kind) + if isinstance(metric, dict) and metric.get("covered") != metric.get("count"): + print( + "INCOMPLETE_FILE " + f"{kind} {metric.get('covered')}/{metric.get('count')} " + f"{file_entry.get('filename')}" + ) + PY + cargo +nightly-2026-08-18 llvm-cov report --branch --lcov --output-path coverage-branches.lcov + python3 - <<'PY' + from pathlib import Path + + source = None + for raw_line in Path("coverage-branches.lcov").read_text(encoding="utf-8").splitlines(): + if raw_line.startswith("SF:"): + source = raw_line[3:] + elif raw_line.startswith("BRDA:"): + line, block, branch, taken = raw_line[5:].split(",", 3) + if taken in {"0", "-"}: + print(f"UNCOVERED_BRANCH {source}:{line}:{block}:{branch}") + PY diff --git a/.github/workflows/python-security.yml b/.github/workflows/python-security.yml index 8453895027..6e07d817c2 100644 --- a/.github/workflows/python-security.yml +++ b/.github/workflows/python-security.yml @@ -46,7 +46,9 @@ jobs: detect-python: name: Detect Python if: github.event.action != 'closed' - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] outputs: has_python: ${{ steps.detect.outputs.has_python }} has_manifest: ${{ steps.detect.outputs.has_manifest }} @@ -77,7 +79,9 @@ jobs: name: Bandit (Python SAST) needs: detect-python if: github.event.action != 'closed' && needs.detect-python.outputs.has_python == 'true' - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] permissions: contents: read security-events: write @@ -204,7 +208,9 @@ jobs: name: pip-audit (Python dependency audit) needs: detect-python if: github.event.action != 'closed' && needs.detect-python.outputs.has_manifest == 'true' - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] permissions: contents: read steps: @@ -227,6 +233,30 @@ jobs: run: | set -euo pipefail status=0 + audit_log="$(mktemp)" + + # Run one pip-audit invocation and classify a non-zero exit. pip-audit + # 2.10.1 prints "Found N known vulnerabilit(y|ies) ... in N package(s)" + # on stderr only when it has findings (pip_audit/_cli.py); any other + # non-zero exit is an audit-service/transport failure (advisory query + # traceback, resolver/_fatal error) and must not be reported as a + # vulnerability finding (#2158). Both keep the gate closed. + run_audit() { + local label="$1" + shift + echo "::group::pip-audit ${label}" + if pip-audit "$@" 2>&1 | tee "${audit_log}"; then + echo "::endgroup::" + return 0 + fi + echo "::endgroup::" + status=1 + if grep -Eq "Found [0-9]+ known vulnerabilit" "${audit_log}"; then + echo "::error::pip-audit found known-vulnerable Python dependencies in ${label}. Remediate the pins listed above." + else + echo "::error::pip-audit could not complete for ${label}: $(grep -Ev '^[[:space:]]*$' "${audit_log}" | tail -n 1). This is an audit-service/transport failure, not a vulnerability finding; rerun the job before treating it as a security result." + fi + } # Audit every discovered requirements file. while IFS= read -r req; do @@ -251,15 +281,11 @@ jobs: base="${req%.txt}" unhashed_base="${base%-hashes}" if [ "$base" != "$unhashed_base" ] && [ -f "${unhashed_base}-overrides.txt" ]; then - echo "::group::pip-audit -r ${req} (--disable-pip --no-deps: overridden lock)" - pip-audit --strict --desc=on --no-deps --disable-pip -r "${req}" || status=1 - echo "::endgroup::" + run_audit "-r ${req} (--disable-pip --no-deps: overridden lock)" --strict --desc=on --no-deps --disable-pip -r "${req}" elif [ "$base" = "$unhashed_base" ] && [ -f "${unhashed_base}-overrides.txt" ]; then echo "::notice::Skipping pip-audit for ${req}: it is the raw input to an overridden lock (${unhashed_base}-hashes.txt), never itself a pip install --require-hashes target, and its compiled hashes file is audited separately with full resolution." else - echo "::group::pip-audit -r ${req}" - pip-audit --strict --desc=on -r "${req}" || status=1 - echo "::endgroup::" + run_audit "-r ${req}" --strict --desc=on -r "${req}" fi done < <(find . -type f -name 'requirements*.txt' -not -path './.git/*') @@ -267,12 +293,10 @@ jobs: if find . -maxdepth 2 -type f \ \( -name 'pyproject.toml' -o -name 'pylock.*.toml' \) \ -not -path './.git/*' -print -quit | grep -q .; then - echo "::group::pip-audit . (project manifest)" - pip-audit --strict --desc=on . || status=1 - echo "::endgroup::" + run_audit ". (project manifest)" --strict --desc=on . fi if [ "${status}" != "0" ]; then - echo "::error::pip-audit reported known-vulnerable Python dependencies." + echo "::error::pip-audit failed for at least one input; the per-input errors above say whether it was a finding or an audit-service failure." exit 1 fi diff --git a/.github/workflows/quarantine-sandbox-runtime-ci.yml b/.github/workflows/quarantine-sandbox-runtime-ci.yml new file mode 100644 index 0000000000..7e8760f9eb --- /dev/null +++ b/.github/workflows/quarantine-sandbox-runtime-ci.yml @@ -0,0 +1,375 @@ +name: Quarantine sandbox runtime CI + +on: + workflow_call: + +permissions: + contents: read + +concurrency: + group: >- + qsr-central-${{ github.workflow }}-${{ github.repository }}-${{ + github.event_name == 'pull_request' && github.event.pull_request.number || github.run_id }} + cancel-in-progress: true + +jobs: + admission: + runs-on: + group: CWL QSR hostile workload + labels: [self-hosted, linux, x64, cwl-hostile-workload] + permissions: + contents: read + steps: + - name: Reject unsupported caller or event + env: + CALLER_REPOSITORY: ${{ github.repository }} + EVENT_NAME: ${{ github.event_name }} + PR_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name || '' }} + run: | + test "$CALLER_REPOSITORY" = "ContextualWisdomLab/quarantine-sandbox-runtime" || exit 1 + case "$EVENT_NAME" in + push) ;; + pull_request) + test "$PR_HEAD_REPOSITORY" = "$CALLER_REPOSITORY" || exit 1 + ;; + *) exit 1 ;; + esac + + verify: + needs: admission + if: >- + github.repository == 'ContextualWisdomLab/quarantine-sandbox-runtime' && + (github.event_name == 'push' || + (github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository)) + runs-on: + group: CWL QSR hostile workload + labels: [self-hosted, linux, x64, cwl-hostile-workload] + steps: + - name: Verify caller and event before checkout + env: + CALLER_REPOSITORY: ${{ github.repository }} + EVENT_NAME: ${{ github.event_name }} + PR_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name || '' }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || '' }} + PUSH_SHA: ${{ github.sha }} + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + test "$CALLER_REPOSITORY" = "ContextualWisdomLab/quarantine-sandbox-runtime" || exit 1 + case "$EVENT_NAME" in + push) + [[ "$PUSH_SHA" =~ ^[0-9a-f]{40}$ ]] || exit 1 + test "$EXPECTED_HEAD_SHA" = "$PUSH_SHA" || exit 1 + ;; + pull_request) + test "$PR_HEAD_REPOSITORY" = "$CALLER_REPOSITORY" || exit 1 + [[ "$PR_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]] || exit 1 + test "$EXPECTED_HEAD_SHA" = "$PR_HEAD_SHA" || exit 1 + ;; + *) exit 1 ;; + esac + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - name: Verify exact checkout + env: + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD_SHA" + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable installer + with: + toolchain: 1.97.1 + components: clippy, rustfmt + - name: Verify dependency lock + run: cargo metadata --locked --no-deps --format-version 1 > /dev/null + - name: Validate repository policy + run: python3 scripts/validate_repository.py + - name: Test coverage evidence parser + run: python3 -m unittest scripts/test_check_coverage.py + - name: Test caller CI contract + run: python3 -B -m unittest scripts/test_central_ci_caller.py scripts/test_central_ci_caller_sensitivity.py + - name: Check formatting + run: cargo fmt --check + - name: Test + run: cargo test --locked --workspace --all-targets + - name: Lint + run: cargo clippy --locked --workspace --all-targets -- -D warnings + - name: Build documentation + env: + RUSTDOCFLAGS: -D warnings + run: cargo doc --locked --workspace --no-deps + + coverage: + needs: admission + if: >- + github.repository == 'ContextualWisdomLab/quarantine-sandbox-runtime' && + (github.event_name == 'push' || + (github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository)) + runs-on: + group: CWL QSR hostile workload + labels: [self-hosted, linux, x64, cwl-hostile-workload] + steps: + - name: Verify caller and event before checkout + env: + CALLER_REPOSITORY: ${{ github.repository }} + EVENT_NAME: ${{ github.event_name }} + PR_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name || '' }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || '' }} + PUSH_SHA: ${{ github.sha }} + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + test "$CALLER_REPOSITORY" = "ContextualWisdomLab/quarantine-sandbox-runtime" || exit 1 + case "$EVENT_NAME" in + push) + [[ "$PUSH_SHA" =~ ^[0-9a-f]{40}$ ]] || exit 1 + test "$EXPECTED_HEAD_SHA" = "$PUSH_SHA" || exit 1 + ;; + pull_request) + test "$PR_HEAD_REPOSITORY" = "$CALLER_REPOSITORY" || exit 1 + [[ "$PR_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]] || exit 1 + test "$EXPECTED_HEAD_SHA" = "$PR_HEAD_SHA" || exit 1 + ;; + *) exit 1 ;; + esac + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - name: Verify exact checkout + env: + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD_SHA" + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable installer + with: + toolchain: 1.97.1 + components: llvm-tools-preview + - name: Verify dependency lock + run: cargo metadata --locked --no-deps --format-version 1 > /dev/null + - name: Install pinned coverage tool + run: cargo +1.97.1 install cargo-llvm-cov --locked --version 0.8.6 + - name: Generate production coverage evidence + run: >- + cargo llvm-cov + --workspace + --lib + --tests + --json + --output-path coverage.json + - name: Upload production coverage evidence + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: coverage-json-${{ github.event.pull_request.head.sha || github.sha }} + path: coverage.json + if-no-files-found: error + - name: Explain and enforce complete production coverage + run: python3 scripts/check_coverage.py coverage.json + + branch-coverage: + needs: admission + if: >- + github.repository == 'ContextualWisdomLab/quarantine-sandbox-runtime' && + (github.event_name == 'push' || + (github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository)) + runs-on: + group: CWL QSR hostile workload + labels: [self-hosted, linux, x64, cwl-hostile-workload] + steps: + - name: Verify caller and event before checkout + env: + CALLER_REPOSITORY: ${{ github.repository }} + EVENT_NAME: ${{ github.event_name }} + PR_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name || '' }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || '' }} + PUSH_SHA: ${{ github.sha }} + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + test "$CALLER_REPOSITORY" = "ContextualWisdomLab/quarantine-sandbox-runtime" || exit 1 + case "$EVENT_NAME" in + push) + [[ "$PUSH_SHA" =~ ^[0-9a-f]{40}$ ]] || exit 1 + test "$EXPECTED_HEAD_SHA" = "$PUSH_SHA" || exit 1 + ;; + pull_request) + test "$PR_HEAD_REPOSITORY" = "$CALLER_REPOSITORY" || exit 1 + [[ "$PR_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]] || exit 1 + test "$EXPECTED_HEAD_SHA" = "$PR_HEAD_SHA" || exit 1 + ;; + *) exit 1 ;; + esac + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - name: Verify exact checkout + env: + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD_SHA" + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable installer + with: + toolchain: nightly-2026-07-01 + components: llvm-tools-preview + - name: Verify dependency lock + run: cargo +nightly-2026-07-01 metadata --locked --no-deps --format-version 1 > /dev/null + - name: Install pinned coverage tool + run: cargo +nightly-2026-07-01 install cargo-llvm-cov --locked --version 0.8.6 + - name: Generate branch coverage evidence + run: >- + cargo +nightly-2026-07-01 llvm-cov + --branch + --workspace + --lib + --tests + --json + --output-path branch-coverage.json + - name: Print missing branch coverage lines + run: >- + cargo +nightly-2026-07-01 llvm-cov report + --branch + --show-missing-lines + - name: Upload branch coverage evidence + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: branch-coverage-json-${{ github.event.pull_request.head.sha || github.sha }} + path: branch-coverage.json + if-no-files-found: error + - name: Enforce complete production branch coverage + run: python3 scripts/check_coverage.py --require-branches branch-coverage.json + + podman-e2e-positive-lsm: + needs: admission + if: >- + github.repository == 'ContextualWisdomLab/quarantine-sandbox-runtime' && + (github.event_name == 'push' || + (github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository)) + runs-on: + group: CWL QSR hostile workload + labels: [self-hosted, linux, x64, cwl-hostile-workload, selinux] + env: + # Required profile only: NOT_PROVISIONED pending .github#1590; no custody claim. + QSR_PODMAN_E2E_IMAGE: docker.io/library/python@sha256:94457973ea8a27a799f0b8ea1fe3e3147fcbebaed63497a8af63b28195a08108 + QSR_HOST_SECRET_CANARY: must-not-enter-the-isolated-application + steps: + - name: Verify caller and event before checkout + env: + CALLER_REPOSITORY: ${{ github.repository }} + EVENT_NAME: ${{ github.event_name }} + PR_HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name || '' }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || '' }} + PUSH_SHA: ${{ github.sha }} + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + test "$CALLER_REPOSITORY" = "ContextualWisdomLab/quarantine-sandbox-runtime" || exit 1 + case "$EVENT_NAME" in + push) + [[ "$PUSH_SHA" =~ ^[0-9a-f]{40}$ ]] || exit 1 + test "$EXPECTED_HEAD_SHA" = "$PUSH_SHA" || exit 1 + ;; + pull_request) + test "$PR_HEAD_REPOSITORY" = "$CALLER_REPOSITORY" || exit 1 + [[ "$PR_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]] || exit 1 + test "$EXPECTED_HEAD_SHA" = "$PR_HEAD_SHA" || exit 1 + ;; + *) exit 1 ;; + esac + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + - name: Verify exact checkout + env: + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: test "$(git rev-parse HEAD)" = "$EXPECTED_HEAD_SHA" + - uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable installer + with: + toolchain: 1.97.1 + - name: Verify dedicated positive LSM runner contract + run: | + test -n "$RUNNER_NAME" + test "$(podman --version)" = "podman version 5.8.4" + podman info --format json > podman-info.json + python3 - <<'PY' + import json + with open("podman-info.json", encoding="utf-8") as handle: + security = json.load(handle)["host"]["security"] + assert security["rootless"] is True + assert security["selinuxEnabled"] is True + print(f"selinuxEnabled={security['selinuxEnabled']}") + PY + - name: Pre-pull immutable fixture outside the application launch path + run: | + podman pull --quiet "$QSR_PODMAN_E2E_IMAGE" + podman image inspect "$QSR_PODMAN_E2E_IMAGE" >/dev/null + - name: Execute real positive LSM isolation and cleanup acceptance + run: | + RUST_TEST_NOCAPTURE=0 python3 -I - <<'PY' + import re + import subprocess + import sys + + test_name = "rootless_podman_effective_isolation_and_cleanup" + command = [ + "cargo", "test", "--locked", "--test", "podman_rootless_e2e", "--", + "--ignored", "--exact", test_name, "--color", "never", "--format", "pretty", + "--test-threads=1", + ] + completed = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + output = completed.stdout.decode("utf-8", errors="replace") + print(output, end="", flush=True) + if completed.returncode: + raise SystemExit(completed.returncode) + lines = output.splitlines() + expected_result = re.compile( + r"test result: ok\. 1 passed; 0 failed; 0 ignored; 0 measured; " + r"[0-9]+ filtered out; finished in [0-9.]+s" + ) + observed = [ + line for line in lines + if line.startswith("running ") + or line.startswith("test ") + or line.startswith("test result:") + ] + expected = [ + "running 1 test", + f"test {test_name} ... ok", + ] + if len(observed) != 3 or observed[:2] != expected or not expected_result.fullmatch(observed[2]): + print("LSM-TEST-001: expected exactly one passing isolation test", file=sys.stderr) + raise SystemExit(1) + PY + - name: Reject leaked runtime-owned resources + if: always() + run: | + leaked_containers="$(podman ps -a --filter label=org.contextualwisdomlab.sandbox.identity --format '{{.ID}}')" + test -z "$leaked_containers" + networks="$(podman network ls --format '{{.Name}}')" + while IFS= read -r network; do + case "$network" in + qsr-net-*) exit 1 ;; + esac + done <<< "$networks" + + acceptance: + if: always() + needs: [admission, verify, coverage, branch-coverage, podman-e2e-positive-lsm] + runs-on: + group: CWL QSR hostile workload + labels: [self-hosted, linux, x64, cwl-hostile-workload] + permissions: + contents: read + steps: + - name: Require every current-head evidence lane to pass + env: + ADMISSION_RESULT: ${{ needs.admission.result }} + VERIFY_RESULT: ${{ needs.verify.result }} + COVERAGE_RESULT: ${{ needs.coverage.result }} + BRANCH_RESULT: ${{ needs.branch-coverage.result }} + LSM_RESULT: ${{ needs.podman-e2e-positive-lsm.result }} + run: | + test "$ADMISSION_RESULT" = "success" + test "$VERIFY_RESULT" = "success" + test "$COVERAGE_RESULT" = "success" + test "$BRANCH_RESULT" = "success" + test "$LSM_RESULT" = "success" diff --git a/.github/workflows/r-package-check.yml b/.github/workflows/r-package-check.yml index 221d66a838..0d23daac2a 100644 --- a/.github/workflows/r-package-check.yml +++ b/.github/workflows/r-package-check.yml @@ -45,8 +45,8 @@ on: description: >- JSON array of {os, r, http-user-agent?} objects for strategy.matrix.config. Default is a single ubuntu-latest/release - leg; override with a JSON array for a multi-OS/multi-R-version - matrix. + leg. Only ubuntu-latest and ubuntu-24.04 Linux/x64 image aliases + execute; other OS/architecture legs are explicitly STOP, not PASS. required: false type: string default: '[{"os": "ubuntu-latest", "r": "release"}]' @@ -91,14 +91,63 @@ permissions: contents: read jobs: + admit-platforms: + runs-on: + group: CWL CI isolated + labels: [self-hosted, Linux, X64, cwlab-ci-isolated] + outputs: + matrix: ${{ steps.admit.outputs.matrix }} + steps: + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.10' + - id: admit + env: + R_MATRIX: ${{ inputs.r_matrix }} + shell: bash + run: | + matrix=$(python - "$R_MATRIX" <<'PY' + """Bound the R matrix to supported Linux/x64 image aliases, never runner labels.""" + import json + import sys + + + def admit(raw): + """Reject unsupported matrix legs before any R job can be scheduled.""" + rows = json.loads(raw) + if not isinstance(rows, list) or not 1 <= len(rows) <= 64: + raise ValueError('Expected 1..64 matrix objects') + for row in rows: + if not isinstance(row, dict) or not isinstance(row.get('os'), str): + raise ValueError('Expected an OS string in each matrix object') + if not isinstance(row.get('r'), str) or not row['r'].strip(): + raise ValueError('Expected a nonempty R version') + if row['os'] in ('ubuntu-latest', 'ubuntu-24.04'): + continue + raise ValueError('STOP unsupported platform leg: ' + repr(row['os'])) + return rows + + + if __name__ == '__main__': + try: + print(json.dumps(admit(sys.argv[1]), separators=(',', ':'))) + except (ValueError, TypeError, IndexError) as error: + print(str(error), file=sys.stderr) + sys.exit(1) + PY + ) + printf 'matrix=%s\n' "$matrix" >> "$GITHUB_OUTPUT" R-CMD-check: - runs-on: ${{ matrix.config.os }} + needs: admit-platforms + runs-on: + group: CWL CI isolated + labels: [self-hosted, Linux, X64, cwlab-ci-isolated] name: ${{ matrix.config.os }} (${{ matrix.config.r }}) strategy: fail-fast: false matrix: - config: ${{ fromJSON(inputs.r_matrix) }} + config: ${{ fromJSON(needs.admit-platforms.outputs.matrix) }} env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-dependency-license-strix-gate.yml b/.github/workflows/release-dependency-license-strix-gate.yml new file mode 100644 index 0000000000..8fbec4df37 --- /dev/null +++ b/.github/workflows/release-dependency-license-strix-gate.yml @@ -0,0 +1,1106 @@ +name: Release Dependency License and Strix Gate + +# Central pre-publish dependency gate (issue #2342). A release workflow calls +# this BEFORE it publishes anything. There is no neutral outcome: the gate +# either succeeds or the release is refused. The organization's scheduled SBOM +# roll-up (scripts/ci/sbom_inventory_aggregator.py) is informational governance +# reporting and is deliberately not reused here. +# +# On success the job returns the complete distribution verdict alongside the +# existing inputs of .github/workflows/exact-artifact-sbom-attestation.yml: +# +# gate: +# uses: ContextualWisdomLab/.github/.github/workflows/release-dependency-license-strix-gate.yml@ +# secrets: inherit +# attest: +# needs: gate +# uses: ContextualWisdomLab/.github/.github/workflows/exact-artifact-sbom-attestation.yml@ +# with: +# source_repository: ${{ needs.gate.outputs.source_repository }} +# ... +# +# The caller must verify the complete verdict separately before admitting the +# full distribution set; the existing attestation still seals one wheel/sdist pair. + +on: + workflow_call: + inputs: + source_repository: + description: Release repository in owner/name form. + required: true + type: string + source_sha: + description: Exact release head commit SHA. + required: true + type: string + ecosystems: + description: Comma-separated ecosystems to enumerate (python and/or cargo). + required: true + type: string + python_lock_path: + description: Hash-pinned Python lock installed into the build environment. + required: false + type: string + default: "" + cargo_manifest_path: + description: Release Cargo.toml whose Cargo.lock and build graph are gated. + required: false + type: string + default: "" + cargo_dev_manifest_path: + description: Development Cargo member whose workspace lock must also be gated. + required: false + type: string + default: "" + distribution_set_artifact_id: + description: Immutable same-run reproducibility record artifact ID containing the complete distribution set manifest. + required: true + type: string + distribution_set_artifact_digest: + description: Expected sha256-prefixed reproducibility record artifact digest from the producer upload. + required: true + type: string + wheel_filename: + description: Exact wheel filename inside the build artifact. + required: true + type: string + sdist_filename: + description: Exact source distribution filename inside the build artifact. + required: true + type: string + evidence_artifact_name: + description: Unique per-call sealed evidence name; also namespaces diagnostic reports. The default retains legacy report names. + required: false + type: string + default: release-dependency-sealed-evidence + # The five provider credentials are declared optional so the licence stage, + # which needs none of them, can run on a review-only negative fixture that + # never reaches Strix. Optional is not lenient: the Strix stage refuses to + # start unless all five are present (STRIX_CREDENTIALS_ABSENT), so an allowed + # input that reaches Strix without credentials fails closed rather than being + # skipped, neutralized, or passed. `required: false` exists so a caller that + # passes no secrets fails on the *licence decision* rather than on + # `workflow_call` schema validation — a schema error is not evidence of a + # licence denial or of Strix being blocked. It is not an invitation to supply + # dummy secrets, and it does not widen any caller's secret exposure. + secrets: + BYTEZ_API_KEY: + required: false + NVIDIA_NIM_API_KEY: + required: false + NVIDIA_NIM_API_KEY_SUB: + required: false + OPENROUTER_API_KEY: + required: false + OPENAI_API_KEY: + required: false + outputs: + full_set_verdict_artifact_id: + description: Immutable same-run complete distribution and dependency verdict artifact ID. + value: ${{ jobs.gate.outputs.full_set_verdict_artifact_id }} + full_set_verdict_artifact_digest: + description: SHA-256 digest of the complete verdict artifact archive. + value: ${{ jobs.gate.outputs.full_set_verdict_artifact_digest }} + source_repository: + description: Gated release repository. + value: ${{ jobs.gate.outputs.source_repository }} + source_sha: + description: Gated release head SHA. + value: ${{ jobs.gate.outputs.source_sha }} + evidence_artifact_id: + description: Immutable same-run sealed evidence artifact ID. + value: ${{ jobs.gate.outputs.evidence_artifact_id }} + evidence_artifact_name: + description: Sealed evidence artifact name. + value: ${{ jobs.gate.outputs.evidence_artifact_name }} + evidence_artifact_digest: + description: Sealed evidence artifact digest in sha256: form. + value: ${{ jobs.gate.outputs.evidence_artifact_digest }} + wheel_filename: + description: Exact gated wheel filename. + value: ${{ jobs.gate.outputs.wheel_filename }} + wheel_sha256: + description: SHA-256 of the exact gated wheel. + value: ${{ jobs.gate.outputs.wheel_sha256 }} + wheel_sbom_filename: + description: CycloneDX SBOM filename for the gated wheel. + value: ${{ jobs.gate.outputs.wheel_sbom_filename }} + wheel_sbom_sha256: + description: SHA-256 of the gated wheel's CycloneDX SBOM. + value: ${{ jobs.gate.outputs.wheel_sbom_sha256 }} + sdist_filename: + description: Exact gated source distribution filename. + value: ${{ jobs.gate.outputs.sdist_filename }} + sdist_sha256: + description: SHA-256 of the exact gated source distribution. + value: ${{ jobs.gate.outputs.sdist_sha256 }} + sdist_sbom_filename: + description: CycloneDX SBOM filename for the gated source distribution. + value: ${{ jobs.gate.outputs.sdist_sbom_filename }} + sdist_sbom_sha256: + description: SHA-256 of the gated source distribution's CycloneDX SBOM. + value: ${{ jobs.gate.outputs.sdist_sbom_sha256 }} + source_identity_sha256: + description: SHA-256 of the sealed source-identity.json. + value: ${{ jobs.gate.outputs.source_identity_sha256 }} + checksum_sha256: + description: SHA-256 of the sealed checksums.sha256. + value: ${{ jobs.gate.outputs.checksum_sha256 }} + predicate_type: + description: Canonical CycloneDX in-toto predicate type. + value: ${{ jobs.gate.outputs.predicate_type }} + cyclonedx_schema: + description: Canonical CycloneDX 1.7 schema URL. + value: ${{ jobs.gate.outputs.cyclonedx_schema }} + +permissions: + contents: read + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + prepare: + name: Verify exact distributions and approve the licence closure + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 180 + permissions: + contents: read + actions: read + outputs: + matrix_json: ${{ steps.fanout.outputs.matrix_json }} + matrix_overflow_json: ${{ steps.fanout.outputs.matrix_overflow_json }} + has_overflow: ${{ steps.fanout.outputs.has_overflow }} + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: Materialize immutable trusted gate + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: ContextualWisdomLab/.github + # Reviewed helper revision; intentionally distinct from workflow revision. + ref: e45f1b144aef900d734ff4c900f9e0010fd5a32d + path: trusted-gate + persist-credentials: false + # The whole scripts/ci tree, not an enumerated file list: the trusted + # Strix gate, the orchestrator sidecar and the token loader each source + # siblings by their own directory (strix_model_utils.sh, + # sanitize_contextual_orchestrator_sidecar_stream.py, + # install_strix_timeout_compat.py, strix_timeout_compat.py, …), and an + # enumeration silently breaks the moment one of them gains another. + sparse-checkout: | + scripts/ci/ + requirements-strix-ci-hashes.txt + sparse-checkout-cone-mode: false + + - name: Verify fixed helper checkout identity + env: + HELPER_ROOT: trusted-gate + CALLER_WORKFLOW_SHA: ${{ github.workflow_sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + expected=e45f1b144aef900d734ff4c900f9e0010fd5a32d + test "$(git -C "$HELPER_ROOT" rev-parse HEAD)" = "$expected" + origin="$(git -C "$HELPER_ROOT" remote get-url origin)" + case "$origin" in + https://github.com/ContextualWisdomLab/.github|https://github.com/ContextualWisdomLab/.github.git) ;; + *) echo "Foreign helper repository" >&2; exit 1 ;; + esac + test "$(git -C "$HELPER_ROOT" rev-parse HEAD:scripts/ci)" = 7f902df89a925f89c4fae69a842508406cd0207c + test "$(git -C "$HELPER_ROOT" rev-parse HEAD:requirements-strix-ci-hashes.txt)" = eb83beda177c9d2e4ca9b7e2888a1ccb55a123ac + git -C "$HELPER_ROOT" diff --exit-code HEAD -- scripts/ci requirements-strix-ci-hashes.txt + test -f "$HELPER_ROOT/scripts/ci/release_dependency_gate.py" + test -f "$HELPER_ROOT/scripts/ci/verify_release_distribution_set.py" + test -f "$HELPER_ROOT/scripts/ci/verify_release_scope_evidence_set.py" + test -f "$HELPER_ROOT/scripts/ci/prescreen_release_runtime_archives.py" + test -f "$HELPER_ROOT/scripts/ci/collect_release_strix_bindings.py" + test -f "$HELPER_ROOT/scripts/ci/verify_exact_artifact_sbom_handoff.py" + test -f "$HELPER_ROOT/requirements-strix-ci-hashes.txt" + printf 'helper_repository=ContextualWisdomLab/.github helper_sha=%s caller_workflow_sha=%s\n' "$expected" "$CALLER_WORKFLOW_SHA" + + - name: Validate the exact release identity before anything else runs + env: + SOURCE_REPOSITORY: ${{ inputs.source_repository }} + SOURCE_SHA: ${{ inputs.source_sha }} + EVIDENCE_ARTIFACT_NAME: ${{ inputs.evidence_artifact_name }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + # Keep sealed evidence outside both diagnostic artifact namespaces. + case "$EVIDENCE_ARTIFACT_NAME" in + release-dependency-sealed-evidence|license-evidence-?*) ;; + *) echo "evidence artifact name must use the license-evidence- namespace" >&2; exit 1 ;; + esac + # `workflow_call` can only type these inputs as `string`, so a branch + # name or a short SHA would otherwise be accepted here and only caught + # by the gate's own 40-hex check after Strix had already run. The shape + # is therefore checked by the trusted gate before the release head is + # even fetched, and long before any credential is materialized. + python3 -I trusted-gate/scripts/ci/release_dependency_gate.py validate-inputs \ + --source-repository "$SOURCE_REPOSITORY" \ + --source-sha "$SOURCE_SHA" + + - name: Check out the exact release head + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: ${{ inputs.source_repository }} + ref: ${{ inputs.source_sha }} + path: release-source + persist-credentials: false + + - name: Set up the release build interpreter + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.13" + + - name: List the current run and attempt artifacts + env: + GH_TOKEN: ${{ github.token }} + SOURCE_REPOSITORY: ${{ inputs.source_repository }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + test "$SOURCE_REPOSITORY" = "$GITHUB_REPOSITORY" + gh api --paginate "repos/${SOURCE_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/artifacts?per_page=100" \ + --jq '.artifacts[]' > "${RUNNER_TEMP}/release-artifacts.jsonl" + gh api "repos/${SOURCE_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/attempts/${GITHUB_RUN_ATTEMPT}" \ + > "${RUNNER_TEMP}/release-attempt.json" + + - name: Verify every immutable distribution before dependency capture + env: + GH_TOKEN: ${{ github.token }} + SOURCE_REPOSITORY: ${{ inputs.source_repository }} + SOURCE_SHA: ${{ inputs.source_sha }} + CONTROL_SHA: ${{ github.sha }} + RECORD_ID: ${{ inputs.distribution_set_artifact_id }} + RECORD_DIGEST: ${{ inputs.distribution_set_artifact_digest }} + WHEEL_FILENAME: ${{ inputs.wheel_filename }} + SDIST_FILENAME: ${{ inputs.sdist_filename }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -I trusted-gate/scripts/ci/verify_release_distribution_set.py \ + --repository "$SOURCE_REPOSITORY" \ + --source-sha "$SOURCE_SHA" \ + --control-sha "$CONTROL_SHA" \ + --run-id "$GITHUB_RUN_ID" \ + --run-attempt "$GITHUB_RUN_ATTEMPT" \ + --record-artifact-id "$RECORD_ID" \ + --record-artifact-digest "$RECORD_DIGEST" \ + --wheel-filename "$WHEEL_FILENAME" \ + --sdist-filename "$SDIST_FILENAME" \ + --metadata "${RUNNER_TEMP}/release-artifacts.jsonl" \ + --attempt "${RUNNER_TEMP}/release-attempt.json" \ + --output release-distributions > "${RUNNER_TEMP}/verified-distributions.json" + + - name: Inventory exact release wheel native links + env: + SOURCE_SHA: ${{ inputs.source_sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -I trusted-gate/scripts/ci/scan_release_native_links.py \ + --verified-distributions "${RUNNER_TEMP}/verified-distributions.json" \ + --distribution-root release-distributions \ + --source-sha "$SOURCE_SHA" \ + --output "${RUNNER_TEMP}/release-native-links.json" + + - name: Verify every immutable scope archive before Strix + env: + GH_TOKEN: ${{ github.token }} + SOURCE_REPOSITORY: ${{ inputs.source_repository }} + SOURCE_SHA: ${{ inputs.source_sha }} + CONTROL_SHA: ${{ github.sha }} + RECORD_ID: ${{ inputs.distribution_set_artifact_id }} + RECORD_DIGEST: ${{ inputs.distribution_set_artifact_digest }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -I trusted-gate/scripts/ci/verify_release_scope_evidence_set.py \ + --repository "$SOURCE_REPOSITORY" \ + --source-sha "$SOURCE_SHA" \ + --control-sha "$CONTROL_SHA" \ + --run-id "$GITHUB_RUN_ID" \ + --run-attempt "$GITHUB_RUN_ATTEMPT" \ + --record-artifact-id "$RECORD_ID" \ + --record-artifact-digest "$RECORD_DIGEST" \ + --verified-distributions "${RUNNER_TEMP}/verified-distributions.json" \ + --metadata "${RUNNER_TEMP}/release-artifacts.jsonl" \ + --attempt "${RUNNER_TEMP}/release-attempt.json" \ + --output release-scope-evidence > "${RUNNER_TEMP}/verified-scope-evidence.json" + + - name: Recheck exact maturin release assets and native links + shell: bash --noprofile --norc -e -o pipefail {0} + run: python3 -I trusted-gate/scripts/ci/verify_release_maturin_tool_assets.py + + - name: Refuse denied runtime wheel licences before Strix + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -I trusted-gate/scripts/ci/prescreen_release_runtime_archives.py \ + --verified-scope "${RUNNER_TEMP}/verified-scope-evidence.json" \ + --scope-root release-scope-evidence \ + --output "${RUNNER_TEMP}/runtime-archive-license-report.json" + + - name: Collect the release closure without installing or executing it + env: + ECOSYSTEMS: ${{ inputs.ecosystems }} + PYTHON_LOCK_PATH: ${{ inputs.python_lock_path }} + CARGO_MANIFEST_PATH: ${{ inputs.cargo_manifest_path }} + CARGO_DEV_MANIFEST_PATH: ${{ inputs.cargo_dev_manifest_path }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python_lock="" + cargo_manifest="" + cargo_dev_manifest="" + if [ -n "$PYTHON_LOCK_PATH" ]; then + python_lock="${PWD}/release-source/${PYTHON_LOCK_PATH}" + fi + if [ -n "$CARGO_MANIFEST_PATH" ]; then + cargo_manifest="${PWD}/release-source/${CARGO_MANIFEST_PATH}" + fi + if [ -n "$CARGO_DEV_MANIFEST_PATH" ]; then + cargo_dev_manifest="${PWD}/release-source/${CARGO_DEV_MANIFEST_PATH}" + fi + bash trusted-gate/scripts/ci/release_dependency_capture_raw.sh \ + --raw-root "${RUNNER_TEMP}/raw" \ + --capture-root "${RUNNER_TEMP}/capture" \ + --ecosystems "$ECOSYSTEMS" \ + --python-lock "$python_lock" \ + --download-root "${RUNNER_TEMP}/collected" \ + --cargo-manifest "$cargo_manifest" \ + --cargo-dev-manifest "$cargo_dev_manifest" + + - name: Assemble per-dependency evidence and isolated synthetic fixtures + env: + SOURCE_REPOSITORY: ${{ inputs.source_repository }} + SOURCE_SHA: ${{ inputs.source_sha }} + ECOSYSTEMS: ${{ inputs.ecosystems }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + jq -n \ + --arg repository "$SOURCE_REPOSITORY" \ + --arg sha "$SOURCE_SHA" \ + --arg ecosystems "$ECOSYSTEMS" \ + '{source_repository: $repository, source_sha: $sha, + ecosystems: ($ecosystems | split(","))}' \ + > "${RUNNER_TEMP}/capture/release.json" + python3 -I trusted-gate/scripts/ci/release_dependency_gate.py capture \ + --raw "${RUNNER_TEMP}/raw" \ + --capture "${RUNNER_TEMP}/capture" + python3 -I trusted-gate/scripts/ci/release_dependency_gate.py capture-license-selections \ + --source release-source --source-sha "$SOURCE_SHA" \ + --capture "${RUNNER_TEMP}/capture" + + - name: Refuse a denied or unverifiable licence before any credential exists + id: license-stage + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + # The licence determination runs here, ahead of every credentialed and + # model step, using the *same* evaluate_dependency_license path the final + # gate uses — so a GPL/LGPL/AGPL dependency, an UNKNOWN licence, or an + # `OR` expression with no recorded permissive selection refuses the + # release before a provider secret is ever read. `capture` alone does not + # reject a licence; it only assembles evidence and fixtures. This stage + # also performs the full-set scope comparison, so an ecosystem whose + # membership cannot be established fails here too. + python3 -I trusted-gate/scripts/ci/release_dependency_gate.py prescreen \ + --source release-source \ + --capture "${RUNNER_TEMP}/capture" \ + --report "${RUNNER_TEMP}/license-report.json" + + - name: Install the prescreened closure into a lock-only environment + if: ${{ inputs.python_lock_path != '' }} + env: + PYTHON_LOCK_PATH: ${{ inputs.python_lock_path }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + # Installing runs dependency code, so it happens only after the licence + # stage above has passed, and only from the bytes that stage judged: + # --no-index --find-links over the collected distributions, with + # --require-hashes so pip proves each file against the lock. Nothing is + # re-resolved or re-downloaded, so the installed bytes are the inspected + # bytes even when the lock records several hashes for a project. + # --without-pip keeps the environment's contents exactly what the lock + # installed, so LOCK_ENV_MISMATCH means a real disagreement. + python3 -m venv --without-pip "${RUNNER_TEMP}/gate-venv" + bash trusted-gate/scripts/ci/release_dependency_capture_raw.sh \ + --install-gated \ + --python-lock "${PWD}/release-source/${PYTHON_LOCK_PATH}" \ + --python-interpreter "${RUNNER_TEMP}/gate-venv/bin/python" \ + --capture-root "${RUNNER_TEMP}/capture" \ + --download-root "${RUNNER_TEMP}/collected" \ + --license-report "${RUNNER_TEMP}/license-report.json" + + - name: Publish the exact licence-approved fixture matrix + id: fanout + env: + CONTROL_SHA: ${{ github.sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -I trusted-gate/scripts/ci/release_dependency_gate.py fanout-plan \ + --capture "${RUNNER_TEMP}/capture" \ + --license-report "${RUNNER_TEMP}/license-report.json" \ + --runtime-archive-license-report "${RUNNER_TEMP}/runtime-archive-license-report.json" \ + --control-sha "$CONTROL_SHA" \ + --run-id "$GITHUB_RUN_ID" \ + --run-attempt "$GITHUB_RUN_ATTEMPT" \ + --output "${RUNNER_TEMP}/strix-fanout-plan.json" + + - name: Export the pre-credential licence report + if: ${{ !cancelled() && steps.license-stage.conclusion != 'skipped' }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 + with: + name: ${{ inputs.evidence_artifact_name == 'release-dependency-sealed-evidence' && 'release-dependency-license-report' || format('release-dependency-license-report--{0}', inputs.evidence_artifact_name) }} + path: ${{ runner.temp }}/license-report.json + if-no-files-found: error + + strix: + name: Strix ${{ matrix.key }} + needs: prepare + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 360 + permissions: + contents: read + strategy: + fail-fast: false + max-parallel: ${{ needs.prepare.outputs.has_overflow == 'true' && 4 || 8 }} + matrix: ${{ fromJSON(needs.prepare.outputs.matrix_json) }} + steps: &strix_steps + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: Materialize immutable trusted gate + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: ContextualWisdomLab/.github + # Reviewed helper revision; intentionally distinct from workflow revision. + ref: e45f1b144aef900d734ff4c900f9e0010fd5a32d + path: trusted-gate + persist-credentials: false + # The whole scripts/ci tree, not an enumerated file list: the trusted + # Strix gate, the orchestrator sidecar and the token loader each source + # siblings by their own directory (strix_model_utils.sh, + # sanitize_contextual_orchestrator_sidecar_stream.py, + # install_strix_timeout_compat.py, strix_timeout_compat.py, …), and an + # enumeration silently breaks the moment one of them gains another. + sparse-checkout: | + scripts/ci/ + requirements-strix-ci-hashes.txt + sparse-checkout-cone-mode: false + + - name: Verify fixed helper checkout identity + env: + HELPER_ROOT: trusted-gate + CALLER_WORKFLOW_SHA: ${{ github.workflow_sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + expected=e45f1b144aef900d734ff4c900f9e0010fd5a32d + test "$(git -C "$HELPER_ROOT" rev-parse HEAD)" = "$expected" + origin="$(git -C "$HELPER_ROOT" remote get-url origin)" + case "$origin" in + https://github.com/ContextualWisdomLab/.github|https://github.com/ContextualWisdomLab/.github.git) ;; + *) echo "Foreign helper repository" >&2; exit 1 ;; + esac + test "$(git -C "$HELPER_ROOT" rev-parse HEAD:scripts/ci)" = 7f902df89a925f89c4fae69a842508406cd0207c + test "$(git -C "$HELPER_ROOT" rev-parse HEAD:requirements-strix-ci-hashes.txt)" = eb83beda177c9d2e4ca9b7e2888a1ccb55a123ac + git -C "$HELPER_ROOT" diff --exit-code HEAD -- scripts/ci requirements-strix-ci-hashes.txt + test -f "$HELPER_ROOT/scripts/ci/release_dependency_gate.py" + test -f "$HELPER_ROOT/scripts/ci/verify_release_distribution_set.py" + test -f "$HELPER_ROOT/scripts/ci/verify_release_scope_evidence_set.py" + test -f "$HELPER_ROOT/scripts/ci/prescreen_release_runtime_archives.py" + test -f "$HELPER_ROOT/scripts/ci/collect_release_strix_bindings.py" + test -f "$HELPER_ROOT/scripts/ci/verify_exact_artifact_sbom_handoff.py" + test -f "$HELPER_ROOT/requirements-strix-ci-hashes.txt" + printf 'helper_repository=ContextualWisdomLab/.github helper_sha=%s caller_workflow_sha=%s\n' "$expected" "$CALLER_WORKFLOW_SHA" + + - name: Require every Strix provider credential before the Strix stage starts + env: + BYTEZ_API_KEY: ${{ secrets.BYTEZ_API_KEY }} + NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} + NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} + OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + # The secrets are optional on the contract so the licence stage above can + # run without them. An allowed input that gets this far must still be + # scanned, so absence is a refusal with STRIX_CREDENTIALS_ABSENT. This is + # deliberately not an `if:` condition: a condition would *skip* the Strix + # stage and let the release proceed unscanned. The reason code names only + # the absent variables and never echoes or measures a present value. + python3 -I trusted-gate/scripts/ci/release_dependency_gate.py require-strix-credentials + + - name: Provision the zero-cost review gateway for Strix + env: + BYTEZ_API_KEY: ${{ secrets.BYTEZ_API_KEY }} + NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} + NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} + OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + bash trusted-gate/scripts/ci/contextual_orchestrator_review_sidecar.sh + + # Scope boundary, recorded rather than left implicit. The steps below install + # the *gate's own* toolchain — this repository's hash-pinned + # requirements-strix-ci-hashes.txt, materialized from github.workflow_sha, and + # the orchestrator sidecar's own pinned lock. They are a different trust domain + # from the caller's release closure: they are pinned and reviewed in this + # repository, and the licence stage that judges the closure cannot judge the + # scanner it has to run first without a cycle. They are therefore NOT covered by + # the prescreen above, and that is a stated limit, not an exemption: bringing + # the gate's own dependencies under a licence verdict is an owner decision, + # tracked separately, and nothing here may be read as evidence that it happened. + - name: Install the pinned Strix toolchain + working-directory: trusted-gate + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + # Mirrors .github/workflows/strix.yml's install invariants: a private + # umask so the credential-bearing console script is not group-writable, + # --no-deps because strix-agent declares cryptography<49 against this + # repository's cryptography==50.0.0 security pin (see + # requirements-strix-ci-overrides.txt, #952), and an absolute, + # non-symlinked executable inside the interpreter's own scripts root. + umask 022 + python3 -m pip install --disable-pip-version-check --no-cache-dir \ + --require-hashes --no-deps -r requirements-strix-ci-hashes.txt + strix_executable="" + if command -v strix >/dev/null 2>&1; then + strix_executable="$(command -v strix)" + fi + if [ -z "$strix_executable" ] || [[ "$strix_executable" != /* ]] \ + || [ ! -f "$strix_executable" ] || [ -L "$strix_executable" ] \ + || [ ! -x "$strix_executable" ]; then + echo "::error::Pinned Strix installation did not produce a trusted absolute executable path." + exit 1 + fi + case "$strix_executable" in + "$GITHUB_WORKSPACE"/*|"$RUNNER_TEMP"/*) + echo "::error::Refusing a Strix executable from a workspace or runner-temp path." + exit 1 + ;; + esac + strix_scripts_root="$(python3 -c 'import sysconfig; print(sysconfig.get_path("scripts"))')" + if [ -z "$strix_scripts_root" ] || [[ "$strix_scripts_root" != /* ]] \ + || [ ! -d "$strix_scripts_root" ] || [ -L "$strix_scripts_root" ]; then + echo "::error::Pinned Strix installation did not produce a trusted absolute scripts root." + exit 1 + fi + case "$strix_executable" in + "$strix_scripts_root"/*) ;; + *) + echo "::error::Pinned Strix executable is outside the trusted scripts root." + exit 1 + ;; + esac + chmod go-w -- "$strix_scripts_root" "$strix_executable" + { + printf 'STRIX_EXECUTABLE_PATH=%s\n' "$strix_executable" + printf 'STRIX_EXECUTABLE_ROOT=%s\n' "$strix_scripts_root" + printf 'STRIX_EXECUTABLE_SHA256=%s\n' \ + "$(sha256sum "$strix_executable" | cut -d' ' -f1)" + } >> "$GITHUB_ENV" + + - name: Bind the zero-cost model, key, and API base for Strix + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + source trusted-gate/scripts/ci/load_contextual_orchestrator_token.sh + sanitized="$(printf '%s' "${CONTEXTUAL_ORCHESTRATOR_TOKEN:-}" | tr -d '\r\n')" + trimmed="$(printf '%s' "$sanitized" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + if [ -z "$trimmed" ]; then + echo '::error::CONTEXTUAL_ORCHESTRATOR_TOKEN is required for Strix scans.' + exit 1 + fi + echo "::add-mask::${trimmed}" + sidecar_base="${CONTEXTUAL_ORCHESTRATOR_BASE_URL:-}" + if [ "$sidecar_base" != "http://127.0.0.1:18080" ]; then + echo '::error::Strix sidecar base URL is not the pinned local gateway origin.' + exit 1 + fi + umask 077 + printf '%s' "$trimmed" > "${RUNNER_TEMP}/llm_api_key.txt" + printf '%s/v1' "${sidecar_base%/}" > "${RUNNER_TEMP}/llm_api_base.txt" + printf '%s' 'orchestrator/free' > "${RUNNER_TEMP}/strix_llm.txt" + { + printf 'LLM_API_KEY_FILE=%s\n' "${RUNNER_TEMP}/llm_api_key.txt" + printf 'LLM_API_BASE_FILE=%s\n' "${RUNNER_TEMP}/llm_api_base.txt" + printf 'STRIX_LLM_FILE=%s\n' "${RUNNER_TEMP}/strix_llm.txt" + } >> "$GITHUB_ENV" + + - name: Run Strix against this isolated synthetic fixture + env: + STRIX_LLM_DEFAULT_PROVIDER: contextual_orchestrator + STRIX_REASONING_EFFORT: none + STRIX_FALLBACK_MODELS: "" + STRIX_FAIL_ON_PROVIDER_SIGNAL: "1" + STRIX_FAIL_ON_MIN_SEVERITY: MEDIUM + STRIX_DISABLE_PR_SCOPING: "1" + STRIX_TARGET_PATH: fixture + STRIX_SOURCE_DIRS: "." + IS_PR_EVIDENCE_RUN: "false" + NPM_CONFIG_IGNORE_SCRIPTS: "true" + PNPM_CONFIG_IGNORE_SCRIPTS: "true" + YARN_ENABLE_SCRIPTS: "false" + BUN_CONFIG_IGNORE_SCRIPTS: "true" + SOURCE_SHA: ${{ inputs.source_sha }} + CONTROL_SHA: ${{ github.sha }} + FIXTURE_JSON: ${{ toJSON(matrix.fixture) }} + FIXTURE_KEY: ${{ matrix.key }} + FIXTURE_DIGEST: ${{ matrix.fixture_sha256 }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + export LLM_TIMEOUT=0 + export STRIX_MEMORY_COMPRESSOR_TIMEOUT=0 + export STRIX_PROCESS_TIMEOUT_SECONDS=0 + export STRIX_TOTAL_TIMEOUT_SECONDS=0 + trusted_gate_root="${PWD}/trusted-gate" + workspace="${RUNNER_TEMP}/strix-workspace" + mkdir -p "$workspace/scripts/ci" "$workspace/fixture" + printf '%s\n' "$FIXTURE_JSON" > "$workspace/fixture/fixture.json" + python3 -I - "$workspace/fixture/fixture.json" "$FIXTURE_KEY" "$FIXTURE_DIGEST" <<'PYCODE' + import json, sys + sys.path.insert(0, 'trusted-gate/scripts/ci') + import release_dependency_gate as gate + fixture = json.load(open(sys.argv[1], encoding='utf-8')) + dependency = fixture['dependency'] + key = fixture.get('id') or f"{dependency['ecosystem']}/{dependency['name']}@{dependency['version']}" + if key != sys.argv[2] or gate.fixture_digest(fixture) != sys.argv[3]: + raise SystemExit('matrix fixture differs from the licence-approved plan') + PYCODE + cp "$trusted_gate_root/scripts/ci/strix_evidence_binding.py" \ + "$workspace/scripts/ci/strix_evidence_binding.py" + (cd "$workspace" && STRIX_REPO_ROOT="$workspace" \ + bash "$trusted_gate_root/scripts/ci/strix_quick_gate.sh") + vulnerabilities="" + if [ -d "$workspace/strix_runs" ]; then + vulnerabilities="$(find "$workspace/strix_runs" -type f -name 'vulnerabilities.json' -print -quit)" + fi + test -n "$vulnerabilities" + findings_json="$(jq -c ' + if type == "array" then . + elif type == "object" and (.vulnerabilities? | type) == "array" then .vulnerabilities + else null end' "$vulnerabilities")" + test "$findings_json" != null + mkdir -p "${RUNNER_TEMP}/binding" + jq -n --argjson fixture "$FIXTURE_JSON" --argjson findings "$findings_json" \ + --arg key "$FIXTURE_KEY" \ + --arg sha "$SOURCE_SHA" --arg control "$CONTROL_SHA" \ + --arg digest "$FIXTURE_DIGEST" --argjson run_id "$GITHUB_RUN_ID" \ + --argjson run_attempt "$GITHUB_RUN_ATTEMPT" ' + {schema: "cwl.release-dependency-strix-binding/1", + dependency: $fixture.dependency, + fixture: {id: $key, + sha256: $digest, scenarios: ($fixture.scenarios | keys)}, + source_sha: $sha, control_sha: $control, + run_id: $run_id, run_attempt: $run_attempt, + findings: $findings, + verdict: (if ($findings | length) == 0 then "no_exploitable_findings" + else "findings_present" end)}' > "${RUNNER_TEMP}/binding/${{ matrix.slug }}.json" + + - name: Upload this run-attempt binding + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 + with: + name: ${{ matrix.artifact_name }} + path: ${{ runner.temp }}/binding/${{ matrix.slug }}.json + if-no-files-found: error + + + strix_overflow: + name: Strix overflow ${{ matrix.key }} + needs: prepare + if: ${{ needs.prepare.outputs.has_overflow == 'true' }} + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 360 + permissions: + contents: read + strategy: + fail-fast: false + max-parallel: 4 + matrix: ${{ fromJSON(needs.prepare.outputs.matrix_overflow_json) }} + steps: *strix_steps + + gate: + needs: [prepare, strix, strix_overflow] + if: >- + ${{ !cancelled() && needs.prepare.result == 'success' && needs.strix.result == 'success' && + ((needs.prepare.outputs.has_overflow == 'true' && needs.strix_overflow.result == 'success') || + (needs.prepare.outputs.has_overflow == 'false' && needs.strix_overflow.result == 'skipped')) }} + name: Collect every Strix binding and seal the complete verdict + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 180 + permissions: + contents: read + actions: read + outputs: + full_set_verdict_artifact_id: ${{ steps.full-set-verdict.outputs.artifact-id }} + full_set_verdict_artifact_digest: sha256:${{ steps.full-set-verdict.outputs.artifact-digest }} + source_repository: ${{ steps.seal.outputs.source_repository }} + source_sha: ${{ steps.seal.outputs.source_sha }} + evidence_artifact_id: ${{ steps.sealed-evidence.outputs.artifact-id }} + evidence_artifact_name: ${{ steps.seal.outputs.evidence_artifact_name }} + evidence_artifact_digest: sha256:${{ steps.sealed-evidence.outputs.artifact-digest }} + wheel_filename: ${{ steps.seal.outputs.wheel_filename }} + wheel_sha256: ${{ steps.seal.outputs.wheel_sha256 }} + wheel_sbom_filename: ${{ steps.seal.outputs.wheel_sbom_filename }} + wheel_sbom_sha256: ${{ steps.seal.outputs.wheel_sbom_sha256 }} + sdist_filename: ${{ steps.seal.outputs.sdist_filename }} + sdist_sha256: ${{ steps.seal.outputs.sdist_sha256 }} + sdist_sbom_filename: ${{ steps.seal.outputs.sdist_sbom_filename }} + sdist_sbom_sha256: ${{ steps.seal.outputs.sdist_sbom_sha256 }} + source_identity_sha256: ${{ steps.seal.outputs.source_identity_sha256 }} + checksum_sha256: ${{ steps.seal.outputs.checksum_sha256 }} + predicate_type: ${{ steps.seal.outputs.predicate_type }} + cyclonedx_schema: ${{ steps.seal.outputs.cyclonedx_schema }} + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: Materialize immutable trusted gate + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: ContextualWisdomLab/.github + # Reviewed helper revision; intentionally distinct from workflow revision. + ref: e45f1b144aef900d734ff4c900f9e0010fd5a32d + path: trusted-gate + persist-credentials: false + # The whole scripts/ci tree, not an enumerated file list: the trusted + # Strix gate, the orchestrator sidecar and the token loader each source + # siblings by their own directory (strix_model_utils.sh, + # sanitize_contextual_orchestrator_sidecar_stream.py, + # install_strix_timeout_compat.py, strix_timeout_compat.py, …), and an + # enumeration silently breaks the moment one of them gains another. + sparse-checkout: | + scripts/ci/ + requirements-strix-ci-hashes.txt + sparse-checkout-cone-mode: false + + - name: Verify fixed helper checkout identity + env: + HELPER_ROOT: trusted-gate + CALLER_WORKFLOW_SHA: ${{ github.workflow_sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + expected=e45f1b144aef900d734ff4c900f9e0010fd5a32d + test "$(git -C "$HELPER_ROOT" rev-parse HEAD)" = "$expected" + origin="$(git -C "$HELPER_ROOT" remote get-url origin)" + case "$origin" in + https://github.com/ContextualWisdomLab/.github|https://github.com/ContextualWisdomLab/.github.git) ;; + *) echo "Foreign helper repository" >&2; exit 1 ;; + esac + test "$(git -C "$HELPER_ROOT" rev-parse HEAD:scripts/ci)" = 7f902df89a925f89c4fae69a842508406cd0207c + test "$(git -C "$HELPER_ROOT" rev-parse HEAD:requirements-strix-ci-hashes.txt)" = eb83beda177c9d2e4ca9b7e2888a1ccb55a123ac + git -C "$HELPER_ROOT" diff --exit-code HEAD -- scripts/ci requirements-strix-ci-hashes.txt + test -f "$HELPER_ROOT/scripts/ci/release_dependency_gate.py" + test -f "$HELPER_ROOT/scripts/ci/verify_release_distribution_set.py" + test -f "$HELPER_ROOT/scripts/ci/verify_release_scope_evidence_set.py" + test -f "$HELPER_ROOT/scripts/ci/prescreen_release_runtime_archives.py" + test -f "$HELPER_ROOT/scripts/ci/collect_release_strix_bindings.py" + test -f "$HELPER_ROOT/scripts/ci/verify_exact_artifact_sbom_handoff.py" + test -f "$HELPER_ROOT/requirements-strix-ci-hashes.txt" + printf 'helper_repository=ContextualWisdomLab/.github helper_sha=%s caller_workflow_sha=%s\n' "$expected" "$CALLER_WORKFLOW_SHA" + + - name: Validate the exact release identity before anything else runs + env: + SOURCE_REPOSITORY: ${{ inputs.source_repository }} + SOURCE_SHA: ${{ inputs.source_sha }} + EVIDENCE_ARTIFACT_NAME: ${{ inputs.evidence_artifact_name }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + # Keep sealed evidence outside both diagnostic artifact namespaces. + case "$EVIDENCE_ARTIFACT_NAME" in + release-dependency-sealed-evidence|license-evidence-?*) ;; + *) echo "evidence artifact name must use the license-evidence- namespace" >&2; exit 1 ;; + esac + # `workflow_call` can only type these inputs as `string`, so a branch + # name or a short SHA would otherwise be accepted here and only caught + # by the gate's own 40-hex check after Strix had already run. The shape + # is therefore checked by the trusted gate before the release head is + # even fetched, and long before any credential is materialized. + python3 -I trusted-gate/scripts/ci/release_dependency_gate.py validate-inputs \ + --source-repository "$SOURCE_REPOSITORY" \ + --source-sha "$SOURCE_SHA" + + - name: Check out the exact release head + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + repository: ${{ inputs.source_repository }} + ref: ${{ inputs.source_sha }} + path: release-source + persist-credentials: false + + - name: Set up the release build interpreter + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.13" + + - name: List the current run and attempt artifacts + env: + GH_TOKEN: ${{ github.token }} + SOURCE_REPOSITORY: ${{ inputs.source_repository }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + test "$SOURCE_REPOSITORY" = "$GITHUB_REPOSITORY" + gh api --paginate "repos/${SOURCE_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/artifacts?per_page=100" \ + --jq '.artifacts[]' > "${RUNNER_TEMP}/release-artifacts.jsonl" + gh api "repos/${SOURCE_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/attempts/${GITHUB_RUN_ATTEMPT}" \ + > "${RUNNER_TEMP}/release-attempt.json" + + - name: Verify every immutable distribution before dependency capture + env: + GH_TOKEN: ${{ github.token }} + SOURCE_REPOSITORY: ${{ inputs.source_repository }} + SOURCE_SHA: ${{ inputs.source_sha }} + CONTROL_SHA: ${{ github.sha }} + RECORD_ID: ${{ inputs.distribution_set_artifact_id }} + RECORD_DIGEST: ${{ inputs.distribution_set_artifact_digest }} + WHEEL_FILENAME: ${{ inputs.wheel_filename }} + SDIST_FILENAME: ${{ inputs.sdist_filename }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -I trusted-gate/scripts/ci/verify_release_distribution_set.py \ + --repository "$SOURCE_REPOSITORY" \ + --source-sha "$SOURCE_SHA" \ + --control-sha "$CONTROL_SHA" \ + --run-id "$GITHUB_RUN_ID" \ + --run-attempt "$GITHUB_RUN_ATTEMPT" \ + --record-artifact-id "$RECORD_ID" \ + --record-artifact-digest "$RECORD_DIGEST" \ + --wheel-filename "$WHEEL_FILENAME" \ + --sdist-filename "$SDIST_FILENAME" \ + --metadata "${RUNNER_TEMP}/release-artifacts.jsonl" \ + --attempt "${RUNNER_TEMP}/release-attempt.json" \ + --output release-distributions > "${RUNNER_TEMP}/verified-distributions.json" + + - name: Inventory exact release wheel native links + env: + SOURCE_SHA: ${{ inputs.source_sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -I trusted-gate/scripts/ci/scan_release_native_links.py \ + --verified-distributions "${RUNNER_TEMP}/verified-distributions.json" \ + --distribution-root release-distributions \ + --source-sha "$SOURCE_SHA" \ + --output "${RUNNER_TEMP}/release-native-links.json" + + - name: Verify every immutable scope evidence archive before dependency capture + env: + GH_TOKEN: ${{ github.token }} + SOURCE_REPOSITORY: ${{ inputs.source_repository }} + SOURCE_SHA: ${{ inputs.source_sha }} + CONTROL_SHA: ${{ github.sha }} + RECORD_ID: ${{ inputs.distribution_set_artifact_id }} + RECORD_DIGEST: ${{ inputs.distribution_set_artifact_digest }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -I trusted-gate/scripts/ci/verify_release_scope_evidence_set.py \ + --repository "$SOURCE_REPOSITORY" \ + --source-sha "$SOURCE_SHA" \ + --control-sha "$CONTROL_SHA" \ + --run-id "$GITHUB_RUN_ID" \ + --run-attempt "$GITHUB_RUN_ATTEMPT" \ + --record-artifact-id "$RECORD_ID" \ + --record-artifact-digest "$RECORD_DIGEST" \ + --verified-distributions "${RUNNER_TEMP}/verified-distributions.json" \ + --metadata "${RUNNER_TEMP}/release-artifacts.jsonl" \ + --attempt "${RUNNER_TEMP}/release-attempt.json" \ + --output release-scope-evidence > "${RUNNER_TEMP}/verified-scope-evidence.json" + + - name: Recheck exact maturin release assets and native links + shell: bash --noprofile --norc -e -o pipefail {0} + run: python3 -I trusted-gate/scripts/ci/verify_release_maturin_tool_assets.py + + - name: Refuse denied or unknown licences in transported runtime wheels + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -I trusted-gate/scripts/ci/prescreen_release_runtime_archives.py \ + --verified-scope "${RUNNER_TEMP}/verified-scope-evidence.json" \ + --scope-root release-scope-evidence \ + --output "${RUNNER_TEMP}/runtime-archive-license-report.json" + + - name: Collect the release closure without installing or executing it + env: + ECOSYSTEMS: ${{ inputs.ecosystems }} + PYTHON_LOCK_PATH: ${{ inputs.python_lock_path }} + CARGO_MANIFEST_PATH: ${{ inputs.cargo_manifest_path }} + CARGO_DEV_MANIFEST_PATH: ${{ inputs.cargo_dev_manifest_path }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python_lock="" + cargo_manifest="" + cargo_dev_manifest="" + if [ -n "$PYTHON_LOCK_PATH" ]; then + python_lock="${PWD}/release-source/${PYTHON_LOCK_PATH}" + fi + if [ -n "$CARGO_MANIFEST_PATH" ]; then + cargo_manifest="${PWD}/release-source/${CARGO_MANIFEST_PATH}" + fi + if [ -n "$CARGO_DEV_MANIFEST_PATH" ]; then + cargo_dev_manifest="${PWD}/release-source/${CARGO_DEV_MANIFEST_PATH}" + fi + bash trusted-gate/scripts/ci/release_dependency_capture_raw.sh \ + --raw-root "${RUNNER_TEMP}/raw" \ + --capture-root "${RUNNER_TEMP}/capture" \ + --ecosystems "$ECOSYSTEMS" \ + --python-lock "$python_lock" \ + --download-root "${RUNNER_TEMP}/collected" \ + --cargo-manifest "$cargo_manifest" \ + --cargo-dev-manifest "$cargo_dev_manifest" + + - name: Assemble per-dependency evidence and isolated synthetic fixtures + env: + SOURCE_REPOSITORY: ${{ inputs.source_repository }} + SOURCE_SHA: ${{ inputs.source_sha }} + ECOSYSTEMS: ${{ inputs.ecosystems }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + jq -n \ + --arg repository "$SOURCE_REPOSITORY" \ + --arg sha "$SOURCE_SHA" \ + --arg ecosystems "$ECOSYSTEMS" \ + '{source_repository: $repository, source_sha: $sha, + ecosystems: ($ecosystems | split(","))}' \ + > "${RUNNER_TEMP}/capture/release.json" + python3 -I trusted-gate/scripts/ci/release_dependency_gate.py capture \ + --raw "${RUNNER_TEMP}/raw" \ + --capture "${RUNNER_TEMP}/capture" + python3 -I trusted-gate/scripts/ci/release_dependency_gate.py capture-license-selections \ + --source release-source --source-sha "$SOURCE_SHA" \ + --capture "${RUNNER_TEMP}/capture" + + - name: Refuse a denied or unverifiable licence before any credential exists + id: license-stage + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + # The licence determination runs here, ahead of every credentialed and + # model step, using the *same* evaluate_dependency_license path the final + # gate uses — so a GPL/LGPL/AGPL dependency, an UNKNOWN licence, or an + # `OR` expression with no recorded permissive selection refuses the + # release before a provider secret is ever read. `capture` alone does not + # reject a licence; it only assembles evidence and fixtures. This stage + # also performs the full-set scope comparison, so an ecosystem whose + # membership cannot be established fails here too. + python3 -I trusted-gate/scripts/ci/release_dependency_gate.py prescreen \ + --source release-source \ + --capture "${RUNNER_TEMP}/capture" \ + --report "${RUNNER_TEMP}/license-report.json" + + - name: Install the prescreened closure into a lock-only environment + if: ${{ inputs.python_lock_path != '' }} + env: + PYTHON_LOCK_PATH: ${{ inputs.python_lock_path }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + # Installing runs dependency code, so it happens only after the licence + # stage above has passed, and only from the bytes that stage judged: + # --no-index --find-links over the collected distributions, with + # --require-hashes so pip proves each file against the lock. Nothing is + # re-resolved or re-downloaded, so the installed bytes are the inspected + # bytes even when the lock records several hashes for a project. + # --without-pip keeps the environment's contents exactly what the lock + # installed, so LOCK_ENV_MISMATCH means a real disagreement. + python3 -m venv --without-pip "${RUNNER_TEMP}/gate-venv" + bash trusted-gate/scripts/ci/release_dependency_capture_raw.sh \ + --install-gated \ + --python-lock "${PWD}/release-source/${PYTHON_LOCK_PATH}" \ + --python-interpreter "${RUNNER_TEMP}/gate-venv/bin/python" \ + --capture-root "${RUNNER_TEMP}/capture" \ + --download-root "${RUNNER_TEMP}/collected" \ + --license-report "${RUNNER_TEMP}/license-report.json" + + - name: Recompute the exact licence-approved fixture matrix + env: + CONTROL_SHA: ${{ github.sha }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -I trusted-gate/scripts/ci/release_dependency_gate.py fanout-plan \ + --capture "${RUNNER_TEMP}/capture" \ + --license-report "${RUNNER_TEMP}/license-report.json" \ + --runtime-archive-license-report "${RUNNER_TEMP}/runtime-archive-license-report.json" \ + --control-sha "$CONTROL_SHA" \ + --run-id "$GITHUB_RUN_ID" \ + --run-attempt "$GITHUB_RUN_ATTEMPT" \ + --output "${RUNNER_TEMP}/strix-fanout-plan.json" + + - name: Refuse unless every current-attempt binding and full gate passes + id: full-stage + env: + GH_TOKEN: ${{ github.token }} + SOURCE_REPOSITORY: ${{ inputs.source_repository }} + SOURCE_SHA: ${{ inputs.source_sha }} + CONTROL_SHA: ${{ github.sha }} + RECORD_ID: ${{ inputs.distribution_set_artifact_id }} + RECORD_DIGEST: ${{ inputs.distribution_set_artifact_digest }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + gh api --paginate "repos/${SOURCE_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/artifacts?per_page=100" \ + --jq '.artifacts[]' > "${RUNNER_TEMP}/release-artifacts.jsonl" + python3 -I trusted-gate/scripts/ci/collect_release_strix_bindings.py \ + --source release-source \ + --capture "${RUNNER_TEMP}/capture" \ + --license-report "${RUNNER_TEMP}/license-report.json" \ + --plan "${RUNNER_TEMP}/strix-fanout-plan.json" \ + --metadata "${RUNNER_TEMP}/release-artifacts.jsonl" \ + --attempt "${RUNNER_TEMP}/release-attempt.json" \ + --repository "$SOURCE_REPOSITORY" --source-sha "$SOURCE_SHA" \ + --control-sha "$CONTROL_SHA" --run-id "$GITHUB_RUN_ID" \ + --run-attempt "$GITHUB_RUN_ATTEMPT" \ + --verified-distributions "${RUNNER_TEMP}/verified-distributions.json" \ + --runtime-archive-license-report "${RUNNER_TEMP}/runtime-archive-license-report.json" \ + --native-report "${RUNNER_TEMP}/release-native-links.json" \ + --verified-scope "${RUNNER_TEMP}/verified-scope-evidence.json" \ + --record-artifact-id "$RECORD_ID" --record-artifact-digest "$RECORD_DIGEST" \ + --report "${RUNNER_TEMP}/gate-report.json" \ + --verdict "${RUNNER_TEMP}/full-set-verdict.json" + + - name: Export the complete distribution and dependency verdict + id: full-set-verdict + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 + with: + name: ${{ inputs.evidence_artifact_name == 'release-dependency-sealed-evidence' && 'release-dependency-sealed-evidence--full-set-verdict' || format('release-dependency-full-set-verdict--{0}', inputs.evidence_artifact_name) }} + path: | + ${{ runner.temp }}/full-set-verdict.json + ${{ runner.temp }}/gate-report.json + ${{ runner.temp }}/runtime-archive-license-report.json + ${{ runner.temp }}/release-native-links.json + if-no-files-found: error + + - name: Seal exactly the gated bytes for attestation + id: seal + env: + EVIDENCE_ARTIFACT_NAME: ${{ inputs.evidence_artifact_name }} + WHEEL_FILENAME: ${{ inputs.wheel_filename }} + SDIST_FILENAME: ${{ inputs.sdist_filename }} + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -I trusted-gate/scripts/ci/release_dependency_gate.py seal \ + --report "${RUNNER_TEMP}/gate-report.json" \ + --wheel "release-distributions/${WHEEL_FILENAME}" \ + --sdist "release-distributions/${SDIST_FILENAME}" \ + --evidence-root "${RUNNER_TEMP}/sealed-evidence" \ + --evidence-artifact-name "$EVIDENCE_ARTIFACT_NAME" + + - name: Export the sealed evidence as one immutable same-run artifact + id: sealed-evidence + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 + with: + name: ${{ inputs.evidence_artifact_name }} + path: ${{ runner.temp }}/sealed-evidence + if-no-files-found: error + + - name: Export the per-dependency gate report + if: ${{ !cancelled() && steps.full-stage.conclusion != 'skipped' }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.0 + with: + name: ${{ inputs.evidence_artifact_name == 'release-dependency-sealed-evidence' && 'release-dependency-gate-report' || format('release-dependency-gate-report--{0}', inputs.evidence_artifact_name) }} + path: ${{ runner.temp }}/gate-report.json + if-no-files-found: error diff --git a/.github/workflows/repository-metadata-reconcile.yml b/.github/workflows/repository-metadata-reconcile.yml index e05a8b155a..fd14ba112f 100644 --- a/.github/workflows/repository-metadata-reconcile.yml +++ b/.github/workflows/repository-metadata-reconcile.yml @@ -18,6 +18,10 @@ on: - "tests/test_repository_label_convergence.py" - "tests/test_repository_label_identity.py" - "tests/test_repository_label_live_verification.py" + - "requirements-opencode-review-ci.txt" + - "requirements-opencode-review-ci-hashes.txt" + - "requirements-noema-document-ci.txt" + - "scripts/ci/compile_opencode_review_lock.sh" - ".github/workflows/repository-metadata-reconcile.yml" schedule: - cron: "23 * * * *" @@ -31,7 +35,9 @@ concurrency: jobs: validate: - runs-on: ubuntu-24.04 + runs-on: + group: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} timeout-minutes: 20 steps: - name: Harden runner @@ -42,6 +48,7 @@ jobs: uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} + fetch-depth: 0 persist-credentials: false - name: Verify exact revision shell: bash @@ -103,7 +110,9 @@ jobs: apply: if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' needs: validate - runs-on: ubuntu-24.04 + runs-on: + group: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} timeout-minutes: 45 environment: repository-metadata-maintenance steps: diff --git a/.github/workflows/sast-semgrep.yml b/.github/workflows/sast-semgrep.yml index 12b7013da3..2f128c1aea 100644 --- a/.github/workflows/sast-semgrep.yml +++ b/.github/workflows/sast-semgrep.yml @@ -38,25 +38,38 @@ permissions: contents: read jobs: - changed-scope: - name: Detect changed scope + semgrep: + name: Semgrep (multi-language SAST) # The org ruleset IGNORES every `on:` filter (paths, branches, types) when it # runs this workflow in another repository, and a trigger-level skip would # leave `.github`'s classic required contexts Pending forever. Both - # mechanisms honour a JOB-level skip, so the doc/image-only decision is made - # here and consumed through `needs`. See + # mechanisms honour a job that runs and concludes on its own, so the + # doc/image-only decision is made by the classifier step below and consumed + # by the expensive steps' `if:` guards. See # docs/doctoring/required-workflow-path-filter-boundary.md. # Fails OPEN: an unreadable, empty, or truncated file list scans everything. + # The gate lives inside this job as a step-level guard (one runner, not two). if: github.event.action != 'closed' - runs-on: ubuntu-24.04 - timeout-minutes: 5 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] permissions: contents: read pull-requests: read - outputs: - code: ${{ steps.scope.outputs.code }} - deps: ${{ steps.scope.outputs.deps }} + security-events: write + actions: read + env: + # Deterministic, no telemetry: registry rules are fetched but no scan data + # is sent back. + SEMGREP_SEND_METRICS: "off" + # Semgrep OSS 1.169.0. Keep the immutable manifest reference in one + # place so hosted scans and local reproduction cannot drift. + SEMGREP_IMAGE: "semgrep/semgrep@sha256:2b33f46ba66cf8cc2ad59ccfa7d22951fd00c632c38f1339e84ec8e6e641a942" steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1 + with: + egress-policy: audit - name: Classify changed paths id: scope env: @@ -99,35 +112,15 @@ jobs: echo "code=${code}" >> "$GITHUB_OUTPUT" echo "deps=${deps}" >> "$GITHUB_OUTPUT" echo "changed-scope code=${code} deps=${deps}" - - semgrep: - name: Semgrep (multi-language SAST) - needs: changed-scope - if: github.event.action != 'closed' && needs.changed-scope.outputs.code == 'true' - runs-on: ubuntu-24.04 - permissions: - contents: read - security-events: write - actions: read - env: - # Deterministic, no telemetry: registry rules are fetched but no scan data - # is sent back. - SEMGREP_SEND_METRICS: "off" - # Semgrep OSS 1.169.0. Keep the immutable manifest reference in one - # place so hosted scans and local reproduction cannot drift. - SEMGREP_IMAGE: "semgrep/semgrep@sha256:2b33f46ba66cf8cc2ad59ccfa7d22951fd00c632c38f1339e84ec8e6e641a942" - steps: - - name: Harden the runner (Audit all outbound calls) - uses: step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40 # v2.20.1 - with: - egress-policy: audit - name: Checkout exact submitted revision + if: steps.scope.outputs.code == 'true' uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }} ref: ${{ github.event.pull_request.head.sha || github.sha }} persist-credentials: false - name: Verify exact submitted revision + if: steps.scope.outputs.code == 'true' env: EXPECTED_CHECKOUT_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name || github.repository }} EXPECTED_CHECKOUT_SHA: ${{ github.event.pull_request.head.sha || github.sha }} @@ -140,6 +133,7 @@ jobs: fi echo "SAST_CHECKOUT scanner=semgrep repository=${EXPECTED_CHECKOUT_REPOSITORY} expected_sha=${EXPECTED_CHECKOUT_SHA} actual_sha=${actual_sha}" - name: Verify pinned Semgrep manifest + if: steps.scope.outputs.code == 'true' run: | set -euo pipefail if [[ "${SEMGREP_IMAGE}" =~ ^semgrep/semgrep@sha256:[0-9a-f]{64}$ ]]; then @@ -151,6 +145,7 @@ jobs: fi - name: Run Semgrep (SARIF) id: semgrep + if: steps.scope.outputs.code == 'true' run: | set +e echo "Using ${SEMGREP_IMAGE}" @@ -219,7 +214,7 @@ jobs: echo "SEMGREP_ENGINE_FAILURE rc=${SEMGREP_RC:-missing}: Semgrep failed without a WARNING/ERROR SARIF result; inspect the scan command output above." fi - name: Enforce Semgrep gate (fail on Medium+ findings) - if: always() && (steps.semgrep_sarif.outputs.finding_count != '0' || steps.semgrep.outputs.rc != '0') + if: always() && steps.scope.outputs.code == 'true' && (steps.semgrep_sarif.outputs.finding_count != '0' || steps.semgrep.outputs.rc != '0') env: SEMGREP_RC: ${{ steps.semgrep.outputs.rc }} SEMGREP_FINDING_COUNT: ${{ steps.semgrep_sarif.outputs.finding_count }} diff --git a/.github/workflows/sbom-generation.yml b/.github/workflows/sbom-generation.yml index 588baefe1e..7d630dd78a 100644 --- a/.github/workflows/sbom-generation.yml +++ b/.github/workflows/sbom-generation.yml @@ -43,7 +43,9 @@ permissions: jobs: generate-sbom: - runs-on: ubuntu-latest + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] permissions: # write is needed for release-asset upload and dependency submission. contents: write diff --git a/.github/workflows/sbom-inventory-scheduler.yml b/.github/workflows/sbom-inventory-scheduler.yml index 8810c702fd..e60d66b7d0 100644 --- a/.github/workflows/sbom-inventory-scheduler.yml +++ b/.github/workflows/sbom-inventory-scheduler.yml @@ -30,7 +30,9 @@ permissions: jobs: aggregate-sbom-inventory: - runs-on: ubuntu-latest + runs-on: + group: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ (github.repository == 'ContextualWisdomLab/.github' && github.ref == 'refs/heads/main' && endsWith(github.workflow_ref, '@refs/heads/main') && fromJSON('{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} permissions: contents: write id-token: write diff --git a/.github/workflows/scheduled-security-scan.yml b/.github/workflows/scheduled-security-scan.yml index 6b6a90aa36..0c2f118d1c 100644 --- a/.github/workflows/scheduled-security-scan.yml +++ b/.github/workflows/scheduled-security-scan.yml @@ -38,7 +38,9 @@ permissions: jobs: detect-languages: name: Detect CodeQL languages - runs-on: ubuntu-latest + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] outputs: matrix: ${{ steps.detect.outputs.matrix }} steps: @@ -72,7 +74,9 @@ jobs: codeql: name: CodeQL periodic (${{ matrix.language }}) needs: detect-languages - runs-on: ubuntu-latest + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] permissions: actions: read contents: read @@ -102,7 +106,9 @@ jobs: trivy-fs: name: Trivy filesystem (periodic) - runs-on: ubuntu-latest + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] permissions: contents: read security-events: write diff --git a/.github/workflows/scorecard-analysis.yml b/.github/workflows/scorecard-analysis.yml index 6222b28b28..2064ab9b54 100644 --- a/.github/workflows/scorecard-analysis.yml +++ b/.github/workflows/scorecard-analysis.yml @@ -29,7 +29,9 @@ permissions: read-all jobs: analysis: name: Scorecard analysis - runs-on: ubuntu-latest + runs-on: + group: CWL CI isolated + labels: ["self-hosted", "linux", "x64", "cwlab-ci-isolated"] permissions: security-events: write id-token: write diff --git a/.github/workflows/secret-scan.yml b/.github/workflows/secret-scan.yml index 948ae583d4..3698e30056 100644 --- a/.github/workflows/secret-scan.yml +++ b/.github/workflows/secret-scan.yml @@ -35,7 +35,9 @@ permissions: jobs: gitleaks: name: gitleaks (secret scan) - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] permissions: contents: read security-events: write diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 500e22b4ab..46cddd322e 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -71,7 +71,9 @@ jobs: # docs/doctoring/required-workflow-path-filter-boundary.md. # Fails OPEN: an unreadable, empty, or truncated file list scans everything. if: github.event.action != 'closed' - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] timeout-minutes: 5 permissions: contents: read @@ -126,7 +128,9 @@ jobs: osv-scan: needs: changed-scope if: github.event.action != 'closed' && needs.changed-scope.outputs.deps == 'true' - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] timeout-minutes: 25 permissions: actions: read @@ -164,7 +168,7 @@ jobs: with: scan-args: | --format=json - --output=old-results.json + --output-file=old-results.json --maven-registry=https://maven-central.storage-download.googleapis.com/maven2 --no-resolve --allow-no-lockfiles @@ -182,7 +186,7 @@ jobs: with: scan-args: | --format=json - --output=old-results.json + --output-file=old-results.json --no-resolve --allow-no-lockfiles -r @@ -215,7 +219,7 @@ jobs: with: scan-args: | --format=json - --output=new-results.json + --output-file=new-results.json --maven-registry=https://maven-central.storage-download.googleapis.com/maven2 --no-resolve --allow-no-lockfiles @@ -233,7 +237,7 @@ jobs: with: scan-args: | --format=json - --output=new-results.json + --output-file=new-results.json --no-resolve --allow-no-lockfiles -r @@ -286,7 +290,7 @@ jobs: uses: google/osv-scanner-action/osv-reporter-action@8e5cf47b818121e8b405931c82126c2630b0b20d # v2.3.8 with: scan-args: | - --output=results.sarif + --output-files=results.sarif --old=old-results.json --new=new-results.json --gh-annotations=true @@ -323,6 +327,10 @@ jobs: uses: github/codeql-action/upload-sarif@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9 with: sarif_file: results.sarif + # The exact head checkout lives in `source`, not the workspace root; + # without this binding upload-sarif logs "does not appear to be a git + # repository" twice and falls back to server-derived commit identity. + checkout_path: ${{ github.workspace }}/source # results.sarif is produced after checkout of the pull request head. # Uploading it against refs/pull/*/merge can race GitHub's synthetic # merge ref and fail with "commit_oid is not a merge commit". @@ -335,6 +343,10 @@ jobs: echo "::warning::OSV SARIF upload to code scanning failed after the base/head comparison. The PR-introduced vulnerability reporter above remains the hard gate, so upload rate limits cannot hide OSV findings." - name: Upload OSV debug artifacts if: always() + # The reporter and SARIF upload above preserve the security verdict. + # Debug retention quota is observability only and must not turn a clean + # comparison into a failed security gate. + continue-on-error: true uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v6.0.0 with: name: osv-scan-debug @@ -348,7 +360,9 @@ jobs: dependency-review: needs: changed-scope if: github.event.action != 'closed' && needs.changed-scope.outputs.deps == 'true' - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] permissions: contents: read pull-requests: read @@ -383,6 +397,23 @@ jobs: set -euo pipefail api_url="${GITHUB_API_URL:-https://api.github.com}" + git_object_id='^[0-9a-f]{40}([0-9a-f]{24})?$' + repository_identity='^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$' + if ! [[ "${BASE_SHA}" =~ $git_object_id ]] || ! [[ "${HEAD_SHA}" =~ $git_object_id ]]; then + echo "::error::Dependency review evidence unavailable: exact 40- or 64-character hexadecimal base and head revisions are required before any compare request. Named refs are not evidence. Verify the pull-request event SHAs, then rerun. Failing closed." + exit 1 + fi + if ! [[ "${REPOSITORY}" =~ $repository_identity ]]; then + echo "::error::Dependency review evidence unavailable: owner/name repository identity is required before any compare request. Verify the pull-request repository, then rerun. Failing closed." + exit 1 + fi + repository_owner="${REPOSITORY%%/*}" + repository_name="${REPOSITORY#*/}" + if [ "${repository_owner}" = "." ] || [ "${repository_owner}" = ".." ] || [ "${repository_name}" = "." ] || [ "${repository_name}" = ".." ]; then + echo "::error::Dependency review evidence unavailable: owner/name repository identity is required before any compare request. Dot or parent-directory path components are not evidence. Verify the pull-request repository, then rerun. Failing closed." + exit 1 + fi + set +e status="$( curl -sS --connect-timeout 10 --max-time 30 \ @@ -432,7 +463,9 @@ jobs: gitleaks: name: gitleaks (secret scan) if: github.event.action != 'closed' && github.repository == 'ContextualWisdomLab/.github' - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] permissions: contents: read security-events: write @@ -522,7 +555,9 @@ jobs: trivy-fs: needs: changed-scope if: github.event.action != 'closed' && needs.changed-scope.outputs.code == 'true' - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] permissions: contents: read security-events: write @@ -549,6 +584,10 @@ jobs: - name: Trivy filesystem scan uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0 with: + # Runner groups can schedule onto different hosts. Reinstall the + # pinned binary instead of trusting a cache entry whose path or + # executable bit belongs to a previous runner. + cache: false scan-type: fs scan-ref: . scanners: vuln,secret,misconfig @@ -629,7 +668,9 @@ jobs: scorecard: needs: changed-scope if: github.event.action != 'closed' && needs.changed-scope.outputs.code == 'true' - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] # SOFT: posture findings are unrelated to the PR diff, so never block merge. continue-on-error: true permissions: diff --git a/.github/workflows/strix.yml b/.github/workflows/strix.yml index 58ed3dab8d..53e704bcfb 100644 --- a/.github/workflows/strix.yml +++ b/.github/workflows/strix.yml @@ -44,12 +44,15 @@ on: # them, so the same doc/image-only decision is enforced by the # changed-scope job below. The run-name # includes the PR number and head SHA for status grouping, while the - # concurrency group is scoped per repository and event class to prevent - # shared-provider key rate-limit storms. Strix runs intentionally do not - # cancel in progress because a pre-job cancellation leaves no scanner log to - # review. GitHub keeps one active and one pending run per group; the merge - # scheduler re-dispatches exact-head evidence when a pending run is - # superseded. For PRs the merge scheduler manages, same-head Strix evidence + # concurrency group is scoped per repository AND pull request (native and + # dispatch PR runs share one group), or per protected branch for push + # events, to prevent shared-provider key rate-limit storms. That group + # runs with cancel-in-progress: true, so a newer head of the same PR or + # branch retires the older run: cancellation is a supersede signal, never + # passing evidence, and the merge scheduler still requires exact-head + # evidence before it will act. schedule and PR-less repository_dispatch + # keep a unique run id and so are never cancelled by a sibling. + # For PRs the merge scheduler manages, same-head Strix evidence # is still forced at merge time via repository_dispatch (which paths-ignore # does not affect), so merged code never loses evidence. paths-ignore: @@ -77,12 +80,36 @@ on: concurrency: # Workflow-level admission is required: job-level groups are never evaluated # while the whole run is queued behind the organization job ceiling. + # Push scans coalesce per protected branch: a newer head of the same branch + # supersedes the older scan exactly as a newer PR head does. A push scan + # covers the whole tree (STRIX_TARGET_PATH is './' outside PR scope) and + # publishes no 'strix' commit status, so the newest head's scan is a + # complete scan OF THE CURRENT TREE -- not a record of every earlier + # commit's findings: code that entered and left main between two heads, and + # findings a retired run never uploaded, are absent from it. With the run-id + # fallback every main push was its own group and nothing ever retired a + # superseded main scan: on 2026-09-05 nine push/main runs were outstanding + # at once in this repository against a 10-30 minute normal scan -- five + # holding runner slots under the shared 60-job ceiling (running for up to + # two hours) and four still queued, which occupy no slot until a runner is + # assigned. schedule and repository_dispatch without a PR number keep a + # unique run id. + # Coverage trade, measured the same day: main moved 50 times in 24 hours + # (median gap 8.4 min, mean 26.5 min, bursty), so with cancel-in-progress + # only the final head of each merge burst completes a scan -- one completed + # main scan per quiet window, not one per push. That is the intended + # exchange: a cancelled push scan gives up its own report, and the scan that + # replaces it re-covers the current tree rather than that commit's history; + # a per-commit evidence-retention guarantee would need its own preservation + # contract. The weekly schedule scan is the floor. group: >- strix-security-scan-${{ github.event.pull_request.base.repo.full_name || github.event.client_payload.target_repository || github.repository }}-${{ github.event.pull_request.number || - github.event.client_payload.pr_number || github.run_id }} + github.event.client_payload.pr_number || + (github.event_name == 'push' && format('push-{0}', github.ref_name)) || + github.run_id }} cancel-in-progress: true # Scorecard Token-Permissions (alert #43): keep the workflow-level token @@ -102,8 +129,10 @@ jobs: # here and consumed through `needs`. See # docs/doctoring/required-workflow-path-filter-boundary.md. # Fails OPEN: an unreadable, empty, or truncated file list scans everything. - if: github.event_name != 'pull_request_target' || (github.event.action != 'closed' && github.event.action != 'converted_to_draft') - runs-on: ubuntu-24.04 + if: (github.event_name != 'pull_request_target' || (github.event.action != 'closed' && github.event.action != 'converted_to_draft')) && (github.event.pull_request.draft != true || github.event.action == 'converted_to_draft' || github.event.action == 'closed') + runs-on: + group: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} timeout-minutes: 5 permissions: contents: read @@ -157,24 +186,101 @@ jobs: admit-current-head: name: Admit current pull request head + # Draft PRs get no runner; ready_for_review re-runs this on the same head. if: >- - github.event_name != 'pull_request_target' || - (github.event.action != 'closed' && github.event.action != 'converted_to_draft') - runs-on: ubuntu-24.04 + ( + github.event_name != 'pull_request_target' || + (github.event.action != 'closed' && github.event.action != 'converted_to_draft') + ) && (github.event.pull_request.draft != true || github.event.action == 'converted_to_draft' || github.event.action == 'closed') + runs-on: + group: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} timeout-minutes: 5 permissions: contents: read pull-requests: read + id-token: write outputs: admitted: ${{ steps.admission.outputs.admitted }} target_repository: ${{ steps.admission.outputs.target_repository }} pr_number: ${{ steps.admission.outputs.pr_number }} steps: + - name: Exchange OpenCode app token for Strix target repository metadata reads + id: metadata_read_app_token + if: >- + github.event_name == 'repository_dispatch' + && github.event.client_payload.target_repository != '' + && github.event.client_payload.target_repository != github.repository + && startsWith(github.event.client_payload.target_repository, format('{0}/', github.repository_owner)) + env: + OIDC_AUDIENCE: opencode-github-action + OPENCODE_API_BASE_URL: https://api.opencode.ai + run: | + set -euo pipefail + + mark_unavailable() { + echo "available=false" >>"$GITHUB_OUTPUT" + } + + if [ -z "${ACTIONS_ID_TOKEN_REQUEST_TOKEN:-}" ] || + [ -z "${ACTIONS_ID_TOKEN_REQUEST_URL:-}" ]; then + echo "OpenCode app token exchange unavailable: OIDC request environment is missing." + mark_unavailable + exit 0 + fi + + request_url="${ACTIONS_ID_TOKEN_REQUEST_URL}" + separator="&" + case "$request_url" in + *\?*) ;; + *) separator="?" ;; + esac + + if ! oidc_response="$( + curl -fsS \ + -H "Authorization: Bearer ${ACTIONS_ID_TOKEN_REQUEST_TOKEN}" \ + "${request_url}${separator}audience=${OIDC_AUDIENCE}" + )"; then + echo "OpenCode app token exchange unavailable: OIDC token request did not complete." + mark_unavailable + exit 0 + fi + + if ! oidc_token="$(jq -ser 'select(length == 1 and (.[0] | type == "object")) | .[0].value | select(type == "string" and length > 0 and (test("[[:space:]]") | not))' <<<"$oidc_response")"; then + echo "OpenCode app token exchange unavailable: OIDC token response was empty." + mark_unavailable + exit 0 + fi + + if ! token_response="$( + curl -fsS \ + -X POST \ + -H "Authorization: Bearer ${oidc_token}" \ + "${OPENCODE_API_BASE_URL}/exchange_github_app_token" + )"; then + echo "OpenCode app token exchange unavailable: app token request did not complete." + mark_unavailable + exit 0 + fi + + if ! app_token="$(jq -ser 'select(length == 1 and (.[0] | type == "object")) | .[0].token | select(type == "string" and length > 0 and (test("[[:space:]]") | not))' <<<"$token_response")"; then + echo "OpenCode app token exchange unavailable: app token response was empty." + mark_unavailable + exit 0 + fi + + echo "::add-mask::$app_token" + { + echo "available=true" + echo "token=$app_token" + } >>"$GITHUB_OUTPUT" + - name: Verify event metadata against the live pull request id: admission env: - GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }} + GH_TOKEN: ${{ steps.metadata_read_app_token.outputs.token || secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN || github.token }} EVENT_NAME: ${{ github.event_name }} + EXPECTED_REPOSITORY_OWNER: ${{ github.repository_owner }} TARGET_REPOSITORY: ${{ github.event.client_payload.target_repository || github.event.pull_request.base.repo.full_name || github.repository }} TARGET_PR_NUMBER: ${{ github.event.client_payload.pr_number || github.event.pull_request.number }} EXPECTED_BASE_REF: ${{ github.event.client_payload.pr_base_ref || github.event.pull_request.base.ref }} @@ -200,6 +306,11 @@ jobs: echo "::error::Strix event metadata is incomplete or malformed." exit 1 fi + if [ "$EVENT_NAME" = "repository_dispatch" ] && + [ "${TARGET_REPOSITORY%%/*}" != "$EXPECTED_REPOSITORY_OWNER" ]; then + echo "::error::Strix dispatch target is outside the workflow repository owner." + exit 1 + fi pull_request_json="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${TARGET_PR_NUMBER}")" live_tuple="$(jq -r '[.state // "", .base.repo.full_name // "", .base.ref // "", .base.sha // "", .head.repo.full_name // "", .head.sha // ""] | @tsv' <<<"$pull_request_json")" expected_tuple="$(printf 'open\t%s\t%s\t%s\t%s\t%s' "$TARGET_REPOSITORY" "$EXPECTED_BASE_REF" "$EXPECTED_BASE_SHA" "$EXPECTED_HEAD_REPOSITORY" "$EXPECTED_HEAD_SHA")" @@ -233,7 +344,9 @@ jobs: github.event.pull_request.base.repo.full_name || github.repository }}-${{ github.event.pull_request.number || github.run_id }} cancel-in-progress: true - runs-on: ubuntu-24.04 + runs-on: + group: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} # Bound this gh-api-only cleanup job so a stuck call (rate limit, hung # `gh api --paginate`) cannot silently occupy a runner for GitHub's # 360-minute platform default -- exactly the window when a busy PR is @@ -296,7 +409,7 @@ jobs: --arg action "$PR_ACTION" --arg repo "$TARGET_REPOSITORY" --arg current "$CURRENT_RUN_ID" ' .workflow_runs[] | select((.id | tostring) != $current) - | select(.name == "Strix Security Scan") + | select(.path == ".github/workflows/strix.yml") | select(.event == "pull_request_target") | ((.display_title // "") | startswith("Strix Security Scan " + $repo + "#" + $pr + "@")) as $title_matches | ((.pull_requests // []) | any((.number | tostring) == $pr)) as $metadata_matches @@ -340,6 +453,13 @@ jobs: done strix: + outputs: + transport_capacity_unavailable: ${{ steps.strix_scan.outputs.transport_capacity_unavailable || steps.strix_sidecar_failure.outputs.transport_capacity_unavailable }} + transport_retry_eligible: ${{ steps.strix_scan.outputs.transport_retry_eligible || steps.strix_sidecar_failure.outputs.transport_retry_eligible }} + transport_retry_delay_seconds: ${{ steps.strix_scan.outputs.transport_retry_delay_seconds || steps.strix_sidecar_failure.outputs.transport_retry_delay_seconds }} + transport_retry_next_attempt: ${{ steps.strix_scan.outputs.transport_retry_next_attempt || steps.strix_sidecar_failure.outputs.transport_retry_next_attempt }} + provider_attempt_count: ${{ steps.strix_sidecar_failure.outputs.provider_attempt_count }} + transport_http_status: ${{ steps.strix_sidecar_failure.outputs.transport_http_status }} needs: [changed-scope, admit-current-head] if: needs.changed-scope.outputs.code == 'true' && needs.admit-current-head.outputs.admitted == 'true' # Large, actively-growing repositories (e.g. contextual-orchestrator) can @@ -348,7 +468,9 @@ jobs: # scans may take more than two hours per model (docs/product-goal-directive.md). # Inference has no wall-clock deadline; cancellation is reserved for an # explicit operator action or a superseded head. - runs-on: ubuntu-24.04 + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] # Least-privilege token scoped to this job (Scorecard alert #43): the scan # exchanges an OIDC token (id-token) and publishes same-repo status evidence # from the scan job only. @@ -372,6 +494,113 @@ jobs: with: python-version: "3.13" + - name: Provision pinned job-local GitHub CLI + shell: bash + run: | + set -euo pipefail + python3 - <<'PY' + import hashlib + import os + from pathlib import Path + import platform + import shutil + import subprocess + import sys + import tarfile + import tempfile + import urllib.request + + version = "2.102.0" + archive_sha256 = "bb766f710eef8ede859c18578c72c327597cd4c8a85b06001b1f3843c6019386" + binary_sha256 = "7469124f706944133d6a169691dd1c6c3511b12e85878d255e044e2948df4c9b" + url = "https://github.com/cli/cli/releases/download/v2.102.0/gh_2.102.0_linux_amd64.tar.gz" + if sys.platform != "linux" or platform.machine() not in ("x86_64", "amd64"): + raise RuntimeError("pinned GitHub CLI requires Linux x64") + parent = Path(os.environ["RUNNER_TEMP"]) + if not parent.is_absolute(): + raise RuntimeError("RUNNER_TEMP must be absolute") + for ancestor in (parent, *parent.parents): + if ancestor.is_symlink() or not ancestor.is_dir(): + raise RuntimeError("runner temporary ancestry must be real directories") + root = Path(tempfile.mkdtemp(prefix="cwl-gh-", dir=parent)) + os.chmod(root, 0o700) + try: + archive = root / "gh.tar.gz" + with urllib.request.urlopen(url) as response, archive.open("xb") as output: + total = 0 + digest = hashlib.sha256() + while True: + chunk = response.read(1024 * 1024) + if not chunk: + break + total += len(chunk) + if total > 20 * 1024 * 1024: + raise RuntimeError("GitHub CLI archive exceeds bound") + digest.update(chunk) + output.write(chunk) + if digest.hexdigest() != archive_sha256: + raise RuntimeError("GitHub CLI archive checksum mismatch") + expected_member = f"gh_{version}_linux_amd64/bin/gh" + with tarfile.open(archive, "r:gz") as source: + members = source.getmembers() + selected = [member for member in members if member.name == expected_member] + if len(selected) != 1 or not selected[0].isfile(): + raise RuntimeError("GitHub CLI archive must contain one regular binary") + member = selected[0] + if not 0 < member.size <= 64 * 1024 * 1024: + raise RuntimeError("GitHub CLI binary size exceeds bound") + stream = source.extractfile(member) + if stream is None: + raise RuntimeError("GitHub CLI binary could not be read") + with stream: + content = stream.read(member.size + 1) + if len(content) != member.size or hashlib.sha256(content).hexdigest() != binary_sha256: + raise RuntimeError("GitHub CLI binary checksum mismatch") + # Copy this single pinned regular member; never extract archive paths or links. + bindir = root / "bin" + bindir.mkdir(mode=0o700) + executable = bindir / "gh" + with executable.open("xb") as output: + output.write(content) + executable.chmod(0o700) + result = subprocess.run([str(executable), "--version"], check=True, + capture_output=True, text=True, timeout=10) + if not result.stdout.splitlines() or not result.stdout.splitlines()[0].startswith(f"gh version {version} "): + raise RuntimeError("GitHub CLI version mismatch") + archive.unlink() + with open(os.environ["GITHUB_PATH"], "a", encoding="utf-8") as output: + output.write(str(bindir) + "\n") + with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8") as output: + output.write("CWL_GH_BIN=" + str(executable) + "\n") + info = root.stat() + output.write("CWL_GH_ROOT=" + str(root) + "\n") + output.write("CWL_GH_ROOT_DEVICE=" + str(info.st_dev) + "\n") + output.write("CWL_GH_ROOT_INODE=" + str(info.st_ino) + "\n") + print("Installed checksum-verified job-local GitHub CLI " + version) + except BaseException: + shutil.rmtree(root) + raise + PY + + - name: Verify pinned job-local GitHub CLI selection + shell: bash + run: | + set -euo pipefail + if [ ! -f "$CWL_GH_BIN" ] || [ -L "$CWL_GH_BIN" ] || [ ! -x "$CWL_GH_BIN" ]; then + echo "::error::Job-local GitHub CLI executable is missing or linked." + exit 1 + fi + if [ "$(command -v gh)" != "$CWL_GH_BIN" ]; then + echo "::error::GitHub CLI PATH selection differs from the pinned job-local binary." + exit 1 + fi + printf '%s %s\n' '7469124f706944133d6a169691dd1c6c3511b12e85878d255e044e2948df4c9b' "$CWL_GH_BIN" | sha256sum --check --status + version_output="$(gh --version)" + case "$version_output" in + 'gh version 2.102.0 '*) printf '%s\n' "GitHub CLI 2.102.0 path and checksum verified." ;; + *) echo "::error::GitHub CLI selected an unsupported version."; exit 1 ;; + esac + - name: Resolve trusted Strix source ref id: trusted_source env: @@ -725,38 +954,70 @@ jobs: - name: Gate Strix secrets id: gate env: - STRIX_MODEL: contextual-orchestrator/orchestrator/free + STRIX_MODEL: auto STRIX_MODEL_REQUESTED: ${{ github.event.client_payload.strix_llm || '' }} run: | requested_model="$(printf '%s' "$STRIX_MODEL_REQUESTED" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" case "$requested_model" in - ""|orchestrator/free|contextual-orchestrator/orchestrator/free) ;; + ""|auto|orchestrator/auto|contextual-orchestrator/orchestrator/auto) ;; *) - echo '::error::Strix model overrides are limited to contextual-orchestrator/orchestrator/free.' + echo '::error::Strix model overrides must select literal auto; free and direct-provider routes are not admitted.' exit 1 ;; esac strix_model="$(printf '%s' "$STRIX_MODEL" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + if [ "$strix_model" != "auto" ]; then + echo '::error::Strix primary model must remain literal auto.' + exit 1 + fi { echo "strix_model=$strix_model" echo 'enabled=true' echo 'provider_mode=contextual_orchestrator' } >> "$GITHUB_OUTPUT" + - name: Set up lock-compatible sidecar Python + if: steps.gate.outputs.enabled == 'true' + id: sidecar_python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.12" + update-environment: false + - name: Provision contextual-orchestrator Strix sidecar + id: strix_sidecar if: steps.gate.outputs.enabled == 'true' env: + SIDECAR_PYTHON: ${{ steps.sidecar_python.outputs.python-path }} BYTEZ_API_KEY: ${{ secrets.BYTEZ_API_KEY }} NVIDIA_NIM_API_KEY: ${{ secrets.NVIDIA_NIM_API_KEY }} NVIDIA_NIM_API_KEY_SUB: ${{ secrets.NVIDIA_NIM_API_KEY_SUB }} OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} CONTEXTUAL_ORCHESTRATOR_REQUIRE_ZDR: ${{ steps.target_visibility.outputs.is_private }} - CONTEXTUAL_ORCHESTRATOR_POOL: free + CONTEXTUAL_ORCHESTRATOR_POOL: auto run: | set -euo pipefail + report_parent="$GITHUB_WORKSPACE/strix_runs" + if [ -L "$report_parent" ]; then + echo '::error::Strix preflight report directory must not be a symbolic link.' + exit 1 + fi + mkdir -p "$report_parent" + rm -f "$report_parent/contextual-orchestrator-preflight.json" bash "$TRUSTED_STRIX_SOURCE/scripts/ci/contextual_orchestrator_review_sidecar.sh" + - name: Classify all-429 Strix sidecar failure + id: strix_sidecar_failure + if: failure() && steps.strix_sidecar.outcome == 'failure' + env: + NOEMA_TRANSPORT_RETRY_ATTEMPT: ${{ toJSON(github.event.client_payload.transport_retry_attempt) }} + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.client_payload.pr_head_sha || '' }} + run: | + python3 "$TRUSTED_STRIX_SOURCE/scripts/ci/noema_preflight_capacity.py" \ + --preflight-report "$GITHUB_WORKSPACE/strix_runs/contextual-orchestrator-preflight.json" \ + --expected-head "$EXPECTED_HEAD_SHA" + - name: Set up Python if: steps.gate.outputs.enabled == 'true' uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 @@ -891,17 +1152,18 @@ jobs: strix_llm_file="$RUNNER_TEMP/strix_llm.txt" strix_model="$(printf '%s' "$STRIX_MODEL" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" case "$strix_model" in - orchestrator/free | contextual-orchestrator/orchestrator/free) - printf '%s' 'orchestrator/free' > "$strix_llm_file" + auto) + printf '%s' 'auto' > "$strix_llm_file" ;; *) - echo '::error::STRIX_LLM must select contextual-orchestrator/orchestrator/free.' + echo '::error::STRIX_LLM must remain literal auto; free fallback is not admitted.' exit 1 ;; esac echo "STRIX_LLM_FILE=$strix_llm_file" >> "$GITHUB_ENV" - name: Run Strix (quick) + id: strix_scan if: steps.gate.outputs.enabled == 'true' # Security invariant for pull_request_target: execute only from the # trusted base checkout. The gate copies PR-head blobs into an isolated @@ -939,6 +1201,7 @@ jobs: PR_BASE_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.base.sha || github.event.client_payload.pr_base_sha }} PR_HEAD_SHA: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.event.client_payload.pr_head_sha }} IS_PR_EVIDENCE_RUN: ${{ (github.event_name == 'pull_request_target' || github.event.client_payload.pr_number != '') && 'true' || 'false' }} + NOEMA_TRANSPORT_RETRY_ATTEMPT: ${{ toJSON(github.event.client_payload.transport_retry_attempt) }} run: | export LLM_TIMEOUT=0 export STRIX_MEMORY_COMPRESSOR_TIMEOUT=0 @@ -949,6 +1212,15 @@ jobs: # Defined before the gate loop so the bounded retry decision below # can classify outcomes without duplicating the patterns later. backend_unavailable_signal='STRIX_PROVIDER_UNAVAILABLE|RateLimitError|Too many requests\. For more on scraping GitHub|exceeded your current quota|insufficient_quota|billing details|"status"[[:space:]]*:[[:space:]]*"RESOURCE_EXHAUSTED"|tokens_limit_reached|Request body too large|Max size:[[:space:]]*[0-9]+[[:space:]]+tokens|Error code:[[:space:]]*500[^[:cntrl:]]*internal_error|Error code:[[:space:]]*413|LLM CONNECTION FAILED|Could not establish connection to the language model|LLM warm-up failed|Configured model and fallback models were unavailable|Configured Vertex model and fallback models were unavailable|emitted provider infrastructure or failure-signal output|before provider infrastructure failure|litellm(\.exceptions)?\.NotFoundError[^[:cntrl:]]*Nvidia_nimException[^[:cntrl:]]*Error code:[[:space:]]*404|Error during penetration test: loginAsGuest failed after [0-9]+ attempts: curl exit 7: curl: \(7\) Failed to connect to 127\.0\.0\.1 port 48080' + # Only explicit connection/rate-limit failures qualify for a new + # attempt; scanner defects and sandbox bootstrap failures do not. + runtime_transport_signal='LLM CONNECTION FAILED|Could not establish connection to the language model|RateLimitError|Too many requests\. For more on scraping GitHub' + # Scanner tooling breakage (Caido GraphQL query/cursor errors) is + # not a provider outcome. It can occur while providers are healthy + # and it can coexist with genuine provider rate limits, so it gets + # its own typed notice instead of being folded into the provider + # verdict. See docs/doctoring/review-failure-taxonomy.md. + tooling_error_signal='Invalid HTTPQL query|Failed to parse cursor|TransportQueryError|caido_sdk_client\.errors' model_behavior_error_signal='(^|[^A-Za-z0-9_])(agents|pydantic_ai|strix)(\.[A-Za-z_][A-Za-z0-9_]*)*\.ModelBehaviorError([^A-Za-z0-9_]|$)' # Any evidence that a vulnerability was actually reported. Its presence # forces a hard failure so real findings are NEVER downgraded. Keep the @@ -1002,10 +1274,22 @@ jobs: # Classify provider/backend exhaustion only when no vulnerability # finding was emitted. Classification improves diagnosis; it never # converts an incomplete scan into passing security evidence. + # Report scanner tooling breakage on its own, whatever the provider + # verdict turns out to be. This never changes the exit code: an + # incomplete scan stays non-passing either way. + if grep -Eq "$tooling_error_signal" "$strix_neutralization_scope_log"; then + echo "::error title=STRIX_TOOLING_ERROR::Strix scanner tooling failed (Caido GraphQL query or cursor error). This is a scanner defect, not a provider outage; a provider notice may also follow. See the strix-reports artifact and run log." + fi + if ( grep -Eiq "$backend_unavailable_signal" "$strix_neutralization_scope_log" \ || grep -Eq "$model_behavior_error_signal" "$strix_neutralization_scope_log" ) \ && ! grep -Eiq "$reported_vulnerability_signal" "$strix_neutralization_scope_log"; then echo "::error title=STRIX_PROVIDER_UNAVAILABLE::Strix could not complete authoritative vulnerability analysis because its provider/backend was unavailable (rate limit, token cap, connection, warm-up, or model-behavior failure). See the strix-reports artifact and run log." + if grep -Eiq "$runtime_transport_signal" "$strix_neutralization_scope_log" \ + && ! grep -Eq "$tooling_error_signal" "$strix_neutralization_scope_log" \ + && ! grep -Fq 'STRIX_SANDBOX_UNAVAILABLE' "$strix_neutralization_scope_log"; then + PYTHONPATH="$TRUSTED_STRIX_SOURCE" python3 -m scripts.ci.strix_runtime_capacity --expected-head "$PR_HEAD_SHA" + fi exit "$strix_rc" fi @@ -1128,11 +1412,48 @@ jobs: fi echo "::warning::Could not publish manual Strix status from scan job; keeping scan evidence result authoritative in the workflow run." + - name: Clean up this job's pinned GitHub CLI + if: always() && env.CWL_GH_ROOT != '' + shell: bash + run: | + set -euo pipefail + python3 - <<'PY' + import os + from pathlib import Path + import re + import shutil + import stat + + root = Path(os.environ["CWL_GH_ROOT"]) + parent = Path(os.environ["RUNNER_TEMP"]) + if (not root.is_absolute() or root.parent != parent + or not re.fullmatch(r"cwl-gh-[A-Za-z0-9_-]+", root.name) + or any(ord(c) < 32 for c in str(root))): + raise RuntimeError("refusing unrelated GitHub CLI cleanup path") + for ancestor in (parent, *parent.parents): + if ancestor.is_symlink() or not ancestor.is_dir(): + raise RuntimeError("refusing changed GitHub CLI temporary ancestry") + try: + info = root.lstat() + except FileNotFoundError: + print("Owned GitHub CLI directory is already absent.") + else: + if (not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid() + or info.st_dev != int(os.environ["CWL_GH_ROOT_DEVICE"]) + or info.st_ino != int(os.environ["CWL_GH_ROOT_INODE"])): + raise RuntimeError("refusing changed GitHub CLI directory identity") + # No glob or shared-temp sweep: delete only this job's original directory. + shutil.rmtree(root) + print("Removed this job's checksum-verified GitHub CLI directory.") + PY + publish-manual-pr-evidence-status: name: publish-manual-pr-evidence-status needs: strix if: ${{ always() && !cancelled() && github.event_name == 'repository_dispatch' && github.event.client_payload.pr_head_sha != '' }} - runs-on: ubuntu-24.04 + runs-on: + group: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} # Single-shot OIDC exchange plus a handful of curl/gh api calls, no loop # or pagination -- same shape as the agent-mention-*-dispatch.yml # validate-and-forward jobs, which bound at timeout-minutes: 5. Without @@ -1345,3 +1666,90 @@ jobs: echo "::error::Could not publish manual Strix status from follow-up job after all configured credentials failed after a non-successful scan; the target PR head is missing required Strix status evidence. See the preceding notices for token-specific reasons." exit 1 + + continue-strix-transport: + needs: [admit-current-head, strix] + if: >- + always() + && needs.admit-current-head.outputs.admitted == 'true' + && !cancelled() + && needs.strix.result == 'failure' + && needs.strix.outputs.transport_capacity_unavailable == 'true' + && needs.strix.outputs.transport_retry_eligible == 'true' + runs-on: + group: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).group }} + labels: ${{ ((github.repository == 'ContextualWisdomLab/.github' || github.repository == 'ContextualWisdomLab/fast-mlsirm') && github.workflow_ref == 'ContextualWisdomLab/.github/.github/workflows/strix.yml@refs/heads/main' && fromJSON('{"group":"CWL central control","labels":["self-hosted","linux","x64","cwlab-control"]}') || fromJSON('{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}')).labels }} + timeout-minutes: 10 + permissions: + contents: write + pull-requests: read + env: + # Consumer required workflows need the existing central dispatch credential. + # The central handler can use its repository-scoped token as fallback. + GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || github.token }} + TARGET_REPOSITORY: ${{ github.event.pull_request.base.repo.full_name || github.event.client_payload.target_repository || github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number || github.event.client_payload.pr_number || '' }} + EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.event.client_payload.pr_head_sha || '' }} + EXPECTED_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.client_payload.pr_base_sha || '' }} + EXPECTED_BASE_REF: ${{ github.event.pull_request.base.ref || github.event.client_payload.pr_base_ref || '' }} + DELAY_SECONDS: ${{ needs.strix.outputs.transport_retry_delay_seconds }} + NEXT_ATTEMPT: ${{ needs.strix.outputs.transport_retry_next_attempt }} + PROVIDER_ATTEMPT_COUNT: ${{ needs.strix.outputs.provider_attempt_count }} + TRANSPORT_HTTP_STATUS: ${{ needs.strix.outputs.transport_http_status }} + steps: + - name: Schedule bounded Strix transport re-dispatch + run: | + set -euo pipefail + if { [ "$GITHUB_REPOSITORY" != "ContextualWisdomLab/.github" ] && + [ "$TARGET_REPOSITORY" != "$GITHUB_REPOSITORY" ]; } || + ! [[ "$TARGET_REPOSITORY" =~ ^ContextualWisdomLab/[A-Za-z0-9_.-]+$ ]] || + ! [[ "$PR_NUMBER" =~ ^[1-9][0-9]*$ ]] || + ! [[ "$EXPECTED_HEAD_SHA" =~ ^[0-9a-f]{40}$ ]] || + ! [[ "$EXPECTED_BASE_SHA" =~ ^[0-9a-f]{40}$ ]]; then + echo "::error::Strix transport re-dispatch rejected an unrelated origin or malformed PR identity." + exit 1 + fi + if ! [[ "$DELAY_SECONDS" =~ ^[1-9][0-9]*$ ]] || [ "$DELAY_SECONDS" -gt 300 ] || + ! [[ "$NEXT_ATTEMPT" =~ ^[12]$ ]]; then + echo "::error::Strix transport re-dispatch refused an unbounded delay or attempt." + exit 1 + fi + echo "::notice::Strix provider capacity unavailable (http_status=${TRANSPORT_HTTP_STATUS:-unknown}, provider_attempt_count=${PROVIDER_ATTEMPT_COUNT:-unknown}); waiting ${DELAY_SECONDS}s before same-head continuation re-dispatch ${NEXT_ATTEMPT}." + sleep "$DELAY_SECONDS" + live_pr="$(gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}")" + live_head="$(jq -r '.head.sha // empty' <<<"$live_pr")" + live_head_repo="$(jq -r '.head.repo.full_name // empty' <<<"$live_pr")" + live_base="$(jq -r '.base.sha // empty' <<<"$live_pr")" + live_base_repo="$(jq -r '.base.repo.full_name // empty' <<<"$live_pr")" + live_base_ref="$(jq -r '.base.ref // empty' <<<"$live_pr")" + live_ready="$(jq -r '.draft == false' <<<"$live_pr")" + live_state="$(jq -r '.state // empty' <<<"$live_pr")" + if [ "$live_head" != "$EXPECTED_HEAD_SHA" ] || + [ "$live_head_repo" != "$TARGET_REPOSITORY" ] || + [ "$live_base" != "$EXPECTED_BASE_SHA" ] || + [ "$live_base_repo" != "$TARGET_REPOSITORY" ] || + [ "$live_base_ref" != "$EXPECTED_BASE_REF" ] || + [ "$live_ready" != "true" ] || + [ "$live_state" != "open" ]; then + echo "::notice::Strix transport re-dispatch retired because the live PR head or base moved or closed." + exit 0 + fi + jq -n \ + --arg target_repository "$TARGET_REPOSITORY" \ + --argjson pr_number "$PR_NUMBER" \ + --arg pr_head_sha "$EXPECTED_HEAD_SHA" \ + --arg pr_base_ref "$EXPECTED_BASE_REF" \ + --arg pr_base_sha "$EXPECTED_BASE_SHA" \ + --argjson transport_retry_attempt "$NEXT_ATTEMPT" \ + '{ + event_type: "strix-scan", + client_payload: { + target_repository: $target_repository, + pr_number: $pr_number, + pr_head_sha: $pr_head_sha, + pr_base_ref: $pr_base_ref, + pr_base_sha: $pr_base_sha, + transport_retry_attempt: $transport_retry_attempt + } + }' | gh api -X POST "repos/ContextualWisdomLab/.github/dispatches" --input - + echo "::notice::Scheduled Strix transport continuation re-dispatch for ${TARGET_REPOSITORY}#${PR_NUMBER} at ${EXPECTED_HEAD_SHA} (attempt ${NEXT_ATTEMPT})." diff --git a/.github/workflows/tepp-documentation-quality.yml b/.github/workflows/tepp-documentation-quality.yml new file mode 100644 index 0000000000..d28d4c0be7 --- /dev/null +++ b/.github/workflows/tepp-documentation-quality.yml @@ -0,0 +1,38 @@ +name: TEPP Documentation Quality + +on: + workflow_call: + +permissions: + contents: read + + +jobs: + validate: + name: Validate planning and governance contracts + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 10 + steps: + - name: Checkout exact head + uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 + with: + persist-credentials: false + fetch-depth: 2 + - name: Set up pinned Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.12' + - name: Create private Python environment + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -m venv "${RUNNER_TEMP:?}/tepp-python-venv" + printf 'VIRTUAL_ENV=%s\n' "${RUNNER_TEMP}/tepp-python-venv" >>"${GITHUB_ENV:?}" + printf '%s/bin\n' "${RUNNER_TEMP}/tepp-python-venv" >>"${GITHUB_PATH:?}" + export PATH="${RUNNER_TEMP}/tepp-python-venv/bin:${PATH}" + python3 -m pip --version + - name: Validate documentation + run: python3 scripts/validate_documentation.py + - name: Reject whitespace errors + run: git diff --check HEAD^ HEAD diff --git a/.github/workflows/tepp-rust-foundation-ci.yml b/.github/workflows/tepp-rust-foundation-ci.yml new file mode 100644 index 0000000000..b9e420e5c4 --- /dev/null +++ b/.github/workflows/tepp-rust-foundation-ci.yml @@ -0,0 +1,348 @@ +name: TEPP Rust Foundation CI + +on: + workflow_call: + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + RUSTFLAGS: -Dwarnings + RUSTDOCFLAGS: -Dwarnings + CARGO_NEXTEST_VERSION: "0.9.140" + CARGO_DENY_VERSION: "0.19.7" + CARGO_LLVM_COV_VERSION: "0.8.6" + + +jobs: + repository-contracts: + name: Repository contracts and Python branch coverage + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 10 + steps: + - name: Checkout exact head + uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 + with: + persist-credentials: false + - name: Set up pinned Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.12' + - name: Create private Python environment + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -m venv "${RUNNER_TEMP:?}/tepp-python-venv" + printf 'VIRTUAL_ENV=%s\n' "${RUNNER_TEMP}/tepp-python-venv" >>"${GITHUB_ENV:?}" + printf '%s/bin\n' "${RUNNER_TEMP}/tepp-python-venv" >>"${GITHUB_PATH:?}" + export PATH="${RUNNER_TEMP}/tepp-python-venv/bin:${PATH}" + python3 -m pip --version + - name: Install pinned Python quality dependency + run: python3 -m pip install --disable-pip-version-check --no-deps --require-hashes -r requirements-quality.txt + - name: Exercise repository tooling with branch coverage + run: python3 -m coverage run --branch -m unittest discover -s tests/quality -p 'test_*.py' + - name: Enforce repository tooling coverage + run: python3 -m coverage report --fail-under=100 --show-missing + - name: Validate workspace contract + run: python3 scripts/check_workspace_contract.py + - name: Validate Rust documentation contract + run: python3 scripts/check_docstrings.py + - name: Generate and validate release SBOM/provenance evidence + run: | + python3 scripts/release_evidence.py generate \ + --repository-root . \ + --output-directory "${RUNNER_TEMP}/tepp-release-evidence" \ + --git-commit "${GITHUB_SHA}" + python3 scripts/release_evidence.py validate \ + --evidence-directory "${RUNNER_TEMP}/tepp-release-evidence" \ + --repository-root . \ + --expected-git-commit "${GITHUB_SHA}" + + rust-quality: + name: Format, lint, test, rustdoc, and dependency policy + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 30 + steps: + - name: Checkout exact head + uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 + with: + persist-credentials: false + - name: Set up pinned Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.12' + - name: Create private Python environment + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -m venv "${RUNNER_TEMP:?}/tepp-python-venv" + printf 'VIRTUAL_ENV=%s\n' "${RUNNER_TEMP}/tepp-python-venv" >>"${GITHUB_ENV:?}" + printf '%s/bin\n' "${RUNNER_TEMP}/tepp-python-venv" >>"${GITHUB_PATH:?}" + export PATH="${RUNNER_TEMP}/tepp-python-venv/bin:${PATH}" + python3 -m pip --version + - name: Bootstrap hash-verified Rustup + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + # Fresh namespaces prevent reuse of persistent HOME state, not whole-VM isolation. + rust_root="$(mktemp -d "${RUNNER_TEMP:?}/tepp-rust-XXXXXXXXXX")" + export CARGO_HOME="$rust_root/.cargo" RUSTUP_HOME="$rust_root/.rustup" + rustup_init="$rust_root/rustup-init-1.29.1" + curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \ + --output "$rustup_init" \ + https://static.rust-lang.org/rustup/archive/1.29.1/x86_64-unknown-linux-gnu/rustup-init + printf '%s %s\n' \ + dda7234360b7f578ca8b0ddcb80145646fa61a67c1720a5abc7051b35c9fcb71 \ + "$rustup_init" | sha256sum --check --strict + chmod 0755 "$rustup_init" + "$rustup_init" -y --default-toolchain none --profile minimal --no-modify-path + rm -f -- "$rustup_init" + "$CARGO_HOME/bin/rustup" --version + printf 'RUSTUP_HOME=%s\nCARGO_HOME=%s\n' "$RUSTUP_HOME" "$CARGO_HOME" >>"${GITHUB_ENV:?}" + printf '%s/bin\n' "$CARGO_HOME" >>"${GITHUB_PATH:?}" + - name: Install pinned Rust toolchain + run: rustup toolchain install 1.98.0 --profile minimal --component clippy --component rustfmt + - name: Install pinned Rust quality tools + run: | + cargo install cargo-nextest --root "$CARGO_HOME" --locked --version "$CARGO_NEXTEST_VERSION" + cargo install cargo-deny --root "$CARGO_HOME" --locked --version "$CARGO_DENY_VERSION" + - name: Verify pinned Rust quality tool versions + run: | + test -x "${CARGO_HOME:?}/bin/cargo-nextest" + test -x "$CARGO_HOME/bin/cargo-deny" + version="$("$CARGO_HOME/bin/cargo-nextest" --version)" + case "$version" in + "cargo-nextest $CARGO_NEXTEST_VERSION"|"cargo-nextest $CARGO_NEXTEST_VERSION "*) ;; + *) echo "Unexpected cargo-nextest version: $version" >&2; exit 1 ;; + esac + version="$("$CARGO_HOME/bin/cargo-deny" --version)" + case "$version" in + "cargo-deny $CARGO_DENY_VERSION"|"cargo-deny $CARGO_DENY_VERSION "*) ;; + *) echo "Unexpected cargo-deny version: $version" >&2; exit 1 ;; + esac + - name: Check formatting + run: cargo fmt --all -- --check + - name: Run Clippy + run: cargo clippy --workspace --all-targets --all-features -- -D warnings + - name: Run test suite without retries + run: cargo nextest run --workspace --all-features + - name: Run doctests separately + run: cargo test --doc --workspace --all-features + - name: Build warning-free documentation + run: cargo doc --workspace --all-features --no-deps + - name: Enforce dependency, license, advisory, and source policy + run: cargo deny check + + production-coverage: + name: Production line and branch coverage + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 45 + steps: + - name: Checkout exact head + uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 + with: + persist-credentials: false + - name: Set up pinned Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.12' + - name: Create private Python environment + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -m venv "${RUNNER_TEMP:?}/tepp-python-venv" + printf 'VIRTUAL_ENV=%s\n' "${RUNNER_TEMP}/tepp-python-venv" >>"${GITHUB_ENV:?}" + printf '%s/bin\n' "${RUNNER_TEMP}/tepp-python-venv" >>"${GITHUB_PATH:?}" + export PATH="${RUNNER_TEMP}/tepp-python-venv/bin:${PATH}" + python3 -m pip --version + - name: Bootstrap hash-verified Rustup + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + # Fresh namespaces prevent reuse of persistent HOME state, not whole-VM isolation. + rust_root="$(mktemp -d "${RUNNER_TEMP:?}/tepp-rust-XXXXXXXXXX")" + export CARGO_HOME="$rust_root/.cargo" RUSTUP_HOME="$rust_root/.rustup" + rustup_init="$rust_root/rustup-init-1.29.1" + curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \ + --output "$rustup_init" \ + https://static.rust-lang.org/rustup/archive/1.29.1/x86_64-unknown-linux-gnu/rustup-init + printf '%s %s\n' \ + dda7234360b7f578ca8b0ddcb80145646fa61a67c1720a5abc7051b35c9fcb71 \ + "$rustup_init" | sha256sum --check --strict + chmod 0755 "$rustup_init" + "$rustup_init" -y --default-toolchain none --profile minimal --no-modify-path + rm -f -- "$rustup_init" + "$CARGO_HOME/bin/rustup" --version + printf 'RUSTUP_HOME=%s\nCARGO_HOME=%s\n' "$RUSTUP_HOME" "$CARGO_HOME" >>"${GITHUB_ENV:?}" + printf '%s/bin\n' "$CARGO_HOME" >>"${GITHUB_PATH:?}" + - name: Install pinned Rust toolchain with LLVM tools + run: rustup toolchain install 1.98.0 --profile minimal --component llvm-tools-preview + - name: Install pinned nightly with LLVM tools + run: rustup toolchain install nightly-2026-08-21 --profile minimal --component llvm-tools-preview + - name: Install cargo-llvm-cov + run: cargo install cargo-llvm-cov --root "$CARGO_HOME" --locked --version "$CARGO_LLVM_COV_VERSION" + - name: Verify pinned cargo-llvm-cov version + run: | + test -x "${CARGO_HOME:?}/bin/cargo-llvm-cov" + version="$("$CARGO_HOME/bin/cargo-llvm-cov" llvm-cov --version)" + case "$version" in + "cargo-llvm-cov $CARGO_LLVM_COV_VERSION"|"cargo-llvm-cov $CARGO_LLVM_COV_VERSION "*) ;; + *) echo "Unexpected cargo-llvm-cov version: $version" >&2; exit 1 ;; + esac + - name: Generate exact line coverage with region diagnostics + id: line-report + # mlx_native_receipt is excluded exactly like sqlx_live.rs: it is a + # macOS/MLX-only device receipt probe whose success path cannot + # execute on the Linux coverage runner (see ADR 0025). Its macOS + # behaviour is covered by the crate's cfg(macos) tests. + run: cargo llvm-cov --workspace --all-features --json --output-path coverage.json --ignore-filename-regex 'sqlx_live\.rs|mlx_native_receipt' + - name: Export exact authored line coverage + run: cargo llvm-cov report --lcov --output-path coverage.lcov --ignore-filename-regex 'sqlx_live\.rs|mlx_native_receipt' + - name: Enforce complete authored line coverage + run: python3 scripts/check_coverage.py coverage.lcov --kind lines --format lcov + - name: Show exact missing line diagnostics + if: ${{ failure() && steps.line-report.outcome == 'success' }} + run: | + LLVM_COV_FLAGS="--show-line-counts-or-regions" cargo llvm-cov report --text --show-missing-lines --show-instantiations + test -f coverage.lcov || cargo llvm-cov report --lcov --output-path coverage.lcov --ignore-filename-regex 'sqlx_live\.rs|mlx_native_receipt' + python3 - <<'PY' + import json + from pathlib import Path + + report = json.loads(Path("coverage.json").read_text(encoding="utf-8")) + for data in report.get("data", []): + for file_record in data.get("files", []): + filename = file_record.get("filename", "") + lines = file_record.get("summary", {}).get("lines", {}) + covered = int(lines.get("covered", 0)) + count = int(lines.get("count", 0)) + if covered < count: + print(f"INCOMPLETE_FILE {filename} {covered}/{count}") + for segment in file_record.get("segments", []): + if len(segment) < 6: + continue + line_number, column_number, execution_count = segment[:3] + has_count, is_region_entry, is_gap_region = segment[3:6] + if ( + has_count + and is_region_entry + and not is_gap_region + and int(execution_count) == 0 + ): + print( + "UNCOVERED_REGION " + f"{filename}:{line_number}:{column_number}" + ) + for function_record in data.get("functions", []): + if int(function_record.get("count", 0)) != 0: + continue + filenames = function_record.get("filenames", []) + filename = filenames[0] if filenames else "" + print( + "UNCOVERED_FUNCTION " + f"{filename} {function_record.get('name', '')}" + ) + + current_source = None + for raw_line in Path("coverage.lcov").read_text(encoding="utf-8").splitlines(): + if raw_line.startswith("SF:"): + current_source = raw_line[3:] + elif raw_line.startswith("DA:") and current_source is not None: + line_number, count, *_ = raw_line[3:].split(",") + if int(count) == 0: + print(f"UNCOVERED_LINE {current_source}:{line_number}") + PY + - name: Upload exact line coverage diagnostics + if: ${{ failure() && steps.line-report.outcome == 'success' }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: line-coverage-diagnostics + path: | + coverage.json + coverage.lcov + if-no-files-found: error + retention-days: 1 + - name: Generate exact branch coverage + id: branch-report + if: ${{ !cancelled() }} + run: cargo +nightly-2026-08-21 llvm-cov --branch --workspace --all-features --json --output-path coverage-branches.json --ignore-filename-regex 'sqlx_live\.rs|mlx_native_receipt' + - name: Enforce complete branch coverage + if: ${{ !cancelled() && steps.branch-report.outcome == 'success' }} + run: python3 scripts/check_coverage.py coverage-branches.json --kind branches + - name: Show exact missing branch diagnostics + if: ${{ failure() && steps.branch-report.outcome == 'success' }} + run: cargo +nightly-2026-08-21 llvm-cov report --branch --text --show-missing-lines + - name: Upload exact branch coverage diagnostics + if: ${{ failure() && steps.branch-report.outcome == 'success' }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: branch-coverage-diagnostics + path: coverage-branches.json + if-no-files-found: error + retention-days: 1 + + live-postgres: + name: Live PostgreSQL integration + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] + timeout-minutes: 20 + services: + postgres: + image: postgres:16.9-alpine + env: + POSTGRES_USER: tepp + POSTGRES_PASSWORD: tepp_ci + POSTGRES_DB: tepp + ports: + - 5432:5432 + options: >- + --health-cmd "pg_isready -U tepp -d tepp" + --health-interval 5s + --health-timeout 5s + --health-retries 10 + env: + TEPP_LIVE_POSTGRES: "1" + DATABASE_URL: postgres://tepp:tepp_ci@localhost:5432/tepp + steps: + - name: Checkout exact head + uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 + with: + persist-credentials: false + - name: Set up pinned Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.12' + - name: Create private Python environment + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + python3 -m venv "${RUNNER_TEMP:?}/tepp-python-venv" + printf 'VIRTUAL_ENV=%s\n' "${RUNNER_TEMP}/tepp-python-venv" >>"${GITHUB_ENV:?}" + printf '%s/bin\n' "${RUNNER_TEMP}/tepp-python-venv" >>"${GITHUB_PATH:?}" + export PATH="${RUNNER_TEMP}/tepp-python-venv/bin:${PATH}" + python3 -m pip --version + - name: Bootstrap hash-verified Rustup + shell: bash --noprofile --norc -e -o pipefail {0} + run: | + # Fresh namespaces prevent reuse of persistent HOME state, not whole-VM isolation. + rust_root="$(mktemp -d "${RUNNER_TEMP:?}/tepp-rust-XXXXXXXXXX")" + export CARGO_HOME="$rust_root/.cargo" RUSTUP_HOME="$rust_root/.rustup" + rustup_init="$rust_root/rustup-init-1.29.1" + curl --proto '=https' --tlsv1.2 --fail --silent --show-error --location \ + --output "$rustup_init" \ + https://static.rust-lang.org/rustup/archive/1.29.1/x86_64-unknown-linux-gnu/rustup-init + printf '%s %s\n' \ + dda7234360b7f578ca8b0ddcb80145646fa61a67c1720a5abc7051b35c9fcb71 \ + "$rustup_init" | sha256sum --check --strict + chmod 0755 "$rustup_init" + "$rustup_init" -y --default-toolchain none --profile minimal --no-modify-path + rm -f -- "$rustup_init" + "$CARGO_HOME/bin/rustup" --version + printf 'RUSTUP_HOME=%s\nCARGO_HOME=%s\n' "$RUSTUP_HOME" "$CARGO_HOME" >>"${GITHUB_ENV:?}" + printf '%s/bin\n' "$CARGO_HOME" >>"${GITHUB_PATH:?}" + - name: Install pinned Rust toolchain + run: rustup toolchain install 1.98.0 --profile minimal + - name: Run live-sqlx PostgreSQL integration + run: cargo test -p persistence_postgres --features live-sqlx --test live_postgres -- --nocapture diff --git a/.github/workflows/traveling-agent-poc-quality.yml b/.github/workflows/traveling-agent-poc-quality.yml new file mode 100644 index 0000000000..08dc2ccd5c --- /dev/null +++ b/.github/workflows/traveling-agent-poc-quality.yml @@ -0,0 +1,123 @@ +name: Traveling Agent PoC quality +on: + workflow_call: +permissions: + contents: read +concurrency: + group: traveling-agent-poc-quality-${{ github.repository }}-${{ github.event.pull_request.number || format('{0}-{1}', github.event_name, github.ref) }} + cancel-in-progress: true +jobs: + offline-contracts: + if: >- + github.repository == 'ContextualWisdomLab/traveling-agent' && + ((github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository && + github.event.pull_request.base.repo.full_name == github.repository && + github.event.pull_request.base.ref == 'main' && + github.event.pull_request.head.sha != '') || + ((github.event_name == 'push' || github.event_name == 'workflow_dispatch') && + github.ref == 'refs/heads/main')) + runs-on: + group: CWL CI isolated + labels: [self-hosted, Linux, X64, cwlab-ci-isolated] + timeout-minutes: 10 + steps: + - name: Admit owned same-repository caller + env: + CALLER_REPOSITORY: ${{ github.repository }} + CALLER_EVENT: ${{ github.event_name }} + EXPECTED_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + set -eu + path="$(command -v python3)" + case "$path" in + /*) ;; + *) exit 1 ;; + esac + test -x "$path" + "$path" --version + python3 - <<'PY' + import json, os, re + assert os.environ['CALLER_REPOSITORY'] == 'ContextualWisdomLab/traveling-agent' + event = os.environ['CALLER_EVENT'] + assert event in ('pull_request', 'push', 'workflow_dispatch') + payload = json.load(open(os.environ['GITHUB_EVENT_PATH'])) + if event == 'pull_request': + pr = payload['pull_request'] + assert pr['head']['repo']['full_name'] == os.environ['CALLER_REPOSITORY'] + assert pr['base']['repo']['full_name'] == os.environ['CALLER_REPOSITORY'] + assert pr['base']['ref'] == 'main' + assert re.fullmatch(r'[0-9a-f]{40}', pr['head']['sha']) + assert pr['head']['sha'] == os.environ['EXPECTED_SHA'] + else: + assert os.environ['GITHUB_REF'] == 'refs/heads/main' + PY + - name: Checkout exact candidate + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + persist-credentials: false + fetch-depth: 0 + - name: Verify runner toolchain and checkout identity + env: + EXPECTED_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + run: | + set -eu + for tool in python3 node git; do + path="$(command -v "$tool")" + case "$path" in + /*) ;; + *) exit 1 ;; + esac + test -x "$path" + "$path" --version + done + test "$(git rev-parse HEAD)" = "$EXPECTED_SHA" + - name: Verify offline Python contracts + run: python3 -m unittest discover -s poc/tests -v + - name: Verify JavaScript syntax and real renderer contract + run: | + node --check poc/web/app.js + node poc/tests/ui_contract_test.js + - name: Check whitespace + env: + EVENT_NAME: ${{ github.event_name }} + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + BEFORE_SHA: ${{ github.event.before }} + AFTER_SHA: ${{ github.event.after }} + run: | + set -eu + python3 - <<'PY' + import os, re, subprocess + + def git(*args): + return subprocess.check_output(['git', *args], input='', text=True).strip() + + def commit(value): + if not re.fullmatch(r'[0-9a-f]{40}', value) or value == '0' * 40: + raise SystemExit('Invalid whitespace range commit') + git('cat-file', '-e', value + '^{commit}') + return value + + if git('rev-parse', '--is-shallow-repository') != 'false': + raise SystemExit('Whitespace check requires complete history') + head = commit(os.environ['HEAD_SHA']) + if git('rev-parse', 'HEAD') != head: + raise SystemExit('Whitespace checkout identity mismatch') + event = os.environ['EVENT_NAME'] + if event == 'pull_request': + base = commit(os.environ['BASE_SHA']) + elif event == 'push': + if commit(os.environ['AFTER_SHA']) != head: + raise SystemExit('Whitespace push identity mismatch') + before = os.environ['BEFORE_SHA'] + base = git('hash-object', '-t', 'tree', '--stdin') if before == '0' * 40 else commit(before) + elif event == 'workflow_dispatch': + parents = git('rev-list', '--parents', '-n', '1', head).split()[1:] + base = parents[0] if parents else git('hash-object', '-t', 'tree', '--stdin') + else: + raise SystemExit('Unsupported whitespace event') + subprocess.run(['git', '-c', 'core.whitespace=blank-at-eol,blank-at-eof,space-before-tab', + 'diff', '--check', base, head, '--'], check=True) + PY diff --git a/.github/workflows/trusted-uv-materializer-quality-ci.yml b/.github/workflows/trusted-uv-materializer-quality-ci.yml index db70ec324c..8a5e9db20c 100644 --- a/.github/workflows/trusted-uv-materializer-quality-ci.yml +++ b/.github/workflows/trusted-uv-materializer-quality-ci.yml @@ -2,28 +2,43 @@ name: Trusted uv Materializer Quality CI on: pull_request: - branches: [main] paths: - ".github/workflows/trusted-uv-materializer-quality-ci.yml" + - ".github/workflows/company-default-head-trust-gate.yml" + - ".github/workflows/company-settlement-validate.yml" + - "tests/test_company_default_head_trust_gate.py" - "scripts/ci/materialize_base_python_requirements.py" + - "scripts/ci/verify_release_maturin_tool_assets.py" - "tests/conftest.py" - "tests/test_materialize*.py" - "tests/test_trusted_uv*.py" - "tests/test_uv*.py" - "tests/test_repository_branch_coverage_*.py" + - "tests/test_verify_release_maturin_tool_assets.py" + - "requirements-opencode-review-ci.txt" + - "requirements-noema-document-ci.txt" - "requirements-opencode-review-ci-hashes.txt" + - "scripts/ci/compile_opencode_review_lock.sh" - "pyproject.toml" push: branches: [main] paths: - ".github/workflows/trusted-uv-materializer-quality-ci.yml" + - ".github/workflows/company-default-head-trust-gate.yml" + - ".github/workflows/company-settlement-validate.yml" + - "tests/test_company_default_head_trust_gate.py" - "scripts/ci/materialize_base_python_requirements.py" + - "scripts/ci/verify_release_maturin_tool_assets.py" - "tests/conftest.py" - "tests/test_materialize*.py" - "tests/test_trusted_uv*.py" - "tests/test_uv*.py" - "tests/test_repository_branch_coverage_*.py" + - "tests/test_verify_release_maturin_tool_assets.py" + - "requirements-opencode-review-ci.txt" + - "requirements-noema-document-ci.txt" - "requirements-opencode-review-ci-hashes.txt" + - "scripts/ci/compile_opencode_review_lock.sh" - "pyproject.toml" concurrency: @@ -39,7 +54,9 @@ env: jobs: minimum-python-contract: name: Python 3.10 compatibility contract - runs-on: ubuntu-latest + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] timeout-minutes: 10 steps: - name: Harden runner @@ -85,7 +102,9 @@ jobs: full-quality-gate: name: Python 3.14 full quality gate - runs-on: ubuntu-latest + runs-on: + group: CWL CI isolated + labels: [self-hosted, linux, x64, cwlab-ci-isolated] timeout-minutes: 20 steps: - name: Harden runner @@ -98,6 +117,7 @@ jobs: with: persist-credentials: false ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 - name: Set up current stable Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 79f94e70f3..2da382f934 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -43,3 +43,11 @@ **Vulnerability:** Denial of Service / Availability **Learning:** Strix security scanners crashed when the backend LLM returned an 'internal server error' HTTP 500 response. This was because 'internal server error' string match was missing from the `is_llm_api_connection_error` function in the Strix retry gate. **Prevention:** Always include `internal server error` in string match conditions when handling HTTP API Connection exceptions for LLM backends to ensure proper fail-closed and retry handling. +## 2024-05-19 - Path Traversal Vulnerability in Unbounded Regex Expressions +**Vulnerability:** Unbounded regular expressions for repository and organization names, such as `^[A-Za-z0-9_.-]+$`, allowed path traversal if user data ended in `.` or `..`. +**Learning:** End-of-string anchors within unbounded lookaheads (e.g. `(?!.*(?:\.\.|\.$|^\.))`) unintentionally fail matches when valid data is followed by trailing text. Bounding validation solely to the captured characters requires simpler lookaheads combined with character-class repetition. +**Prevention:** Always use negative lookaheads without end-of-string anchors (e.g. `^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$`) to prevent path traversal in parameters used for URL construction or file access. +## 2024-05-20 - Unhandled 502 Bad Gateway causing DoS in LLM integration +**Vulnerability:** Denial of Service / Availability +**Learning:** Strix security scanners crashed when the backend LLM returned an 'HTTP Error 502: Bad Gateway' response. This was because 'bad gateway' string match and generic 'APIError' were missing from the `is_llm_api_connection_error` function in the Strix retry gate. +**Prevention:** Always include `bad gateway` and `APIError` in string match conditions when handling HTTP API Connection exceptions for LLM backends to ensure proper fail-closed and retry handling. diff --git a/AGENTS.md b/AGENTS.md index e955f8b36a..0972af51c5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,6 +30,14 @@ see [`docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md`](docs/adr/0003 2026-08-30 amendment and its 2026-08-31 correction, which retracts an earlier false claim of explicit owner direction and records the resulting availability risk as open and unreviewed, not accepted. +Sidecar diagnostics may retain only a server-generated `request_id` matching +exactly 32 lowercase hexadecimal characters, plus the producer's explicit `-` +or `` marker where that event contract permits it. Keep free-form +provider errors omitted; malformed, uppercase, short, long, or otherwise +unbounded identifiers must not pass the sanitizer. +HTTP success summaries are narrower still: preserve correlation only for the +review sidecar's fixed health, chat-completions, and responses paths. Never +allowlist arbitrary request paths merely because the producer stripped queries. The materialization contract is also covered by [`docs/doctoring/exact-artifact-sbom-attestation.md`](docs/doctoring/exact-artifact-sbom-attestation.md). ## Actions queue and protected-merge procedure @@ -68,6 +76,8 @@ The materialization contract is also covered by [`docs/doctoring/exact-artifact- ## Verification discipline +- producer가 안전한 로그 필드를 추가하면 exact revision 쌍으로 consumer sanitizer를 통과시켜 allowlist의 누락을 확인한다. producer 단위 테스트 성공만으로 CI artifact 보존을 주장하지 않으며, 연결 검증에서도 raw 본문 비출력을 유지한다. + Many agent sessions work this organization concurrently under the same standing brief. Silence is not evidence: "I have not touched X" describes one session's history, never the organization's actual state. diff --git a/CHANGELOG.d/20260912-javascript-materializer-docstrings.md b/CHANGELOG.d/20260912-javascript-materializer-docstrings.md new file mode 100644 index 0000000000..1165181540 --- /dev/null +++ b/CHANGELOG.d/20260912-javascript-materializer-docstrings.md @@ -0,0 +1 @@ +Document trusted JavaScript lock discovery and validation boundaries with explanatory contracts enforced by CI. diff --git a/CHANGELOG.d/20260914-pingora-declared-artifact-paths.md b/CHANGELOG.d/20260914-pingora-declared-artifact-paths.md new file mode 100644 index 0000000000..e5f375c909 --- /dev/null +++ b/CHANGELOG.d/20260914-pingora-declared-artifact-paths.md @@ -0,0 +1,3 @@ +### Pingora edge policy admits declared research/data artifact paths + +- `scripts/ci/pingora_edge_policy.py` previously admitted binary or non-UTF-8 content only by path shape (`DOCUMENTATION_DIRECTORIES` via `_is_known_documentation_path`, plus the `evidence`/`figures` publication directories from #2149), so a research repository's raw data and fitted-model artefacts kept elsewhere by deliberate, owner-approved design -- e.g. `ContextualWisdomLab/late-life-anxiety-reanalysis`'s `local/` and evidence-preservation paths -- were rejected on path shape alone, with no route except relocating them under `docs/` (already done once, for 66 images) or leaving the PR unmergeable. `evaluate_pull_request` now accepts an optional `base_ref` and, when given, resolves a new `.github/edge-policy-artifact-paths.txt` declaration (one relative path prefix per line, no globs, capped at `MAX_DECLARED_ARTIFACT_PREFIXES=64` entries and `MAX_DECLARED_ARTIFACT_PREFIX_DEPTH=8` segments) **only from that base ref, never the pull-request head** -- a PR that adds or widens the declaration gets no benefit from it until that change is itself reviewed and merged, proven by a same-PR self-authorization regression test. The declaration replaces only the path-shape test: `_runtime_path_rule` matches stay rejected inside a declared prefix exactly as inside `docs/` today, and a suffix with no `BINARY_DOCUMENT_MAGIC` entry (most research-data formats have none -- `.xlsx`, `.sav`, `.rds`, `.npz`, …) is admitted only on the stricter "no diff patch + fetched bytes are not valid UTF-8" evidence, so a file that decodes as valid UTF-8 is always still content-scanned, never silently admitted. `.hwpx`/`.pdf`/`.png` under a declared prefix keep the existing structural-evidence checks. A malformed declaration (absolute path, `..`, bare `.`/`/`, a glob character, or over either bound) is a hard `PolicyError` naming the offending entry; a repository with no declaration file at all behaves identically to before this feature existed. `evaluate_pull_request` now also emits a `::notice::` naming the declared prefix and the base ref it came from whenever a declared-prefix admission occurs, so a reviewer can trace it back to the reviewed declaration. `.github/workflows/opencode-review.yml`'s `pull_request_target`-derived `github.event.pull_request.base.sha` is threaded through as `--base-ref` with no new permissions. `tests/test_pingora_edge_policy.py` adds coverage for base-ref admission, the self-authorization refusal, runtime-form and valid-UTF-8 rejection inside a declared prefix, every malformed-declaration shape, and the no-declaration regression guard; `tests/test_pingora_edge_workflow_contract.py` pins the new workflow wiring. `pingora_edge_policy.py` remains 100% branch coverage and 100% `interrogate` docstring coverage. Refs #2193, #2149, #2116. diff --git a/CHANGELOG.d/20260917-codeql-versioned-handler-bootstrap.md b/CHANGELOG.d/20260917-codeql-versioned-handler-bootstrap.md new file mode 100644 index 0000000000..55741503cc --- /dev/null +++ b/CHANGELOG.d/20260917-codeql-versioned-handler-bootstrap.md @@ -0,0 +1,14 @@ +## Changed + +- Add a backward-compatible `codeql-scan`/`codeql-scan-v2` protocol bridge to + the single protected CodeQL dispatch handler. Legacy clients keep their + exact title, payload, and status context while v2 requires source/base/head + provenance. Language scans are `actions:read`; one post-matrix settlement + revalidates the live PR, required run/jobs, handler gate steps, and SARIF + artifacts before one run-wide rerun. The legacy path has an explicit + protected-v2/in-flight-drain/zero-caller removal condition. Failed + credential attempts retain their diagnostics but cannot leak an HTTP error + body into a later successful API response. Nested rerun authority is bound + to string schema `"1"`, and settlement stops before mutation when the + required run reaches attempt 48, preserving capacity below GitHub's limit of + 50 re-runs. ADR-0025. diff --git a/CHANGELOG.d/20260917-coverage-vcs-python-root-helper.md b/CHANGELOG.d/20260917-coverage-vcs-python-root-helper.md new file mode 100644 index 0000000000..6f85d0d7dc --- /dev/null +++ b/CHANGELOG.d/20260917-coverage-vcs-python-root-helper.md @@ -0,0 +1,15 @@ +### Coverage image VCS import-root resolver is executable and contract-proven + +- #2123 already admitted immutable `python/` layouts so the trusted coverage + tool image no longer dies at docker step #17 on `fast-mlsirm@09f762ded` + (`python/fast_mlsirm`). The #2157 follow-up extracts that exact admission logic into + `scripts/ci/resolve_opencode_base_vcs_import_root.sh`, which + `opencode-review-dispatch.yml` installs into the coverage build context and the + Dockerfile `COPY`s/executes — so candidate discovery cannot silently drift inside an + untested HEREDOC. `tests/test_opencode_vcs_python_source_root_contract.py` proves + `python/` package and single-module layouts resolve, `src/` and repository-root + layouts still resolve, and missing/ambiguous/namespace/compiled trees still fail + closed with the historical diagnostics. Doctoring + `docs/doctoring/opencode-vcs-python-source-root.md` and gap + `CONTROL-OPENCODE-VCS-PYROOT-01` record #2123 supersession; issue #2157 stays open + until a consumer `coverage-evidence` job past step #17 is linked. Refs #2157, #2123. diff --git a/CHANGELOG.d/20260917-maturin-offline-coverage-build.md b/CHANGELOG.d/20260917-maturin-offline-coverage-build.md new file mode 100644 index 0000000000..3d636602f7 --- /dev/null +++ b/CHANGELOG.d/20260917-maturin-offline-coverage-build.md @@ -0,0 +1,19 @@ +### Coverage sandbox builds PyO3/maturin extensions offline before pytest + +- `maturin==1.15.0` (MIT/Apache-2.0) is added to `requirements-opencode-review-ci.txt` / + `requirements-opencode-review-ci-hashes.txt` (hashes verified against PyPI JSON metadata for the + exact release), closing the last gap `materialize_base_rust_dependencies.py` (#2222, #2223) left + open: the base commit's Cargo dependency graph was vendored for `cargo llvm-cov`, but nothing + ever built the PyO3 extension itself, so `python3 -m coverage run -m pytest` kept failing + collection with `ImportError: cannot import name '_core'` on 8 of the last 10 fast-mlsirm + fallbacks (fast-mlsirm#1907). `.github/workflows/opencode-review-dispatch.yml`'s + `run_python_test_coverage` now calls a new `build_maturin_extension_if_needed` helper for every + tracked Python project whose `pyproject.toml` declares `build-backend = "maturin"`: it runs + `maturin build --offline --release` against the vendored Cargo dependencies with + `CARGO_NET_OFFLINE=true CARGO_BUILD_JOBS=1` (the sandbox is memory-constrained), then + `pip install --user --no-index --no-deps` installs the built wheel before pytest runs, entirely + inside the existing `--network=none` sandbox. `tests/test_maturin_offline_build_contract.py` + proves both halves of the claim against a real PyO3 fixture crate: the vendored-offline build + produces an importable `_core` extension, and a dependency only a pull request's head added + (never seen by the base-commit materializer) is never fetched -- the offline build fails closed + on the missing crate instead of reaching the network. Refs fast-mlsirm#1907. diff --git a/CHANGELOG.d/20260923-release-dependency-license-strix-gate.md b/CHANGELOG.d/20260923-release-dependency-license-strix-gate.md new file mode 100644 index 0000000000..784333b9c3 --- /dev/null +++ b/CHANGELOG.d/20260923-release-dependency-license-strix-gate.md @@ -0,0 +1,186 @@ +### Central pre-publish dependency gate: parsed license denial, resolved-graph reconciliation, per-dependency Strix bindings + +- `origin/main` had **no** fail-closed pre-publish dependency gate. The only license signal was + `scripts/ci/sbom_inventory_aggregator.py`, a *scheduled, informational* org SBOM roll-up that + flags GPL/AGPL/NOASSERTION for governance: it is not per-dependency, not fail-closed, and not + bound to a release head. That gap blocked fast-mlsirm's 0.11.5 PyPI release and + contextual-orchestrator's VCS-pin removal (#2342). +- New reusable `workflow_call` workflow `.github/workflows/release-dependency-license-strix-gate.yml` + runs **before** a release workflow publishes. It has no `continue-on-error`, no `if: always()`, + no neutral outcome, and no bypass; `permissions` is `contents: read` at both workflow and job + scope, and every action is pinned to the same commits `exact-artifact-sbom-attestation.yml` uses. + The decision code is materialized from `ContextualWisdomLab/.github` at `github.workflow_sha` + into `trusted-gate/`, so a caller's tree can never supply it. +- New `scripts/ci/spdx_license_policy.py` is a recursive-descent SPDX 2.3 expression parser + (`AND`/`OR`/`WITH`/parentheses/legacy `+`). Policy is applied to the parsed tree, never by + substring matching: GPL, LGPL, and AGPL are denied in every version and in both the `-only` and + `-or-later` spellings, an exception never rescues a denied base (`GPL-2.0-only WITH + Classpath-exception-2.0` stays denied), and `missing`, `NOASSERTION`, `NONE`, `UNKNOWN`, + `custom`, `LicenseRef-*`, and any unparseable expression fail closed. A dual-licensed dependency + passes only when a non-denied operand is explicitly selected with a written rationale, which is + copied into the artifact provenance as a CycloneDX component property. Bundled `LICENSE`, + `COPYING`, and `NOTICE` text *is* substring-scanned — correct for prose — so metadata claiming + MIT while shipping GPL text fails as a disagreement. +- New `scripts/ci/release_dependency_gate.py` enumerates both ecosystems and refuses any + asymmetry: the hash-pinned Python lock against `pip inspect` of the build environment + (`LOCK_ENV_MISMATCH`), and `Cargo.lock` against the full resolved build graph including + build-dependencies and every `cfg()`-gated target (`CARGO_LOCK_GRAPH_MISMATCH`, + `CARGO_CHECKSUM_MISSING`). It records name, version, source hash, license, license source, and + distribution inclusion per dependency; verifies the captured source hash against the pin + (`SOURCE_HASH_MISMATCH`); evaluates static and dynamic linking targets of shipped native + libraries against an explicit, auditable platform-runtime soname allowlist (glibc, the GCC + runtime-library-exception libraries, `libpython`) so a real compiled wheel can pass at all; and + runs deterministic archive-escape and install-hook detectors (`ARCHIVE_PATH_ESCAPE`, + `INSTALL_HOOK`). +- Strix evidence is accepted **only** as a machine-readable binding, one isolated synthetic + fixture per dependency, simulating file parsing, install hooks, archive traversal, native library + loading, credential/network attempts, and known-vulnerability surface. A textual "0 findings" or + "No exploitable vulnerabilities detected" is rejected (`STRIX_TEXTUAL_PASS_REJECTED`), and a + missing or malformed binding is a failure, never neutral (`STRIX_BINDING_MISSING`, + `STRIX_BINDING_MALFORMED`, `STRIX_BINDING_UNBOUND`). The trusted binder is resolved next to the + gate script's **own** directory, adopting `strix_quick_gate.sh`'s trusted-path semantics in new + code without touching that file (PR #2291 owns its one-line repair). +- On success the gate seals exactly the six members + `scripts/ci/verify_exact_artifact_sbom_handoff.py` expects — wheel, sdist, their CycloneDX 1.7 + SBOMs, `source-identity.json`, `checksums.sha256` — and emits all 17 inputs of + `exact-artifact-sbom-attestation.yml` as workflow outputs, so provenance covers exactly the bytes + that were gated. `tests/test_release_dependency_gate_capture_and_seal.py` proves the sealed + directory is accepted verbatim by that verifier. +- Strix itself is invoked through the organization's existing trusted entry point + `scripts/ci/strix_quick_gate.sh`, once per isolated fixture workspace via `STRIX_REPO_ROOT`, + with `strix.yml`'s bootstrap invariants mirrored verbatim (private install umask, + `--require-hashes --no-deps` against the unmodified `requirements-strix-ci-hashes.txt`, absolute + non-symlinked executable inside the interpreter's scripts root, `chmod go-w`, digest pinned into + `GITHUB_ENV`, sidecar-provided `LLM_API_KEY_FILE`/`LLM_API_BASE_FILE`/`STRIX_LLM_FILE`, and + `orchestrator/free` as the only accepted model). The trusted binder is copied into each fixture + workspace so the gate's binder lookup resolves both on current `main` and after #2291, without + editing that file. `strix_runs/**/vulnerabilities.json` is normalized to an array only when it + already is one (or carries a `vulnerabilities` array); any other shape writes no binding, so the + gate refuses with `STRIX_BINDING_MISSING` rather than inventing a result. +- `scripts/ci/release_dependency_capture_raw.sh` runs the runner-only tools (`pip inspect`, + `pip download`, `cargo metadata --locked`, `cargo fetch`, archive listing, `readelf -d`) and + writes their output verbatim; every decision lives in the unit-tested Python that reads it. It + inspects a `python3 -m venv --without-pip` environment holding exactly the lock, so the + no-exemption lock/environment rule is not defeated by setup-python's preinstalled `pip`, and it + fetches by exact pin with hash checking deliberately disabled so `SOURCE_HASH_MISMATCH` is + observable rather than pre-empted by pip. The gate adds no Python dependency and does not touch + any `anyio` pin or `requirements-strix-ci*` (#2278 owns that lane). Refs #2342. +- The gate now runs in **two stages**, so the licence determination precedes every credential and + model step. `release_dependency_gate.py prescreen` (`stage: license`) enumerates the full + dependency scope and applies the *same* `evaluate_dependency_license` decision the final gate + uses, reading no Strix binding and requiring no provider credential: a GPL/LGPL/AGPL dependency, + an `UNKNOWN`/missing licence, or an `OR` expression with no recorded permissive selection refuses + the release before a secret is read. `capture` alone never rejected a licence — it only assembles + evidence and fixtures — so making the secrets optional would not by itself have produced a + pre-Strix rejection. The five provider secrets are therefore declared `required: false`, which is + not leniency: `require-strix-credentials` refuses the Strix stage with `STRIX_CREDENTIALS_ABSENT` + when any is absent, as a failing command rather than an `if:` condition, because a condition would + *skip* the scan and let the release proceed unscanned. The reason code names only the absent + variables and never echoes or measures a present value. Only a `full`-stage report may be sealed, + so a passing prescreen can never stand in for the Strix stage. +- Dependency **scope is compared as a whole set**, per ecosystem, with `expected_count`, + `enumerated_count`, `collected_count`, and `matched_count` recorded in the report and equality + required. CO#1226 accepted coverage because one component of one ecosystem existed; an ecosystem + this gate cannot enumerate is now `SCOPE_UNVERIFIABLE` rather than silently skipped, a collected + set that is a subset of the producer's declared set is `SCOPE_SET_MISMATCH`, and so is capture + material for something no declared ecosystem expects. Scope is direct, transitive, build, dev, + optional and platform: `resolve_cargo_graph` walks every `resolve.nodes` edge regardless of + `dep_kind` or target `cfg`, so a UEFI-only crate such as `r-efi` is an expected member and gets no + target-based exemption. +- Licence metadata is read from **each fetched distribution's own** `METADATA`/`PKG-INFO`, by the + trusted gate's `distribution-metadata`, which also re-checks that the archive declares the pinned + project and version. It cannot come from `pip inspect` of the lock-only environment any more, + because no such environment exists yet when the licence is judged; the enumeration is built from + the same fetched set, in the `pip inspect` shape the lock/environment reconciliation already reads, + so identity and licence stay consistent by construction and an entry that is not present exactly + once is an error rather than a default. The previous metadata step ran `python3 -m pip show` + without the `--python` target its neighbours carried, so it inspected the *runner's* global + interpreter where the release dependencies are not installed at all. +- The exact release identity is shape-checked **first**. `workflow_call` can only type + `source_sha` as `string`, and the gate's own 40-hex check was reached only after Strix had run, so + `validate-inputs` now refuses a branch name or a short SHA before the release head is fetched. +- Failure evidence survives the failure that produced it: each report upload is bound to the step + that writes it, running whether that step passed or failed but not when it never ran and not on + cancellation. This is deliberately narrower than a blanket `always()`, and with + `if-no-files-found: error` a report that should have been written but was not stays a failure + instead of being masked. Neither upload can rescue the run. Refs #2342. +- **Install and capture now resolve from the same validated sources.** `pip install -r ` reads + the real lock and honors `--index-url`, `--extra-index-url` and `--find-links` in it, while the + capture step's `pip download` used a reconstructed plain requirements file built with + `grep -oE '^[A-Za-z0-9._-]+==[^ ;]+'`, which dropped every `-`-prefixed directive. Collection could + therefore resolve from a different source than install, and any release lock using a private or + extra index failed capture outright. The fix never forwards what the lock says: `lock-source-options` + parses each directive, validates it, and only then emits an explicit option list, reusing the + trusted-origin and bounded-path policy `materialize_base_python_requirements.py` already applies + (HTTPS, default port, host allowlist, no userinfo; normalized relative path with no `.`/`..` and + none of `\\ : ? #`). An unlisted origin is `LOCK_SOURCE_ORIGIN_DENIED`, a URL carrying userinfo is + `LOCK_SOURCE_CREDENTIAL_IN_URL` and withholds the whole URL from both the message and the report, a + path leaving the release tree is `LOCK_SOURCE_PATH_ESCAPE`, and a nested `-r`/`-c` include, an + environment marker, or any other directive form is `LOCK_SOURCE_UNSUPPORTED`. Nothing is dropped + silently, because silent dropping was the defect. The supported dialect is deliberately narrow and + this organization's own `requirements-*-hashes.txt` files use none of these forms. The options are + read into a bash array with the validator's exit status checked explicitly — *not* through + `mapfile < <(…)`, where `set -e` discards a refusal and it would read as "no options" and resolve + from the default index anyway. Source resolution decides only where pip looks: the hash pin still + decides what is acceptable, so `SOURCE_HASH_MISMATCH` remains observable and an offline + `--find-links` root cannot substitute different bytes. Refs #2342. +- **Nothing is installed before it has been adjudicated.** The gate's premise is that a denied, + unknown or untrusted dependency is refused before any of it runs, but the workflow installed the + whole release closure in a step that preceded *both* the lock-source validation and the licence + prescreen. A GPL/LGPL/AGPL or `UNKNOWN` dependency therefore reached the environment first, and a + lock pointing at an untrusted index had its directives honoured by that install while only the + later capture validated them — so the first network action of the run was the unvalidated one. The + order is now: validate the lock's sources (no network), collect the closure with + `pip download --no-deps --only-binary=:all:` (wheels only, because `pip download` executes an + sdist's build backend for metadata even with `--no-deps`), judge the licence, and only then + install. The install is `--require-hashes --only-binary=:all: --no-index --find-links ` + over the very bytes that were inspected, so nothing is re-resolved or re-downloaded and the + installed bytes are the judged bytes even where the lock records several hashes for one project — + which a second hash-less download could not have established. `install-authorized` refuses the + install unless a prescreen report records a passed `license` stage, so a missing, malformed or + failing report fails closed instead of defaulting to permitted. + One consequence is stated plainly rather than papered over: `LOCK_ENV_MISMATCH` is now evaluated + against the *collected* closure, because no installed environment exists when the gate reads its + capture. Agreement between that closure and the environment is enforced at install time instead, + by pip itself: `--require-hashes` with `--no-index --find-links ` can only install a + file from the collected root that matches a hash the lock records, so a disagreement fails the + install rather than being reported by a later inspect. + `tests/test_release_dependency_install_ordering.py` pins the wiring rather than the parser: with + `RELEASE_GATE_PIP` pointed at a recorder, a refused lock directive performs **no** pip call at all, + an unauthorized licence stage performs **no** `install`, an authorized release performs exactly one + offline hash-checked `install` from the collected root, and the workflow's step order is asserted + because the defect lived there. Refs #2342. +- **Three release-blocking defects found by independent review of `03ba1777`, each with its own + regression.** (1) *A permissive declaration was accepted as licence evidence.* The decision + allowed the declared SPDX expression and then only looked for a **denied** title in the bundled + text, so `scan_license_text` returning `None` was read as "the text is fine" — it only means no + GPL/LGPL/AGPL title was found. Reproduced: MIT metadata with `license_texts = {}`, with + `LICENSE = UNKNOWN`, and with `LICENSE = Commercial redistribution is prohibited.` each passed + the licence stage with an empty failure list. `recognize_license_text` is the positive half — + it returns the SPDX identifiers a body actually supports — so absent text is now + `LICENSE_TEXT_MISSING`, an unrecognizable body is `LICENSE_TEXT_UNVERIFIED`, and a recognized + body naming none of the declared identifiers is `LICENSE_TEXT_DISAGREEMENT`. Two of this + repository's own fixtures were declaring one licence while bundling another and are corrected. + (2) *The approval was not bound to what was installed.* `install_is_authorized` checked only + `stage` and `result`, and the install re-read the original lock, so a two-field report authorized + it and a lock recording several hashes for one project let `--require-hashes` accept an artifact + whose licence and contents were never judged. The verdict now records `python_lock_sha256`, and + `bind-install` refuses unless that lock still digests to what the verdict read, every judged + artifact is present in the collected root **by digest**, and the root holds no other + distribution; it then writes a requirements file pinning each project to the one judged digest, + which is what the install reads. A swapped artifact, an extra unjudged wheel, an edited lock and + a failing report each install nothing. (3) *The install could never run.* `python3 -m venv` + symlinks `bin/python` on POSIX, and the interpreter guard refused symlinks outright, so a normal + virtual environment exited 2 before pip was reached. The guard now resolves the link and requires + the resolved target to be a regular executable file, which a real venv satisfies while a dangling + link and a directory still fail. Refs #2342. +- **The gate's own toolchain is out of the prescreen's scope, and that limit is now written down + instead of being implicit.** The same review noted that the pinned Strix toolchain + (`requirements-strix-ci-hashes.txt`, materialized from `github.workflow_sha`) and the orchestrator + sidecar's own lock are installed without passing through the licence stage. They are a different + trust domain from the caller's release closure — pinned and reviewed in this repository — and the + stage that judges the closure cannot judge the scanner it must run first without a cycle. The + workflow says so at the install step: not an automatic exception for CI/build/dev dependencies, + but a stated limit whose removal is an owner decision tracked separately. Nothing in this gate's + output may be read as evidence that the gate's own dependencies were licence-judged. Refs #2342. + diff --git a/CHANGELOG.d/20260926-noema-draft-before-sidecar.md b/CHANGELOG.d/20260926-noema-draft-before-sidecar.md new file mode 100644 index 0000000000..3baf00837f --- /dev/null +++ b/CHANGELOG.d/20260926-noema-draft-before-sidecar.md @@ -0,0 +1,18 @@ +### Noema checks live draft state before provisioning the orchestrator sidecar + +- `noema-review.yml`'s `noema-review` job provisioned the contextual-orchestrator review + sidecar (10-13 minutes) before `two_phase.py --prepare-verdict-file` read the live PR and + printed `PR is draft; Noema verdict preparation skipped.`, so every draft run held a runner + for ~13 minutes and produced nothing (newsdom-api job 108077744310 on 2026-09-25, `.github` + job 106665379126 on 2026-09-22) while the organization's Actions concurrency is saturated. + A new `live_draft` step, placed after `Validate current pull request head` / `Resolve Noema + target repository visibility`, reads the live PR with the same reviewer token and REST lookup + and gates sidecar provisioning, the HWP document reader, and `Prepare Noema model verdict` on + `steps.live_draft.outputs.live_draft != 'true'`. The decision stays runtime-only (no trigger + filter, no `github.event.pull_request.draft`), fails open to today's full path on a lookup + error, leaves `noema_prepare` outputs unset so publication stays skipped exactly as before, + and the job still concludes success for drafts. Ruleset-launched runs in other repositories + keep identical outcomes: a draft never produced a Noema verdict at runtime; only the check + moved earlier. `tests/test_noema_draft_admission_before_sidecar.py` pins ordering, gating, + and the fail-open step behavior; `docs/doctoring/noema-draft-before-sidecar.md` records the + rationale. diff --git a/CHANGELOG.d/20260927-codeql-obsolete-pr-verdict.md b/CHANGELOG.d/20260927-codeql-obsolete-pr-verdict.md new file mode 100644 index 0000000000..0846ecd8a2 --- /dev/null +++ b/CHANGELOG.d/20260927-codeql-obsolete-pr-verdict.md @@ -0,0 +1,8 @@ +### Correct CodeQL compatibility results after a PR closes or changes + +Closed PRs and superseded changes no longer produce a missing-verdict failure +when a queued compatibility check starts later. Current changes still require +a verified scan result; absent or failed evidence continues to block them. + +The scanner’s AnyIO dependency is pinned to the patched 4.14.2 release, with +verified release hashes and a source/lock parity guard. diff --git a/CHANGELOG.d/20260927-codeql-terminal-proof.md b/CHANGELOG.d/20260927-codeql-terminal-proof.md new file mode 100644 index 0000000000..ea4154fcec --- /dev/null +++ b/CHANGELOG.d/20260927-codeql-terminal-proof.md @@ -0,0 +1,9 @@ +## Fixed + +- Require a successful GHAS base/head configuration-identity proof and preserved + SARIF before a clean central CodeQL gate may settle or satisfy an exact required + run. A failed post-gate identity check can no longer be promoted to GREEN by a + wake-only fallback. +- Bind CodeQL terminal receipts to the live base, required run, head, and merge + source through the v2 dispatch protocol, preventing a trusted but stale commit + status from satisfying a retargeted or later required run. diff --git a/CHANGELOG.d/20260929-coverage-runner-hygiene.md b/CHANGELOG.d/20260929-coverage-runner-hygiene.md new file mode 100644 index 0000000000..1c37aeee44 --- /dev/null +++ b/CHANGELOG.d/20260929-coverage-runner-hygiene.md @@ -0,0 +1,8 @@ +## Fixed + +- Rust coverage on the self-hosted OpenCode runner can find `cargo` again. The + runner keeps it in `~/.cargo/bin`, which its service does not put on PATH, + so every Rust coverage gate failed and no fast-mlsirm review could approve. + A missing toolchain is now reported as such instead of a bare exception name. +- Coverage jobs remove coverage images older than two hours before measuring. + Runs dispatched before per-run cleanup existed had filled the runner disk. diff --git a/CHANGELOG.d/20260929-fix-scheduler-control-runner.md b/CHANGELOG.d/20260929-fix-scheduler-control-runner.md new file mode 100644 index 0000000000..75941c91d1 --- /dev/null +++ b/CHANGELOG.d/20260929-fix-scheduler-control-runner.md @@ -0,0 +1,7 @@ +## Fixed + +- Run the central review-fix scheduler's dispatch job on the idle control + runners instead of the saturated hosted queue, where hourly repair runs had + waited up to 12 hours. Only the central main caller is routed there; the job + reads the GitHub API and dispatches autofix and never checks out pull-request + content. diff --git a/CHANGELOG.d/20260929-opencode-queue-priority-runbook.md b/CHANGELOG.d/20260929-opencode-queue-priority-runbook.md new file mode 100644 index 0000000000..0be341d167 --- /dev/null +++ b/CHANGELOG.d/20260929-opencode-queue-priority-runbook.md @@ -0,0 +1,7 @@ +## Added + +- Operator runbook `scripts/ci/opencode_queue_priority.py` for the OpenCode + review queue. It keeps PRs whose `review-priority` label was applied by a + maintainer (a fork author cannot use it to jump the queue), reports backlog + metrics, posts the cancel list to an issue before cancelling, and cancels + only runs that are still queued. diff --git a/CHANGELOG.d/20260929-sidecar-venv-clear.md b/CHANGELOG.d/20260929-sidecar-venv-clear.md new file mode 100644 index 0000000000..4a4173e9f6 --- /dev/null +++ b/CHANGELOG.d/20260929-sidecar-venv-clear.md @@ -0,0 +1,7 @@ +## Fixed + +- Rebuild the review sidecar's virtual environment on every run. On the + OpenCode runner a damaged environment survived between jobs, so every later + OpenCode review failed while installing its dependencies + (`No module named pip.__main__`) and no verdict was published after + 2026-09-27. diff --git a/CHANGELOG.d/20260930-coverage-base-pinned-rust.md b/CHANGELOG.d/20260930-coverage-base-pinned-rust.md new file mode 100644 index 0000000000..66dd5d7596 --- /dev/null +++ b/CHANGELOG.d/20260930-coverage-base-pinned-rust.md @@ -0,0 +1,14 @@ +### Coverage sandbox installs the base-pinned Rust release + +- The coverage image shipped Debian's rustc 1.85, but fast-mlsirm pins 1.97.1 and its + dependencies need at least 1.90. `maturin build --offline` failed, `_core` never imported, and + every fast-mlsirm coverage run failed its configured pytest suite at collection, so no + fast-mlsirm pull request could reach an APPROVED review. When the base commit pins an exact `1.x.y` release in + `rust-toolchain(.toml)`, the image now installs it from a SHA-256-verified `rustup-init` + (minimal profile plus `llvm-tools-preview`) and binds Rust coverage to that release's LLVM tools. + Repositories without a pin keep the Debian toolchain, and a failed toolchain layer rebuilds the + previous image. See `docs/doctoring/opencode-rust-coverage-runtime-boundary.md`. +- The offline maturin build now also copies the wheel's compiled extension into the project's + `tool.maturin.python-source` package (`scripts/ci/place_maturin_extension.py`), as + `maturin develop` would. fast-mlsirm's pytest `pythonpath = ["python"]` imports the source tree + first, so a `_core` present only in site-packages stayed shadowed even after a successful build. diff --git a/CHANGELOG.d/20260930-coverage-workspace-reclaim.md b/CHANGELOG.d/20260930-coverage-workspace-reclaim.md new file mode 100644 index 0000000000..4e99d05c99 --- /dev/null +++ b/CHANGELOG.d/20260930-coverage-workspace-reclaim.md @@ -0,0 +1,6 @@ +## Fixed + +- Coverage jobs also remove run-numbered coverage workspaces older than two + hours. The sandbox writes them as root, so the runner's own temp cleanup + could not, and they refilled the self-hosted runner's disk. Shared coverage + directories are never removed. diff --git a/CHANGELOG.d/20260930-draft-pr-runner-guard.md b/CHANGELOG.d/20260930-draft-pr-runner-guard.md new file mode 100644 index 0000000000..c2dfeff294 --- /dev/null +++ b/CHANGELOG.d/20260930-draft-pr-runner-guard.md @@ -0,0 +1,7 @@ +## Changed + +- Draft pull requests no longer take a control-runner slot for the OpenCode, + Strix, CodeQL PR and merge-scheduler entry jobs. They previously ran only to + conclude that a draft needs no verdict; marking the pull request ready runs + them again on the same head. Noema is unchanged because it reads the live + draft state. diff --git a/CHANGELOG.d/20260930-github-api-http-error-close.md b/CHANGELOG.d/20260930-github-api-http-error-close.md new file mode 100644 index 0000000000..df2feb4a78 --- /dev/null +++ b/CHANGELOG.d/20260930-github-api-http-error-close.md @@ -0,0 +1,7 @@ +## Fixed + +- Close file-like `HTTPError` responses in the central CodeQL, Strix, Noema, + Pingora, review-preflight, Pages, and sandbox-readiness clients after bounded + status/telemetry extraction, preventing Python 3.14 resource leaks without + permitting redirects, suppressing warnings, or weakening bearer-token + authority checks; cap CodeQL diagnostic error-body reads at 400 bytes. diff --git a/CHANGELOG.d/20260930-opencode-worktree-prune.md b/CHANGELOG.d/20260930-opencode-worktree-prune.md new file mode 100644 index 0000000000..5344ef4781 --- /dev/null +++ b/CHANGELOG.d/20260930-opencode-worktree-prune.md @@ -0,0 +1,6 @@ +## Fixed + +- OpenCode reviews no longer fail before running after a self-hosted runner's + temp directory is cleaned. The trusted checkout outlives jobs and still + registered the removed pull-request-head worktree; it is now pruned before + the worktree is recreated. diff --git a/CHANGELOG.d/20260930-rust-coverage-timeout-not-measured.md b/CHANGELOG.d/20260930-rust-coverage-timeout-not-measured.md new file mode 100644 index 0000000000..7a717fba79 --- /dev/null +++ b/CHANGELOG.d/20260930-rust-coverage-timeout-not-measured.md @@ -0,0 +1,8 @@ +### Rust coverage that only exceeds the per-command cap is disclosed as not measured + +- fast-mlsirm's `mlsirm-core` suite runs for tens of minutes under `cargo llvm-cov` (56 min for + the workspace on a 4-job M-series host), past the sandbox's 900 s per-command cap, so every + Rust-changing PR failed coverage evidence on the timeout alone. For the Rust coverage commands + only, exit 124 (`timeout`) is now reported as `NOT MEASURED` and listed in the Coverage Decision, + instead of counting as a failure. Test failures, below-threshold coverage, kills (137) and every + other command still fail. diff --git a/CHANGELOG.d/20260930-semgrep-maturin-asset-urlopen.md b/CHANGELOG.d/20260930-semgrep-maturin-asset-urlopen.md new file mode 100644 index 0000000000..9a950691b9 --- /dev/null +++ b/CHANGELOG.d/20260930-semgrep-maturin-asset-urlopen.md @@ -0,0 +1,9 @@ +### Semgrep no longer fails every .github PR on the maturin asset verifier + +- `scripts/ci/verify_release_maturin_tool_assets.py` fetches from a fixed + `https://github.com/PyO3/maturin/releases/download/v1.15.0/` origin, and `verify_assets` admits only + five literal asset names. The downloader now uses a standard-library opener + that admits one credential-free HTTPS redirect only from the exact GitHub + release path to `release-assets.githubusercontent.com`, bounds the response, + and closes it on every path. It uses neither `urlopen` nor + `HTTPSConnection`, and carries no `nosemgrep` or `nosec` suppression. diff --git a/CHANGELOG.d/20260930-sidecar-disk-free-guard.md b/CHANGELOG.d/20260930-sidecar-disk-free-guard.md new file mode 100644 index 0000000000..3b4b8554d9 --- /dev/null +++ b/CHANGELOG.d/20260930-sidecar-disk-free-guard.md @@ -0,0 +1,5 @@ +### Sidecar provisioning names a full runner disk + +- `scripts/ci/contextual_orchestrator_review_sidecar.sh` now checks free space under `RUNNER_TEMP` + before cloning or building the sidecar venv and warns below 2 GiB and stops with a "runner disk" error below 512 MiB. On + 2026-09-29 a full cwlab-s1-04 disk surfaced as venv/pip failures that read like a provider outage. diff --git a/CHANGELOG.d/20260930-strix-report-scope-directories.md b/CHANGELOG.d/20260930-strix-report-scope-directories.md new file mode 100644 index 0000000000..708a8127d6 --- /dev/null +++ b/CHANGELOG.d/20260930-strix-report-scope-directories.md @@ -0,0 +1,8 @@ +## Fixed + +- A completed Strix PR scan whose report names the scanned PR-scope + directory that contains a changed file (for example + `/workspace/strix-pr-scope./crates/core`) now counts as scoped. Such + reports were rejected with "scan report does not identify a changed source + file" even when they described the changed code. The scope root itself and + unrelated directories still do not count. diff --git a/CHANGELOG.d/20260930-strix-unverified-dependency.md b/CHANGELOG.d/20260930-strix-unverified-dependency.md new file mode 100644 index 0000000000..44112822d4 --- /dev/null +++ b/CHANGELOG.d/20260930-strix-unverified-dependency.md @@ -0,0 +1,9 @@ +## Changed + +- The Strix gate no longer fails closed on a finding that names only packages + the repository does not depend on. When such a finding has no file location, + every package in its Target, Package and Introduced By fields is absent from + every dependency manifest and lockfile, and the pull request changes no + manifest, the gate records it as unverified with a warning annotation. A + model reported a lodash CVE (itself misattributed) on a Rust/Python + repository with no JavaScript dependencies. diff --git a/CHANGELOG.d/20261001-gap-baseline-source-restoration.md b/CHANGELOG.d/20261001-gap-baseline-source-restoration.md new file mode 100644 index 0000000000..a2d7d53a8f --- /dev/null +++ b/CHANGELOG.d/20261001-gap-baseline-source-restoration.md @@ -0,0 +1,5 @@ +## Fixed + +- Restore the complete product and technical gap baseline after a connector + display truncation replaced the source artifact, and add a regression guard + that rejects future truncation banners. diff --git a/CHANGELOG.d/20261001-noema-document-reader-dependencies.md b/CHANGELOG.d/20261001-noema-document-reader-dependencies.md new file mode 100644 index 0000000000..486643700c --- /dev/null +++ b/CHANGELOG.d/20261001-noema-document-reader-dependencies.md @@ -0,0 +1,5 @@ +### Noema document reader closes new transitive advisories + +- The generated npm lock now selects `fast-uri` 3.1.8 and `ip-address` 10.7.1, + removing the three medium-severity findings published against the previous + transitive versions while leaving the direct runtime manifest unchanged. diff --git a/CHANGELOG.d/20261001-noema-document-reader-transitive-cves.md b/CHANGELOG.d/20261001-noema-document-reader-transitive-cves.md new file mode 100644 index 0000000000..4a19c4bb5e --- /dev/null +++ b/CHANGELOG.d/20261001-noema-document-reader-transitive-cves.md @@ -0,0 +1,8 @@ +### Noema document reader transitive security updates + +- Updated the locked `fast-uri` dependency from 3.1.7 to 3.1.8 and + `ip-address` from 10.7.0 to 10.7.1. These are the first releases outside + the affected ranges for GHSA-hrr3-gc8f-f4qj, GHSA-j6r3-76f7-8jcv, and + GHSA-h3mg-xc3c-68pw. The direct dependency ranges are unchanged. +- Added a deterministic regression contract that rejects reintroduction of + vulnerable hoisted or nested copies of either transitive package. diff --git a/CHANGELOG.d/20261001-strix-litellm-credential-exfiltration.md b/CHANGELOG.d/20261001-strix-litellm-credential-exfiltration.md new file mode 100644 index 0000000000..e4d748764c --- /dev/null +++ b/CHANGELOG.d/20261001-strix-litellm-credential-exfiltration.md @@ -0,0 +1,6 @@ +### Shared Strix LiteLLM security floor + +- Pinned LiteLLM 1.94.3 in the Strix source input and regenerated the complete + hash lock after exact-head `pip-audit` found CVE-2026-84377 in 1.94.1. +- Added a source/lock parity regression contract and preserved the stacked Noema + document-reader transitive security repair without copying its implementation. diff --git a/CHANGELOG.d/20261001-trusted-review-archive-retry.md b/CHANGELOG.d/20261001-trusted-review-archive-retry.md new file mode 100644 index 0000000000..783b243562 --- /dev/null +++ b/CHANGELOG.d/20261001-trusted-review-archive-retry.md @@ -0,0 +1,6 @@ +### Central review source survives transient GitHub archive failures + +- Required Noema, Required OpenCode, and the PR review merge scheduler now use + bounded native `curl` retries when materializing the immutable trusted + `.github` source archive. Authentication, exact-SHA binding, extraction, and + fail-closed behavior are unchanged. diff --git a/CHANGELOG.md b/CHANGELOG.md index 06b3dba425..39f8819cc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,315 @@ +### Strix report identity rejects colon-suffixed file lookalikes + +- The completed-report scope validator no longer treats every colon as proof + that a changed path token ended. A distinct Linux filename such as + `scripts/ci/strix_quick_gate.sh:backup` now fails closed, including beneath + the private Strix scope root, while numeric `path:line` and + `path:line:column` evidence remains valid. A sentence period also ends a + path token only before a real delimiter, so `strix_quick_gate.sh.:backup` + and `strix_quick_gate.sh.@backup` are rejected in both report forms. + Focused evidence is 69 tests across the three scope suites with 100% + statement and branch coverage for `strix_report_scope.py`; hosted + exact-head Checks and qualifying independent review remain required. + +### Law CI cleanup rejects symlinked PostgreSQL descendants + +- The central law-agent cleanup now refuses symbolic links at every component + from the owned scratch root through the PostgreSQL cluster and + `postmaster.pid`. Cancellation and normal-exit cleanup therefore retain + evidence instead of invoking `pg_ctl` through an untrusted descendant path. + Contracts cover all five descendant boundaries in normal and Actions-mode + execution. Hosted exact-head Checks and independent review remain required. +### Security scanner setup no longer inherits runner-local failures + +- Disable `trivy-action`'s binary cache on the heterogeneous `CWL CI isolated` + runner group. Exact run `37242578328`, job `112314054734`, restored a + `trivy-binary-v0.70.0-Linux-X64` cache entry but then failed before scanning + with `trivy: command not found`; installing the pinned binary per job removes + that runner-local path and executable-bit dependency. +- Treat the always-run OSV debug artifact upload as observability rather than a + security verdict. Exact job `112314054619` completed the base/head comparison, + reported no PR-introduced finding, and uploaded SARIF before GitHub artifact + quota exhaustion failed the debug upload. The scanner and SARIF gates remain + unchanged and fail closed; only that optional retention step is non-blocking. +- RED `cc42969ba8ed4abeb1819412c146af2a9dadae8f` adds both workflow contracts; + GREEN `26f993c0bbe88d50bf81473e31d179ad7b317657` passes 5,488 tests, 10 skips, + and 40 subtests. PR #2565 remains Draft/HOLD for fresh hosted evidence, + isolated-capacity canary, and qualifying independent approval. + +### Isolated CI routing gains a runner-group authority boundary + +- Reject the complete reusable R matrix when any requested leg is not one of + the admitted Linux/x64 image aliases. A required macOS, Windows, ARM, or + unknown leg can no longer disappear while the remaining Ubuntu leg reports + success; callers must request only executable legs or receive a failed + admission job. +- Serialize the caller-controlled R matrix OS label with GitHub `toJSON()` + before inserting it into the group-scoped runner selector. Quotes, + backslashes, newlines, and duplicate `group`/`labels` text remain one + data label and cannot replace the fixed `CWL CI isolated` authority + boundary. The executable workflow contract covers benign and hostile values. + The integrated Maturin verifier now also documents its archive extraction and + command entry point, restoring the repository's 100% public-doc gate. +- Scope every ordinary and non-main self-hosted fallback through the dedicated + `CWL CI isolated` runner group as well as the existing platform/isolation + labels. A durable regression rejects label-only isolated selectors, while + workflow-specific contracts preserve dynamic matrix and trusted-main routing. + The reusable R matrix selector now escapes its literal JSON braces for + GitHub's `format()` expression, so Linux, Windows, macOS and unsupported OS + labels render a valid group-scoped `runs-on` object instead of failing before + runner admission. + The change remains Draft/HOLD until operators provision the group, restrict + its repository access, demonstrate disposable cleanup, and return exact-head + canary and required-Check evidence. + +### Maturin download failures close every transport response + +- Refactor the bounded Maturin asset downloader so successful and rejected + responses share one unconditional close path while `HTTPError` keeps its own + explicit close path. A new regression exercises a non-200 response and an + opener-raised HTTP error. This removes an impossible optional-response branch + without changing hosts, redirects, byte limits, hashes, or fail-closed error + mapping; the focused suite is 17 passed with 100% statement and branch + coverage. The trusted full-suite workflow now also tracks the verifier source + and its focused test, so a future lifecycle change cannot omit the repository + coverage gate that detected this regression. The pull-request trigger admits + stacked canonical-owner bases as well as `main`; the protected-branch push + trigger remains restricted to `main`. + +### Shared Strix lock advances beyond the PyJWT recursion DoS + +- Advance the explicit Strix source pin and generated hash lock from PyJWT + `2.14.0` to `2.15.0`, closing GHSA-42vr-xj54-vc7v / CVE-2026-101918. Exact Security + Scan run `36741151937` found the advisory in dependent PR #2540; the + canonical owner repair stays in #2531. A source/lock contract, deterministic + lock regeneration, and pip-audit evidence keep the dependent branch free of a + leaf workaround and prevent a return to `2.14.0`. Exact-head hosted security + Checks, independent approval, ordinary protected integration, and immutable + consumer-pin advancement remain required before release admission. + +### Shared urllib3 locks close proxy and streaming CVEs + +- Pin urllib3 2.8.0 as an explicit source input in both the pip-audit and + Strix security-tooling closures, regenerate their hash locks without unrelated + version movement, and add a four-file parity contract. This closes + CVE-2026-97687 and CVE-2026-97689 found by exact-head Python Security while + preserving hash checking and the existing Strix cryptography override. + +### Full-suite parser locks and honest branch coverage converge + +- Consolidated the complete valid ancestry of `.github#2521` into `.github#2530` + with ordinary two-parent merges so the parser-lock and repository-coverage + gates no longer wait on one another. Behavior-level tests exercise the final + branches in the OpenCode queue, Strix dependency classifier, release runtime + prescreener, and release dependency gate without exclusions, pragmas, + threshold reductions, or sample shrinking. Direct consumers of the common + generated lock now also track both source requirements and the canonical + compiler. `.github#2532`'s warning-fatal HTTP response-lifecycle repair is + carried in the same successor so the complete suite can regenerate one + exact-head receipt. Protected hosted Checks and qualifying independent review + remain mandatory before ordinary merge; predecessors remain open until + merged-tree equivalence is proven. The combined Python 3.14 warnings-fatal + suite passes 5,291 tests with 5 optional skips and 40 subtests; all 18,729 + production statements and 7,642 branches are covered, and production + Docstring coverage is 100%. The durable review-repair owner also triggers, + executes, compiles, and measures both launcher runtime suites at 100%. + Exact Git blobs, Cargo development locks, runtime receipts, final fanout caps, + and the Python 3.10 TOML fallback are covered as explicit trust boundaries; + one unreachable postcondition was removed only after prior fail-closed + validation made that state mechanically impossible. + +### Full-suite quality environments install their collection parsers + +- The common OpenCode quality input now owns the existing hash-pinned + `defusedxml` document parser and `PyYAML` workflow parser used during complete + repository test collection. Its Python 3.14/Linux lock was regenerated by + the repository compiler without manual hash edits. Local verification + reproduced the lock byte-for-byte, installed the common and Noema locks + together, imported both parsers, and passed 73 focused contracts with two + optional skips. Hosted exact-head Checks and qualifying independent review + remain required before protected merge. + +### Shared security fixtures use patched PyJWT and PyO3 releases + +- The Strix hash lock now takes PyJWT `2.14.0` as an explicit source input, + closing CVE-2026-102274 without hiding the dependency in the cryptography-only + override file. The offline Rust coverage fixture advances from PyO3 `0.22.6` + to `0.29.2`, beyond the `0.29.0` fixes for GHSA-36hh-v3qg-5jq4 and + GHSA-chgr-c6px-7xpp. Source/lock parity tests prevent either generated lock + from silently returning to the vulnerable versions. Protected integration, + immutable consumer-pin advancement, and fresh exact-head hosted security + Checks remain required before release admission. +### Dependency Review requires a completed authenticated comparison + +- The reusable Dependency Review preflight now requires both curl exit zero + and HTTP 200 before publishing `available=true`. A regression reproduces a + partial transfer that prints `200` but exits 18, preserving the fail-closed + gate. The bundled Security Scan already enforced both signals and remains + unchanged. `.github#2565` now preserves the complete `.github#1725` owner + delta through published ordinary two-parent merge + `650bc6e35409d52d24fec890c2ab74063794e9b8` (tree + `9522f0b9c96247b3412e147bc6f89fd2595c0776`) while retaining its isolated + runner-group selectors. The merged-tree focused contract is 53/53 and the + warning-fatal full suite is 5,437 passed, 10 skipped, 40 subtests. Both PRs remain + Proposed until exact-head hosted Checks and qualifying independent review are + complete. The carried security decision is ADR-0034, the concurrent owned + CodeQL decision is ADR-0033, and a repository-wide executable contract now + rejects duplicate numeric ADR identities. + +### Intel macOS native archives are bound to x86_64 bytes + +- The release prescreener now requires every native member in an Intel macOS + continuation wheel to contain x86_64 code. Architecture inspection happens + before package/hash deduplication, so a wheel already reviewed for the + universal2 release leg cannot bypass the Intel-specific check. Universal2 + binaries that contain x86_64 remain valid; aarch64-only binaries fail closed. + Exact-tree evidence is 4,061 passed, 8 skipped, and 40 subtests passed, with + all 17,383 production statements and 7,098 branches covered. + +### Intel macOS runtime archives enter the exact release dependency gate + +- Require three same-run Intel macOS install receipts for the universal2 wheels. + The central verifier checks each artifact ZIP digest, source and distribution + identity, x86_64 interpreter, and dependency archive bytes before licence + prescreen. Distinct x86_64 dependency wheels join the Strix fixture matrix; + the final verdict seals the three artifact IDs and digests. The thirteen + publishable distribution identities remain unchanged. Local focused tests + are 77 passed, the full suite is 4,063 passed and 4 skipped, and the three + changed production modules have 100% statement and branch coverage. Release + admission remains HOLD pending the fast-mlsirm consumer and hosted checks. + +### Exact native-link review is bound before release verdict sealing + +- Release wheel and build-interpreter native links now fail closed unless each + target is a reviewed operating-system, interpreter, self-install-name, or + named external runtime. The immutable report advances to + `cwl.release-native-links/2` and records the review basis beside every needed + library. Concurrent coverage work was preserved by an ordinary two-parent + merge, including its exact Maturin release-asset verifier. That integration + first reproduced a 99% coverage failure with 22 missing statements and 10 + partial branches; behavior contracts now cover bounded downloads, archive + shapes, executable identity, native-link review, CLI dispatch, and prescreen + rejection paths. Current-tree evidence is 4,049 passed, 8 skipped, and 40 + subtests passed; all 17,302 production statements and 7,058 branches are + covered. Ruff E9/F/I, compileall, and diff checks also pass. Hosted exact-head + Checks and independent review remain required before admission. + +### Native release prescreen coverage remains fail-closed + +- Added behavior-level contracts for directory entries, cached analyzer reuse, oversized and unreadable native members, build-snapshot files omitted from package receipts, runtime wheels with unknown dynamic links, and malformed static-link evidence. This repairs the coverage regression introduced when runtime wheels and build-interpreter snapshots began using the pinned native-link analyzer. The exact-tree suite is 4,037 passed, 8 skipped, and 40 subtests passed; all 17,186 production statements and 7,000 branches are covered. Release admission remains Draft/HOLD pending fresh exact-head hosted Checks and qualifying independent review. + +### Canonical Rust materializer integration closes the repository coverage gate + +- Ordinary-merged the complete `ContextualWisdomLab/.github#2360` owner branch into the release-control stack, preserving its foundation ancestry, multi-root `cargo vendor --sync --locked` implementation, target-path confinement, real-Cargo integration cases, and toolchain-independent mock/error/CLI contracts. The focused materializer suite is 26 passed and 3 real-Cargo skips with `materialize_base_rust_dependencies.py` at 155/155 statements and 60/60 branches. The merged exact tree is 4,030 passed, 8 skipped, and 40 subtests passed; all 17,144 production statements and 6,982 branches are covered. Draft remains required until fresh exact-head hosted Checks and qualifying independent review complete. + +### Noema document-reader trust boundaries reach 100% executable coverage + +- Added behavior-level coverage for unsupported and oversized inputs, bounded DOCX ZIP/XML structure, empty documents, visible Word controls, ragged and escaped tables, missing or unstartable local HWP readers, oversized/non-UTF-8/empty adapter output, UTF-8-safe prompt truncation, and both CLI outcomes. Production reader behavior is unchanged. The focused suite is 11 passed and 2 optional real-fixture skips with `noema_review_document.py` at 144/144 statements and 52/52 branches. The warnings-as-errors full suite is 3,988 passed, 28 skipped, and 40 subtests passed; only the independently owned Rust dependency materializer on `ContextualWisdomLab/.github#2360` remains below 100%, so the repository gate remains RED and this PR remains Draft. + +### Queue-health ownership matches the documented boundary and reaches 100% coverage + +- Removed the dead duplicate `collect_snapshot()` and CLI `main()` from `actions_queue_health_core.py`; the executable `actions_queue_health.py` remains the single owner of collection, retry, exact-head reconciliation, and process exit behavior, while the core retains bounded parsing and report primitives. New boundary cases cover both pre-evidence identity retries, malformed active and terminal run IDs, obsolete target cancellations, and remediation-action deduplication. The focused queue-health suite is 80 passed with both queue-health modules at 100% statement and branch coverage. The full exact tree is 3,982 passed, 28 skipped, and 40 subtests passed; uncovered statements fell from 249 to 163 and partial branches from 26 to 19, leaving only the Noema document reader and Rust dependency materializer owners. + +### Release dependency gate trust boundaries reach executable 100% coverage + +- `release_dependency_gate.py` now has behavior-level coverage for bounded archive reads, unsafe or absent declared licence files, symlink/special members, archive-member limits, raw-capture and destination symlinks, Cargo workspace identity, Strix fanout identity/fixture/runtime-report validation, and install-time licence rebinding. The no-caller `parse_member_listing` helper and its isolated test were removed; immutable archive bytes remain the sole member authority. Focused evidence is 442 passed with 1,126/1,126 statements and 472/472 branches; the warnings-as-errors repository suite is 3,976 passed and 28 skipped. Repository-wide coverage rises from 98% to 99%, so the overall 100% release gate remains RED and the PR stays Draft. + +### Pingora declared binary artifacts reject readable runtime directives + +- A file under a base-owned declared research/data prefix no longer gains binary admission merely by adding an invalid UTF-8 byte to readable Nginx runtime content. For suffixes without recognized format magic, the bounded replacement-decoded bytes must also contain no prohibited runtime pattern; `.github#2386` covers `.sh`, `.dat`, and `.txt` names through the production evaluation boundary. +### Queue-health permission contract rejects aggregate token grants + +- The queue-health workflow contract now pins both workflow-level and collector-job permissions to exactly `contents: read` plus `actions: read`, rejecting scalar `read-all`/`write-all`, quoting/spacing variants, inline maps, and unexpected write scopes. + +### OpenCode coverage image materializes every Dockerfile lock input + +- Required OpenCode run `35370902053` for `.github#2266@12621f75e` failed before executing PR code because its trusted Dockerfile copied `requirements-noema-document-ci-hashes.txt` while the isolated build context contained only the OpenCode lockfile. The coverage owner now validates both lockfiles as regular non-symlink files and copies both into the trusted build context before the networked image build. `tests/test_opencode_agent_contract.py` pins the complete input boundary. Hosted exact-head acceptance remains Proposed until the new run reaches the image-build and coverage steps. + +### Noema transport capacity schedules a bounded continuation re-dispatch + +- After gateway failover, HTTP 429/5xx no longer end only as a permanent required-check failure with `caller attempts=1`. ADR-0031 classifies that class as `provider_capacity_unavailable`, keeps the single gateway request per job, surfaces `provider_attempt_count` from the orchestrator error envelope, and authorizes at most two same-head `repository_dispatch` retries after a capped `Retry-After` or deterministic 60–180 s jitter. Review is never skipped. Refs #2165. + +### Strix evidence binding distinguishes PR-delta from baseline and fails closed on false remediation + +- Required Strix on `.github#2106` attributed findings against base-identical `scripts/ci/pingora_edge_policy.py` / `scripts/ci/contextual_orchestrator_review_policy.py` as if they were PR-introduced (#2159). Separately, LineageWeave Strix run `34746057545` claimed a fix was "already applied" after `apply_patch` missed `/workspace/backend/app/main.py` (#2168). `scripts/ci/strix_evidence_binding.py` now classifies findings as `pr_delta` / `repository_baseline` / `context_dependency` / `unmapped` against the authenticated changed-file inventory (renames + hunks), and remediation claims fail closed unless workspace bytes or a source commit receipt prove the edit. The gate labels decisions with `evidence_scope=` and sanitizes report artifacts after each attempt. Contract tests: `tests/test_strix_evidence_binding.py`; doctoring: `docs/doctoring/strix-evidence-binding-2159-2168.md`. + +### OpenCode coverage admits immutable `python/` VCS source roots + +- Central OpenCode coverage run [34701472466](https://github.com/ContextualWisdomLab/.github/actions/runs/34701472466) failed before executing `contextual-orchestrator#1149`: the trusted image builder resolved VCS packages only from repository root or `src/`, while the exact immutable `fast-mlsirm@09f762ded35786dd1078222a4577ff09d649816f` exposes `fast_mlsirm` from `python/fast_mlsirm`. The builder now admits the explicit `python/` source root, retains the one-and-only-one import-root invariant, symlink/namespace/compiled-artifact/installed-metadata rejection, exact commit verification, and the later credential-free networkless sandbox. Contract tests pin both package and single-module `python/` layouts. Refs `contextual-orchestrator#1149`. Exact-head Runtime Quality [job 103581110552](https://github.com/ContextualWisdomLab/.github/actions/runs/34704176931/job/103581110552) then caught the required independent workflow-blob trust pin still naming the predecessor blob; `683cb053` advances only that pin to exact blob `f315683208d57ba89a2942502c525abe7355e2fd`. + +### Contextual-orchestrator pin advance removes the implicit 90 s model request timeout + +- Advanced the central sidecar's pinned immutable CO revision from `414f2297` to protected `main@767e67fbc6b881a452761f32abb69b9971b9b03b`, carrying contextual-orchestrator#1053 into Strix, OpenCode, and Noema. Root cause: `ModelClient.__init__` defaulted `timeout=90`, and the review gateway constructed its client without a timeout, so long structured-output completions on NVIDIA NIM (`google/gemma-4-31b-it`) hit `TimeoutError` at exactly 90 s on every attempt; the orchestrator then cycled circuit open/reset on the same route for ~20 min and answered `502 provider_connection_error` (fast-mlsirm#1860 run 34748511702, sidecar artifact 10315556637: 15 of 27 failed attempts at 90.0 s; fast-mlsirm#1825 run 34752130895 same signature). #1053 removes the implicit deadline (null by default, administrator `model_timeout_seconds` per model) and was merged under the infrastructure exception because the pre-fix sidecar was failing its own Noema/OpenCode gates. Hosted acceptance is the first Noema/OpenCode/Strix run on this pin against a consumer PR; not claimed here. Refs ContextualWisdomLab/contextual-orchestrator#1053, ContextualWisdomLab/fast-mlsirm#1860. + +### Pingora edge policy admits HWPX evidence documents without UTF-8 decoding + +- `scripts/ci/pingora_edge_policy.py`'s `BINARY_DOCUMENT_MAGIC` only knew `.pdf` and `.png`, and `_is_binary_documentation_asset` only admitted a `doc`/`docs`/`documentation` directory, so a ZIP-based `.hwpx` evidence attachment under `evidence/` matched neither rule and fell through to the strict UTF-8 decode every other candidate gets. Observed on ContextualWisdomLab/late-life-anxiety-reanalysis#10, head `a1cd5bc6783c6510dfcf937f523c733366e82213`, run `34700409497`, job `103571044859`: "Pingora edge policy could not establish complete evidence: Runtime policy candidate evidence/reviewer_response_draft.hwpx is not valid UTF-8". The fix adds `.hwpx` (`PK\x03\x04`) to `BINARY_DOCUMENT_MAGIC` and extends `_is_binary_documentation_asset` to admit an `.hwpx` under an `evidence` path segment, gated on a bounded container check in the new `_is_complete_hwpx` -- unprefixed ZIP, exact EOCD record, unique members with `mimetype` first, a stored (not deflated) `mimetype` entry exactly `application/hwp+zip`, and a non-empty, unencrypted `Contents/content.hpf` manifest -- so no document body is ever parsed or rendered and no malware inspection is implied. The runtime-path guard and the Nginx-runtime-text fallback scan for disguised or malformed archives are unchanged. `tests/test_pingora_hwpx_evidence.py` runs the production policy boundary offline: RED (test-only apply) showed 3 failing / 19 passing; GREEN (full patch) showed 90 passing across that file plus `tests/test_pingora_edge_policy.py` and `tests/test_pingora_edge_workflow_contract.py`. Branch coverage of the touched module is 100% (388 statements, 174 branches, 0 missed) and `interrogate scripts/ci -q` reports 100.0% docstrings. Hosted acceptance still requires a newly loaded central source SHA to re-run the consumer's exact head bootstrap. Refs ContextualWisdomLab/.github#2116. + +### Review policy ZDR feed keys routes by the wrong field, catalog always empty under `--require-zdr` + +- `_load_zdr_endpoints` (`scripts/ci/contextual_orchestrator_review_policy.py`, introduced by 17052a7ca / #1360) built ZDR route keys from `endpoint.get("model_name")`, but on the real `https://openrouter.ai/api/v1/endpoints/zdr` feed `model_name` is a human display string (e.g. "DeepSeek: DeepSeek V4.1 Flash") while `model_id` is the slug contextual-orchestrator discovery reports as `model` (e.g. `inclusionai/ling-3.0-flash-vl:free`). No live-feed key ever matched `is_zdr_model(...)`, so every `--require-zdr` consumer (every private/internal caller, per ADR-0003) saw an empty catalog and failed closed with `PolicyError: no attested ZDR model route is available with the ZDR policy; orchestrator/free would fail closed`. Confirmed as the cause of `noema-review` and `strix` failing on `ContextualWisdomLab/late-life-anxiety-reanalysis#10` (head `a1cd5bc6783c6510dfcf937f523c733366e82213`, runs `34700409452`/`103571267389` and `34700409446`/`103571829483`) against central `fb17ef556f94f673234aa557254ae52779e9a7b0`. `_load_zdr_endpoints` now keys on `model_id`, with no fallback to the display name; the three existing fixtures that put slugs into `model_name` (masking the bug since #1360) now carry the real feed schema. Offline reproduction against a 60-row consumer discovery snapshot and the live 859-entry ZDR feed: before, `--require-zdr --pool free` exits 1 with the `PolicyError` above; after, it exits 0 and selects 3 attested `openrouter` ZDR routes (`inclusionai/ling-3.0-flash-{vl,sante,fin}:free`, served by `Novita`). Hosted acceptance on the private consumer's exact head is still required and is not claimed here. Refs ContextualWisdomLab/late-life-anxiety-reanalysis#10, ContextualWisdomLab/.github#2122. + +### CodeQL required workflow denies private consumers a read they need for their own PR + +- `.github/workflows/codeql-pr.yml`'s `analyze-head` and `dispatch-current-head` jobs called `gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"` and later `repos/${TARGET_REPOSITORY}/commits/${PR_HEAD_SHA}/statuses` while holding only `contents: read` (plus `id-token: write`, and `actions: read` on the coordinator job) -- reads GitHub's REST contract gates behind the `pull-requests: read` and `statuses: read` fine-grained permissions on a private repository. Public consumers never surfaced this because GET on a public repository needs no such grant, but private consumer ContextualWisdomLab/late-life-anxiety-reanalysis's PR #10 (head `a1cd5bc6783c6510dfcf937f523c733366e82213`, run `34700410434`) failed both required-workflow jobs (`103571590442`, `103571810868`) at their first API call with `gh: Resource not accessible by integration (HTTP 403)`. Both jobs now also hold `pull-requests: read` and `statuses: read`; no write permission is added anywhere, and `actions: write` stays absent, so `tests/test_codeql_pr_workflow_contract.py::test_codeql_required_workflow_does_not_gain_actions_write` needed no change. New regression test `test_codeql_pr_jobs_hold_read_grants_private_consumers_need` pins the exact grant set. See `docs/doctoring/codeql-pr-private-consumer-read-permissions.md`. Refs ContextualWisdomLab/late-life-anxiety-reanalysis#10. + +### Failed-check finding names the Strix sandbox instead of the gateway + +- `opencode-review-dispatch.yml`'s `emit_strix_provider_failure_finding` rendered one fixed finding for every `STRIX_PROVIDER_UNAVAILABLE` line, whose Root cause read "The contextual-orchestrator gateway or its discovered provider pool was unavailable for this run". `#1953` had just given the Strix sandbox bootstrap failure its own second verdict token (`STRIX_SANDBOX_UNAVAILABLE`) precisely because that attribution is wrong for it -- the sandbox container never reaches its Caido proxy, so the run dies before the gateway serves anything -- and this consumer re-applied the wrong attribution one step downstream, into the review findings and the failure census. The emitter now branches on the second token: a sandbox verdict gets a finding that names Strix's sandbox, says the verdict does not name the gateway, and tells the reader not to change gateway or provider configuration on its strength. A `STRIX_PROVIDER_UNAVAILABLE` line without the token keeps its existing text verbatim, so the gateway class has no regression surface. No test covered this finding text at all before (`gateway or its discovered provider pool` matched nothing under `tests/`); `tests/test_opencode_dispatch_strix_sandbox_finding.py` now runs the production emitter from the published run block and pins both directions plus the no-signal case. Refs #1953, #1935. + +### Strix gate keeps a recovered transient model error from failing a completed scan + +- `scripts/ci/strix_quick_gate.sh` `sanitize_known_strix_report_warnings` now also strips strix-agent's `strix.core.execution: transient model/provider error for ; replaying turn (attempt n/m, backoff Ns): …` WARNING lines before the report failure-signal scan. strix-agent 1.5.3 (`strix/core/execution.py:763`) emits that line only inside its bounded transient-retry branch, immediately before the replay runs; an exhausted retry logs `agent run failed for …; marking failed` at ERROR with a traceback and exits non-zero, and both of those still fail the gate. Observed on `.github#1689` run `34013778497`: a completed 63-minute scan (`run.json` `completed`, SARIF 0 results, attempt exit 0) was failed closed as `STRIX_PROVIDER_UNAVAILABLE … exhausted` on three such warnings, and the scheduler then dispatched another same-head scan. The pattern is anchored before the exception repr so the same class keeps matching after a gateway pin advance changes the exception type; re-verify the message format on every strix-agent bump. One documented side effect: when a provider's 503 body appears only inside a retry line's exception repr, removing that line also removes the only text `has_strix_report_provider_failure_signal` would have matched in the report log, which can make `is_model_retryable_error`'s report-only branch read a genuine outage as non-retryable. The direction is fail-closed (an exhausted retry still exits non-zero with its ERROR and traceback retained), and with a contextual-orchestrator primary the verdict branch answers before that classifier is consulted, so no path today changes its outcome; if fallback-model classification is ever wanted for a non-gateway primary, read the pre-sanitize attempt copy that `preserve_attempt_log` already keeps. Tests: `tests/test_strix_recovered_transient_sanitizer.py`. + +### Review sidecar preflight postpones a rate-limited account's candidates instead of banning them + +- `_preflight_review_agents` no longer ends its walk when every credential account has answered 429 twice in a row. A candidate set aside by `REVIEW_PREFLIGHT_ACCOUNT_SKIP_AFTER_429` is postponed to the end of the walk, and once the first pass ends with the readiness target unmet and probe budget left, the postponed candidates are probed in catalog order until the sixteen-probe budget is spent. On 2026-09-06 five sidecar boots whose probes began between 07:24Z and 08:05Z read `probed 6 / skipped 18 / ready 0` and failed closed: `.github` run 34016207820's six probes across all three accounts were refused 429 between 07:49:35.111Z and 07:49:35.767Z, so the rule set every account aside on two same-account requests about 310 ms apart and gave up with ten of sixteen probes unspent — and because deferral needs one ready route, nothing was served either; `keyverse#143`'s 08:20Z `noema-review` repeated it in a second repository (six probes, 369 ms, all 429). The pools are not dead in those minutes: run 34016093772 was inside its own preflight during that burst, and its `llama-3.2-11b` probes on the same two NVIDIA keys answered ready at 07:50:58.7Z and 07:50:59.0Z, 84 seconds after those keys refused. Whether the unspent probes would have found a ready route inside a burst is unmeasured and is not claimed; the change is justified by ending a walk under target with the budget in hand. Of the fourteen boots that ran the merged rule, eight spend all sixteen probes in the first pass and are unchanged; one (`argos` 34014143870, a serving boot at `12 / 12 / 3`) exhausts its candidates under budget and now gains a second pass, as do the five burst boots. The cost is stated rather than assumed: a refused probe costs about 120 ms, a silent one up to the 90 s receive timeout, and the postponed tail holds both (`google/gemma-4-31b-it` answered `TimeoutError` in 15 of the 19 probes that reached it), so the worst case adds up to about 15 minutes to a boot that still fails and the two-stage auto path goes from 8 to 24 requests including the priced stage. The second pass never draws on the shared escalation budget, so the priced fallback keeps the escalations it had. The report gains `postponed_probed_count` (`skipped_count` now counts postponed candidates the budget never reached) and, on a refused probe, `retry_after_s` when the response carried a whole-seconds `Retry-After` header — evidence only, nothing waits on it, so the next census can decide whether a delayed second pass is worth proposing. ADR-0029 is amended. Refs #1948, #1949. + +### Superseded OpenCode review dispatches coalesce before they take a runner + +- `opencode-review-dispatch.yml` now carries a workflow-level `concurrency` group keyed by the dispatched pull request (`opencode-review-dispatch--`, `cancel-in-progress: true`), matching `codeql-scan-dispatch.yml`'s workflow-level group and the rationale already recorded in `strix.yml`, `noema-review.yml` and `opencode-review.yml`: a job-level group is never evaluated while the whole run waits behind the organization job ceiling. The workflow kept its group only on the long `opencode-review-target` job, so two dispatches for one pull request each queued for hours and each was allocated a runner before the older one could be discarded. Measured on 2026-09-06: four of the five dispatch runs that passed `validate-pr-metadata` were rejected hours later by the privileged metadata check because the head had moved while they queued (runs `34002473295`, `34010256951`, `34015973300`, `34016922761`), each after `coverage-source-tree` and `coverage-evidence` had run. The privileged check itself is unchanged -- it rejected exactly what it should; what changes is that the superseded run is now cancelled at creation instead of spending a slot to discover its subject moved. + +### Strix gate names the sandbox bootstrap failure and retries it once + +- `scripts/ci/strix_quick_gate.sh` gives the Caido sandbox bootstrap race (`loginAsGuest failed after 10 attempts` on `127.0.0.1:`, upstream usestrix/strix#1036/#1037/#1056) its own bounded same-model retry budget, `STRIX_SANDBOX_BOOTSTRAP_RETRIES` (default 1), drawn on top of `STRIX_TRANSIENT_RETRY_PER_MODEL`. That budget is 0 in production because the gateway owns model failover, so the documented sandbox retry never ran: `argos` Strix run 34013128112 (2026-09-06) shows one attempt, `Docker image ready`, the proxy never reachable, Strix exiting after 240 s -- while the sidecar reported four ready and four deferred routes that were never called. The budget is charged in the same branch that grants the attempt, so a log matching the sandbox class together with a gateway class cannot extend the loop without charging it (caught by adversarial review of the first draft). The primary-scan verdict for that class now reads `STRIX_PROVIDER_UNAVAILABLE: STRIX_SANDBOX_UNAVAILABLE: the last Strix attempt ended in the sandbox bootstrap (...) after N sandbox-specific same-model retries (budget B); this verdict names Strix's sandbox, not the LLM gateway.` instead of `orchestrator/free exhausted`, stating only what the gate observed; the leading token is unchanged so the workflow's finding-free classification and its tests are untouched, and the second token lets the review census split sandbox outages from gateway ones (two of six recent Strix artifacts were this class). Refs #1948. + +### Review sidecar preflight fills the served set lazily to a readiness target + +- `_preflight_review_agents` now treats the catalog as a candidate list, probed in its tier-then-round-robin order until `REVIEW_PREFLIGHT_TARGET_READY = 8` routes are ready or `REVIEW_PREFLIGHT_MAX_PROBES = 16` probes are spent (ADR-0029). The two-stage candidate budget rises from 12 to 24 (`REVIEW_PREFLIGHT_MAX_TOTAL_ROUTES`; auto pool split 16 free / 8 priced; the sidecar's and the launcher's `ORCHESTRATOR_CATALOG_LIMIT` defaults follow), the production `free` pool lists all 24 (12 before), and the per-account cap stays 8. An account that answers 429 to `REVIEW_PREFLIGHT_ACCOUNT_SKIP_AFTER_429 = 2` consecutive probes has its remaining candidates skipped without a probe (a 429 is a per-key answer), so the probes it would have spent reach the other accounts' next candidates — under the real 2026-09-06 order that is the difference between about five ready routes and the target of eight — and a fully rate-limited hour costs two probes per account instead of the whole budget; the report gains `skipped_count` and `account_skip_after_429`. The sidecar's job-log echo of the preflight JSON grows from 160 to 400 lines so 16 probed routes are not cut off exactly in the dead hour the summary matters. A permanently dead candidate -- NIM lists `gemma-3-12b`/`gemma-3-4b` and answers 404 on every run -- now costs one probe instead of a served slot, and a healthy pool stops early instead of always probing every candidate. Motivation: after #1939's four-per-account slice each NVIDIA key's slots were its first four models alphabetically, two of them those 404s, so preflight readiness fell from 6/12 to 1–3/12 and `noema-review` on this repository went from 7 successes / 14 failures to 0 / 22. The report gains `candidate_count`, `target_ready` and `probe_budget`; `probed_count` counts probes actually sent. ADR-0003's stage-budget sentence is amended. Refs #1939, #1947, #1948. + +### Sidecar sanitizer keeps the exception type and innermost frame per traceback + +- `scripts/ci/sanitize_contextual_orchestrator_sidecar_stream.py` now reduces each Python traceback in the sidecar stream to one line, `unexpected_exception type= frame=contextual_orchestrator/.py::` (the type identifier and the innermost package frame only; the exception message, source echoes and non-package frames are never re-emitted; a traceback cut off by the sidecar dying or without a package frame reports `unknown`). The previous single, once-per-stream `sidecar emitted an unexpected exception` line kept neither the count nor the type: `.github#1812`'s strix run (33993155419) ended on 83 gateway `500 internal_error` responses -- the orchestrator's generic request handler prints one traceback per unhandled exception -- and no artifact could say which exception escaped or where. Chain sentences (`During handling of the above exception…`, `The above exception was the direct cause…`) are consumed, so a chained exception yields cause then effect. +### Contextual-orchestrator pin advance fixes orchestrator/free retry-stacking + +- Advanced the central sidecar's pinned immutable CO revision from `2e414d15` to protected `main@414f22973658c4ddc3d4320fcf7acd9b4e8ba991`, carrying contextual-orchestrator#1081's fix into Strix, OpenCode, and Noema. Root cause: `TaskOrchestrator._invoke`'s own retry-then-failover decision for a retryable 5xx (budgeted `1 + tool_retry_attempts` real tries per candidate) was getting multiplied by `ModelClient._send_with_retry`'s independent transient-retry-with-backoff underneath it (`max_retries + 1` further tries per call) -- up to 6 real network attempts against one already-flagged-flaky `orchestrator/free` agent before `_invoke` ever tried the next ranked candidate. Confirmed as the cause of independently observed incidents in #1912, #1231, #1503, and #1198, each spending 9-57+ minutes on one escalated route and surfacing that same route's model in its final error, never reaching a cleanly-ready sibling preflight had already found. The fix (`ModelClient.single_attempt_transport()`) changes only which agent gets tried next; no per-attempt timeout changed. Reproduced the bug directly against unmodified contextual-orchestrator `main` before the fix (6 real attempts) and confirmed the fix resolves it (<=2) before advancing this pin. `docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md`'s 2026-09-06 amendment and `tests/test_contextual_orchestrator_review_sidecar_contract.py`'s `ORCH_PIN_SHA` were updated alongside this pin. All callers still consume an exact SHA; no branch or tag is introduced. + +### Review sidecar preflight keeps transient-rejected routes as deferred failover + +- `_preflight_review_agents` no longer discards a route whose 16-token probe answered with a status the serving gateway itself retries and fails over across (`408 409 425 429 500 502 503 504 529`, the vendored orchestrator's `TRANSIENT_HTTP_STATUS`). Such routes are kept as **deferred**, ranked after every ready route by a catalog-priority penalty, so a stalled or rate-limited ready route has somewhere to fail over to; `ready_count` is unchanged, a new `deferred_count` is reported, and `rejected_count` covers only routes the gateway would not retry either (404, auth failures, invalid responses). With no ready route the stage still fails as before, so ADR-0005's priced-catalog fallback contract is untouched. Motivation: `noema-review` run 33993637015 (2026-09-05) rejected 11 of 12 routes -- six with 429, three of them on NVIDIA keys whose sibling routes were ready -- served the single ready route for 542 s and returned 502; under this rule the same run would have served 1 ready + 6 deferred. The sanitized stream gains a `preflight_route_deferred` line alongside `preflight_route_rejected`. + +### Noema review ships sidecar evidence on failure + +- `noema-review.yml` now uploads `strix_runs/contextual-orchestrator-sidecar.stderr.log` and `strix_runs/contextual-orchestrator-preflight.json` as the `noema-sidecar-evidence` artifact when the verdict phase fails (`if: failure()`, the same pinned `actions/upload-artifact` Strix uses, `if-no-files-found: ignore`, 5-day retention). Until now a failed Noema run left `artifacts=0` -- run `33981136873` spent 3122 s walking six ready routes twice each and ended in HTTP 502 with no per-route trace anywhere but the sidecar's stderr -- so the only diagnosis available was the caller's one-line summary. The stderr file is the sanitizer's bounded allowlist output (`sanitize_contextual_orchestrator_sidecar_stream.py`), the same file Strix already publishes in `strix-reports`; per-attempt route outcomes still need an allowlisted structured line from the orchestrator to appear in it. Refs #1935, #1939. +### Sidecar sanitizer admits orchestrator route and circuit events + +- `scripts/ci/sanitize_contextual_orchestrator_sidecar_stream.py` now passes the orchestrator's own `provider_attempt`, `provider_attempt_failed` (cut before the free-text `error_message=`), `provider_backoff`, `provider_exhausted`, `provider_rejected_permanent`, `provider_no_retry_budget` and `circuit_failure|opened|reset|cleared` lines (whose `failures`/`reset_seconds` are floats at runtime, `2.0`/`30.0`), matched field by field against bounded identifier and number charsets, with either Python's default `LEVEL:name:` prefix or the sidecar formatter's `asctime LEVEL name` prefix (the timestamp is kept so per-route durations can be read as differences). Until now every one of these lines was folded into `omitted_unstructured_lines`, so the `provider_exhausted` WARNING that already fires today after a route's retry budget is spent never reached an artifact, and a 3122 s walk across six ready routes (run `33981136873`) had no per-route trace. Companion to #1943 (sidecar DEBUG logging) and #1944 (Noema uploads the file on failure). Refs #1935, #1939. +### Review sidecar records the orchestrator's per-attempt trace + +- `contextual_orchestrator_review_launcher.py` now configures the orchestrator process's logging before serving (`_configure_sidecar_logging`, calling the vendored `contextual_orchestrator.debug_logging.configure_logging`), defaulting to `DEBUG` with a timestamped format and overridable through `ORCHESTRATOR_SIDECAR_LOG_LEVEL`. The orchestrator logs every provider attempt, its classified failure, backoff, and circuit event at `DEBUG` and only `provider_exhausted`/`circuit_opened` at the default `WARNING`, so a failed review left no way to see which routes were tried or how long each took: a 3122 s `noema-review` 502 on 2026-09-05 could only be attributed to "six ready routes, two retry layers, about 548 s per hop" by reading source, not the log. None of the `DEBUG` sites at the vendored pin carries prompt or response content, and the sidecar already pipes this stderr through the redacting sanitizer before it is written to `strix_runs/contextual-orchestrator-sidecar.stderr.log`; a companion change uploads that file as a failure artifact. + +### Review sidecar catalog interleaves credential accounts + +- `build_zdr_prioritized_catalog` now fills each free/ZDR tier round-robin across independently credentialed accounts instead of in provider-name order. The sidecar exports `ORCHESTRATOR_CATALOG_ACCOUNT_CAP=8` with `ORCHESTRATOR_CATALOG_LIMIT=12`, and the sorted fill took 8 `nvidia_nim` routes and 4 `nvidia_nim_sub` routes before any `openrouter` route was reached, so a review that admitted 62 free routes across three accounts served a NVIDIA-only catalog (`noema-review` run 33969842312: `free_pool_admitted_routes` 62, `free_selected_count` 12, runtime preflight `ready_count` 2 of 12) and the failover loop had no other account to leave a stalled NVIDIA endpoint for -- the `noema-review` 502 class tracked in contextual-orchestrator#1045. Tier order (free before priced, ZDR before non-ZDR), the account cap, the limit, and the discovery-order independence contract are unchanged; the same input now yields 4 + 4 + 4. Contrasts with #1476, which hardens `_routable_discovered_models` against a pin that regresses the OpenRouter `evidence_only` flag: on the current pin (`2e414d15`, includes contextual-orchestrator#949) OpenRouter rows already reach the catalog builder, and the selection was what dropped them. + +### Scheduler holds pre-review branch updates while checks are in flight + +- `inspect_pr` now decides `wait` instead of `update_branch` when a behind, unreviewed head still has queued or running check runs (`has_in_flight_check_runs`, built on the existing `latest_check_runs`/`running_check_state`). Under a saturated runner queue each PR's own delayed `pull_request_target` scheduler run merged `main` into the head before review dispatch, cancelling every queued check on the old head (22/28 on #1926, 21/30 on #1484) and requeueing the PR at the back, so no head ever completed its checks: 76 of the 77 PRs merged into this repository since 2026-09-04 had 0/12 required contexts satisfied at merge time. The hold has no age cap on purpose -- a check that never finishes keeps the head in place instead of restarting that loop, and the update resumes once every newest check run is terminal. `CLAUDE.md` now describes both update paths. Tracked in #1935. + +### CodeQL scan dispatch matrix serialisation + +- Serialised the dispatched CodeQL matrix with `toJSON()` in `codeql-scan-dispatch.yml`. `codeql-pr.yml` sends `client_payload.matrix` as an array and the handler assigned it straight into `env:`, where a value must be a scalar, so GitHub rejected the step with "A sequence was not expected" and the dispatched scan never ran -- 0 successes against 136 failures since the handler was added in #1776. The validate step already consumes the value through `jq`, so JSON text is the shape it was written for and no consumer changes. Added a string contract test, because neither `yaml.safe_load` nor `actionlint` 1.7.12 flags this: it is an Actions template rule, so only GitHub's own validator rejects it and no local gate catches the class. + ### Contextual-orchestrator pin refresh - Advanced the central sidecar's default immutable CO revision to protected `main@2e414d15ba58f28597751b625a8a2f00fc9fadcf`, carrying current provider discovery, `orchestrator/free` workflow budget, web-search gateway, OpenCode Go, OpenRouter composition, and CI fixes into Strix, OpenCode, and Noema. The shared ModelClient default-timeout removal remains pending in contextual-orchestrator PR #1053. All callers still consume an exact SHA; no branch or tag is introduced. @@ -11,6 +323,8 @@ - Raised `hourly-review-repair.yml`'s discovery ceiling from 50 to 200 while rotating deterministic 50-PR deep-inspection windows by hourly run number. The scheduler hydrates only the selected window and stops immediately after its single dispatch, preserving access to newer PRs without quadrupling expensive review/check/comment work. See `docs/doctoring/hourly-review-repair-single-file-consolidation.md`'s 2026-09-03 follow-up. ## [Unreleased] +- **Bind GitHub REST redirect evidence to both production opener chains.** `.github#2279` now feeds a synthetic same-authority 302 through the CodeQL identity and Strix evidence clients' real module-level openers, proving the redirect target is never contacted and the bearer header is never forwarded. Removing `_RejectRedirects` from either opener makes the contract fail on the forbidden second request. Four stale Strix HTTP/transport/JSON fixtures now patch that same production seam; direct handler unit cases and standalone CodeQL materialization remain unchanged. +- **Define an evidence-backed repository README quality standard.** Added `docs/repository-readme-quality-standard.md` as the shared review contract for product-first structure, code-current onboarding, authority boundaries, durable quality signals, and repository/source/dependency license due diligence. Product repositories continue to own their own README prose; the standard is linked from the root documentation map and does not centralize or generate product claims. - Include merge-scheduler entrypoint, core, and regression-test changes in the existing runtime-quality workflow's trigger and suite selector. Scheduler workflow edits retain queue checks and also select the full review-repair diff --git a/CLAUDE.md b/CLAUDE.md index f1b2cbb482..7dde78f5d2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -48,9 +48,10 @@ an actually-executed PoC via `scripts/ci/sandboxed_verify.py` or `scripts/ci/san split `Developer experience:` / `User experience:` sections). Deterministic code may repair only trusted `path:line` bindings on LLM probes that already carry an independent proof and source-line digest; it never invents observed -results. The scheduler updates a PR branch only -when the latest review is approved, no current-head check has failed, and GitHub reports the PR as -behind. The mechanical merge scheduler itself never synthesizes a fix: it gives `DIRTY`/`CONFLICTING` +results. The scheduler updates a PR branch in two cases: after approval, when no current-head check +has failed and GitHub reports the PR as behind; and before review dispatch, when the PR is behind and +no current-head check is still queued or running (an in-flight check is evidence the update would +discard; see #1935). The mechanical merge scheduler itself never synthesizes a fix: it gives `DIRTY`/`CONFLICTING` PRs repair guidance. A separate edit-capable autofix flow (`scripts/ci/pr_review_fix_scheduler.py` → `.github/workflows/pr-review-autofix.yml`) may, for an approved same-repository-head PR, merge the base into the head and resolve the conflict markers; the @@ -145,6 +146,13 @@ repeatable compile command. `contextual-orchestrator/orchestrator/free`). Keep the ZDR-first policy and the exact-head/vendoring pins in `scripts/ci/zdr_policy.py` and `scripts/ci/contextual_orchestrator_review_sidecar.sh` in sync with their contract tests. + Sanitized route diagnostics preserve only server request IDs that are exactly + 32 lowercase hexadecimal characters, plus an event contract's explicit `-` or + `` marker; never widen that field to arbitrary text or re-emit provider + error messages. + HTTP success correlation is limited to the review sidecar's fixed health, + chat-completions, and responses paths; query stripping alone does not make an + arbitrary request path safe for CI artifacts. - **`pull_request_target` trust boundary.** The required review workflows run the *base branch's* trusted scripts. A PR that edits the trusted review workflows can fail its own checks until the base branch catches up; a same-head manual `workflow_dispatch` Strix run may supply review evidence diff --git a/GOAL.md b/GOAL.md new file mode 100644 index 0000000000..fdcd70af0e --- /dev/null +++ b/GOAL.md @@ -0,0 +1,84 @@ +# Goal — K-CSAP 중앙 self-hosted 실행과 조건부 병합 + +갱신: 2026-10-13 KST. 이 worktree의 단일 작성자는 goosefish 부모다. + +## 최신 직접 권한과 실행 분해 + +사용자는 인프라 장애에 대응하기 위한 bypass merge를 직접 허용했다. 아래 이전의 일반 병합 전용·일괄 HOLD 조건은 이 권한 범위에서 대체한다. 소스 전달과 운영 활성화를 분리한다. 확인된 코드·보안 결함은 수리하고, 미실행 CI/App 검사는 PASS나 APPROVED로 만들지 않는다. 사람 팀원을 추가로 요구하지 않는다. 미래 head 실행을 그 head의 게시 선행조건으로 두지 않는다. + +| 조각 | 담당 | 다음 실제 동작 | 완료 증거 | 진짜 선행 조건 | +|---|---|---|---|---| +| 현재 PR2615 전달 가능성 | 부모 + 읽기전용 평가자 | live head/base/checks/threads와 retained source review를 대조하고 우회할 인프라 check를 특정 | exact-head 병합 사전판정, 실제 merge와 readback 또는 실제 거절 | 미해결 substantive 소스 결함이 없음 | +| 전체 gate 실패 분리 | 별도 읽기전용 평가자 | 기존 9개 실패와 coverage 미달을 환경·상속 소스·미검증으로 분류하고 기존 수리 찾아 결합 범위 제시 | 현재 bytes에 근거한 최소 수리 경로 | label/runner/App 대기 불필요 | +| canonical label | 기존 shared owner + 부모 | 기존 한 줄 patch 수락과 producer config 연결, 기본 actionlint 실행 | canonical invocation 직접 exit | shared 파일 쓰기 경계만 직렬화 | +| central exact-head runtime 연결 | 부모 producer + oscar runtime | 실제 consumer 인자·승인 계약에 맞춘 adapter 구현·시험 | runnable adapter와 승인된 Linux lifecycle 증거 | 실행 시 runner custody·credential 분리 | +| literal auto/App 복구 | Remora/#2560 | 기존 경로의 실제 inference와 독립 App 게시 복구 | 실제 verdict/review object | 키·비공개 자료 보호 | + +인프라 복구는 source delivery와 병렬로 진행한다. bypass는 exact PR/head/base와 실제 인프라 실패에 한정한다. 보호 설정 삭제·가짜 status·자기 승인·force push·키/ACL 확대는 하지 않는다. 로컬 untracked CLI의 검증은 현재 원격 PR의 검증과 구별한다. 이전 head와 일정은 아래 역사적 기록이며 live 조회로 대체한다. + + +## 완료 결과 + +중앙 `ContextualWisdomLab/.github` PR #2615를 검증과 독립 App 승인 후 일반 병합으로 `main`에 전달한다. 제품 `k-csap-skills`에는 검증된 중앙 commit을 호출하는 얇은 caller만 남긴다. 실제 self-hosted job과 개인 LiteLLM `auto` 리뷰를 실행하고, 작성자와 다른 Noema/OpenCode App의 현재 head 승인을 확인한다. + +사용자는 commit/push/조건부 merge와 Startup failure 경로의 중앙 self-hosted 전환을 승인했다. 전환 불가 hosted 절차의 비활성화도 승인했다. 이는 검증·개인정보·키 관리·보호 규칙 완화나 미실행 증거의 통과 승격 승인이 아니다. 별도의 인간 개발자를 전제로 하지 않는다. + +## 2026-10-10 기준 직접 확인 (역사 기록; 현재 값은 위 표와 live 조회로 대체) + +- PR #2615 head: `aacc387349c62b54ed96a9bd96b17c022296897b`; OPEN/Draft/BLOCKED, formal review 0. +- 중앙 protected base: `7554587c2e3106a388998bcad048a3d7121de25e`. +- 2026-10-10 기준 공유 #2565 head: `ef0c9d4c668fac7f01609b8fbd61a4126b103f5c`; 당시 아직 protected main에 전달되지 않았다. 이후 #2565는 main에 병합됐다. +- callee workflow SHA-256: `2b14845ebf761652ca78ecdaac86c6e851ea41dc573312f41ade2695b0a719b7`. +- test SHA-256: `36bbf733ea0bec7e0cd3b03cb07d6c466cf19fffb578f16114e820a9d69c3737`. +- fresh source review `dbc0d58d24a098c68f33268fcd8dc1a6de00f57bfe729aa0dd0272fd4e29ff62`: 조건부 소스 수용. actual admission 35개와 SHA 소비자 4개 통과, frozen test 28개 통과. 실행 도구 대체는 합성 fixture이며 제품 runtime 실행이 아니다. +- setup-uv review `2159eec4f6343b82250124b78af57ed8925deaa8a63dc007a6f7e15a9670da83`: 실제 pinned dist macOS arm64/Node24 설치·checksum 거부·post cleanup 확인. Linux X64/Node20 및 Python runtime 무결성은 미검증이다. +- 제품 repo runner id 2 `k-csap-isolated-linux`는 online/idle이며 labels `[self-hosted, Linux, X64, k-csap-isolated]`다. online/label은 격리·실행 증거가 아니다. Group 13은 사용하지 않는다. +- 제품은 비공개다. 원문·추출본·private 증적·키를 공개 중앙 저장소로 전송하지 않는다. + +## 소유권과 기존 경로 + +| 영역 | 단일 담당 | 이 Goal의 경계 | +|---|---|---| +| PR #2615 producer, 전용 tests/hash lock, 이 Goal, doctoring 문서 | goosefish 부모 | exact reviewed bytes만 수정·출판 | +| 제품 thin caller와 runner 활성화 | oscar 및 기존 운영 담당 | 승인된 새 caller/격리 packet을 받아 연결; 경쟁 편집·runner 이동 금지 | +| acceptance 조정 | salmon, 독립 admission oracle | actual packet을 확인하며 보고를 승인으로 승격하지 않음 | +| shared routing/Noema/OpenCode/canonical actionlint | #2565/Remora 담당 | accepted exact-hunk 인계 전 shared 파일 편집 금지 | +| 서비스 키·relay·effective auto route | 기존 #2560 담당 | Secret 값 읽기·중복 발급·덮어쓰기 금지 | +| 제품 dynamic Code Quality 설정 | kelp | GET not-configured 확인 완료, 이 session 제품 설정 write 0 | + +## 실행 계약 + +- `workflow_call`만 사용하고 executable inputs/inherited secrets는 없다. +- caller 신뢰 gate를 runner 배정 전 job-level `if`에 둔다. 첫 step는 checkout 전 방어층이며 host 격리를 대신하지 않는다. skip은 quality 통과가 아니다. +- same-repo open PR과 positive-number `refs/pull/N/merge`만 허용한다. PR head와 event SHA/state는 분리한다. missing head/state fallback은 금지한다. +- PR exact head, master push/manual event SHA를 admission에서 확정한다. checkout과 HEAD 검증은 동일 `expected_sha` 출력만 소비한다. merge candidate 검증은 별도다. +- 일회용 source child와 private uv tool/cache/temp/venv, archive checksum 및 정상 종료 cleanup을 유지한다. hostile same-UID 동시 작업·잔류 프로세스·강제 종료에 대한 격리는 별도 운영 gate다. +- Python 3.11.14와 uv 0.9.7로 locked ruff/pytest/validate/diff-check를 실행한다. 로컬 contract 환경 Python 3.11.16을 workflow pin 실행으로 주장하지 않는다. +- canonical actionlint의 `k-csap-isolated` vocabulary와 전용 test lock 설치 linkage를 수락해야 한다. scratch vocabulary 통과는 canonical gate 통과가 아니다. +- 모델은 literal `auto`, endpoint는 `https://litellm.poinnetworks.net`다. `orchestrator/free` 실행은 대체 수락하지 않는다. 비용·private retention 사실을 임의 attestation으로 만들지 않는다. + +## 병합 전 수락 단계 + +1. 실제 source bytes와 로컬 독립 review의 hash binding을 유지한다. unchanged suite 반복 대신 다음 미충족 gate로 진행한다. +2. exact-head 운영 packet에서 Linux/Node runtime, toolchain provenance, 격리·동시 작업·network·cleanup을 확인한다. approved runtime이 없으면 운영 담당과 좁게 수리한다. +3. 공유 auto route의 base chain을 default `main`까지 추적한다. feature-stack merge는 배포가 아니다. canonical lint label과 test dependency linkage도 해당 소유자와 결합한다. +4. 실제 self-hosted run/job ID와 runner_name, 실행된 source SHA 및 required step exits를 확인한다. +5. 실제 개인 gateway 인증 추론과 substantive verdict, author-distinct canonical App의 현재 head/base review를 확인한다. Draft skip/무료 요약/인프라 오류는 approval이 아니다. +6. 리뷰 요청 단계와 병합 수락 단계를 분리한다. Ready는 승인·검증 통과가 아니다. 소스 검토와 live head/base/필수 규칙/미해결 finding 확인 후, 실행 가능한 승인된 reviewer 경로가 있으면 Ready만 전환할 수 있다. 실제 callee 증거는 oscar가 승인된 임시 caller에서 immutable 후보 SHA를 사용해 병합 전에 확보한다. legacy/다른 모델 실행은 요구된 `auto` 증거로 세지 않는다. +7. 병합 직전 live head/checks/required rules/unresolved findings를 fail-closed로 판정한다. 실패·skip·부재·다른 head 증거가 있으면 merge하지 않는다. 모든 조건 충족 후 일반 merge를 수행하고 state/mergedAt/mergeCommit/default branch를 읽어 확인한다. 최종 제품 caller는 immutable protected producer pin으로 전환하고 실제 caller-context 실행을 확인한다. hosted fallback 없이 교체한다. + +## 현재 수락 상태 + +현재 사용자 지시에 따라 병합 작업까지 진행한다. PR #2615는 `OPEN/DRAFT/BLOCKED`, head `aacc387349c62b54ed96a9bd96b17c022296897b`, source review는 `CONDITIONAL_SOURCE_ACCEPTANCE`다. 이는 merge gate 충족이 아니다. source owner scope는 #2615의 여섯 파일이며 shared #2565, #2560, Remora, kelp 설정 파일은 소유하지 않는다. + +## 다음 실제 작업 + +1. 읽기전용 운영 보고의 최종 해시를 검증했다. online/container 관측은 배타 lease나 process settlement/cleanup 통과가 아니다. oscar의 기존 운영 수리와 caller 후보를 재사용한다. +2. permissive umask에서 source와 `.ci-tools`가 0777로 만들어지는 실제 shell RED 2개를 확인했다. 각 생성 단계에 `umask 077`을 적용하고 계약 테스트 30개 통과를 확인했다. 이는 다른 UID의 접근 제한이며 같은 UID의 적대적 writer 격리 증거가 아니다. 독립 delta 리뷰 후 게시한다. +3. canonical CI lock의 PyYAML 누락은 실제 collection RED exit 2로 확인했다. dedicated PyYAML 6.0.3 hash block을 추가한 scratch 제안은 28개 통과다. shared 소유자에게 comment `6098771884`로 전달했다. 2026-10-11 remote head `b78007bfc976faaa4d416d2b6578b114e2725a82`의 canonical lock에 PyYAML pin이 추가된 사실을 확인했다. 해당 pin의 설치 검증 및 보호된 main 통합은 별도다. canonical lint의 `k-csap-isolated`는 이 head에서도 없다. +4. bootstrap plan review `5eaa615dc1f6ac143f526329ae46d68f95284452114cada4a83c77e3391586b1`의 CHANGES_REQUESTED를 채택했다. Ready와 merge를 분리하고 임시 exact-SHA caller로 실행 순환을 해소한다. 실제 `auto`/App 및 운영 수락 조건은 유지한다. +5. 기존 caller 후보는 PR32 base 위의 oscar 소유 2파일이며 아직 미게시다. source가 바뀌면 기존 후보 pin의 증거를 새 SHA로 이전하지 않는다. 독립 리뷰와 운영 승인 후 소유자가 pin을 갱신하고 실행한다. + +## Hosted 절차 중단 상태 + +중앙 동적 Code Quality는 supported PATCH로 `not-configured`를 설정하고 GET으로 확인했다. generic workflow disable은 422여서 registry active identity와 feature 활성화를 구분한다. advanced CodeQL security workflow는 보존했다. 제품 설정은 kelp receipt `92e725eaa5422cbc0d3356a74afc749f75f4779854bcfda270572ea24e734f94`에서 not-configured이며 중복 write는 하지 않았다. 과거 billing failure와 artifact quota 실패는 역사 증거로 유지한다. disabled/unavailable 절차를 성공·보안 면제·merge 우회로 간주하지 않는다. diff --git a/README.md b/README.md index 1e9f51103a..d9becf39f5 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,7 @@ test suite. | Document | Role | | --- | --- | | [profile/README.md](profile/README.md) | Public org profile, DIKW checkpoints, project catalog, APA 7th references | +| [docs/repository-readme-quality-standard.md](docs/repository-readme-quality-standard.md) | Evidence-backed quality contract for repository-owned product READMEs | | [docs/pr-review-and-merge-procedure.md](docs/pr-review-and-merge-procedure.md) | Bot/agent review, exact-head, successor-head, and merge procedure | | [PR_GOVERNANCE_AUDIT.md](PR_GOVERNANCE_AUDIT.md) | Live audit and per-repo DX/UX transfer decisions | | [docs/org-required-workflow-rollout.md](docs/org-required-workflow-rollout.md) | Ruleset `18156473` ledger and sibling onboarding | diff --git a/config/actions_queue_health_repositories.json b/config/actions_queue_health_repositories.json new file mode 100644 index 0000000000..ac38aeb412 --- /dev/null +++ b/config/actions_queue_health_repositories.json @@ -0,0 +1,18 @@ +{ + "repositories": [ + "ContextualWisdomLab/.github", + "ContextualWisdomLab/ConceptWeave", + "ContextualWisdomLab/ELUNVERA", + "ContextualWisdomLab/LineageWeave", + "ContextualWisdomLab/OriginWeave", + "ContextualWisdomLab/TEPP", + "ContextualWisdomLab/contextual-orchestrator", + "ContextualWisdomLab/disksage", + "ContextualWisdomLab/fast-mlsirm", + "ContextualWisdomLab/mhtml-etl-gateway", + "ContextualWisdomLab/naruon", + "ContextualWisdomLab/noema", + "ContextualWisdomLab/pg-llm-batch", + "ContextualWisdomLab/quarantine-sandbox-runtime" + ] +} diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 04dc04c7a2..7543f736e8 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -24,7 +24,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`2e414d15ba58f28597751b625a8a2f00fc9fadcf` today) into `RUNNER_TEMP`. The + (`01bf92a3ec67a0e1f9b68978eb16b60301e985fd` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. @@ -50,9 +50,12 @@ all five, and auto-optimize routing by cost. route rejects the real runtime request contract does it rebuild once from fully price-attested routes and record the rejected primary attempt. This is evidence-triggered failover, not an arbitrary free/paid mixing ratio. - Both stages share one twelve-route startup budget: no more than eight routes - enter the free primary stage and only its remaining capacity may enter priced - fallback. Full discovery counts remain in policy evidence, and the transient + Both stages share one bounded startup budget of twenty-four candidates: no + more than sixteen enter the free primary stage and only its remaining + capacity may enter priced fallback. Candidates are probed lazily in catalog + order until eight routes are ready or sixteen probes are spent per stage + (ADR-0029), so a dead candidate costs one probe, not a served slot. Full + discovery counts remain in policy evidence, and the transient priced catalog is removed immediately after loading. 3. **ZDR-first within each cost tier**: `scripts/ci/zdr_policy.py` defines ZDR the way OpenRouter does ("a provider will not store your data for any period @@ -256,3 +259,59 @@ all five, and auto-optimize routing by cost. fault. Accepted-size and tool-schema probes call the pinned client's deterministic mock response explicitly and therefore perform no provider call. +- **2026-09-13 amendment: advance the governed runtime pin to remove the + implicit 90 s model request timeout.** The vendored pin advances from + `414f22973658c4ddc3d4320fcf7acd9b4e8ba991` to + `767e67fbc6b881a452761f32abb69b9971b9b03b`, the commit that merges + `contextual-orchestrator#1053`. Under the previous pin `ModelClient` + defaulted to `timeout=90`, so every NVIDIA NIM `google/gemma-4-31b-it` + attempt in the Noema sidecar ended in `TimeoutError` at exactly 90 s (15 of + 27 attempts in fast-mlsirm#1860 run 34748511702) and the gateway surfaced + `502 provider_connection_error` after ~20 min of circuit retries. #1053 makes + the model timeout null by default and administrator-configured per model + (`model_timeout_seconds`), matching this ADR's rule that model inference + carries no wall-clock deadline. +- **2026-09-06 amendment: advance the governed runtime pin to fix + `orchestrator/free` retry-stacking.** The vendored pin advances from + `2e414d15ba58f28597751b625a8a2f00fc9fadcf` to + `414f22973658c4ddc3d4320fcf7acd9b4e8ba991`, the commit that merges + `contextual-orchestrator#1081`. That PR fixes `TaskOrchestrator._invoke`'s + per-agent retry-then-failover decision (`RETRY_SAME_AGENT` for a retryable + 5xx, budgeted at `1 + tool_retry_attempts` real tries per candidate) getting + multiplied by `ModelClient._send_with_retry`'s own, independent + transient-retry-with-backoff loop underneath it (`max_retries + 1` further + tries per call) — up to `(tool_retry_attempts + 1) × (max_retries + 1)` real + network attempts (6 at production defaults) against one already-flagged-flaky + `orchestrator/free` agent before `_invoke` ever tried the next ranked + candidate. This is the confirmed root cause of independently observed + incidents in `ContextualWisdomLab/.github` PRs #1912, #1231, #1503, and + #1198, each spending 9–57+ minutes on one escalated route and surfacing that + same route's model in its final error, never reaching a cleanly-ready + sibling preflight had already found. The fix adds + `ModelClient.single_attempt_transport()` (a thread-local context manager + mirroring the existing `request_settings()` pattern) that forces + `_send_with_retry`'s retry budget to 0 for the duration of `_invoke`'s own + per-agent attempt; it changes only *which* agent gets tried next, never any + per-attempt timeout, consistent with the 2026-08-31 amendment above. No + other contextual-orchestrator behavior changes with this pin advance. +- **2026-09-25 amendment: adopt bounded 429 recovery in the review runtime.** + Advance the vendored pin from `767e67fbc6b881a452761f32abb69b9971b9b03b` + to `0d0637d032560417a9a08a8477c4aaf3a5942e0a`, the protected-main + revision containing the merged rate-limit admission repair (#1179). The + old runtime advanced to another provider after one 429 but returned a 429 + when all eligible free routes were cooling. The new runtime honors a + provider cooldown within its bounded request budget and returns a typed + 429 when no eligible route can recover in time. It keeps + `orchestrator/free` inside the admitted free pool and retains the default + null model timeout. Both revisions have byte-identical `requirements.lock`. + This pin change still needs protected delivery and a successful exact-head + Noema or OpenCode review; preflight success alone is not that evidence. + +- **2026-09-27 amendment: retain cooldown recovery with a patched dependency lock.** + The deployed pin is `01bf92a3ec67a0e1f9b68978eb16b60301e985fd`, a merged CO main revision containing + #1179 recovery and AnyIO 4.14.2. Auditing the earlier proposed `0d0637d0` + pin with pip-audit 2.10.1 found CVE-2026-63374, CVE-2026-64847, and + CVE-2026-63349 in AnyIO 4.14.1. The replacement hash lock has no known + vulnerabilities in the same audit. The earlier byte-identical-lock claim + describes the superseded proposal, not this amended target. No review + completion or runtime provider success is inferred from the lock audit. diff --git a/docs/adr/0025-codeql-required-workflow-dispatch-architecture.md b/docs/adr/0025-codeql-required-workflow-dispatch-architecture.md index 065a9d4d0f..b9a156417b 100644 --- a/docs/adr/0025-codeql-required-workflow-dispatch-architecture.md +++ b/docs/adr/0025-codeql-required-workflow-dispatch-architecture.md @@ -1,6 +1,6 @@ # 0025 — Restore central CodeQL as a required workflow via repository_dispatch -**Status:** Proposed · **Date:** 2026-09-03 · **Owner intent recorded:** loop-brief item 41 +**Status:** Proposed, amended 2026-09-12 (versioned handler-first bootstrap) · **Date:** 2026-09-03 · **Owner intent recorded:** loop-brief item 41 ## Problem @@ -96,14 +96,29 @@ codeql-pr.yml (required workflow, runs in target repo context) does) before dispatching. analyze-head (matrix) -- SAME REQUIRED-CHECK NAME: "CodeQL compatibility analysis (${{ matrix.language }})". - No codeql-action reference. On attempt one it - dispatches its exact run id, job id, language, - and head, then fails intentionally to release - the runner. The trusted handler publishes the - terminal status and reruns only that failed - job. On attempt two the shard reads the - authenticated current-head status once and - reflects it as this job's own exit code. + No codeql-action reference and no + repository_dispatch. On attempt one it + re-checks the live head, consumes an + authenticated codeql-dispatch/ + status when one exists, and otherwise fails + pending to release the runner. The trusted + handler publishes the terminal status and + reruns only that failed job. On the woken + attempt the shard reads the authenticated + current-head status once and reflects it as + this job's own exit code. + dispatch-current-head -- NEW: needs analyze-head, runs on attempt one + of an open current-head PR after the shards + have job ids. Collects those ids from this + run's jobs API, POSTs event_type codeql-scan + once with the remaining language matrix and + required_jobs: [{language, job_id}, ...], and + fails closed if any shard job id is missing. + Skips the POST when every language already + has a terminal verdict. github.run_attempt == 1 + is required: a single-job wake re-runs + dependents, and a second POST would cancel + the in-flight multi-language handler. .github/workflows/codeql-scan-dispatch.yml (NEW, runs natively in .github, NOT admitted through the ruleset, so codeql-action is unrestricted here) @@ -151,24 +166,49 @@ NOT admitted through the ruleset, so codeql-action is unrestricted here) closed and leaves the required job failed. ``` -### Concurrency identity is per pull request and language shard - -Each required `analyze-head` matrix job dispatches one language and supplies a -matching `required_language`. The native handler therefore serializes only the -same repository, pull request, and language tuple. A newer dispatch for that -tuple cancels its stale predecessor, while Python, JavaScript/TypeScript, and -Actions scans for the same head remain independent. - -This distinction is required by the exact-job wake contract. On 2026-09-05, -contextual-orchestrator PR #1049 dispatched all three current-head language -jobs, but central run `33938784437` was the sole survivor because the handler's -group omitted `required_language`. The sibling runs cancelled one another, -leaving their required jobs failed in the documented `pending` handoff state. -The chosen key adds the already validated language to the existing workflow, -repository, and pull-request identity. Sending the full language matrix in one -dispatch was rejected because the handler validates one shard and wakes one -exact required job per run; changing that contract would enlarge the security -and recovery surface without solving another observed need. +### Concurrency identity is per pull request; language independence is the job matrix + +The required `analyze-head` matrix still publishes one named check per +language. It no longer POSTs. One `dispatch-current-head` job sends every +still-pending language in a single `codeql-scan` payload (`matrix` plus +`required_jobs`). The native handler's concurrency group is +`codeql-scan-dispatch-${target_repository}-${pr_number}` with +`cancel-in-progress: true`, so a newer HEAD of the same pull request cancels +its predecessor and other repositories or pull requests stay independent. + +Language independence is `strategy.fail-fast: false` on that one run's job +matrix. Each scan job still publishes `codeql-dispatch/` and wakes +only its own required job. One language's failure cannot cancel or skip a +sibling. + +#### 2026-09-07 amendment: one dispatch per pull request, adopted for the 60-job ceiling + +The 2026-09-05 per-language run was the right fix for the accident it +recorded. contextual-orchestrator PR #1049 dispatched three current-head +language jobs, and central run `33938784437` was the sole survivor because +the handler's group omitted `required_language`. Sibling runs cancelled one +another and left their required jobs failed in the `pending` handoff state. +Sending the full language matrix in one dispatch was rejected then because +the handler validated one shard and woke one exact required job per run; +enlarging that surface had no observed need. + +That need now exists. On 2026-09-07 the organization job ceiling (60 jobs) +was saturated by this fan-out: ContextualWisdomLab/.github had ~300 queued +runs, 149 of them `codeql-scan-dispatch.yml`, covering 60 PR@SHA tuples +(n=2:29, n=3:27, n=4:2). Duplicate cancellation could not collapse them: +the language is not present on the run name, the job name, or the REST +payload. The user-facing concurrency contract for pull-request workflows is +`{workflow}-{repository}-{PR}` with `cancel-in-progress: true` only for a +superseded HEAD of the same pull request, and a language suffix is +forbidden. + +The 2026-09-05 rejection of "full matrix in one dispatch" is therefore +superseded. The sibling-cancel failure mode is gone because siblings are +jobs in one run, not runs in one concurrency group. The exact-job wake +contract is preserved: `required_jobs` is a 1:1 map of language to canonical +job id, each scan shard looks up only its own id, and a missing, stale, or +mismatched identity still fails closed. The old scalar +`required_job_id`/`required_language` payload is retired. ## Scope decision: `analyze-merge` is dropped, not migrated @@ -240,9 +280,10 @@ blocker for this one. inline Python between `analyze-head`/`analyze-merge` today. exact run/job wake-up follows the OpenCode runner-release pattern while avoiding one occupied runner per language for the scan's full duration. -- A repository and pull request can now have one active native handler per - language. This modest concurrency increase is bounded by the detected CodeQL - matrix and prevents valid sibling evidence from being treated as stale work. +- A repository and pull request have one active native handler run. Language + parallelism is bounded by the detected CodeQL matrix inside that run, and a + superseded HEAD of the same pull request cancels the in-flight handler + instead of queuing another copy per language. - Re-admitting `codeql-pr.yml` to ruleset `18156473` must happen only after this design is implemented, tested, and its `detect-languages`/ `dispatch-analysis`/`analyze-head` jobs are confirmed free of any @@ -265,3 +306,55 @@ blocker for this one. required `workflows` list (admin:org PUT, same mechanism used to remove it) and verify a real PR observes a successful, correctly-named required check before declaring this ADR's status Accepted. + +## 2026-09-12 amendment: versioned handler-first bootstrap + +The initial rollout created a protected-branch/client dependency cycle. A +candidate producer can dispatch a stronger evidence envelope, but +`repository_dispatch` always executes the handler from protected `main`. +Conversely, landing the stronger handler first would reject the protected +client's legacy payload and status context. This ADR therefore adopts a +staged protocol on the single canonical handler; it does not create a copied +workflow or permit branch-selected execution. + +The protected bootstrap accepts exactly two event types: + +- `codeql-scan` is temporary legacy v1. It keeps the protected client's + current run title, top-level `required_jobs`, and + `codeql-dispatch/` status context. It rejects nested `pr_head`, + `producer_source_sha`, `rerun_request`, and explicit `rerun_mode` fields so + a v2 caller cannot downgrade its identity checks. +- `codeql-scan-v2` is the proposed v2 contract. The event type is the version + discriminator and consumes no `client_payload` property. It requires the + versioned head envelope, exact synthetic merge `producer_source_sha`, live + base/head parent binding, base-bound status context, and exact handler + gate/SARIF/artifact evidence. + +Both modes share one repository-and-PR concurrency group and one post-matrix +`settle-required-run` job. The matrix scan has `actions:read`; only settlement +has `actions:write`. Settlement revalidates the open PR, repository, base ref +and SHA, head ref and SHA, required run, complete required-job map, terminal +handler jobs, gate steps, and non-expired SARIF artifacts before issuing one +run-wide rerun request. The common concurrency identity prevents v1 and v2 +from becoming simultaneous writers during cutover. + +Live evidence for the amendment is recorded in +`docs/doctoring/codeql-versioned-handler-bootstrap-20260912.md`. In short, +handler run `34684228601` completed both language scans but its matrix-owned +legacy wakes raced: Actions started the required run and Python received HTTP +403. Later same-tuple handler runs were repeatedly cancelled by concurrency, +including `34684575249`, leaving a clean scan without a converged terminal +receipt. This is a settlement-timing defect, not a CodeQL finding. + +Landing sequence is normative: + +1. Land this dual-event, legacy-compatible handler from fresh protected main. +2. Non-force restack the complete successor (#2040), switch its producer to + `codeql-scan-v2`, and generate fresh exact-head end-to-end evidence. +3. Keep legacy v1 until the protected v2 producer is live, all in-flight v1 + required runs are terminal, and repository-wide caller inventory is zero; + then remove v1 with its bridge tests in a separate proven cleanup. + +The ADR remains **Proposed** until that sequence passes ordinary protection +and a real consumer reaches a successful required CodeQL conclusion. Open PR +code is not production authority. diff --git a/docs/adr/0029-sidecar-preflight-lazy-fill.md b/docs/adr/0029-sidecar-preflight-lazy-fill.md new file mode 100644 index 0000000000..15001441cb --- /dev/null +++ b/docs/adr/0029-sidecar-preflight-lazy-fill.md @@ -0,0 +1,157 @@ +# ADR-0029: Review sidecar preflight fills the served set lazily to a readiness target + +- **Status:** Proposed +- **Date:** 2026-09-06 +- **Scope:** `scripts/ci/contextual_orchestrator_review_launcher.py` (`_preflight_review_agents`, the stage limits), `scripts/ci/contextual_orchestrator_review_sidecar.sh` (`ORCHESTRATOR_CATALOG_LIMIT` default), ADR-0003 §2's stage budget sentence +- **Amends:** ADR-0003 (the "twelve-route startup budget" clause). ADR-0005's attempt counts are historical and are not restored. + +## Problem + +The review sidecar selected a fixed catalog of twelve routes and probed every one of them, then served whatever was ready. `.github#1939` made the selection diverse (round-robin across credential accounts inside each cost/ZDR tier, four routes per account), which was right, but it exposed a second defect: the per-account slice is filled from an alphabetically sorted model list, and for both NVIDIA NIM keys the first four models are `deepseek-v4-flash`, `deepseek-v4-pro`, `gemma-3-12b`, `gemma-3-4b`. NIM lists the two `gemma-3` models but answers `404` to every chat request on every run observed. Each NVIDIA key therefore served two working routes, both the most contended models, while the pre-#1939 eight-slot fill had reached `meta/llama-3.2-11b`, `llama-3.2-90b` and `meta/muse-glimmer-30b`, which were ready in every Strix artifact of that afternoon. + +Measured on `ContextualWisdomLab/.github` (lane jan's census on `#1948`, verdict-step conclusions only, draft skips excluded): + +| window | preflight ready of 12 | `noema-review` success / failure | +|---|---|---| +| before `#1939` (`main@f2f91b80`, 2026-09-05T17:25Z) | 6, 6, 5 (16:37–16:56Z artifacts) | 7 / 14 | +| after | 1–3 (23:47Z onward) | 0 / 22 | + +The evening's rate-limit pressure is a confound; the mechanism is not. A fixed slice from a list with dead entries wastes the slice, and probing every candidate regardless of how many are already ready spends per-key rate budget (`#1948`) for nothing. + +## Constraints + +1. No model name is hard-coded anywhere in the fill; a dead candidate is discovered by its probe, not by a list. +2. Probe spend per sidecar boot stays bounded and is stated as a number, because the probes themselves consume the per-key budgets the served routes need (`#1948`). +3. `#1947`'s deferral (a probed route that answered a transient status is kept behind the ready routes) applies unchanged to whatever was probed. +4. ADR-0003's evidence-triggered priced fallback (only after every free candidate rejects) keeps its shape; the two stages still share one startup budget. +5. `ready_count` keeps its meaning (routes proven ready by a probe) so the peers' post-merge discriminators stay comparable. + +## Decision + +The catalog is a **candidate list**, not the served set. `build_zdr_prioritized_catalog` keeps its tier-then-round-robin order (`#1939`) and is asked for up to `REVIEW_PREFLIGHT_MAX_TOTAL_ROUTES = 24` candidates (per-account cap unchanged at 8; the sidecar's `ORCHESTRATOR_CATALOG_LIMIT` default rises from 12 to 24). `_preflight_review_agents` probes candidates **in that order and stops** as soon as `REVIEW_PREFLIGHT_TARGET_READY = 8` routes are ready or `REVIEW_PREFLIGHT_MAX_PROBES = 16` probes have been spent, whichever comes first. The auto pool's split becomes 16 free candidates and up to 8 priced fallback candidates; the production `free` pool (the sidecar default; it has no fallback stage) lists all 24. A silent candidate's probe costs up to one transport timeout (one artifact spent 805 s on 19 probes), so the probe cap bounds preflight wall time as well as request count. + +**Account skip.** *(The "skipped without a probe" and "two probes per account" claims in this paragraph are superseded by the 2026-09-06 amendment below: such a candidate is postponed, and the leftover budget is spent on it.)* A 429 at preflight is a per-key answer, not a per-model one. Once one credential account has answered 429 to `REVIEW_PREFLIGHT_ACCOUNT_SKIP_AFTER_429 = 2` consecutive probes, its remaining candidates are skipped without a probe and the walk continues with the other accounts' next candidates; the two probed routes are still deferred. Under the real 2026-09-06 candidate order (lane jan's table on `#1949`, rebuilt from `#1938`'s Strix artifact: both NVIDIA keys list deepseek ×2, gemma-3 ×2 (404), gemma-4-31b (empty), then the llama and muse routes; every OpenRouter free route answers 429) the plain sixteen-probe walk yields about five ready and five deferred and the readiness target is unreachable, because five probes go to an account whose every route had answered 429 in every artifact since 21:00Z and four to the dead gemma-3 entries. With the skip, the same sixteen probes reach both keys' `llama-3.2` routes and the target of eight. This is why the free pool lists 24 candidates while probing at most 16: the tail is reachable exactly when an account is skipped, and the report separates `skipped_count` from the unreached remainder (`candidate_count − probed_count − skipped_count`). A rate-limited hour therefore costs two probes per account instead of the full budget. + +The sidecar's job-log echo of the preflight JSON (`sed -n '1,400p'`, previously 160 lines) now fits 16 probed routes; the artifact copy was always complete. + +The report gains `candidate_count`, `target_ready` and `probe_budget`; `probed_count` now counts probes actually sent, and `rejected_count` is `probed − ready − deferred`. Unprobed candidates get no `routes` row. + +## Consequences + +- **Good:** a dead candidate costs one probe and yields its place to the next candidate in the same account's list; a healthy hour stops after about eight to twelve probes instead of always twelve; a bad hour is bounded at sixteen probes per stage. +- **Cost:** in an hour where nothing is ready the sidecar sends up to 16 probes per stage where it sent 12, a third more against already exhausted keys. This is the price of finding routes past the dead ones; `#1948`'s shared rate ledger is the lever above it. The cap is also a wall-time bound: a 16-token probe can hold the full 90 s receive timeout (`#1661` run 34008191123, 04:48Z, both NVIDIA keys' deepseek-v4-pro probes at 90.06 s and 90.10 s), so a fully silent hour costs at most 16 × 90 s = 24 minutes of preflight against 18 today, and the account-skip rule cuts a rate-limited hour to two probes per account. *(That last clause is superseded by the 2026-09-06 amendment: a rate-limited hour now spends the whole probe budget rather than two probes per account.)* +- **Unchanged:** a route that answers the probe and then goes silent at request time still costs the gateway's full retry budget (`contextual-orchestrator#1045`); readiness is measured at 16 tokens (`#1454`). +- **Discriminator:** post-merge, `probed_count` versus `candidate_count` per boot and `ready_count` of the served set, read from the `runtime preflight summary` in the job log or the `noema-sidecar-evidence` artifact, compared with the table above. + +## Alternatives considered + +- **Raise the per-account cap back to 8 with a 12-route limit** — restores the pre-#1939 pool but reintroduces the single-account fill that `#1939` fixed; the 404s would still occupy slots. +- **Exclude models that 404 by name** — a hard-coded exclusion list the next discovery change silently invalidates; rejected by constraint 1. The discovery-side question (why NIM lists models it does not serve) remains open in `contextual-orchestrator`. +- **Family-level interleave inside each account's list before the cap** (jan's second layer) — would make each NVIDIA key's first six candidates span deepseek, gemma, llama, muse, minimax, mistral, but it needs a model-family equivalence derived from names, which ADR-0003/#1468 deliberately avoid; kept in reserve if the post-merge census shows same-family contention as the residual after the account skip. +- **Probe all 24 candidates** — best served set, double the probe spend in the hour that can least afford it; rejected by constraint 2. + +## Amendment 2026-09-06: a set-aside candidate is postponed, not banned + +**Evidence.** Sixteen sidecar artifacts were collected on 2026-09-06 across `.github`, `argos`, `bandscope` and `naruon`; **fourteen** ran the merged rule (two, `argos` 34013128112 and `bandscope` 34013146167, still carry the pre-`#1949` report shape and are excluded). The fourteen fall into three classes, not two. + +| class | boots | `probed / skipped / ready` | second pass? | outcome | +|---|---|---|---|---| +| budget spent in the first pass | 8 | 16 / 4 / 5–6 | no — budget already gone | served; the sixth ready route (`llama-3.2-11b` on the second NVIDIA key, catalog position 17, ready in exactly these 8 artifacts) is reached **only** because four OpenRouter probes were set aside — the benefit the rule was designed for | +| candidates exhausted, budget left | 1 | 12 / 12 / 3 (`argos` 34014143870, 06:56Z) | **yes**, up to 4 probes | served with 5 deferred, but the target of 8 was unmet with 4 probes unspent | +| every account set aside | 5 | 6 / 18 / 0 (`rejected 6`, all 429) | **yes**, up to 10 probes | preflight failed closed | + +So the change is not confined to bursts: one served, ordinary-minute boot also ends its first pass under target with budget in hand. Only a boot that spends all sixteen probes in the first pass is untouched. + +The sidecar stderr of `.github` run 34016207820 shows its six probes (both NVIDIA keys' two deepseek routes, two OpenRouter routes) refused 429 between 07:49:35.111Z and 07:49:35.767Z. Because the walk is a round-robin across three accounts, "two consecutive 429s" on one account is two requests about **310 ms** apart (`nvidia_nim` at .111 and .422), not two probes a tenth of a second apart. The rule set all three accounts aside, the walk ended **with ten of its sixteen probes unspent**, and because deferral requires one ready route (`#1947`) nothing was served either. The five boots of that class span 07:24:50Z to 08:04:41Z. + +A refusal is not a verdict on the account. Run 34016093772 was inside its *own* preflight while that burst happened (its probes run from 07:46:21Z), and its `llama-3.2-11b` probes on the **same two NVIDIA keys** answered ready at 07:50:58.7Z and 07:50:59.0Z — 84 seconds after those keys refused 429 at 07:49:35Z. That boot ended `probed 16 / ready 5`. + +What is **not** measured: whether the ten unspent probes would have found a ready route *inside* the burst itself. No artifact answers it, because nothing records how long a refusal lasts — hence `retry_after_s` below. The pre-`#1949` walk failed similar windows for a different reason (`.github` runs 34006939646 / 34008191123 / 34008575125, 04:24–05:11Z: the same six 429s, then six gemma 404s, `ready 0` at `probed 12`), so the ban is not a regression this amendment invents; it is the ban meeting a 24-candidate list whose tail it can no longer reach. + +**Decision.** A candidate set aside by the account rule is appended to a postponed list in catalog order. Once the first pass ends with the readiness target unmet and probe budget left, the postponed candidates are probed in that order until the budget is spent; no account rule applies in that second pass. A boot that spends all sixteen probes in the first pass is unchanged; the other two classes above gain a second pass. The justification is not that the second pass rescues a burst — that is unmeasured — but that ending a walk under target with probe budget in hand is indefensible when the catalog's tail is where the ready routes live. Constraint 2 holds unchanged: at most sixteen probes per stage, and a silent second-pass probe is bounded by that count, not by a clock (ADR-0003 admits no time rule here). + +**Cost.** The second pass spends probes the walk used to abandon, so it lengthens the boot it rescues and the boot it does not. A refused probe costs about 120 ms. A **silent** one costs up to the full 90 s receive timeout (`#1661` run 34008191123, both NVIDIA keys' `deepseek-v4-pro` probes at 90.06 s and 90.10 s), and the postponed tail is full of them: `google/gemma-4-31b-it` answered `TimeoutError` in 15 of the 19 probes that reached it across these artifacts. The measured burst is therefore not a 1.2-second case — replaying 34016207820's catalog, its second pass would reach both `gemma-4-31b-it` entries, so about 3 minutes — and the worst case is 10 × 90 s ≈ **15 minutes** added to a boot that will still fail, taking a dead window from about 4 minutes to about 19 and holding the runner slot for it. + +**The two-stage path costs more than the free pool's figure.** Whenever a stage lists no more candidates than the probe budget — which is exactly the auto split, 16 free primary and 8 priced fallback — the account rule now saves nothing there, because the second pass re-probes everything it set aside. Measured on a two-account, all-429 auto run: `origin/main` sends 8 requests (4 primary, 4 priced), this design sends 24 (16 primary, 8 priced). The priced stage spends paid credit, so it doubles from 4 probes to 8 in a rate-limited hour. That is accepted for the same reason as the free pool — the priced stage only runs after every free route rejected, and stopping it half-probed is the same defect one layer down — but it is a real, stated cost, not a side effect. + +Two things are deliberately **not** traded away. The second pass never draws on the shared escalation budget (`REVIEW_PREFLIGHT_MAX_ESCALATIONS`, one counter for the whole run, carried into the priced stage by `#1458`): a postponed candidate that answers with the budget-too-small signature is rejected as `escalation_reserved_for_first_pass` rather than escalating, because otherwise candidates the previous design never probed would take escalations from the priced stage that had them, and a two-stage run measurably stops serving a route it used to serve. + +That competes directly with the org's 60-job ceiling work, and `#1949`'s measured benefit ("a dead window fails closed in about 4 minutes and returns the slot") is partly traded back for the chance to reach the catalog tail. It stays inside the probe budget this ADR bounds, `postponed_probed_count` plus the provisioning step's duration make the trade visible per boot, and `REVIEW_PREFLIGHT_MAX_PROBES` is the lever if the census says the exchange is bad. + +The report adds `postponed_probed_count`; `skipped_count` now means "postponed and never reached", and `candidate_count − probed_count − skipped_count` keeps its meaning. A refused probe additionally records `retry_after_s` when the response carried a whole-seconds `Retry-After` header (the HTTP-date form and out-of-range values record nothing). Nothing waits on that value; it exists so the next census can answer the question this amendment could not. + +**Discriminator.** `postponed_probed_count > 0` marks any boot that reached a second pass, which includes the `12 / 12 / 3` class as well as the burst class. To isolate the all-429 class, read the first `probed_count − postponed_probed_count` rows of `routes` (they are in probe order) and require every one to carry `http_status` 429. The next census asks (a) whether such boots end with `ready_count ≥ 1`, (b) what fraction of 429 rows carry `retry_after_s` and how long the refusals claim to last, (c) whether the healthy-minute figures (`ready 5–6`) are unchanged, and (d) the provisioning step's duration on those boots, so the benefit in (a) and the cost above are read from one table. If (a) is consistently 0 **and** (b) shows providers publishing a usable delay, the follow-up is to spend the second pass after that delay rather than immediately — a decision this ADR deliberately leaves to that data. `#1948`'s shared rate ledger remains the lever above all of it. + +## 2026-09-27 amendment — concurrent readiness (Proposed) + +### Context + +The 90-second probe-duration examples above predate ADR-0003's 2026-09-13 +runtime pin update. They are historical measurements, not current wall-time +bounds. At pin `767e67fbc6b881a452761f32abb69b9971b9b03b`, model inference +has no configured deadline. In fast-mlsirm run `36237188327`, retained artifact +`10919666896` shows discovery completed and several serial probes finished, +then `nvidia_nim` `meta/llama-3.2-90b-vision-instruct` began at +2026-09-26T19:06:49.173Z without a later outcome before hosted cancellation +at 2026-09-27T01:00:56.653Z. Health readiness and scanning were never reached. + +### Decision + +In the shared review startup, facing a pending provider probe that prevents +later candidates from being validated, we use concurrent validation with the +existing total probe budget, in order to reach the same eight-ready target +without classifying slow inference as failure, accepting more simultaneous +provider traffic within the unchanged request-count budget. + +The shared launcher uses the existing per-route validator and payload. It +processes available completions before scheduling more candidates, postpones +future candidates after observed consecutive account 429s, and spends at most +16 base probes per stage and four escalations per run (shared across primary +and fallback). The same probe budget bounds outstanding calls; no new numeric +limit is introduced. Already outstanding calls cannot be retroactively +postponed when another call reports 429. Only completed validated routes and +explicitly retryable responses enter the serving pool. Pending rows are +recorded separately, never rejected or admitted. The snapshot seals further +escalations; pending base calls may complete but cannot spend another retry. +No model call is cancelled when the readiness target is reached. Their threads +remain within the sidecar lifecycle and end when that process is explicitly +terminated or its host ends. The priced fallback still begins only after the +primary stage terminates with no ready route. + +### Consequences + +A pending probe no longer serializes all later candidates. The ready target, +free/ZDR selection, validation, and global request budgets remain intact. +A pool without enough responding routes can still wait indefinitely; this +change makes no inference deadline or hosted-capacity guarantee. Simultaneous +traffic can expose provider capacity limits sooner. Readiness membership follows +completion order, while serving priority and evidence rows retain catalog +scheduling order. The +snapshot may contain pending calls that subsequently finish; it is startup +admission evidence, not a final verdict on every candidate. + +### Alternatives considered + +- A fixed inference timeout conflicts with ADR-0003 and was rejected. +- Lowering the eight-ready target weakens the intended validated pool and was + rejected. +- Admitting unprobed candidates removes provider validation and was rejected. +- Eight outstanding calls recreate the same obstruction when eight pending + probes precede eight healthy ones; the regression oracle demonstrated this, + so the existing total probe budget also bounds outstanding calls. +- Runner cancellation discards valid current-head work and does not repair + startup scheduling. + +### Verification + +Event-controlled tests keep one or eight probes pending while eight later +routes become ready. The scheduler must return before the test releases those +calls. Separate checks cover all-429 failure, global escalation budget, +primary/fallback ordering, deferred-route admission, and unexpected worker +faults. Hosted acceptance is required; this amendment is not a claim that the +repair has been deployed. + +Implementation uses only the standard library's [Thread and Lock contracts](https://docs.python.org/3/library/threading.html) +and [synchronized Queue](https://docs.python.org/3/library/queue.html). Pending +probe threads deliberately share the sidecar process lifecycle; interpreter +shutdown is not a resumable-provider guarantee. diff --git a/docs/adr/0030-ci-centralization-scope-given-plan-ceiling.md b/docs/adr/0030-ci-centralization-scope-given-plan-ceiling.md new file mode 100644 index 0000000000..2dbe8a7070 --- /dev/null +++ b/docs/adr/0030-ci-centralization-scope-given-plan-ceiling.md @@ -0,0 +1,82 @@ +# 0030. CI centralization: what it can and cannot fix, given the plan-level concurrency ceiling + +## Status + +Proposed (informational/scoping ADR — no workflow behavior changes yet) + +## Context + +`docs/ci-baseline-20260916.md` measured 24h of Actions runs across all 79 org repos (9,353 runs, +400 (repo, workflow, trigger) groups) to quantify PR queue stalls, starting from the observed +symptom of `fast-mlsirm` PRs sitting with 20+ checks `QUEUED` and 0 completed for extended periods. + +That baseline reproduces, live and two weeks later, the exact signature already recorded in +[`docs/doctoring/actions-plan-concurrency-ceiling-20260903.md`](../doctoring/actions-plan-concurrency-ceiling-20260903.md): +single-digit `in_progress` runs against triple/quadruple-digit `queued` runs, org-wide +(`fast-mlsirm`: 8 vs 220; `.github`: 6 vs 220 at measurement time). That record's root-cause finding — +a plan-level concurrent-job ceiling (user-reported 58-60/60 at the time), not workflow-file +duplication — is not something a workflow change in this repository can lift. It also explicitly +warns that a large workflow-consolidation project undertaken on the theory that it fixes the queue +"would be solving the wrong layer of the problem, at real cost." + +This ADR exists so the next PR against this effort starts from that constraint instead of +re-discovering it, and scopes what centralization *is* still good for. + +## What GitHub's mechanisms actually do (for reference) + +- **Reusable workflows (`workflow_call`)** ([GitHub docs](https://docs.github.com/en/actions/using-workflows/reusing-workflows)): + let a thin per-repo caller invoke a workflow defined once in `.github`. Reduces file drift and the + number of independent `.yml` files to keep security-equivalent across repos. Does **not** change + how many jobs the org can run concurrently — each `workflow_call` job still consumes one slot + against the same org-wide ceiling as any other job. +- **Concurrency groups with `cancel-in-progress`** ([GitHub docs](https://docs.github.com/en/actions/using-jobs/using-concurrency)): + cancel a stale run when a newer one starts in the same group. This *does* directly reduce + concurrent-job pressure, by retiring superseded work instead of letting it sit `queued` (or worse, + `in_progress`) behind newer pushes. This is the one lever here that actually shrinks the number of + jobs competing for the ceiling, not just the number of files. +- **Organization required-workflow rulesets** ([GitHub docs](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-rulesets/about-rulesets#require-workflows-to-pass-before-merging)): + run one canonical workflow file's job graph in every target repo's context; already how this repo + centralizes `opencode-review`, `strix`, `admit-current-head`, etc. Centralizes *maintenance*, not + *capacity*. +- **Usage limits** ([GitHub docs](https://docs.github.com/en/actions/administering-github-actions/usage-limits-billing-and-administration#usage-limits)): + the concurrent-job ceiling is a plan/billing property (GitHub Free/Team/Enterprise tiers set + different concurrent-job maximums), not something exposed or changeable via the Actions or + rulesets APIs. Confirmed via this session's own `gh api` exploration: no REST or GraphQL field + surfaces the org's current ceiling; it's Settings → Billing → Plans and usage only. + +## Decision + +1. **Do not scope further work here as "fix the queue by centralizing more workflows."** The baseline + shows that lever is largely already pulled (org-required workflows already cover + opencode-review/strix/noema/sast/codeql/secrets; concurrency groups already exist on every + event-triggered required workflow that isn't a reusable `workflow_call` target or an + `issue_comment`/`schedule` trigger — see baseline doc for the file-by-file check). +2. **The ceiling itself is an org-owner billing decision** (raise plan tier, buy additional included + concurrency, or provision runners with a separate capacity pool), per the 2026-09-03 doctoring + record. This ADR does not propose a workflow change to address it, because none exists. +3. **The one remaining code-level lever that reduces total *concurrent job count per PR head*, and + therefore genuinely helps under a fixed ceiling, is folding required checks that are + `needs:`-serial or logically redundant into fewer jobs/runners** — the pattern already used for + `sast-semgrep.yml` (2026-09-13 fold, see baseline doc and + `docs/product-technical-gap-baseline.md`) and for the `opencode-review.yml` chain-depth cut + (`#1910`). Any future PR in this space should look for the same fold opportunity rather than + proposing new centralization for its own sake. +4. **`bandscope`'s 89% cancellation rate across all 7 of its required workflows** (91 runs each, + ~80 cancelled, in the 24h baseline) is the one concrete duplication/thrash signal this baseline + surfaced and is not yet explained — worth a scoped follow-up investigation (what's re-triggering + pushes that often on that repo) before proposing a fix, since the cause is unconfirmed. + +## Consequences + +- No PR follows directly from this ADR: the smallest safe next step this session could find + (add missing `concurrency:` blocks) was already done org-wide, and consolidating further reusable + workflows would add maintenance surface without moving the KPI this task defined (p95 queue time, + jobs per PR head) — that KPI is dominated by the plan ceiling, not file count. +- The KPI itself needs a caveat added wherever it's used: run-level `created_at` → `run_started_at` + queue time is close to 0 for nearly every workflow in this org (see baseline doc) because a run's + status flips to `in_progress` as soon as one job starts, even while other jobs in the same run sit + `queued`. Any future measurement of this KPI should use job- or check-suite-level `started_at`, + not run-level, or it will systematically under-report the stall. +- Escalating the plan-ceiling question to the org owner (or confirming it's already been acted on + since 2026-09-03) is the highest-leverage next action, and is outside what a repository-scoped PR + can do. diff --git a/docs/adr/0031-noema-transport-capacity-redispatch.md b/docs/adr/0031-noema-transport-capacity-redispatch.md new file mode 100644 index 0000000000..793fb4e8c0 --- /dev/null +++ b/docs/adr/0031-noema-transport-capacity-redispatch.md @@ -0,0 +1,96 @@ +# ADR-0031: Noema transport-capacity failures schedule a bounded continuation re-dispatch + +- **Status:** Accepted +- **Date:** 2026-09-17 +- **Scope:** `scripts/ci/noema_review_gate.py`, `.github/actions/noema-review/two_phase.py`, `.github/workflows/noema-review.yml` +- **Issue:** ContextualWisdomLab/.github#2165 +- **Does not amend:** ADR-0003 (gateway owns provider failover), ADR-0005 (no model-path wall-clock timeout; superseded attempt ceilings stay historical) + +## Problem + +`Required Noema Review` is an organization required check. The Noema caller issues +exactly one gateway request and delegates provider discovery, repair, and failover to +contextual-orchestrator (ADR-0003). When every free-pool route is transiently +unavailable or rate-limited, the gateway returns a terminal HTTP 429 or 5xx after it has +already exhausted its own failover chain. The caller then fails closed with +`NoemaTransportError` and `caller attempts=1`. That is correct for review integrity — +the check must not be skipped — but it leaves consumer PRs permanently BLOCKED until a +human re-dispatches into a healthier window, even when the PR's own code checks are green +(#2165 evidence on four-pillars and fast-mlsirm). + +Holding the same job open to retry the model call would occupy a scarce Actions runner +for provider capacity that the gateway already reported as exhausted. Product goal +directive §8 and ADR-0005 forbid converting elapsed inference time into a local +model-failure verdict or restoring fixed model-path attempt ceilings. + +## Decision + +1. **Classify, do not re-interpret.** HTTP 429 and 5xx from the already-failed-over + gateway are typed as `provider_capacity_unavailable`. Malformed model output, 4xx + other than 429, and local validation failures stay terminal review failures. The + required check still fails; review is never skipped or auto-approved. +2. **One gateway request per job stays the contract.** `call_llm` does not gain a caller-side retry loop. Provider failover remains contextual-orchestrator's job. +3. **Continuation re-dispatch is the recovery lever.** When the failure is + `provider_capacity_unavailable` and the run's `transport_retry_attempt` is below the + bound (`MAX_TRANSPORT_REDISPATCH_ATTEMPTS = 2`), the workflow schedules exactly one + same-head `repository_dispatch` (`noema-review`) with an incremented attempt counter + after a short jitter delay. The new job is a fresh admission/continuation; the failed + job remains failed evidence for that attempt. A malformed supplied counter exhausts + the budget rather than starting it over. +4. **Jitter is post-failure scheduling, not a model timeout.** Prefer a whole-seconds + `Retry-After` from the gateway error when present and in `[1, 300]`. Otherwise use a + deterministic delay in `[60, 180]` seconds derived from the exact head SHA and attempt + number so concurrent capacity failures do not stampede the free pool. That sleep runs + only in the post-failure scheduling step and never wraps `opener.open`. +5. **Surface gateway attempt evidence.** When the error envelope carries an `attempts` + list (orchestrator failover telemetry), the public Actions warning and exception text + include `provider_attempt_count=` alongside the existing last-attempt fields so + capacity incidents are distinguishable from code-review verdicts without dumping raw + provider bodies. +6. **Isolate dispatch authority.** The failed review job exports only typed retry + evidence and retains read-only repository contents access. A dependent job alone + receives repository-scoped Contents write through `GITHUB_TOKEN`; it has no + checkout or model inputs, and rechecks the live repository, PR head, and base + before dispatch. The reviewer App token remains limited to Contents read. + +## Consequences + +- A capacity storm produces at most three Noema jobs per head (initial + two automatic + re-dispatches) before failing closed for operator intervention. +- Runner occupancy for a dead pool is one failed inference plus ≤180 s of scheduling + jitter, not another multi-hour in-job wait on the same slot. +- Gateway routing bugs (for example a non-transient 400 on one ready route while others + remain unused) are **out of scope** here; they belong to contextual-orchestrator route + selection, not this caller (#2165 consumer notes on vision-model 400s). +- Private-target ZDR pool exhaustion (#2148) shares the classification and attempt + evidence, but does not widen this ADR's re-dispatch bound. + +## Alternatives considered + +- **In-job retry of `call_llm`.** Rejected: duplicates gateway failover, burns the runner + against an exhausted pool, and conflicts with the single-request contract tests. +- **Mark the required check neutral/success on capacity loss.** Rejected: weakens + "review cannot be skipped." +- **Unbounded re-dispatch.** Rejected: amplifies 429 pressure (#2165 filing notes). +- **Rely only on the merge scheduler's next tick.** Deferred as a complementary path; + it does not give the Noema workflow its own bounded, evidence-typed recovery when the + scheduler is not looking at that head. + +## Proposed Strix startup extension — 2026-09-27 + +- **Status:** Proposed; deployment and independent review remain unverified. +- **Context:** Strix all-429 preflight fails before its model gate can retry; + late-life-anxiety-reanalysis #257/#269 have exact-job evidence of this path. +- **Decision:** Reuse the bounded classifier in a separate post-failure dispatch + job, with live repository/head/base/ref/Ready validation and the same two-attempt + ceiling. Retain the failed scan and status; never infer approval from recovery. +- **Consequences:** Automatic recovery can enter a healthier provider window and + uses up to two additional scan admissions. Persistent capacity failure still + requires operator action. Consumer execution needs the existing central + dispatch credential; its absence remains visible and fail-closed. +- **Alternatives:** Model-gate retries cannot run before successful startup; + in-job startup loops hold a scan runner; unbounded dispatch amplifies capacity + pressure; neutral/success status would weaken the required security gate. + +See `../doctoring/strix-preflight-capacity-continuation-20260927.md` for evidence +and the local-versus-hosted verification boundary. diff --git a/docs/adr/0034-dependency-review-fail-closed-permission-envelope.md b/docs/adr/0034-dependency-review-fail-closed-permission-envelope.md new file mode 100644 index 0000000000..75ec5f9cd2 --- /dev/null +++ b/docs/adr/0034-dependency-review-fail-closed-permission-envelope.md @@ -0,0 +1,72 @@ +# ADR-0034: Fail closed on ambiguous Dependency Review authority and preserve caller permissions + +- **Status:** Proposed +- **Date:** 2026-09-02 +- **Scope:** `.github/workflows/dependency-review.yml`, its thin product callers, and the reusable-workflow security contract +- **Supersedes:** ADR-0024 only where ADR-0024 treated HTTP 403/404 as confirmed Dependency Graph unavailability or showed callers without an explicit permission envelope + +## Problem + +The protected-main consolidation from #1724 exposed two independent security-contract defects. + +First, the reusable workflow classified Dependency Graph compare HTTP 403/404 as `available=false` and therefore skipped the blocking Dependency Review action. Those responses are not authoritative proof that the feature is unavailable: they can also be authorization or policy failures. Turning an ambiguous authorization-shaped response into success silently removes a security gate. + +Second, the migration examples and thin callers omitted the permission envelope that the original repository-local workflows carried. GitHub reusable workflows cannot elevate `GITHUB_TOKEN` permissions through the call chain. A called workflow may maintain or reduce permissions granted by the caller, but it cannot manufacture `pull-requests: read` when the caller did not grant it. The result is a workflow-level `startup_failure` before any job is created. + +## Constraints + +1. Dependency Review remains a distinct hard gate; OSV-Scanner, Scorecard, or another scanner cannot substitute for an ambiguous Dependency Review failure. +2. The called workflow needs only `contents: read` and `pull-requests: read`; no write permission is introduced. +3. Product callers remain thin and repository-owned. They keep repository-specific trigger, severity, allowlist, and `continue_on_error` policy. +4. Consumers pin the reusable workflow to an immutable protected-main commit after this proposal is merged. `@main`, branch URLs, and unmerged PR heads are not production authority. +5. Non-`pull_request` invocations may skip because they lack an exact PR base/head pair; pull requests fail closed unless the comparison endpoint returns HTTP 200. + +## Considered alternatives + +### Treat 403/404 as feature unavailable + +Rejected. The status code alone cannot distinguish a genuinely unavailable Dependency Graph from denied authorization/policy. A false negative here converts a required security control into a warning. + +### Infer support from repository visibility or GHAS assumptions + +Rejected. Visibility is not a capability proof and was already the weaker design ADR-0024 replaced. + +### Rely on the called workflow's `permissions:` block + +Rejected as insufficient. GitHub does not let a reusable workflow elevate permissions beyond the caller's grant. The called workflow still declares its least-privilege ceiling, but each thin caller must explicitly grant the same read scopes. + +### Grant broader token permissions globally + +Rejected. It increases blast radius and hides a caller-contract defect instead of repairing it. + +## Decision + +1. For `pull_request`, the Dependency Graph compare preflight sets `available=true` only when curl exits zero and the endpoint returns HTTP 200. Every transport failure or other HTTP status is emitted with an error and terminates the job nonzero. +2. Remove the pull-request "Dependency graph unavailable" success path. No alternate scanner is described as replacement authority. +3. Keep the called workflow at `contents: read` + `pull-requests: read` and require every thin caller to declare at least those same scopes explicitly. +4. Make the executable central contract fail when the canonical caller example omits either required scope. +5. Replace the mutable `@main` caller example with an immutable `` placeholder. After merge, consumers pin the resulting protected-main SHA. + +## Exact evidence + +- #1725 first RED: `cb07b8bb28ef9d3a147cc966a0c70654d132da1d`; first production repair: `31f60e532e135008cabd09fcddd46a53062b0ea0`. +- Permission-envelope RED: `ee0f1ce544965772775b590050e40476df4ea8f6`; it changes only the contract and requires the missing caller permission example. +- Permission-envelope production repair: `ca3bdbd210de988ccd31f7fb96d3a97adfdb9bff`. +- `newsdom-api#784@1623977e6c37c78cb1a94a7a48c48f6d02cac86c`: run `33622976911`, `startup_failure`, zero jobs, reusable workflow immutably resolved to `.github@0bcd22d8bb07650aafb0a8f116e4c2bbb8744f03`. +- `mightyETL#330@65efdf7b4064df5b9811c0403defb707e6efbc02`: run `33623035969`, `startup_failure`, zero jobs. +- Consumer permission repairs then produced materialized current-head runs: newsdom-api `9a798d5ac7b9b295a1accb2327fc76611352290f` run `33623818000`; mightyETL `4576f863ede9fca0673d6cce5ae8a4093246f5ab` run `33623854807`; scopeweave `db8b8ed6d36a6dc6cc1d07255a7a9a86bc88bf4f` run `33623761776`; Argos #557 `ee4c5dd326977407435b0f2425fdecebc34a810f` run `33623867278`. + +Hosted exact-current-head Checks and independent review remain required before this ADR may become Accepted. + +## Consequences and follow-up + +- A missing or denied Dependency Graph comparison is visible as a blocking failure instead of silent coverage loss. +- Caller permission omissions become an executable contract defect rather than an undocumented deployment prerequisite. +- The central workflow still cannot repair a consumer's omitted permissions by itself; each consumer must carry the explicit read-only envelope and later bump its immutable reusable-workflow pin to the protected-main SHA that contains this decision. +- #1643's canary run remains independent diagnostic evidence for the required Security Scan path; its run/check result is not transferred to this gate. #1725 nevertheless integrates the durable immutable-identity validation source delta into the canonical reusable Dependency Review owner and verifies that integrated behavior with its own tests and exact-head Checks. + +## References + +GitHub. (n.d.). *Reusing workflow configurations*. GitHub Docs. https://docs.github.com/actions/using-workflows/reusing-workflows + +GitHub. (n.d.). *Use GITHUB_TOKEN for authentication in workflows*. GitHub Docs. https://docs.github.com/actions/security-guides/automatic-token-authentication diff --git a/docs/adr/2026-10-11-public-personal-auto-authorization.md b/docs/adr/2026-10-11-public-personal-auto-authorization.md new file mode 100644 index 0000000000..54831d8757 --- /dev/null +++ b/docs/adr/2026-10-11-public-personal-auto-authorization.md @@ -0,0 +1,22 @@ +# Owner authorization for public personal auto review + +## Status + +Accepted on 2026-10-13 after ContextualWisdomLab/.github#2607 merged. The maintainer's requested endpoint and model are explicit session instructions. Runtime App review and approval remain separate acceptance evidence. + +## Context and decision drivers + +The maintainer requested `https://litellm.poinnetworks.net` with model `auto`. Its scoped inference key is configured, but supplier cost metadata is absent. Authentication does not establish zero cost or retention. The existing admission helper consequently blocks the requested public route. + +## Considered options + +- Require zero-cost evidence for every personal route. +- Admit the explicitly authorized public route while retaining private supplier gates. + +## Decision outcome + +Use the second option. A maintainer-controlled `PERSONAL_REVIEW_PUBLIC_AUTO_AUTHORIZED=true` may admit only a definitely public target. Its default is false. It is routing authorization, not PR approval or supplier attestation. Private and unknown targets cannot use this exception; private routing still requires zero-cost and zero-retention attestations. The fixed endpoint, literal wire model `auto`, read-only review permissions, source-bound verdict validation, independent reviewer identity and required checks remain unchanged. + +## Consequences + +Public review can use the requested route without a false cost declaration. Supplier cost remains unknown. Private disclosure and protected merge are still fail-closed; a configured key alone proves neither inference nor approval. diff --git a/docs/adr/adr-0032-release-gate-exact-set-fanout.md b/docs/adr/adr-0032-release-gate-exact-set-fanout.md new file mode 100644 index 0000000000..331b669203 --- /dev/null +++ b/docs/adr/adr-0032-release-gate-exact-set-fanout.md @@ -0,0 +1,149 @@ +--- +title: "ADR-0032: Bind release dependency verdicts to the complete artifact set" +status: "Proposed" +date: "2026-09-26" +authors: "CWL release gate maintainers" +tags: ["architecture", "decision", "release", "supply-chain"] +supersedes: "" +superseded_by: "" +--- + +# ADR-0032: Bind release dependency verdicts to the complete artifact set + +## Status + +**Proposed**. No release HOLD may be removed on the strength of this record. The +implementation and exact-head hosted evidence are still required by #2342. + +## Context + +The reusable gate in #2347 scans all resolved dependencies sequentially in one +360-minute job and seals one wheel and one sdist. ContextualWisdomLab/fast-mlsirm#2135 builds twelve +wheels and one sdist. Its admission job currently exits with an unconditional HOLD +because the existing handoff does not authenticate a same-run full licence and +Strix verdict or the complete release artifact set. A successful two-file seal +cannot establish a verdict for the other eleven wheels. + +GitHub Actions reusable-workflow outputs from a matrix contain the value from +the last successful completing call that set a value. That output cannot +represent a complete verdict set. A matrix job's aggregate result can prove +that every invocation succeeded, but still does not identify which artifacts +each invocation examined. An uploaded JSON field claiming `PASS` is likewise +not a trusted job conclusion. + +## Decision + +- **DEC-001**: The protected release workflow uses its existing + `reproducibility-record` job to produce one immutable, same-run manifest of + all thirteen publishable fast-mlsirm distributions. Each row carries target, + filename, file SHA-256, upload artifact ID, name, and archive digest. The + trusted job enforces the expected twelve-wheel-plus-one-sdist set before it + passes the manifest's ID and digest to the central gate. +- **DEC-002**: The central reusable gate takes that manifest by immutable + artifact ID and digest, checks its run ID and attempt against the current + invocation, downloads every referenced artifact by ID, and recomputes every + file digest. It derives the dependency set and synthetic fixtures from the + exact release source and collected build evidence before any Strix credential + exists. Missing, duplicate, extra, expired, wrong-run, wrong-attempt, or + wrong-digest evidence fails the gate. +- **DEC-003**: Strix runs in a dynamic matrix with exactly one dependency + fixture per job. Each job uses the pinned trusted helper, the same source SHA, + and an isolated fixture; it uploads a uniquely named immutable binding that + includes dependency identity, fixture digest, source SHA, run ID, and attempt. + The matrix fan-out has a reviewable concurrency bound and refuses a fixture + set above GitHub Actions' 256-job matrix limit. Elapsed model time is not + converted into a passing or failing security verdict. +- **DEC-004**: A downstream collector runs only when the licence stage and + every matrix job succeeded. It compares the exact expected dependency keys + with the binding-artifact keys, checks every binding and digest, and produces + one full-set verdict artifact with its own ID and digest. It does not aggregate + matrix job outputs and it cannot turn a failed or skipped scan into success. +- **DEC-005**: fast-mlsirm admission depends on the pinned central reusable + gate's job result in the same workflow run. It verifies the returned verdict + artifact by ID and digest, source SHA, run ID and attempt, and equality of all + thirteen distribution rows to its locally verified manifest. It also + requires a trusted, target-specific closure inventory for runtime, build, + dev, optional, native, and bundled scopes. An `UNKNOWN` scope or a + declaration identity without resolved dependency evidence refuses + admission. Only then may it write `admitted-manifest.tsv`; the existing tag + and publish jobs remain downstream of admission. +- **DEC-006**: The unconditional admission HOLD remains until hosted RED and + GREEN runs on exact current heads prove this entire path, including a real + Strix binding. Unit fixtures alone do not authorize its removal. + +## Consequences + +### Positive + +- **POS-001**: The release verdict covers the bytes of every distribution the + publish job can consume, including each wheel target. +- **POS-002**: An incomplete matrix, forged `PASS` document, or artifact from + another run cannot satisfy the collector and admission contracts. +- **POS-003**: Each Strix scan has its own job lifetime, while the matrix's + concurrency bound limits organization runner occupancy. + +### Negative + +- **NEG-001**: Fan-out and exact-set collection add jobs, artifacts, and + validation code to a security-sensitive workflow. +- **NEG-002**: The full closure may occupy the Actions queue for many hours; + queued jobs are pending evidence, not a passing verdict. +- **NEG-003**: The existing six-member, seventeen-output wheel/sdist + attestation contract does not itself cover thirteen distributions. The + full-set verdict must be verified separately until a reviewed generalized + attestation contract replaces it. +- **NEG-004**: Source declaration hashes and one Ubuntu dependency capture do + not establish the native and bundled closure of Linux, macOS, and Windows + wheel build environments. Per-target collection and verification add work + before the current scope HOLD can be removed. + +## Alternatives Considered + +### One sequential Strix job + +- **ALT-001**: Keep the current single job and increase its timeout. +- **ALT-002**: Rejected because the job is already at GitHub's 360-minute + ceiling, while one dependency's model path may take more than two hours. + +### One reusable gate invocation per wheel + +- **ALT-003**: Call the existing two-file gate twelve times, pairing each wheel + with the same sdist. +- **ALT-004**: Rejected as the final design because it repeats the entire + dependency scan twelve times. A caller can use the matrix job result and + exact same-run artifact set without relying on its last-wins outputs, so + this remains a possible intermediate wiring step while the release HOLD + stays in force. + +### Trust a seal or report by its filename + +- **ALT-005**: Download a named artifact and accept its declared `PASS` field. +- **ALT-006**: Rejected because a name and a payload do not prove that the + pinned gate succeeded in this run on these thirteen bytes. + +## Implementation Notes + +- **IMP-001**: First add RED cases for missing, duplicate, extra, stale-run, + stale-attempt, wrong-source, altered archive, altered distribution, and + omitted matrix binding. Include a scope record that remains `UNKNOWN` or + substitutes declarations for a resolved platform inventory. Each must + refuse before admission or publication. +- **IMP-002**: Keep source validation, pre-credential licence refusal, and + immutable build-artifact intake from #2347. Preserve diagnostic artifacts on + failures without an `always()` path that could allow downstream release jobs. +- **IMP-003**: The hosted GREEN case must exercise real capture, Strix, + collection, sealing, and fast-mlsirm admission on an exact head. Record run + and job IDs, all thirteen file digests, and the pinned central workflow SHA. +- **IMP-004**: Keep #2135 draft and release HOLD until #2342 acceptance and + both repositories' exact-head required checks are terminal green. Merge, + tag, and PyPI publication are separate later decisions. +- **IMP-005**: Preserve the existing unconditional refusal in + `verify_scope_identities` as well as the workflow's final admission HOLD + until the collector verifies all six scopes for every wheel target and the + sdist. Removing only the workflow HOLD cannot make admission succeed. + +## References + +- **REF-001**: ContextualWisdomLab/.github#2342 and #2347; ContextualWisdomLab/fast-mlsirm#2135. +- **REF-002**: [GitHub reusable workflow matrix output behavior](https://docs.github.com/en/actions/how-tos/reuse-automations/reuse-workflows#using-a-matrix-strategy-with-a-reusable-workflow). +- **REF-003**: [GitHub Actions matrix output rules](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idoutputs) and [immutable upload artifact IDs and digests](https://github.com/actions/upload-artifact/blob/main/README.md#outputs). diff --git a/docs/adr/adr-0033-owned-codeql-status-and-settlement-authority.md b/docs/adr/adr-0033-owned-codeql-status-and-settlement-authority.md new file mode 100644 index 0000000000..70e554756e --- /dev/null +++ b/docs/adr/adr-0033-owned-codeql-status-and-settlement-authority.md @@ -0,0 +1,50 @@ +--- +title: "ADR-0033: Owned CodeQL status and settlement authority" +status: Proposed +date: "2026-09-27" +authors: "Codex" +tags: [architecture, ci, security] +supersedes: "" +superseded_by: "" +--- + +# ADR-0033: Owned CodeQL status and settlement authority + +## Status + +Proposed. Requires #2405 complete terminal-proof foundation, owned-app installation permission acceptance, and an unchanged-head live canary before protected deployment is accepted. + +## Context + +DiskSage #473 dispatch 36305375849 encountered cross-repository HTTP403 during status publication and required-run settlement. Public app and organization installation metadata confirm opencode-agent is owned by anomalyco and has Actions/read and statuses/read. A consumer cannot change the external owner's app permissions. The organization-owned cwl-noema-review (app4291520) is already installed on all repositories with security_events/read; its private-key organization secret is available to central workflows. The existing target-scoped analysis-read token remains the GHAS reader. + +## Decision + +Use the existing owned Noema app for separate target-repository tokens: statuses/write solely for authenticated CodeQL receipt publication, and Actions/write solely for exact required-run settlement. Keep security_events/read in its existing separate read token. The installation must authorize those two write permissions; credentials cannot mint permissions the installation lacks. Optional mint failures retain existing fallback credentials and never create validation success. + +The owned status writer must publish as cwl-noema-review or cwl-noema-review[bot]; another returned creator is rejected. No arbitrary actor is added. Complete base/head/run/source/workflow receipt and terminal SARIF/GHAS proof from #2405 remain prerequisites; do not deploy the new receiver trust before that foundation. Preserve exact-run identity, supersession, rerun budget, SARIF preservation and Medium+ gates. + +## Consequences + +- POS-001: Removes dependence on an external app owner's unavailable write grants. +- POS-002: Reuses an installed app and keeps analysis, publication and lifecycle tokens separate and target scoped. +- NEG-001: Expands the owned installation's capabilities and therefore the impact of its private-key compromise. Restrict key access and retain the trusted default-branch workflow boundary; never export keys into reviewed source or logs. +- NEG-002: Needs owner-authenticated app settings and installation acceptance plus live verification. Unit contracts do not prove deployment or permission availability. + +## Alternatives Considered + +- ALT-001: Change the external OpenCode app. Rejected because anomalyco owns that app and its current grants cannot satisfy writes. +- ALT-002: Transfer a user's CLI token into CI. Rejected: broad personal credentials are unnecessary and not copied. +- ALT-003: Bypass identity/receipt checks or synthesize success. Rejected because that removes the security proof. +- ALT-004: Reuse the analysis-read token for mutations. Rejected because its read-only contract must remain unchanged. + +## Implementation Notes + +- IMP-001: Pin the existing create-github-app-token action and request exactly one target repository and one write permission per writer token. +- IMP-002: Grant Actions/write and Commit statuses/write to the owned app and accept the installation update; do not add Code Scanning writes. +- IMP-003: Accept deployment only after real current-head scan, GHAS identity, receipt creator, one exact run-wide wake and terminal required verdict are verified. References: ContextualWisdomLab/.github#2276, #1929 and #2405. + +## References + +GitHub. (n.d.). *Create GitHub App token*. https://github.com/actions/create-github-app-token +GitHub. (n.d.). *Choosing permissions for a GitHub App*. https://docs.github.com/en/apps/creating-github-apps/setting-up-a-github-app/choosing-permissions-for-a-github-app diff --git a/docs/ci-baseline-20260916.csv b/docs/ci-baseline-20260916.csv new file mode 100644 index 0000000000..9cee0a61ac --- /dev/null +++ b/docs/ci-baseline-20260916.csv @@ -0,0 +1,401 @@ +repo,workflow,event,runs,still_queued_now,cancelled,startup_failure,queue_p50_s,queue_p95_s,queue_max_s,dur_p50_s,dur_p95_s +LineageWeave,.github/workflows/tests.yml,pull_request,259,0,132,0,0.0,0.0,0.0,217.5,19032.2 +OriginWeave,.github/workflows/ci.yml,pull_request,189,0,50,0,0.0,0.0,0.0,7.0,2939.9 +.github,.github/workflows/opencode-review-dispatch.yml,repository_dispatch,137,0,4,0,0.0,0.0,0.0,15059.0,64003.4 +.github,.github/workflows/codeql-scan-dispatch.yml,repository_dispatch,128,0,0,0,0.0,0.0,0.0,30598.0,34865.0 +pingora-gateway,.github/workflows/supply-chain.yml,pull_request,113,0,26,0,0.0,0.0,0.0,2.5,14268.6 +pingora-gateway,.github/workflows/ci.yml,pull_request,113,0,27,0,0.0,0.0,0.0,7.0,14920.9 +fast-mlsirm,.github/workflows/ci.yml,pull_request,103,0,35,0,0.0,0.0,0.0,16837.0,38459.0 +.github,.github/workflows/agent-mention-router.yml,issue_comment,100,0,0,0,0.0,0.0,0.0,1.0,10.0 +bandscope,.github/workflows/sast-semgrep.yml,pull_request,91,0,79,0,0.0,0.0,0.0,82.0,16524.8 +bandscope,.github/workflows/ci.yml,pull_request,91,0,80,0,0.0,0.0,0.0,53.0,6683.0 +bandscope,.github/workflows/codeql-pr.yml,pull_request,91,0,81,0,0.0,0.0,0.0,52.0,6683.0 +bandscope,.github/workflows/sbom.yml,pull_request,91,0,80,0,0.0,0.0,0.0,56.5,10902.9 +bandscope,.github/workflows/security-scan.yml,pull_request,91,0,81,0,0.0,0.0,0.0,53.0,6683.0 +bandscope,.github/workflows/build-baseline.yml,pull_request,91,0,79,0,0.0,0.0,0.0,102.0,16667.0 +bandscope,.github/workflows/opencode-review.yml,pull_request_target,91,0,81,0,0.0,0.0,0.0,54.0,6682.0 +bandscope,.github/workflows/noema-review.yml,pull_request_target,91,0,81,0,0.0,0.0,0.0,78.0,16500.8 +bandscope,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,91,0,79,0,0.0,0.0,0.0,81.5,16271.5 +bandscope,.github/workflows/strix.yml,pull_request_target,91,0,81,0,0.0,0.0,0.0,78.0,16655.3 +bandscope,dynamic/github-code-quality/codeql,dynamic,90,0,67,0,0.0,0.0,0.0,3491.0,16723.8 +fast-mlsirm,dynamic/github-code-scanning/codeql,dynamic,74,0,16,0,0.0,0.0,0.0,13077.0,18377.5 +quarantine-sandbox-runtime,.github/workflows/ci.yml,pull_request,74,0,69,0,0.0,0.0,0.0,143.0,20039.4 +disksage,.github/workflows/release.yml,pull_request,73,0,0,0,0.0,0.0,0.0,2.0,3.4 +disksage,.github/workflows/test.yml,pull_request,72,0,52,0,0.0,0.0,0.0,2253.0,14104.0 +TEPP,.github/workflows/ci.yml,pull_request,72,0,15,0,0.0,0.0,0.0,2.0,13105.0 +fast-mlsirm,.github/workflows/codeql.yml,pull_request,68,0,0,0,0.0,0.0,0.0,14445.0,18564.5 +fast-mlsirm,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,68,0,12,0,0.0,0.0,0.0,13925.0,19356.6 +fast-mlsirm,.github/workflows/strix.yml,pull_request_target,68,0,20,0,0.0,0.0,0.0,19045.0,40863.0 +fast-mlsirm,.github/workflows/noema-review.yml,pull_request_target,68,0,20,0,0.0,0.0,0.0,19162.5,31834.2 +fast-mlsirm,.github/workflows/opencode-review.yml,pull_request_target,68,0,21,0,0.0,0.0,0.0,30313.5,46277.8 +fast-mlsirm,.github/workflows/codeql-pr.yml,pull_request,67,0,22,0,0.0,0.0,0.0,31176.0,50321.3 +fast-mlsirm,.github/workflows/security-scan.yml,pull_request,67,0,17,0,0.0,0.0,0.0,25723.0,33874.0 +fast-mlsirm,.github/workflows/sast-semgrep.yml,pull_request,67,0,12,0,0.0,0.0,0.0,13808.0,19099.8 +LineageWeave,dynamic/github-code-quality/codeql,dynamic,53,0,38,0,0.0,0.0,0.0,3700.0,14349.2 +LineageWeave,.github/workflows/codeql-pr.yml,pull_request,52,0,45,0,0.0,0.0,0.0,1473.0,9029.6 +LineageWeave,.github/workflows/noema-review.yml,pull_request_target,52,0,46,0,0.0,0.0,0.0,1558.0,19357.5 +LineageWeave,.github/workflows/opencode-review.yml,pull_request_target,52,0,46,0,0.0,0.0,0.0,1558.0,19357.8 +LineageWeave,.github/workflows/sast-semgrep.yml,pull_request,52,0,45,0,0.0,0.0,0.0,1718.5,14341.4 +LineageWeave,.github/workflows/security-scan.yml,pull_request,52,0,46,0,0.0,0.0,0.0,1558.0,19360.3 +LineageWeave,.github/workflows/strix.yml,pull_request_target,52,0,47,0,0.0,0.0,0.0,1472.0,9030.3 +LineageWeave,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,52,0,45,0,0.0,0.0,0.0,1718.5,13820.0 +LineageWeave,dynamic/github-code-scanning/codeql,dynamic,52,0,38,0,0.0,0.0,0.0,3423.0,14226.6 +accounting-information-platform,.github/workflows/security-scan.yml,pull_request,43,0,42,0,0.0,0.0,0.0,2722.5,3767.3 +accounting-information-platform,.github/workflows/codeql-pr.yml,pull_request,43,0,42,0,0.0,0.0,0.0,2722.0,3767.3 +accounting-information-platform,.github/workflows/ci.yml,pull_request,43,0,42,0,0.0,0.0,0.0,2723.0,3767.3 +accounting-information-platform,.github/workflows/sast-semgrep.yml,pull_request,43,0,42,0,0.0,0.0,0.0,2722.0,3767.3 +accounting-information-platform,.github/workflows/noema-review.yml,pull_request_target,43,0,42,0,0.0,0.0,0.0,2721.5,3769.2 +accounting-information-platform,.github/workflows/strix.yml,pull_request_target,43,0,42,0,0.0,0.0,0.0,2722.0,3769.2 +accounting-information-platform,.github/workflows/opencode-review.yml,pull_request_target,43,0,42,0,0.0,0.0,0.0,2722.0,3769.2 +accounting-information-platform,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,43,0,42,0,0.0,0.0,0.0,2721.5,3768.3 +accounting-information-platform,dynamic/github-code-quality/codeql,dynamic,43,0,41,0,0.0,0.0,0.0,3378.0,3879.0 +Orgmetra,.github/workflows/foundation-ci.yml,pull_request,41,0,36,0,0.0,0.0,0.0,409.0,11994.2 +Orgmetra,.github/workflows/sast-semgrep.yml,pull_request,41,0,36,0,0.0,0.0,0.0,409.0,11704.9 +Orgmetra,.github/workflows/codeql-pr.yml,pull_request,41,0,38,0,0.0,0.0,0.0,410.0,26380.4 +Orgmetra,.github/workflows/security-scan.yml,pull_request,41,0,38,0,0.0,0.0,0.0,409.0,14796.5 +Orgmetra,.github/workflows/strix.yml,pull_request_target,41,0,39,0,0.0,0.0,0.0,410.0,14797.6 +Orgmetra,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,41,0,37,0,0.0,0.0,0.0,409.0,11904.7 +Orgmetra,.github/workflows/opencode-review.yml,pull_request_target,41,0,38,0,0.0,0.0,0.0,410.0,14797.6 +Orgmetra,.github/workflows/noema-review.yml,pull_request_target,41,0,39,0,0.0,0.0,0.0,410.0,14797.6 +Orgmetra,dynamic/github-code-quality/codeql,dynamic,41,0,32,0,0.0,0.0,0.0,6667.0,12578.6 +newsdom-api,dynamic/github-code-quality/codeql,dynamic,40,0,0,0,0.0,0.0,0.0,13123.5,18989.8 +newsdom-api,.github/workflows/scorecards.yml,pull_request,39,0,0,0,0.0,0.0,0.0,13225.0,19198.8 +newsdom-api,.github/workflows/container-image.yml,pull_request,39,0,0,0,0.0,0.0,0.0,13146.0,19554.7 +newsdom-api,.github/workflows/tests.yml,pull_request,39,0,0,0,0.0,0.0,0.0,13132.0,19251.9 +newsdom-api,.github/workflows/sast-semgrep.yml,pull_request,39,0,0,0,0.0,0.0,0.0,13363.0,19582.3 +newsdom-api,.github/workflows/security-scan.yml,pull_request,39,0,2,0,0.0,0.0,0.0,28601.0,34408.5 +newsdom-api,.github/workflows/clusterfuzzlite.yml,pull_request,39,0,0,0,0.0,0.0,0.0,13775.0,19581.5 +newsdom-api,.github/workflows/codeql.yml,pull_request,39,0,0,0,0.0,0.0,0.0,13232.0,19226.1 +newsdom-api,.github/workflows/codeql-pr.yml,pull_request,39,0,18,0,0.0,0.0,0.0,47789.0,58592.1 +newsdom-api,.github/workflows/strix.yml,pull_request_target,39,0,13,0,0.0,0.0,0.0,31266.0,45547.2 +newsdom-api,.github/workflows/noema-review.yml,pull_request_target,39,0,12,0,0.0,0.0,0.0,31776.0,46323.4 +newsdom-api,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,39,0,0,0,0.0,0.0,0.0,13251.0,19824.7 +newsdom-api,.github/workflows/opencode-review.yml,pull_request_target,39,0,13,0,0.0,0.0,0.0,33498.0,46099.0 +naruon,.github/workflows/docker-publish.yml,pull_request,35,0,1,0,0.0,0.0,0.0,13998.0,20734.4 +naruon,.github/workflows/sast-semgrep.yml,pull_request,35,0,17,0,0.0,0.0,0.0,7513.0,18756.7 +naruon,.github/workflows/bandit.yml,pull_request,35,0,0,0,0.0,0.0,0.0,13353.0,18958.2 +naruon,.github/workflows/security-scan.yml,pull_request,35,0,22,0,0.0,0.0,0.0,3291.0,33264.2 +naruon,.github/workflows/codeql-pr.yml,pull_request,35,0,24,0,0.0,0.0,0.0,2598.0,45980.7 +naruon,.github/workflows/app-ci.yml,pull_request,35,0,17,0,0.0,0.0,0.0,8014.0,18519.0 +naruon,.github/workflows/opencode-review.yml,pull_request_target,35,0,24,0,0.0,0.0,0.0,3290.0,36546.2 +naruon,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,35,0,17,0,0.0,0.0,0.0,7824.0,18184.4 +naruon,.github/workflows/noema-review.yml,pull_request_target,35,0,24,0,0.0,0.0,0.0,3290.0,29846.2 +naruon,.github/workflows/strix.yml,pull_request_target,35,0,25,0,0.0,0.0,0.0,3862.5,30509.3 +naruon,dynamic/github-code-quality/codeql,dynamic,35,0,8,0,0.0,0.0,0.0,12626.5,19167.2 +naruon,dynamic/github-code-scanning/codeql,dynamic,35,0,8,0,0.0,0.0,0.0,12551.5,18902.5 +pingora-gateway,.github/workflows/sast-semgrep.yml,pull_request,33,0,31,0,0.0,0.0,0.0,34.0,5156.1 +pingora-gateway,.github/workflows/security-scan.yml,pull_request,33,0,32,0,0.0,0.0,0.0,33.0,5155.7 +pingora-gateway,.github/workflows/codeql-pr.yml,pull_request,33,0,31,0,0.0,0.0,0.0,32.0,1990.0 +pingora-gateway,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,33,0,31,0,0.0,0.0,0.0,33.0,5155.7 +pingora-gateway,.github/workflows/strix.yml,pull_request_target,33,0,32,0,0.0,0.0,0.0,33.0,5155.7 +pingora-gateway,.github/workflows/opencode-review.yml,pull_request_target,33,0,32,0,0.0,0.0,0.0,33.0,5155.2 +pingora-gateway,.github/workflows/noema-review.yml,pull_request_target,33,0,32,0,0.0,0.0,0.0,33.0,5154.7 +.github,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,31,0,13,0,0.0,0.0,0.0,12612.5,17817.8 +.github,.github/workflows/opencode-review.yml,pull_request_target,31,0,13,0,0.0,0.0,0.0,16588.5,45368.3 +.github,.github/workflows/strix.yml,pull_request_target,31,0,16,0,0.0,0.0,0.0,16651.5,47312.1 +.github,.github/workflows/noema-review.yml,pull_request_target,31,0,14,0,0.0,0.0,0.0,17100.0,47291.0 +codec-carver,dynamic/github-code-scanning/codeql,dynamic,30,0,0,0,0.0,0.0,0.0,13311.0,18710.5 +.github,.github/workflows/codeql-pr.yml,pull_request,30,0,14,0,0.0,39570.3,74723.0,17101.0,53333.8 +.github,.github/workflows/security-scan.yml,pull_request,30,0,10,0,0.0,0.0,0.0,24974.0,33218.0 +.github,.github/workflows/sast-semgrep.yml,pull_request,30,0,7,0,0.0,0.0,0.0,12978.0,17406.8 +TEPP,.github/workflows/docs-quality.yml,pull_request,30,0,9,0,0.0,0.0,0.0,7.0,9507.8 +codec-carver,.github/workflows/ci.yml,pull_request,29,0,12,0,0.0,0.0,0.0,33855.0,45463.2 +codec-carver,.github/workflows/codeql-pr.yml,pull_request,29,0,10,0,0.0,0.0,0.0,42959.0,51301.5 +codec-carver,.github/workflows/fuzz.yml,pull_request,29,0,13,0,0.0,0.0,0.0,33855.0,45520.2 +codec-carver,.github/workflows/sast-semgrep.yml,pull_request,29,0,0,0,0.0,0.0,0.0,13011.0,19129.0 +codec-carver,.github/workflows/security-scan.yml,pull_request,29,0,0,0,0.0,0.0,0.0,29678.0,33718.2 +codec-carver,.github/workflows/opencode-review.yml,pull_request_target,29,0,13,0,0.0,0.0,0.0,33854.0,46398.0 +codec-carver,.github/workflows/noema-review.yml,pull_request_target,29,0,10,0,0.0,0.0,0.0,31606.0,46769.4 +codec-carver,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,29,0,0,0,0.0,0.0,0.0,13330.0,18777.0 +codec-carver,.github/workflows/strix.yml,pull_request_target,29,0,13,0,0.0,0.0,0.0,31924.0,43385.0 +html4tree,.github/workflows/codeql-pr.yml,pull_request,28,0,8,0,0.0,0.0,0.0,46483.5,53759.1 +html4tree,.github/workflows/sast-semgrep.yml,pull_request,28,0,1,0,0.0,0.0,0.0,13886.0,18559.6 +html4tree,.github/workflows/security-scan.yml,pull_request,28,0,1,0,0.0,0.0,0.0,27262.0,33155.8 +html4tree,.github/workflows/ci.yml,pull_request,28,0,0,0,0.0,0.0,0.0,13438.0,18516.7 +html4tree,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,28,0,1,0,0.0,0.0,0.0,13567.0,19175.3 +html4tree,.github/workflows/noema-review.yml,pull_request_target,28,0,5,0,0.0,0.0,0.0,30727.0,45249.5 +html4tree,.github/workflows/opencode-review.yml,pull_request_target,28,0,8,0,0.0,0.0,0.0,42808.0,46311.8 +html4tree,.github/workflows/strix.yml,pull_request_target,28,0,11,0,0.0,0.0,0.0,40996.0,46570.2 +html4tree,dynamic/github-code-scanning/codeql,dynamic,28,0,0,0,0.0,0.0,0.0,13326.0,18305.3 +linux-cluster-ops,.github/workflows/security-scan.yml,pull_request,28,0,19,0,0.0,0.0,0.0,56.0,31120.5 +linux-cluster-ops,.github/workflows/fuzz.yml,pull_request,28,0,16,0,0.0,0.0,0.0,70.0,17127.0 +linux-cluster-ops,.github/workflows/pr-governance.yml,pull_request,28,0,16,0,0.0,0.0,0.0,70.0,17782.2 +linux-cluster-ops,.github/workflows/auto-approve.yml,pull_request,28,0,0,0,0.0,0.0,0.0,13343.0,18725.8 +linux-cluster-ops,.github/workflows/lint.yml,pull_request,28,0,16,0,0.0,0.0,0.0,69.0,16816.2 +linux-cluster-ops,.github/workflows/sast-semgrep.yml,pull_request,28,0,16,0,0.0,0.0,0.0,70.0,17000.6 +linux-cluster-ops,.github/workflows/codeql-pr.yml,pull_request,28,0,21,0,0.0,0.0,0.0,55.0,44896.0 +linux-cluster-ops,.github/workflows/noema-review.yml,pull_request_target,28,0,18,0,0.0,0.0,0.0,56.0,31393.1 +linux-cluster-ops,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,28,0,16,0,0.0,0.0,0.0,69.0,16850.0 +linux-cluster-ops,.github/workflows/opencode-review.yml,pull_request_target,28,0,22,0,0.0,0.0,0.0,56.0,32158.4 +linux-cluster-ops,.github/workflows/strix.yml,pull_request_target,28,0,20,0,0.0,0.0,0.0,56.0,31511.2 +linux-cluster-ops,dynamic/github-code-quality/codeql,dynamic,28,0,9,0,0.0,0.0,0.0,7462.0,17863.9 +argos,.github/workflows/security-scan.yml,pull_request,28,0,0,0,0.0,0.0,0.0,27228.0,32293.0 +argos,.github/workflows/codeql-pr.yml,pull_request,28,0,9,0,0.0,0.0,0.0,45556.5,56291.4 +argos,.github/workflows/sast-semgrep.yml,pull_request,28,0,0,0,0.0,0.0,0.0,14091.0,18689.0 +argos,.github/workflows/opencode-review.yml,pull_request_target,28,0,10,0,0.0,0.0,0.0,31111.0,44705.0 +argos,.github/workflows/noema-review.yml,pull_request_target,28,0,8,0,0.0,0.0,0.0,29582.5,36558.2 +argos,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,28,0,0,0,0.0,0.0,0.0,13655.0,18463.0 +argos,.github/workflows/strix.yml,pull_request_target,28,0,10,0,0.0,0.0,0.0,29579.0,34131.0 +pg-erd-cloud,.github/workflows/sast-semgrep.yml,pull_request,27,0,0,0,0.0,0.0,0.0,13093.0,19860.0 +pg-erd-cloud,.github/workflows/security-scan.yml,pull_request,27,0,2,0,0.0,0.0,0.0,27269.0,33290.5 +pg-erd-cloud,.github/workflows/ci.yml,pull_request,27,0,0,0,0.0,0.0,0.0,13114.0,19561.0 +pg-erd-cloud,.github/workflows/codeql-pr.yml,pull_request,27,0,8,0,0.0,0.0,0.0,45668.0,57635.5 +pg-erd-cloud,.github/workflows/opencode-review.yml,pull_request_target,27,0,11,0,0.0,0.0,0.0,34429.0,46992.2 +pg-erd-cloud,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,27,0,0,0,0.0,0.0,0.0,13506.0,20266.0 +pg-erd-cloud,.github/workflows/noema-review.yml,pull_request_target,27,0,9,0,0.0,0.0,0.0,33513.0,46996.7 +pg-erd-cloud,.github/workflows/strix.yml,pull_request_target,27,0,12,0,0.0,0.0,0.0,32706.0,47164.0 +pg-erd-cloud,dynamic/github-code-quality/codeql,dynamic,27,0,0,0,0.0,0.0,0.0,13221.0,19816.0 +argos,.github/workflows/ci.yml,pull_request,27,0,0,0,0.0,0.0,0.0,13452.0,18679.4 +clearfolio,.github/workflows/fuzz.yml,pull_request,23,0,0,0,0.0,0.0,0.0,13224.0,19062.9 +clearfolio,.github/workflows/ci.yml,pull_request,23,0,0,0,0.0,0.0,0.0,13060.0,19164.3 +clearfolio,.github/workflows/security-scan.yml,pull_request,23,0,1,0,0.0,0.0,0.0,28825.5,34303.2 +clearfolio,.github/workflows/codeql-pr.yml,pull_request,23,0,8,0,0.0,0.0,0.0,46882.0,58446.0 +clearfolio,.github/workflows/sast-semgrep.yml,pull_request,23,0,0,0,0.0,0.0,0.0,13113.0,18941.8 +clearfolio,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,23,0,0,0,0.0,0.0,0.0,13165.5,20027.2 +clearfolio,.github/workflows/strix.yml,pull_request_target,23,0,6,0,0.0,0.0,0.0,34157.5,48279.6 +clearfolio,.github/workflows/opencode-review.yml,pull_request_target,23,0,3,0,0.0,0.0,0.0,44570.0,47051.0 +clearfolio,.github/workflows/noema-review.yml,pull_request_target,23,0,3,0,0.0,0.0,0.0,31859.0,39944.1 +clearfolio,dynamic/github-code-scanning/codeql,dynamic,23,0,0,0,0.0,0.0,0.0,13092.5,18674.2 +appguardrail,dynamic/github-code-quality/codeql,dynamic,23,0,0,0,0.0,0.0,0.0,13855.0,18666.0 +.github,.github/workflows/python-security.yml,pull_request,23,0,7,0,0.0,0.0,0.0,25142.0,33339.0 +appguardrail,.github/workflows/codeql-pr.yml,pull_request,22,0,5,0,0.0,0.0,0.0,44130.5,53044.5 +appguardrail,.github/workflows/security-process.yml,pull_request,22,0,0,0,0.0,0.0,0.0,13506.5,18873.3 +appguardrail,.github/workflows/retention-audit-coverage.yml,pull_request,22,0,0,0,0.0,0.0,0.0,13460.0,18773.5 +appguardrail,.github/workflows/pinned-https-coverage.yml,pull_request,22,0,0,0,0.0,0.0,0.0,13340.5,18850.9 +appguardrail,.github/workflows/openssf-evidence-coverage.yml,pull_request,22,0,0,0,0.0,0.0,0.0,13730.5,18954.5 +appguardrail,.github/workflows/security-scan.yml,pull_request,22,0,1,0,0.0,0.0,0.0,27195.0,33461.2 +appguardrail,.github/workflows/sast-semgrep.yml,pull_request,22,0,0,0,0.0,0.0,0.0,14403.5,19699.2 +appguardrail,.github/workflows/tests.yml,pull_request,22,0,0,0,0.0,0.0,0.0,13832.0,19384.8 +appguardrail,.github/workflows/scan-path-context-coverage.yml,pull_request,22,0,0,0,0.0,0.0,0.0,13573.0,18941.6 +appguardrail,.github/workflows/opencode-review.yml,pull_request_target,22,0,8,0,0.0,0.0,0.0,35098.0,44504.0 +appguardrail,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,22,0,0,0,0.0,0.0,0.0,13748.0,19109.3 +appguardrail,.github/workflows/strix.yml,pull_request_target,22,0,5,0,0.0,0.0,0.0,29769.5,42607.4 +appguardrail,.github/workflows/noema-review.yml,pull_request_target,22,0,3,0,0.0,0.0,0.0,29152.0,39568.5 +appguardrail,dynamic/github-code-scanning/codeql,dynamic,22,0,0,0,0.0,0.0,0.0,13466.5,19246.3 +.github,dynamic/github-code-quality/codeql,dynamic,22,0,2,0,0.0,0.0,0.0,12650.0,17718.4 +seedream_evasepic,.github/workflows/codeql-pr.yml,pull_request,22,0,8,0,0.0,0.0,0.0,46580.0,57799.5 +seedream_evasepic,.github/workflows/security-scan.yml,pull_request,22,0,0,0,0.0,0.0,0.0,28705.0,32615.6 +seedream_evasepic,.github/workflows/sast-semgrep.yml,pull_request,22,0,0,0,0.0,0.0,0.0,14594.0,18607.2 +seedream_evasepic,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,22,0,0,0,0.0,0.0,0.0,14728.0,18680.8 +seedream_evasepic,.github/workflows/strix.yml,pull_request_target,22,0,5,0,0.0,0.0,0.0,36423.5,46095.3 +seedream_evasepic,.github/workflows/opencode-review.yml,pull_request_target,22,0,6,0,0.0,0.0,0.0,38074.0,46243.6 +seedream_evasepic,.github/workflows/noema-review.yml,pull_request_target,22,0,3,0,0.0,0.0,0.0,31570.5,45136.0 +seedream_evasepic,dynamic/github-code-scanning/codeql,dynamic,22,0,0,0,0.0,0.0,0.0,13704.0,18623.8 +TEPP,.github/workflows/codeql-pr.yml,pull_request,21,0,18,0,0.0,0.0,0.0,359.5,46877.4 +TEPP,.github/workflows/sast-semgrep.yml,pull_request,21,0,15,0,0.0,0.0,0.0,723.0,18755.0 +TEPP,.github/workflows/security-scan.yml,pull_request,21,0,16,0,0.0,0.0,0.0,601.0,34719.0 +TEPP,.github/workflows/strix.yml,pull_request_target,21,0,17,0,0.0,0.0,0.0,478.0,39798.5 +TEPP,.github/workflows/noema-review.yml,pull_request_target,21,0,17,0,0.0,0.0,0.0,600.5,39748.6 +TEPP,.github/workflows/opencode-review.yml,pull_request_target,21,0,17,0,0.0,0.0,0.0,359.0,35856.5 +TEPP,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,21,0,15,0,0.0,0.0,0.0,723.0,18661.0 +TEPP,dynamic/github-code-quality/codeql,dynamic,21,0,9,0,0.0,0.0,0.0,13463.0,19148.0 +wardnet,.github/workflows/ci.yml,pull_request,20,0,7,0,0.0,0.0,0.0,11849.5,19802.2 +.github,.github/workflows/pr-review-merge-scheduler.yml,pull_request_review,18,0,3,0,0.0,0.0,0.0,13057.5,19023.8 +.github,.github/workflows/hourly-review-repair.yml,schedule,17,0,0,0,0.0,0.0,0.0,25900.0,27509.6 +seedream_evasepic,.github/workflows/cli-ux.yml,pull_request,17,0,0,0,0.0,0.0,0.0,13506.0,18492.6 +life-os,.github/workflows/verify-plugin-operator-replay-sql-snapshot.yml,push,17,0,0,0,0.0,0.0,0.0,19262.0,20229.2 +.github,.github/workflows/pr-review-autofix.yml,repository_dispatch,16,0,2,0,0.0,0.0,0.0,14361.0,34108.0 +pg-llm-batch,.github/workflows/release-acceptance.yml,pull_request,15,0,2,0,0.0,0.0,0.0,13312.5,16059.1 +pg-llm-batch,.github/workflows/ci.yml,pull_request,15,0,2,0,0.0,0.0,0.0,13721.0,16895.8 +scopeweave,.github/workflows/fuzz.yml,pull_request,13,0,0,0,0.0,0.0,0.0,13394.0,16658.0 +scopeweave,.github/workflows/server-tests.yml,pull_request,13,0,0,0,0.0,0.0,0.0,13637.5,17001.5 +scopeweave,.github/workflows/security-scan.yml,pull_request,13,0,0,0,0.0,0.0,0.0,29650.0,32303.2 +scopeweave,.github/workflows/sast-semgrep.yml,pull_request,13,0,0,0,0.0,0.0,0.0,13735.0,16900.8 +scopeweave,.github/workflows/codeql-pr.yml,pull_request,13,0,6,0,0.0,0.0,0.0,46752.5,51025.5 +scopeweave,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,13,0,0,0,0.0,0.0,0.0,13772.0,16637.8 +scopeweave,.github/workflows/opencode-review.yml,pull_request_target,13,0,5,0,0.0,0.0,0.0,35355.0,44709.7 +scopeweave,.github/workflows/noema-review.yml,pull_request_target,13,0,3,0,0.0,0.0,0.0,31973.0,36544.7 +scopeweave,.github/workflows/strix.yml,pull_request_target,13,0,3,0,0.0,0.0,0.0,30805.0,35426.4 +scopeweave,dynamic/github-code-quality/codeql,dynamic,13,0,0,0,0.0,0.0,0.0,13748.5,17116.8 +scopeweave,dynamic/github-code-scanning/codeql,dynamic,13,0,0,0,0.0,0.0,0.0,13876.5,16890.0 +noema,dynamic/github-code-quality/codeql,dynamic,13,0,9,0,0.0,0.0,0.0,2433.0,15446.0 +noema,dynamic/github-code-scanning/codeql,dynamic,13,0,9,0,0.0,0.0,0.0,2433.0,15402.2 +life-os,.github/workflows/sast-semgrep.yml,pull_request,13,0,8,0,0.0,0.0,0.0,622.0,16544.8 +life-os,.github/workflows/appguardrail.yml,pull_request,13,0,7,0,0.0,0.0,0.0,97.5,16689.1 +life-os,.github/workflows/security-scan.yml,pull_request,13,0,9,0,0.0,0.0,0.0,622.0,29886.5 +life-os,.github/workflows/codeql-pr.yml,pull_request,13,0,8,0,0.0,0.0,0.0,145.5,45894.2 +life-os,.github/workflows/commercial-readiness.yml,pull_request,13,0,7,0,0.0,0.0,0.0,97.5,16615.1 +life-os,.github/workflows/ci.yml,pull_request,13,0,8,0,0.0,0.0,0.0,97.5,28761.6 +life-os,.github/workflows/noema-review.yml,pull_request_target,13,0,9,0,0.0,0.0,0.0,153.0,28033.0 +life-os,.github/workflows/opencode-review.yml,pull_request_target,13,0,9,0,0.0,0.0,0.0,153.0,38923.0 +life-os,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,13,0,8,0,0.0,0.0,0.0,622.0,16650.1 +life-os,.github/workflows/strix.yml,pull_request_target,13,0,9,0,0.0,0.0,0.0,622.5,33408.0 +life-os,dynamic/github-code-quality/codeql,dynamic,13,0,3,0,0.0,0.0,0.0,14938.0,17131.5 +fast-mlsirm,.github/workflows/cflite_pr.yml,pull_request,12,0,1,0,0.0,0.0,0.0,10.0,17479.7 +wardnet,.github/workflows/noema-review.yml,pull_request_target,12,0,8,0,0.0,0.0,0.0,12793.5,30650.5 +wardnet,.github/workflows/strix.yml,pull_request_target,12,0,5,0,0.0,0.0,0.0,11476.5,37712.8 +wardnet,.github/workflows/opencode-review.yml,pull_request_target,12,0,8,0,0.0,0.0,0.0,12793.5,43202.7 +wardnet,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,12,0,5,0,0.0,0.0,0.0,11333.0,16597.0 +wardnet,dynamic/github-code-scanning/codeql,dynamic,12,0,4,0,0.0,0.0,0.0,11619.0,16562.0 +noema,.github/workflows/patch-validator-image.yml,pull_request,11,0,9,0,0.0,0.0,0.0,473.0,13296.5 +noema,.github/workflows/ci.yml,pull_request,11,0,9,0,0.0,0.0,0.0,473.0,13315.0 +noema,.github/workflows/reviewer-ci.yml,pull_request,11,0,9,0,0.0,0.0,0.0,473.0,13288.5 +noema,.github/workflows/security-scan.yml,pull_request,11,0,9,0,0.0,0.0,0.0,377.0,19177.2 +wardnet,.github/workflows/sast-semgrep.yml,pull_request,10,0,4,0,0.0,0.0,0.0,11649.0,17000.0 +wardnet,.github/workflows/security-scan.yml,pull_request,10,0,3,0,0.0,0.0,0.0,10878.5,24466.9 +wardnet,.github/workflows/codeql-pr.yml,pull_request,10,0,7,0,0.0,0.0,0.0,12793.5,32378.1 +life-os,.github/workflows/verify-plugin-delivery-attempt-row-collection.yml,push,10,0,0,0,0.0,0.0,0.0,17843.0,20364.2 +late-life-anxiety-reanalysis,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,10,0,6,0,0.0,0.0,0.0,6163.0,15521.6 +late-life-anxiety-reanalysis,.github/workflows/strix.yml,pull_request_target,10,0,6,0,0.0,0.0,0.0,1853.0,25980.0 +late-life-anxiety-reanalysis,.github/workflows/opencode-review.yml,pull_request_target,10,0,6,0,0.0,0.0,0.0,1853.0,13238.2 +late-life-anxiety-reanalysis,.github/workflows/noema-review.yml,pull_request_target,10,0,6,0,0.0,0.0,0.0,1853.0,25116.9 +late-life-anxiety-reanalysis,dynamic/github-code-quality/codeql,dynamic,10,0,3,0,0.0,0.0,0.0,9971.0,14166.8 +late-life-anxiety-reanalysis,dynamic/github-code-scanning/codeql,dynamic,10,0,3,0,0.0,0.0,0.0,10307.5,14224.5 +wardnet,.github/workflows/fuzz.yml,pull_request,9,0,2,0,0.0,0.0,0.0,12250.5,17298.5 +xtrmLLMBatchPython,dynamic/github-code-quality/codeql,dynamic,8,0,0,0,0.0,0.0,0.0,16709.5,19057.2 +linux-cluster-ops,.github/workflows/pr-governance-body-edit.yml,pull_request,8,0,5,0,0.0,0.0,0.0,3116.0,17151.5 +nonnest2,.github/workflows/R-CMD-check.yaml,pull_request,7,0,0,0,0.0,0.0,0.0,13249.0,18898.2 +nonnest2,.github/workflows/security-scan.yml,pull_request,7,0,1,0,0.0,0.0,0.0,27969.5,31668.5 +nonnest2,.github/workflows/sast-semgrep.yml,pull_request,7,0,0,0,0.0,0.0,0.0,13923.5,19003.2 +nonnest2,.github/workflows/codeql-pr.yml,pull_request,7,0,3,0,0.0,0.0,0.0,39259.0,45262.0 +nonnest2,.github/workflows/opencode-review.yml,pull_request_target,7,0,4,0,0.0,0.0,0.0,29912.0,35630.0 +nonnest2,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,7,0,0,0,0.0,0.0,0.0,13660.5,19018.5 +nonnest2,.github/workflows/noema-review.yml,pull_request_target,7,0,1,0,0.0,0.0,0.0,29715.0,35329.3 +nonnest2,.github/workflows/strix.yml,pull_request_target,7,0,1,0,0.0,0.0,0.0,28509.5,32403.1 +nonnest2,dynamic/github-code-scanning/codeql,dynamic,7,0,0,0,0.0,0.0,0.0,13149.5,18562.5 +xtrmLLMBatchPython,.github/workflows/codeql-pr.yml,pull_request,7,0,2,0,0.0,0.0,0.0,44849.0,45144.2 +xtrmLLMBatchPython,.github/workflows/python-security.yml,pull_request,7,0,0,0,0.0,0.0,0.0,18176.0,18876.8 +xtrmLLMBatchPython,.github/workflows/a2z-compliance.yml,pull_request,7,0,0,0,0.0,0.0,0.0,18519.0,18625.0 +xtrmLLMBatchPython,.github/workflows/jsonl-governance.yml,pull_request,7,0,0,0,0.0,0.0,0.0,18504.0,19448.2 +xtrmLLMBatchPython,.github/workflows/security-scan.yml,pull_request,7,0,2,0,0.0,0.0,0.0,29509.0,32998.7 +xtrmLLMBatchPython,.github/workflows/ci.yml,pull_request,7,0,0,0,0.0,0.0,0.0,18243.0,18712.4 +xtrmLLMBatchPython,.github/workflows/postgres_smoke.yml,pull_request,7,0,0,0,0.0,0.0,0.0,18207.0,18531.0 +xtrmLLMBatchPython,.github/workflows/sast-semgrep.yml,pull_request,7,0,0,0,0.0,0.0,0.0,18547.0,19416.2 +xtrmLLMBatchPython,.github/workflows/validate-compliance.yml,pull_request,7,0,0,0,0.0,0.0,0.0,18546.0,18588.4 +xtrmLLMBatchPython,.github/workflows/env-guard.yml,pull_request,7,0,0,0,0.0,0.0,0.0,18293.0,18520.8 +xtrmLLMBatchPython,.github/workflows/opencode-review.yml,pull_request_target,7,0,4,0,0.0,0.0,0.0,29729.0,33153.6 +xtrmLLMBatchPython,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,7,0,0,0,0.0,0.0,0.0,18353.0,18521.0 +xtrmLLMBatchPython,.github/workflows/noema-review.yml,pull_request_target,7,0,1,0,0.0,0.0,0.0,28805.5,32551.8 +xtrmLLMBatchPython,.github/workflows/strix.yml,pull_request_target,7,0,1,0,0.0,0.0,0.0,29493.0,33129.9 +xtrmLLMBatchPython,.github/workflows/python-security.yml,push,7,0,0,0,0.0,0.0,0.0,18362.0,18723.0 +xtrmLLMBatchPython,.github/workflows/jsonl-governance.yml,push,7,0,0,0,0.0,0.0,0.0,18387.0,18777.6 +.github,.github/workflows/agent-review-runtime-quality-ci.yml,pull_request,7,0,1,0,0.0,0.0,0.0,13044.0,13289.6 +.github,.github/workflows/agent-mention-router.yml,schedule,7,0,1,0,0.0,0.0,0.0,19658.0,25098.2 +ContextualWisdomLab.github.io,dynamic/github-code-quality/codeql,dynamic,7,0,0,0,0.0,0.0,0.0,13758.5,14189.5 +semantic-data-portal,.github/workflows/fuzz.yml,pull_request,7,0,2,0,0.0,0.0,0.0,12668.5,17507.8 +life-os,.github/workflows/verify-habit-review-hostile-sql-evidence.yml,push,7,0,0,0,0.0,0.0,0.0,14837.0,15583.0 +LineageWeave,.github/workflows/repair-877-tick-i18n.yml,push,7,0,0,0,0.0,0.0,0.0,13379.5,18035.6 +mightyETL,.github/workflows/hourly-pr-disposition.yml,schedule,6,0,0,0,0.0,0.0,0.0,13418.0,16671.0 +appguardrail,.github/workflows/commercial-readiness-loop.yml,schedule,6,0,0,0,0.0,0.0,0.0,13969.0,16609.4 +appguardrail,.github/workflows/org-security-failure-collector.yml,schedule,6,0,3,0,0.0,0.0,0.0,18815.5,30330.8 +.github,.github/workflows/repository-metadata-reconcile.yml,schedule,6,0,0,0,0.0,0.0,0.0,14056.0,21230.0 +.github,.github/workflows/organization-commercial-readiness-loop.yml,schedule,6,0,0,0,0.0,0.0,0.0,13551.0,23118.0 +.github,.github/workflows/sbom-inventory-scheduler.yml,schedule,6,0,0,0,0.0,0.0,0.0,13612.0,19855.8 +ContextualWisdomLab.github.io,.github/workflows/security-scan.yml,pull_request,6,0,0,0,0.0,0.0,0.0,29145.0,32324.5 +ContextualWisdomLab.github.io,.github/workflows/codeql-pr.yml,pull_request,6,0,2,0,0.0,0.0,0.0,46376.0,51269.3 +ContextualWisdomLab.github.io,.github/workflows/sast-semgrep.yml,pull_request,6,0,0,0,0.0,0.0,0.0,14282.5,15023.4 +ContextualWisdomLab.github.io,.github/workflows/noema-review.yml,pull_request_target,6,0,1,0,0.0,0.0,0.0,33804.0,39044.1 +ContextualWisdomLab.github.io,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,6,0,0,0,0.0,0.0,0.0,13872.5,14172.6 +ContextualWisdomLab.github.io,.github/workflows/strix.yml,pull_request_target,6,0,1,0,0.0,0.0,0.0,33203.0,34945.2 +ContextualWisdomLab.github.io,.github/workflows/opencode-review.yml,pull_request_target,6,0,2,0,0.0,0.0,0.0,37315.5,39775.2 +ContextualWisdomLab.github.io,dynamic/github-code-scanning/codeql,dynamic,6,0,0,0,0.0,0.0,0.0,13922.0,14412.8 +contextual-orchestrator,.github/workflows/opencode-hourly-loop.yml,schedule,6,0,0,0,0.0,0.0,0.0,16304.0,19399.0 +contextual-orchestrator,.github/workflows/provider-catalog-sync.yml,schedule,6,0,0,0,0.0,0.0,0.0,14133.0,18716.8 +fast-mlsirm,.github/workflows/ci.yml,push,6,0,0,0,0.0,0.0,0.0,, +noema,.github/workflows/hourly-commercial-readiness.yml,schedule,6,0,2,0,0.0,0.0,0.0,11471.0,15701.0 +keyverse,.github/workflows/hourly-product-development.yml,schedule,6,0,0,0,0.0,0.0,0.0,13572.0,22078.4 +ThreadWeave,.github/workflows/hourly-product-development.yml,schedule,6,0,0,0,0.0,0.0,0.0,14038.0,21582.6 +ThreadWeave,.github/workflows/actions-registry-audit.yml,schedule,6,0,4,0,0.0,0.0,0.0,11354.0,17553.2 +ThreadWeave,.github/workflows/hourly-pr-maintenance.yml,schedule,6,0,0,0,0.0,0.0,0.0,13881.0,16881.6 +saju-caldav,.github/workflows/hourly-product-loop.yml,schedule,6,0,0,0,0.0,0.0,0.0,13102.0,15790.4 +life-os,.github/workflows/ai-proposal-live-conformance.yml,schedule,6,0,0,0,0.0,0.0,0.0,13511.0,22065.4 +life-os,.github/workflows/verify-habit-rule-change-authority.yml,push,6,0,0,0,0.0,0.0,0.0,, +life-os,.github/workflows/commercial-readiness.yml,schedule,6,0,0,0,0.0,0.0,0.0,13260.0,16172.8 +life-os,.github/workflows/opencode-commercial-development.yml,schedule,6,0,0,0,0.0,0.0,0.0,14658.0,18183.2 +life-os,.github/workflows/verify-plugin-delivery-status-k6.yml,push,6,0,4,0,0.0,0.0,0.0,93.0,15575.0 +life-os,.github/workflows/verify-planning-task-completion-sql-snapshot.yml,push,6,0,0,0,0.0,0.0,0.0,13580.0,16021.5 +four-pillars,.github/workflows/hourly-nim-product-development.yml,schedule,6,0,0,0,0.0,0.0,0.0,13132.0,20991.2 +four-pillars,.github/workflows/hourly-product-loop.yml,schedule,6,0,0,0,0.0,0.0,0.0,12898.0,21232.4 +DiagramWeave,.github/workflows/hourly-product-development.yml,schedule,6,0,0,0,0.0,0.0,0.0,16057.0,24340.4 +DiagramWeave,.github/workflows/hourly-pr-maintenance.yml,schedule,6,0,0,0,0.0,0.0,0.0,14006.0,16935.6 +OriginWeave,.github/workflows/hourly-product-development.yml,schedule,6,0,0,0,0.0,0.0,0.0,13593.0,22509.4 +mhtml-etl-gateway,.github/workflows/hourly-product-gap.yml,schedule,6,0,0,0,0.0,0.0,0.0,13942.0,20199.0 +LineageWeave,.github/workflows/prov-o-contract.yml,pull_request,6,0,1,0,0.0,0.0,0.0,5.5,10290.2 +LineageWeave,.github/workflows/ontology-pages.yml,pull_request,6,0,1,0,0.0,0.0,0.0,5.0,10747.8 +Orgmetra,.github/workflows/recovery-rehearsal-quality.yml,pull_request,6,0,4,0,0.0,0.0,0.0,1748.0,13628.8 +late-life-anxiety-reanalysis,.github/workflows/sast-semgrep.yml,pull_request,6,0,3,0,0.0,0.0,0.0,10991.5,14806.2 +late-life-anxiety-reanalysis,.github/workflows/codeql-pr.yml,pull_request,6,0,3,0,0.0,0.0,0.0,6163.0,7777.6 +late-life-anxiety-reanalysis,.github/workflows/security-scan.yml,pull_request,6,0,3,0,0.0,0.0,0.0,7059.0,28984.1 +aFIPC,.github/workflows/security-audit.yml,pull_request,5,0,0,0,0.0,0.0,0.0,14250.0,17808.6 +aFIPC,.github/workflows/sast-semgrep.yml,pull_request,5,0,0,0,0.0,0.0,0.0,14475.0,18166.0 +aFIPC,.github/workflows/r.yml,pull_request,5,0,0,0,0.0,0.0,0.0,13969.0,18161.4 +aFIPC,.github/workflows/code-quality.yml,pull_request,5,0,0,0,0.0,0.0,0.0,14282.0,17781.8 +aFIPC,.github/workflows/codeql-pr.yml,pull_request,5,0,1,0,0.0,0.0,0.0,41637.5,45473.1 +aFIPC,.github/workflows/security-scan.yml,pull_request,5,0,0,0,0.0,0.0,0.0,27337.0,31702.0 +aFIPC,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,5,0,0,0,0.0,0.0,0.0,14669.0,17903.8 +aFIPC,.github/workflows/noema-review.yml,pull_request_target,5,0,2,0,0.0,0.0,0.0,28500.0,45562.3 +aFIPC,.github/workflows/opencode-review.yml,pull_request_target,5,0,1,0,0.0,0.0,0.0,37697.5,45364.4 +aFIPC,.github/workflows/strix.yml,pull_request_target,5,0,1,0,0.0,0.0,0.0,29044.0,45910.1 +.github,.github/workflows/agent-mention-router-quality-ci.yml,pull_request,5,0,0,0,0.0,0.0,0.0,12597.0,17941.2 +fast-mlsirm,.github/workflows/hourly-pr-governance.yml,schedule,5,0,1,0,0.0,0.0,0.0,12239.0,17469.0 +life-os,.github/workflows/verify-plugin-delivery-status-row-collection.yml,push,5,0,0,0,0.0,0.0,0.0,15560.0,19519.4 +OriginWeave,dynamic/github-code-quality/codeql,dynamic,5,0,0,0,0.0,0.0,0.0,12464.0,13228.1 +newsdom-api,dynamic/dependabot/dependabot-updates,dynamic,4,0,0,0,0.0,0.0,0.0,, +.github,.github/workflows/pr-auto-rebase.yml,schedule,4,0,0,0,0.0,0.0,0.0,13212.0,17586.9 +.github,.github/workflows/agent-mention-opencode-dispatch.yml,repository_dispatch,4,0,3,0,0.0,0.0,0.0,8.0,9567.7 +OriginWeave,.github/workflows/sast-semgrep.yml,pull_request,4,0,2,0,0.0,0.0,0.0,54.0,11773.8 +OriginWeave,.github/workflows/codeql-pr.yml,pull_request,4,0,2,0,0.0,0.0,0.0,54.0,37268.1 +OriginWeave,.github/workflows/security-scan.yml,pull_request,4,0,2,0,0.0,0.0,0.0,54.0,22857.3 +OriginWeave,.github/workflows/noema-review.yml,pull_request_target,4,0,3,0,0.0,0.0,0.0,55.0,43007.5 +OriginWeave,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,4,0,2,0,0.0,0.0,0.0,55.0,10479.7 +OriginWeave,.github/workflows/strix.yml,pull_request_target,4,0,2,0,0.0,0.0,0.0,57.0,37261.2 +OriginWeave,.github/workflows/opencode-review.yml,pull_request_target,4,0,2,0,0.0,0.0,0.0,56.0,36768.8 +mhtml-etl-gateway,.github/workflows/ci.yml,pull_request,4,0,0,0,0.0,0.0,0.0,14738.0,15898.7 +mhtml-etl-gateway,.github/workflows/sast-semgrep.yml,pull_request,4,0,0,0,0.0,0.0,0.0,14396.0,14777.0 +mhtml-etl-gateway,.github/workflows/security-scan.yml,pull_request,4,0,0,0,0.0,0.0,0.0,26394.0,30830.2 +mhtml-etl-gateway,.github/workflows/codeql-pr.yml,pull_request,4,0,1,0,0.0,0.0,0.0,49160.0,52807.7 +mhtml-etl-gateway,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,4,0,0,0,0.0,0.0,0.0,13867.5,14871.1 +mhtml-etl-gateway,.github/workflows/noema-review.yml,pull_request_target,4,0,2,0,0.0,0.0,0.0,37435.5,45107.4 +mhtml-etl-gateway,.github/workflows/opencode-review.yml,pull_request_target,4,0,0,0,0.0,0.0,0.0,43863.0,46411.8 +mhtml-etl-gateway,.github/workflows/strix.yml,pull_request_target,4,0,2,0,0.0,0.0,0.0,41331.0,45745.2 +mhtml-etl-gateway,dynamic/github-code-quality/codeql,dynamic,4,0,0,0,0.0,0.0,0.0,13801.0,14291.1 +LineageWeave,.github/workflows/repair-866-report-axis-empty.yml,push,4,0,0,0,0.0,0.0,0.0,11981.0,14100.8 +.github,.github/workflows/repository-metadata-reconcile.yml,pull_request,3,0,0,0,0.0,0.0,0.0,12057.0,12861.6 +.github,.github/workflows/trusted-uv-materializer-quality-ci.yml,pull_request,3,0,0,0,0.0,0.0,0.0,12872.0,17439.5 +psychometrics-commons,.github/workflows/sbom-evidence.yml,pull_request,3,0,0,0,0.0,0.0,0.0,12978.0,17799.3 +psychometrics-commons,.github/workflows/supply-chain-provenance.yml,pull_request,3,0,0,0,0.0,0.0,0.0,13158.0,18282.6 +psychometrics-commons,.github/workflows/security-scan.yml,pull_request,3,0,0,0,0.0,0.0,0.0,26497.0,26497.0 +psychometrics-commons,.github/workflows/sast-semgrep.yml,pull_request,3,0,0,0,0.0,0.0,0.0,12926.0,18529.4 +psychometrics-commons,.github/workflows/codeql-pr.yml,pull_request,3,0,0,0,0.0,0.0,0.0,37971.0,37971.0 +psychometrics-commons,.github/workflows/ci.yml,pull_request,3,0,0,0,0.0,0.0,0.0,13190.0,18481.1 +psychometrics-commons,.github/workflows/strix.yml,pull_request_target,3,0,0,0,0.0,0.0,0.0,45929.0,45929.0 +psychometrics-commons,.github/workflows/noema-review.yml,pull_request_target,3,0,0,0,0.0,0.0,0.0,29760.0,29760.0 +psychometrics-commons,.github/workflows/opencode-review.yml,pull_request_target,3,0,0,0,0.0,0.0,0.0,37879.0,37879.0 +psychometrics-commons,.github/workflows/pr-review-merge-scheduler.yml,pull_request_target,3,0,0,0,0.0,0.0,0.0,13800.0,18307.2 +psychometrics-commons,dynamic/github-code-quality/codeql,dynamic,3,0,0,0,0.0,0.0,0.0,13020.0,18384.9 +naruon,dynamic/dependabot/dependabot-updates,dynamic,2,0,0,0,0.0,0.0,0.0,18749.5,18759.8 +.github,.github/workflows/pr-review-merge-scheduler.yml,repository_dispatch,2,0,0,0,0.0,0.0,0.0,15475.0,18490.0 +noema,.github/workflows/reviewer-ci.yml,push,2,0,0,0,0.0,0.0,0.0,14802.5,17439.0 +noema,.github/workflows/ci.yml,push,2,0,0,0,0.0,0.0,0.0,15140.5,17667.2 +keyverse,dynamic/dependabot/dependabot-updates,dynamic,2,0,0,0,0.0,0.0,0.0,, +LineageWeave,.github/workflows/converge-867-current-866.yml,push,2,0,0,0,0.0,0.0,0.0,5629.0,10695.1 +xtrmLLMBatchPython,.github/workflows/postgres_smoke.yml,push,1,0,0,0,0.0,0.0,0.0,18278.0,18278.0 +xtrmLLMBatchPython,.github/workflows/validate-compliance.yml,schedule,1,0,0,0,0.0,0.0,0.0,18826.0,18826.0 +clearfolio,.github/workflows/fuzz.yml,schedule,1,0,0,0,0.0,0.0,0.0,12308.0,12308.0 +scopeweave,.github/workflows/fuzz.yml,schedule,1,0,0,0,0.0,0.0,0.0,12896.0,12896.0 +codec-carver,.github/workflows/fuzz.yml,schedule,1,0,0,0,0.0,0.0,0.0,, +vooster,.github/workflows/world-health.yml,schedule,1,0,0,0,0.0,0.0,0.0,, +vooster,.github/workflows/verify.yml,schedule,1,0,0,0,0.0,0.0,0.0,13711.0,13711.0 +.github,.github/workflows/pr-review-merge-scheduler.yml,schedule,1,0,0,0,0.0,0.0,0.0,14219.0,14219.0 +.github,.github/workflows/audit-central-ruleset.yml,schedule,1,0,0,0,0.0,0.0,0.0,12049.0,12049.0 +.github,.github/workflows/product-performance-attestation-quality.yml,pull_request,1,0,0,0,0.0,0.0,0.0,11437.0,11437.0 +.github,.github/workflows/javascript-coverage-quality-ci.yml,pull_request,1,0,0,0,0.0,0.0,0.0,12656.0,12656.0 +hyosung-itx-slogan-brief,dynamic/github-code-scanning/codeql,dynamic,1,0,0,0,0.0,0.0,0.0,11323.0,11323.0 +fast-mlsirm,.github/workflows/statistical-studies.yml,schedule,1,0,0,0,0.0,0.0,0.0,, +semantic-data-portal,.github/workflows/fuzz.yml,schedule,1,0,0,0,0.0,0.0,0.0,13564.0,13564.0 +noema,.github/workflows/private-vulnerability-reporting-audit.yml,schedule,1,0,0,0,0.0,0.0,0.0,12091.0,12091.0 +noema,.github/workflows/acquisition-readiness-scan.yml,schedule,1,0,0,0,0.0,0.0,0.0,15285.0,15285.0 +noema,.github/workflows/readiness-scan.yml,schedule,1,0,0,0,0.0,0.0,0.0,18336.0,18336.0 +wardnet,.github/workflows/fuzz.yml,schedule,1,0,0,0,0.0,0.0,0.0,14840.0,14840.0 +wardnet,.github/workflows/scorecard-analysis.yml,schedule,1,0,0,0,0.0,0.0,0.0,13194.0,13194.0 +feelanet-adfs,dynamic/github-code-scanning/codeql,dynamic,1,0,0,0,0.0,0.0,0.0,11469.0,11469.0 +disksage,dynamic/github-code-quality/codeql,dynamic,1,0,0,0,0.0,0.0,0.0,13352.0,13352.0 +free-router,dynamic/github-code-scanning/codeql,dynamic,1,0,0,0,0.0,0.0,0.0,, +free-router,.github/workflows/model-catalog-sync.yml,schedule,1,0,0,0,0.0,0.0,0.0,13861.0,13861.0 +RankWeave,dynamic/github-code-quality/codeql,dynamic,1,0,0,0,0.0,0.0,0.0,, +ThreadWeave,dynamic/github-code-quality/codeql,dynamic,1,0,0,0,0.0,0.0,0.0,13060.0,13060.0 +life-os,.github/workflows/verify-planning-completion-http-restack.yml,push,1,0,0,0,0.0,0.0,0.0,, +life-os,.github/workflows/verify-planning-task-due-authority.yml,push,1,0,0,0,0.0,0.0,0.0,, +life-os,.github/workflows/verify-habit-definition-history.yml,push,1,0,0,0,0.0,0.0,0.0,13039.0,13039.0 +metering-billing-platform,dynamic/github-code-quality/codeql,dynamic,1,0,0,0,0.0,0.0,0.0,12859.0,12859.0 +opencode,dynamic/github-code-quality/codeql,dynamic,1,0,0,0,0.0,0.0,0.0,, diff --git a/docs/ci-baseline-20260916.md b/docs/ci-baseline-20260916.md new file mode 100644 index 0000000000..9f836229c6 --- /dev/null +++ b/docs/ci-baseline-20260916.md @@ -0,0 +1,106 @@ +# CI baseline — 2026-09-16 + +Measurement window: last 24h ending 2026-09-16T12:57:58Z, all 79 non-archived +`ContextualWisdomLab` repositories, via `GET /repos/{owner}/{repo}/actions/runs?created=>=` +(REST, one repo at a time; GraphQL used only for the repo list). Raw run rows: 9,353 +across 400 (repo, workflow, event) groups. Full per-group detail: `ci-baseline-20260916.csv`. + +## Headline numbers (org-wide, run level) + +| Metric | Value | +|---|---| +| Runs in 24h | 9,353 | +| Cancelled/superseded | 4,000 (42.8%) | +| Run-level queue time (`created_at` → `run_started_at`), p50 / p95 | 0.0s / 0.0s | +| Run-level queue time, max observed | 74,723s (~20.8h), `.github` `codeql-pr.yml` | +| Distinct (repo, workflow, trigger) groups | 400 | + +**The run-level queue KPI is not the right signal here — read the note below before using it.** +`run_started_at` flips to non-null as soon as *any* job in the run leaves the queue, so a run with +one fast job and ten stuck jobs still reports ~0s queue time. This is why p50/p95 are 0.0s for +almost every group in the CSV even on repos with visibly stuck checks. + +## The real symptom: job/check-suite level queuing, confirmed live + +Live GraphQL check-suite query against `fast-mlsirm`'s 5 most recently updated open PRs +(2026-09-16, same session): + +| PR | Rollup state | GitHub Actions check suites, all `QUEUED` | +|---|---|---| +| #1886 | SUCCESS | 0 (only non-GH-Actions app suites, which stay QUEUED indefinitely and are not CI) | +| #1885 | SUCCESS | 0 | +| #1882 | PENDING | 11 | +| #1883 | PENDING | 11 | +| #1884 | PENDING | 11 | + +Newer PR heads (#1885, #1886) completed; older heads (#1882–#1884) sit with all 11 +GitHub-Actions-run check suites permanently `QUEUED`. That head-of-line pattern — a few heads +running, many stuck — reproduces exactly the signature already on record in +[`docs/doctoring/actions-plan-concurrency-ceiling-20260903.md`](doctoring/actions-plan-concurrency-ceiling-20260903.md): +single-digit `in_progress` against triple/quadruple-digit `queued`, org-wide. Re-checked live in this +session: + +| Repo | `in_progress` | `queued` | +|---|---|---| +| `fast-mlsirm` | 8 | 220 | +| `.github` | 6 | 220 | +| `bandscope` | 0 | 73 | +| `naruon` | 0 | 67 | + +That doctoring record's conclusion, dated 2026-09-03 and still consistent with this session's +2026-09-16 numbers: the primary bottleneck is a **plan-level concurrent-job ceiling** (user-reported +58-60/60 concurrent jobs in use at the time), not per-repo or per-workflow-file duplication. It +explicitly warns that a large cross-repo workflow-consolidation effort "would be solving the wrong +layer of the problem." This baseline does not contradict that finding — it corroborates it two weeks +later with the same queued≫in_progress shape. + +## Duration (`run_started_at` → completion), heaviest groups + +Excerpt (see CSV for all 400 rows). These durations mostly reflect **policy-accepted long model-review +runs** (see `docs/product-goal-directive.md` §8: OpenCode/Strix/Noema may legitimately run 2+ hours; +`#1889`/`#1890`/`#1892` timeout attempts were reverted on this evidence), not stalls: + +| Repo | Workflow | Trigger | Runs | Cancelled | Duration p50 | Duration p95 | +|---|---|---|---|---|---|---| +| fast-mlsirm | opencode-review.yml | pull_request_target | 68 | 21 (31%) | 8.4h | 12.9h | +| fast-mlsirm | strix.yml | pull_request_target | 68 | 20 (29%) | 5.3h | 11.4h | +| fast-mlsirm | noema-review.yml | pull_request_target | 68 | 20 (29%) | 5.3h | 8.8h | +| bandscope | strix.yml | pull_request_target | 91 | 81 (89%) | 78s | 4.6h | +| `.github` | opencode-review-dispatch.yml | repository_dispatch | 137 | 4 (3%) | 4.2h | 17.8h | + +`bandscope`'s 89% cancellation rate on `strix.yml` (and similarly high on its other 6 required +workflows, all pinned at 91 runs / ~80 cancelled) stands out as the one clear duplication/thrash +signal in this dataset: nearly every PR push on that repo cancels and re-triggers all 7 of its +required workflows, which is exactly the per-push-supersession pattern centralized concurrency +groups are meant to absorb — worth a follow-up look at what is re-triggering so often there. + +## Scheduled/hourly workflows per repo (event = `schedule`) + +22 repos run at least one scheduled workflow in the 24h window; `.github` itself runs 8 distinct +schedules (`agent-mention-router`, `audit-central-ruleset`, `hourly-review-repair`, +`organization-commercial-readiness-loop`, `pr-auto-rebase`, `pr-review-merge-scheduler`, +`repository-metadata-reconcile`, `sbom-inventory-scheduler`) — already the central scheduler this +task's Step 3 asked to consolidate *toward*. Per-repo counts, full list in the aggregate output; +most repos run 1-2 product-loop schedules of their own (`hourly-product-development.yml`, +`commercial-readiness*.yml`, etc.) that are product-specific automation, not CI/security gates, and +are out of scope for the CI-centralization goal. + +## Duplicate-check check + +No case was found in this 24h window where the *same* check category (e.g. CodeQL, Semgrep, secret +scan) runs from both a per-repo workflow file and an independent org-required workflow on the same +head for the same purpose — `docs/doctoring/ci-workflow-duplication-audit-20260902.md` (existing, +2026-09-02) already covers this ground in more depth than this session re-derived and found the same: +duplication is not the primary driver of queue depth. + +## What this baseline changes about the task's plan + +Given the above, the Step 2/3 "centralize workflows to fix queue stalls" framing needs one +correction before more PRs get written against it: **workflow centralization is real hygiene +(fewer files to keep in sync, one required-check set) but is not a fix for the current queue +depth**, per the existing, still-live doctoring finding. The concurrency-group gap search in this +session (`grep` across `.github/workflows/*.yml` for a missing `concurrency:` block) found no +event-triggered required workflow lacking one — the 6 files without a `concurrency:` block are all +`workflow_call` reusable workflows (concurrency is correctly the caller's job) or +`issue_comment`/`schedule`-triggered (not supersession-prone). That specific low-risk fix this task +proposed as the smallest first step is already done. diff --git a/docs/doctoring/20260924-release-gate-negative-fixture-verification-plan.md b/docs/doctoring/20260924-release-gate-negative-fixture-verification-plan.md new file mode 100644 index 0000000000..ca4508b2b7 --- /dev/null +++ b/docs/doctoring/20260924-release-gate-negative-fixture-verification-plan.md @@ -0,0 +1,196 @@ +# Negative-fixture verification plan for the central release dependency gate (#2342, #2347) + +Prepared, **not approved to run**. No hosted run, publish, merge, approval or re-run is authorized +by this document. It closes the two written gaps the coordinator required alongside the reviewable +exact head, and records the source-policy constraints on the proposed fixture. + +Historical central head described by the original plan: `65727fa8411ec92672e03b1c6447b3a47d2616fc` on +`feat/release-dependency-license-strix-gate-2342`, on top of the reviewed +`3c3ca9b1445d4a73a9d47216ff88996f012f1757`. + +Source-directive assessment updated after integration `79be9bd3d2d1aeea62f0c32659d22318521b0a6c`; hosted-run claims below remain unverified by this document. + +Two claims are kept apart throughout, and must stay apart in any report that cites this file: + +- **Reason codes verified locally.** Unit results from `pytest`, which prove a decision outcome and + its reason code and nothing else. +- **Run-level facts.** "The Strix step did not start", "the gate job concluded `failure`", "the + publish job did not start". None of these is established here. A failing unit-test wrapper is + never a real release-gate failure, and a passing test is never a release PASS. + +## Gap 1 — how a negative case enters the real gate + +### The path, by step, subcommand and function + +| # | Workflow step (`release-dependency-license-strix-gate.yml`) | Runs | Decides | +|---|---|---|---| +| 1 | `Validate the exact release identity before anything else runs` | `release_dependency_gate.py validate-inputs` | `validate_release_identity` — 40-hex `source_sha`, `owner/name` repository | +| 2 | `Install the release dependency closure into a lock-only environment` | `pip install --require-hashes --only-binary=:all: -r release-source/` into a `--without-pip` venv | — | +| 3 | `Download the exact distributions the caller intends to publish` | `actions/download-artifact` → `release-distributions/` | — | +| 4 | `Collect raw resolved-dependency evidence from both ecosystems` | `release_dependency_capture_raw.sh` | — (writes tool output verbatim; `capture_python` derives each `metadata.json` from `python/installed.json`) | +| 5 | `Assemble per-dependency evidence and isolated synthetic fixtures` | `release_dependency_gate.py capture` | `capture` → `build_evidence` → `evidence/.json`, `strix/fixtures/.json` | +| 6 | `Refuse a denied or unverifiable licence before any credential exists` | `release_dependency_gate.py prescreen` | **`gate(stage="license")` → `evaluate_dependency_license` → `declared_license_expression` → `spdx_license_policy.evaluate_license_expression`** | +| 7 | `Require every Strix provider credential before the Strix stage starts` | `release_dependency_gate.py require-strix-credentials` | `require_strix_credentials` → `STRIX_CREDENTIALS_ABSENT` | +| 8–11 | gateway, toolchain, credential binding, Strix | `strix_quick_gate.sh` per fixture workspace | — | +| 12 | `Refuse the release unless every dependency passes` | `release_dependency_gate.py gate` | `gate(stage="full")` — the same licence decision **plus** `validate_strix_binding` | +| 13 | `Seal exactly the gated bytes for attestation` | `release_dependency_gate.py seal` | `seal` — refuses a non-`PASS` **and** a non-`full` report | + +The licence decision in step 6 is the same function the final gate calls in step 12. There is one +decision implementation, not a prescreen copy of one. + +`needs` path traversed: the gate is a single `workflow_call` job (`jobs.gate`). A caller composes +`gate` → `attest`, and any mock job models only the edge out of `jobs.gate`. + +### No collection-bypass input exists + +Verified by reading the current source: + +- The workflow's `workflow_call` inputs are the release identity, ecosystems, lock/manifest paths, + artifact and filenames. **None of them skips capture, skips the licence stage, or injects a + verdict.** `test_workflow_is_reusable_and_never_branch_selectable` and + `test_gate_has_no_bypass_of_any_kind` pin the absence of a bypass shape. +- Step 4 always runs; step 5 always runs; step 6 always runs. The only `if:` conditions in the + workflow are the lock-only install guard and the two evidence-retention uploads + (`test_failure_evidence_survives_the_failure_that_produced_it` asserts there are exactly three). +- A hand-written `evidence/.json` cannot manufacture a case. The expected set comes from the + producer's own lock (`_enumerate_python`) and `Cargo.lock` (`_enumerate_cargo`), and + `_scope_rows` refuses collected material that no declared ecosystem expects with + `SCOPE_SET_MISMATCH`. Test: `test_collected_material_outside_every_expected_set_is_a_scope_mismatch`. + +**Consequence, stated plainly: a denial case cannot be fed in as a bare JSON blob.** It must arrive +as something the real capture path genuinely collects — a distribution present in the lock, with a +real `sha256`, whose own metadata carries the case. + +### The fixture-distribution shape: bounded source policy + +The earlier dropped-directive defect has been fixed. The capture script validates +`lock-source-options` and passes the resulting `source_options` to `pip download`. +It does not silently discard source directives. + +The supported sources remain deliberately narrow: + +- Index URLs must use HTTPS, the default port, and no user information, with a host + of `pypi.org` or `files.pythonhosted.org`. +- `--find-links` must name a normalized, bounded relative directory inside the + lock-file directory. The separately downloaded `release-distributions/` + directory in this plan does not meet that constraint. +- Environment markers, `-r`/`--requirement`, and `-c`/`--constraint` are rejected + with `LOCK_SOURCE_UNSUPPORTED`. + +A locally authored fixture therefore requires a hash-bound wheel under a permitted +lock-relative directory. This document supplies no hosted collection result and +establishes no release acceptance. + +### A GPL-declaring fixture package is rejected + +The coordinator has **retracted** the idea of authoring or installing a fixture package whose +metadata declares a copyleft identifier. It is not to be built. The two evidence classes are split +instead: + +- **Per-reason denial codes stay unit-level.** A self-authored, **data-only** SPDX string is a valid + input to the production decision functions, and `LICENSE_DENIED_GPL`, `LICENSE_DENIED_LGPL`, + `LICENSE_DENIED_AGPL`, `LICENSE_UNPARSEABLE`, `LICENSE_UNRECOGNIZED`, `LICENSE_MISSING`, + `LICENSE_SELECTION_REQUIRED` and `LICENSE_SELECTION_INVALID` are proven exactly there, by direct + calls to `evaluate_dependency_license` / `declared_license_expression` in the existing + `tests/test_release_dependency_gate.py`. Those SPDX strings are **not** extended into the real + package-install path. +- **The real capture path is exercised with a self-authored artifact containing no forbidden + source**, verified through a `LICENSE_MISSING` rejection. Nothing copyleft is fetched, declared or + installed at any point. + +The `LICENSE_MISSING` fixture must also satisfy the bounded source policy above. +Its current placement outside the lock directory is unsuitable; collectibility +requires compliant placement and an actual non-deploy collection run. + +### Naming discipline for the eventual run + +What such a run can prove, and the only way it may be described: +**real collection → licence-missing rejection → Strix blocked → `mock_publish` gated by `needs`.** + +It is **not** a "GPL real-collection-refusal E2E" and must never be called one. The per-reason +copyleft denials are unit-level decision evidence and belong in a separate, separately labelled +section of any report. The link between the real capture path and the decision function is for the +coordinator to review from the exact-head source; it is not established by this prose. + +## Gap 2 — the `mock_publish` job's contract and its limits + +Name: **`mock_publish`**. Never `publish`, `release`, or `deploy`, so no reader or later script +mistakes it for the release job. + +What it verifies: **only that the gating edge behaves as the real caller's publish job would.** Its +`needs` and `if` must be character-identical to the real release workflow's publish job, and both +must be quoted side by side in the run's evidence. It models the *edge*, nothing else: it does not +show that a real publish job would not start, because it is not that job and does not share its +environment, permissions or triggers. + +Prohibited in `mock_publish`, and unnecessary for the contract: any `permissions:` beyond +`contents: read`, any token or secret, any `environment:`, any tag creation, any release creation, +any registry credential, any upload to a registry. Its steps are `echo` only. + +**The real publish job's `needs`/`if` cannot be quoted here.** The caller workflow is FMLS-owned and +is not present at this head, so the two conditions must be quoted from the FMLS caller at its exact +SHA when the run is proposed. This plan does not invent them. + +### Judgement rule and the exact fields to read + +A skipped job can still carry a `started_at` in GitHub's payload, so **nothing may be inferred from +an absent or present `started_at` alone.** Judge from the raw payload plus whether steps actually +executed: + +From `GET /repos/{owner}/{repo}/actions/runs/{run_id}` — `id`, `head_sha` (must equal the fixture +commit exactly), `status`, `conclusion`. + +From `GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs` per job — `name`, `status`, +`conclusion`, and the full `steps[]` array, reading each step's `name`, `status`, `conclusion` and +`number`. + +Decision rules: + +- **Gate refused**: the `gate` job has `conclusion == "failure"`, and the step named + `Refuse a denied or unverifiable licence before any credential exists` has + `conclusion == "failure"`. The reason code is read from the + `release-dependency-license-report` artifact's `failures[].code`, not from log prose. +- **Strix never started**: every step from `Provision the zero-cost review gateway for Strix` + through `Run Strix against one isolated synthetic fixture per dependency` has + `conclusion == "skipped"`. A step that ran and failed is a different outcome and must not be + reported as "did not start". +- **Credentials were never required for the licence decision**: the step + `Require every Strix provider credential before the Strix stage starts` also has + `conclusion == "skipped"`, which places it after the licence refusal. +- **`mock_publish` did not execute**: its `conclusion == "skipped"` **and** its `steps[]` is empty or + every entry has `conclusion == "skipped"`. `started_at` is recorded verbatim and explicitly **not** + used as evidence either way. +- **Evidence survived the failure**: the `release-dependency-license-report` artifact exists on the + failed run, which is the behavior the bound-to-producing-step upload condition exists to provide. + +Anything not on this list stays unverified. + +## Remaining end-to-end verification scope + +Splitting the evidence into unit-level denials and one `LICENSE_MISSING` collection run does **not** +shrink the requirement, and nothing here may be marked fully complete. Still unproven, with the kind +of run that would prove each: + +| Unproven | What would prove it | +|---|---| +| A copyleft dependency is refused by the **real collection path** | A run whose collected metadata carries a denied licence. No such run is planned, because authoring or installing a copyleft-declaring package is rejected. This gap stays open by policy. | +| `release_dependency_capture_raw.sh` executes at all | Any hosted run that reaches step 4. No step of that script has ever executed, here or in CI. | +| A locally authored fixture distribution is collectible | A hash-bound wheel in a permitted lock-relative directory, then one non-deploy run. | +| Strix succeeds and produces a real binding | A credentialed run that reaches step 12 with `verdict` and `findings` from an actual scan. | +| `seal` output is accepted by the real attestation workflow on real bytes | A run composing `gate` → `attest` on a real wheel and sdist. Locally only the *shape* is checked, against a synthetic sealed directory. | +| Capture/hash/metadata/artifact binding agree end to end | The same composed run, comparing `wheel_sha256` and `sdist_sha256` against the published artifact digests. | +| A real publish job would not start | Nothing planned proves this. `mock_publish` models the gating edge only. | + +## What remains unverified without a hosted run + +Verified locally by execution: every reason code above, produced by the production functions through +the existing harness in `tests/test_release_dependency_gate.py` and its siblings. + +Not verified, and not claimable until a single approved non-deploy run exists: that the gate job +concludes `failure` on a real runner; that the Strix steps report `skipped`; that `mock_publish` +does not execute; that the capture script's real `pip inspect`/`pip download`/`cargo metadata` +invocations behave as read (no step of `release_dependency_capture_raw.sh` has ever been executed, +here or in CI); that `seal` and `exact-artifact-sbom-attestation.yml` agree on real bytes; and that +the fixture distribution is collectible under the bounded source policy described above. + +Refs #2342, #2347. diff --git a/docs/doctoring/actions-capacity-root-cause-20260917.md b/docs/doctoring/actions-capacity-root-cause-20260917.md new file mode 100644 index 0000000000..75a463b98d --- /dev/null +++ b/docs/doctoring/actions-capacity-root-cause-20260917.md @@ -0,0 +1,136 @@ +# Doctoring record: the multi-hour review durations are inter-job global queue wait, not model/build time (2026-09-17) + +- **Date:** 2026-09-17 +- **Subject:** `docs/ci-baseline-20260916.md` measured multi-hour p50/p95 durations for the long + AI-review workflows (`opencode-review.yml` p50 8.4h/p95 12.9h, `strix.yml` p50 5.3h, + `noema-review.yml` p50 5.3h, `.github` `opencode-review-dispatch.yml` p50 4.2h) and this task's + original framing proposed capping concurrency for that job class. Maintainer steering asked for a + per-step time breakdown before any capping: is the duration model API latency, retries/backoff, + rate-limit waits, un-batched per-file/per-chunk calls, sleep/poll loops, repeated + dependency installs/builds, or duplicated coverage/test execution? This record answers that with + measured job-level timestamps from two completed runs of the workflow that does the actual heavy + work (`opencode-review-dispatch.yml` in `.github` — see "Where the work actually happens" below). +- **Decision record:** none yet — this is the root-cause measurement the next decision (whether a + capacity-reservation concurrency cap is still needed) should be based on. + +## Where the work actually happens + +`opencode-review.yml` is the `pull_request_target`-triggered required-check entry point that runs +in each target repo's context. Its `coverage-source-tree` and `coverage-evidence` jobs are +deliberately no-op placeholders — each is a single `echo` step with no `needs:` edge between them — +whose inline comment already documents why: a real `needs:` edge between two jobs that declare no +`outputs:` only orders two context holders, and "under a saturated queue each link waits out the +whole queue again," citing a prior measurement on `naruon#1528` (run 33581213805) where that exact +pattern cost 22h41m of pure queueing for two single-echo jobs before it was fixed by depending both +directly on `admit-current-head` so they run in parallel. The actual coverage measurement and review +publication happen in `opencode-review-dispatch.yml` (`.github`, `repository_dispatch`-triggered), +which `opencode-review.yml` invokes. That workflow's job chain is +`validate-pr-metadata` → `coverage-source-tree` → `coverage-evidence` → `opencode-review-target`, +with real (not placeholder) `needs:` edges: `coverage-source-tree` uploads a +`opencode-coverage-source` tarball artifact that `coverage-evidence` downloads, and +`opencode-review-target` consumes `coverage-evidence`'s output. + +## Measured evidence + +Job-level `started_at`/`completed_at` timestamps, `repos/ContextualWisdomLab/.github/actions/runs//jobs`, +gathered 2026-09-17 for two completed runs of `OpenCode Review Dispatch` (workflow id `322670888`): + +**Run 34931908846 (started 2026-09-15, during the saturated period this baseline documents):** + +| Job | Started | Completed | Job duration | Wait since prior job completed | +|---|---|---|---|---| +| `validate-pr-metadata` | 17:44:59 | 17:45:04 | 5s | — | +| `coverage-source-tree` | 21:50:17 | 21:50:24 | 7s | 4h05m13s | +| `coverage-evidence` | 01:27:35 (+1d) | 01:28:47 | 1m12s | 3h37m11s | +| `opencode-review-target` | 07:23:03 | 07:42:31 | 19m28s | 5h54m16s | + +Total wall time (first job start → last job completion): ~13h57m. Sum of actual job execution: +5s + 7s + 72s + 1168s ≈ **21 minutes (2.5% of wall time)**. Sum of inter-job queue wait: +**~13h36m (97.5% of wall time)**. + +**Run 34756591400 (started 2026-09-13, lighter load) — the identical 4-job chain:** + +| Job | Started | Completed | Wait since prior job completed | +|---|---|---|---| +| `validate-pr-metadata` | 12:22:15 | 12:22:20 | — | +| `coverage-source-tree` | 12:24:38 | 12:24:47 | 2m18s | +| `coverage-evidence` | 12:25:13 | 12:27:43 | 26s | +| `opencode-review-target` | 12:28:40 | 12:36:55 | 57s | + +Total wall time: 14m40s, essentially all of it job execution. The workflow's own logic and step +content did not change between these two runs — the ~57x difference in total wall time (13h57m vs +14m40s) is explained entirely by how long each job waited to be admitted to a runner, which tracks +org-wide Actions saturation at the time, not anything the workflow does. + +## What this rules out + +- **Model API latency / retries / rate-limit waits:** the `opencode-review-target` job — which is + where the actual model calls happen — took 19m28s and 8m15s respectively in the two sampled runs. + Consistent with ordinary model-review work, not a multi-hour stall. +- **Sleep/poll loops waiting on another run:** none exist in `strix.yml`, `noema-review.yml`, or + `opencode-review.yml`; `opencode-review-dispatch.yml`'s few `sleep 5`/`sleep 10` occurrences are + bounded (≤120s) retry backoffs for transient `gh api` failures during head-fetch/publication, not + busy-waits on another job or run. `opencode-review.yml`'s required job specifically forbids + `sleep `/`while :; do`/`poll_interval_seconds` and is contract-tested to stay that way + (`tests/test_opencode_required_rerun_capacity.py`); it wakes via a targeted + `repository_dispatch` callback instead of polling. +- **Repeated, cacheable dependency installs/builds:** real, but already the subject of active fixes + landed just before this session (`11a56305b` "build PyO3/maturin extensions offline before + coverage", `efc35f72b` "vendor Cargo deps offline for the coverage sandbox") — and even fully + un-cached, those builds run inside the `coverage-evidence` job, whose own execution time (72s and + 2m30s in the two samples) is a small fraction of the job's total wait. +- **Duplicated coverage/test execution:** `opencode-review.yml`'s own `coverage-source-tree`/ + `coverage-evidence` jobs do not re-run coverage; they are no-op placeholders that exist only to + keep a stable required-check name in branch protection, per their own inline comment. + +## What this confirms + +The dominant cost is **inter-job wait for a fresh runner inside a single workflow run**, compounding +once per `needs:` edge, under the org's global concurrent-job ceiling +(`docs/doctoring/actions-plan-concurrency-ceiling-20260903.md`). `opencode-review.yml` already +applied the available fix for this (parallelize independent placeholder jobs instead of chaining +them) after discovering the identical pattern on `naruon#1528`. The same fix is **not available** +for `opencode-review-dispatch.yml`'s chain, because unlike the placeholder jobs, these three jobs +have a genuine data dependency (source tree → build artifact → review) *and* a deliberate, +already-documented trust boundary: `coverage-evidence` runs untrusted PR-head test/build code with +only `actions: read` permission (its own inline comment: "No repository-content, identity, secret, +or write token is available to untrusted tests"), isolated from `coverage-source-tree`'s +`id-token: write` app-token exchange and `opencode-review-target`'s broad write permissions +(`issues: write`, `pull-requests: write`, `statuses: write`, `security-events: read`). Merging these +jobs to remove queue-wait would let untrusted PR content execute in a process that recently held (or +will hold) elevated/write-capable tokens — a security regression this task's rules explicitly +forbid trading against speed. Reducing job count is therefore not an available lever for this +specific chain; the queue-wait can only be reduced by changing how many jobs of this class compete +for runners at once, which is what a capacity-reservation concurrency cap (if adopted) would target +directly, with this measurement as its justification rather than a bypassed diagnosis step. + +## Bandscope re-trigger check (task item 3, partial) + +Checked whether `bandscope`'s 89% required-workflow cancellation rate wastes runner-seconds (jobs +cancelled after starting) or is pure pre-admission churn (cancelled before a runner is ever +assigned). Sampled commit and check-suite timestamps on 5 open `bandscope` PRs via GraphQL: pushes +arrive in bursts (6–10 commits within 5–10 minutes, single author identity, consistent with this +org's documented shared agent-session identity actively iterating on a PR — not a bot loop or +webhook misfire) and the concurrency-group cancellation for the prior commit's check suites completes +within 1–3 seconds of the next commit's check suites being created — i.e. before any of those jobs +could plausibly have reached `in_progress`. The high cancellation rate is the existing +`cancel-in-progress` concurrency groups working as designed against a fast push cadence; it is not +evidence of wasted runner-slot time and does not, by itself, justify a workflow change. No further +action taken on item 3 in this record; still open whether the push cadence itself (many small commits +per PR in a short window) is worth addressing for reasons other than Actions capacity (e.g. review +noise), which is outside this task's scope. + +## Audit trail + +- `repos/ContextualWisdomLab/.github/actions/runs/34931908846/jobs` and + `repos/ContextualWisdomLab/.github/actions/runs/34756591400/jobs` (REST, GitHub API, 2026-09-17). +- `.github/workflows/opencode-review.yml` lines ~290–319 (placeholder jobs and their inline + queue-wait comment citing `naruon#1528` run 33581213805). +- `.github/workflows/opencode-review-dispatch.yml` `coverage-source-tree`/`coverage-evidence`/ + `opencode-review-target` job definitions and their `permissions:` blocks. +- `tests/test_opencode_required_rerun_capacity.py` (event-driven wake contract, no polling). +- Commits `11a56305b`, `efc35f72b` (offline build caching already landed). +- GraphQL `checkSuites`/commit timestamps on `ContextualWisdomLab/bandscope` PRs #1227, #1188, + #1221, #1204, #1126 (2026-09-17). +- `docs/ci-baseline-20260916.md`, `docs/doctoring/actions-plan-concurrency-ceiling-20260903.md`, + `docs/adr/0030-ci-centralization-scope-given-plan-ceiling.md`. diff --git a/docs/doctoring/actions-queue-24h-remeasurement-20260917.md b/docs/doctoring/actions-queue-24h-remeasurement-20260917.md new file mode 100644 index 0000000000..15131ace08 --- /dev/null +++ b/docs/doctoring/actions-queue-24h-remeasurement-20260917.md @@ -0,0 +1,135 @@ +# Doctoring record: Actions queue remeasurement after #2232/#2233/#2235/#2236 (2026-09-17) + +- **Date:** 2026-09-17 +- **Subject:** After the coalesce-tick observability and fail-open repairs + (`#2232`, `#2233`) plus the same-day Strix/Noema evidence-binding merges + (`#2235`, `#2236`), remeasure org Actions queue depth, the concurrent-job + ceiling signal, and whether the five-minute coalesce tick now produces run + records under saturation. +- **Decision record:** none — diagnostic remeasurement only; does not authorize + plan-tier changes or further workflow edits by itself. +- **Measured at:** `2026-09-17T13:11:09Z`–`13:16:06Z` (UTC), via REST + (`gh api`), against the live `ContextualWisdomLab` organization. + +## Merge timeline (prerequisite) + +| PR | Merged (UTC) | Head (short) | Title | +|---|---|---|---| +| `#2232` | `2026-09-17T10:33:07Z` | `b49641744ed6` | step-scope coalesce tick gate so schedule produces run records | +| `#2233` | `2026-09-17T10:35:23Z` | `d35788d73ab5` | fail-open coalesce when tick has not completed recently | +| `#2235` | `2026-09-17T12:06:56Z` | `130ce425f74c` | Strix: bind findings/remediation claims to authenticated evidence | +| `#2236` | `2026-09-17T12:53:17Z` | `4fda7f504e58` | Noema: bounded transport-capacity re-dispatch | + +Protected `main` at measurement: `4fda7f504e58` (`#2236`). + +## 1. Org Actions queue depth + +Full census of all **66** non-archived, non-fork repositories +(`orgs/ContextualWisdomLab/repos`), summing +`actions/runs?status=in_progress|queued&per_page=1` → `.total_count`: + +| Metric | Value | +|---|---| +| Org-wide workflow runs `in_progress` (sum) | **48** | +| Org-wide workflow runs `queued` (sum) | **1,911** | +| `.github` alone | `in_progress=10`, `queued=342`–`343` | +| Open PRs org-wide (`search/issues` `is:pr is:open`) | **4,288** | +| `.github` schedule runs currently `queued` | **15** | + +Top queued repositories at the same sample: + +| Repository | `in_progress` | `queued` | +|---|---|---| +| `.github` | 10 | 342 | +| `codec-carver` | 11 | 131 | +| `fast-mlsirm` | 3 | 131 | +| `late-life-anxiety-reanalysis` | 0 | 121 | +| `newsdom-api` | 5 | 121 | +| `pg-erd-cloud` | 1 | 114 | +| `appguardrail` | 4 | 112 | +| `contextual-orchestrator` | 1 | 112 | +| `clearfolio` | 2 | 108 | + +**Reading:** the backlog shape is unchanged from the 2026-09-03 ceiling diagnosis +(`docs/doctoring/actions-plan-concurrency-ceiling-20260903.md`): single- to +low-double-digit `in_progress` against four-digit `queued` org-wide. The +coalesce/fail-open repairs did not drain the queue; they were never expected to. +Compared with that earlier 3-repo snapshot (`.github` 1,877 queued alone), +`.github`'s own queued count is lower (~342), but the org-wide sum remains +~1.9k with thousands of open PRs still feeding required workflows. + +## 2. Concurrent-job ceiling signal + +GitHub still does not expose the org plan concurrent-job quota through REST. +This remeasurement therefore corroborates the previously recorded **~60** +plan-level ceiling with live occupancy proxies: + +| Proxy | Value | Notes | +|---|---|---| +| Prior primary evidence | ~58–60 / 60 | User-observed billing UI, 2026-09-03 (same ceiling doc) | +| Org-wide runs `in_progress` | 48 / 66 repos | Run-level proxy; one run may hold multiple jobs | +| Jobs `in_progress` in 15 busiest repos | **42** | Sampled `runs?status=in_progress` → per-run `/jobs` | +| Hosted org runners API | `total_count=0` | No self-hosted pool; hosted plan quota is the ceiling | + +The occupancy band (mid-40s jobs/runs concurrently active while ~1.9k runs sit +`queued`) remains the signature of a hard org-wide concurrent-job ceiling, not +of a per-repository workflow defect. No billing-UI re-read was available to this +session; the **60** figure is carried forward from the prior primary evidence, +not independently re-derived from Settings → Actions. + +## 3. Coalesce tick run records since `#2232` + +Workflow: `.github/workflows/opencode-review-coalesce-tick.yml` (id `360129488`), +cron `*/5 * * * *`, concurrency group `opencode-review-coalesce-tick` with +`cancel-in-progress: false`. Repo variable +`OPENCODE_REVIEW_COALESCE_ENABLED=false` (coalescing still inert by design). + +| Observation | Evidence | +|---|---| +| Workflow `runs` total | `total_count=2` | +| Pre-`#2232` sample | `35191169833` at `2026-09-17T06:44:23Z`, `completed`/`skipped`, job skipped immediately (job-level gate era or equivalent) | +| Post-`#2232` sample | **`35219385415`** at `2026-09-17T12:07:50Z`, still `status=queued` at `13:16Z` | +| Post-merge job shape | Job `coalesce-tick` is `status=queued` (waiting for a runner), **not** immediately job-skipped — proves the step-scope gate admits the job into the runner queue | +| Flag still off | `vars.OPENCODE_REVIEW_COALESCE_ENABLED=false` → when the job eventually runs, the first step exits inert and remaining steps stay skipped | +| Schedule still enqueueing generally | `.github` `event=schedule&status=queued` → 15 runs (Daily Review Recovery, PR Auto Rebase, Required PR Review Merge Scheduler, this tick, …) | + +**Reading relative to `docs/doctoring/actions-schedule-run-records-20260917.md`:** +that earlier record measured `total_count=0` for this workflow while the gate was +job-scoped. After `#2232`, at least one schedule run record exists and is sitting +in the same org admission backlog as every other schedule job. Only one post-merge +tick run is present as of this sample (created ~94 minutes after `#2232` merged); +the workflow concurrency group (at most one active + one pending, no cancel of +in-flight) plus multi-hour runner wait explains why the five-minute cron does not +accumulate unbounded stacked run records while the first tick remains `queued`. + +`#2233`'s scheduler fail-open path is not exercised while the flag is `false` +(coalescing disabled → dispatch does not wait on tick completion). It remains +the safety net for the day the flag is flipped on under the same saturation. + +`#2235` / `#2236` are evidence-binding / transport repairs; they do not change +queue depth or tick observability. They are listed here only because this +remeasurement was gated on all four merges landing. + +## What this does / does not decide + +- **Does confirm:** step-scoped coalesce tick observability works under live + saturation (run + job enter `queued` instead of vanishing). +- **Does confirm:** org queue remains ceiling-bound (~48 concurrent runs / + ~42 sampled concurrent jobs vs ~1.9k queued). +- **Does not:** raise the plan tier, enable `OPENCODE_REVIEW_COALESCE_ENABLED`, + or claim the backlog is draining. +- **Still owner-only:** verify the exact concurrent-job quota on the org + Actions/Billing UI if the ~60 figure must be re-attested for a purchase + decision. + +## Audit trail + +- REST census script output: `/tmp/cwl-queue-census.json` (66-repo + `in_progress`/`queued` totals; ephemeral local cache for this session). +- `repos/ContextualWisdomLab/.github/actions/workflows/opencode-review-coalesce-tick.yml/runs` +- `repos/ContextualWisdomLab/.github/actions/runs/35219385415` (+ `/jobs`) +- `repos/ContextualWisdomLab/.github/actions/variables/OPENCODE_REVIEW_COALESCE_ENABLED` +- Prior related records: + `docs/doctoring/actions-schedule-run-records-20260917.md`, + `docs/doctoring/actions-plan-concurrency-ceiling-20260903.md`, + `docs/doctoring/actions-capacity-root-cause-20260917.md` diff --git a/docs/doctoring/actions-queue-cancelled-before-runner.md b/docs/doctoring/actions-queue-cancelled-before-runner.md new file mode 100644 index 0000000000..983f36549a --- /dev/null +++ b/docs/doctoring/actions-queue-cancelled-before-runner.md @@ -0,0 +1,56 @@ +# Actions queue cancellation before runner assignment + +## Status + +Proposed owner-side diagnostic extension for `ContextualWisdomLab/.github#1150` and the organization Actions incident tracked by `ContextualWisdomLab/.github#712`. + +## Problem + +A current pull-request head can produce a terminal GitHub Actions run whose job was cancelled before any runner was assigned or any step executed. Treating that evidence as a generic terminal job loses the first non-executed boundary and can mislead incident triage even though it must never count as passing evidence. + +Observed organization evidence on 2026-09-02 includes `.github#1653`, where a current-head `Repository Metadata Reconcile` job terminated `cancelled` after previously showing `runner_id=0`, empty runner identity, and `steps=[]`. Separate ContextualWisdomLab repositories also reproduce zero-job `startup_failure` and long-lived unassigned queue states, so these states must remain distinct rather than being collapsed into a product-source failure. + +A second adapter-boundary case is `pull_request_target`: GitHub records the workflow run against the base commit while the linked pull-request object carries the exact pull-request head. A terminal diagnostic collector that searches only by the current pull-request `head_sha` therefore cannot see a target-triggered cancellation even when its linked pull-request identity is current. Conversely, once target evidence is collected, a concurrent push or close can make the identity snapshot used for classification stale unless the collector revalidates the PR view after all terminal/job reads. + +## Decision + +The queue-health collector keeps external GitHub conclusion values unchanged at the adapter boundary and adds a semantic internal/report classification. For exact current heads it now: + +- retains `startup_failure` and `cancelled` terminal diagnostics from the bounded exact-`head_sha` `status=completed` query for ordinary pull-request/head-bound runs, filtering the returned conclusion locally; +- performs a bounded `status=cancelled&event=pull_request_target` candidate read for the target-triggered cancellation case and retains a candidate only after the existing linked pull-request number/head identity resolver proves it belongs to an exact current head; +- does **not** send `status=startup_failure` to GitHub's workflow-run list endpoint because that value is not in the endpoint's documented `status`/conclusion filter enumeration; target-triggered zero-job startup-failure discovery therefore remains a separate unresolved diagnostic gap rather than being implemented through an invalid REST request; +- fetches job evidence only for retained current-head terminal diagnostics; +- re-reads the bounded open-PR identity view after terminal and job evidence collection and fails the repository snapshot if PR number/state/head identity differs from the view used for classification; +- classifies a job as `cancelled_before_runner_assignment` only when both the run and that job conclude `cancelled`, the job has no runner assignment, and it has zero executed/materialized steps; +- never reclassifies sibling jobs that concluded `skipped`, `success`, or another non-cancelled state merely because their parent run concluded `cancelled`; +- keeps ordinary exact-head zero-job startup failures as `startup_failure_before_job_materialization`; +- reports an additive `admission_state` and a summary count without changing any GitHub check conclusion or synthesizing success; +- recommends inspection of Actions runner admission, billing/usage, runner-group policy, scheduler capacity, concurrency, and cancellation provenance rather than leaf-source churn or gate weakening. + +## TDD lineage + +RED commit `af72a26e0d1d845a7b447a63c7d4de4867815a87` added the first deterministic regression whose current-head cancelled run has one job with `runner_id=0`, an empty runner name, and `steps=[]`. GREEN commit `79e0758d0583474934327039b065956976c64453` introduced the initial cancellation classification. + +RED commit `b4f95bc290e625649b8ce7ae59e157c3869466f2` then captured two successor defects found on the live writer: a `pull_request_target` cancellation whose run-level SHA is the base commit but whose linked pull-request head is current, and a skipped sibling job inside a cancelled run that must not be counted as a pre-runner cancellation incident. GREEN commit `5a4950bb996f80f7be2519432a3f5b74bea02d58` added target-event candidate collection with linked-head verification and required the matched job itself to conclude `CANCELLED` before applying the semantic incident classification. + +Primary-source verification then found that GitHub's documented repository workflow-run `status` filter accepts `completed`, `action_required`, `cancelled`, `failure`, `neutral`, `skipped`, `stale`, `success`, `timed_out`, `in_progress`, `queued`, `requested`, `waiting`, and `pending`, but not `startup_failure`. RED `b99839bdcffecccc88b364a9813676b3964535b9` rejects any attempted `status=startup_failure` request in the deterministic target-cancellation fixture. GREEN `f567b1182308e4b45e22bf2f13b214998f59f5d0` narrows target-event filtering to the supported `cancelled` conclusion while leaving ordinary exact-head `status=completed` collection and local `startup_failure` conclusion classification intact. + +Review of that successor exposed a final consistency-window defect: target cancellation/job reads occurred after the collector's prior `final_pull_requests` read, so a later push or close could allow stale target evidence to survive. RED `d3a11383ce717217ec4c80a5d65c84aa947570e3` changes the PR head only after target and job evidence has been read and requires fail-closed rejection. GREEN `7683d2219c8007f9e7fa6001c98d0944290fa756` adds the post-evidence identity read and rejects any number/state/head divergence before a repository snapshot is emitted. + +## Compatibility and risk + +This is an additive diagnostic-contract change. It does not mutate repository branches outside the canonical PR, cancel/rerun Actions, alter branch protection, change database state, or modify an external GitHub schema. `status`, `conclusion`, `runner_id`, and related GitHub payload keys remain vendor-owned adapter fields; organization-owned report vocabulary uses semantic multiword names. + +Exact-head completed-run searches retain the existing twenty-page / 1,000-result fail-closed ceiling. Because GitHub's repository workflow-run API does not expose a pull-request-number filter for `pull_request_target`, cancelled target-event candidates are read by supported `cancelled` status and event under the same bounded ceiling, then filtered by linked current-head identity before retention. If that bounded candidate set is exceeded, or if the post-evidence PR identity view changes, the repository becomes explicit incomplete collection evidence rather than silently truncating or preserving stale evidence. This is an availability trade-off, not permission to synthesize success or churn leaf repositories. + +A cancelled run with a runner-assigned, step-executing, or non-cancelled matched job remains ordinary terminal evidence and is not reclassified as a pre-runner admission failure. A `pull_request_target` startup failure that cannot be discovered through the supported target-cancellation query also remains incomplete evidence; it is not silently treated as healthy. + +## Primary-source traceability + +GitHub, Inc. (2026). *REST API endpoints for workflow runs*. GitHub Docs. Retrieved September 2, 2026, from https://docs.github.com/en/rest/actions/workflow-runs + +The documented endpoint contract is treated as the authority for request-filter vocabulary; live GitHub run payloads remain the authority for observed run conclusions. The distinction prevents an undocumented observed conclusion such as `startup_failure` from being incorrectly assumed to be a valid REST query-filter value. + +## Verification + +Only checks produced from the unchanged final `ContextualWisdomLab/.github#1150` head qualify. Queued, pending, cancelled, zero-job startup failures, predecessor checks, or stale reviews are incomplete evidence and must not be transferred to a newer head. The RED/GREEN lineage above documents source intent; hosted 100% statement/branch/docstring and required-workflow evidence must be re-established on the final exact head before ordinary merge. \ No newline at end of file diff --git a/docs/doctoring/actions-queue-health.md b/docs/doctoring/actions-queue-health.md new file mode 100644 index 0000000000..e7d08007a2 --- /dev/null +++ b/docs/doctoring/actions-queue-health.md @@ -0,0 +1,106 @@ +# GitHub Actions queue-health evidence + +The scheduled `actions-queue-health.yml` workflow reads a fixed allowlist of +CWL repositories once per hour and publishes a JSON report plus a keyboard- +readable HTML report as an artifact. The collector uses only `gh api` reads +through the configured cross-repository `PR_REVIEW_MERGE_TOKEN` or +`OPENCODE_APPROVE_TOKEN`; it fails visibly when neither credential is present. +It does not cancel runs, mutate branches, dispatch workflows, or alter merge +gates, and it never relies on the central repository's scoped `GITHUB_TOKEN` +for sibling-repository reads. + +The report schema is `actions.queue_health.v1`. Each observed run records its +repository, pull-request number, head SHA, event, run attempt, concurrency +group (or an explicit unavailable marker), stable workflow identity, queue age, +job state, and runner assignment. When GitHub supplies a positive +`workflow_id`, the report exposes `workflow_identity` as `workflow_id:` and +uses that value for duplicate-lane grouping; `workflow_name` remains +presentation data. Older/offline v1 snapshots that lack `workflow_id` retain a +compatibility fallback of `workflow_name:`. A malformed present +`workflow_id` fails closed instead of being coerced. + +A run is `current_head` only when its linked open pull request and head SHA +match. The match compares the open pull request's head SHA against the *linked* +pull-request entry's head SHA carried on the run (`run.pull_requests[].head.sha`), +never against the run-level `head_sha`. `pull_request_target`-triggered runs +report the base-branch commit that was checked out as their run-level +`head_sha`, so comparing against that value would misclassify a genuinely +active, current required-workflow run as obsolete and skip its job evidence. +Stale linked runs are `obsolete`; runs without a pull-request link are +`unlinked`. Queued evidence remains incomplete even when a report is +successfully produced. GitHub's `waiting` job status (paused on an environment +or deployment approval) is also treated as pending evidence, distinct from a +runner-capacity blocker. + +Pull-request identity is sampled before and after the bounded active-run +sweeps. The repository snapshot is accepted only when the open pull-request +number/state/head view is unchanged. A push, closure, or other identity change +between those samples becomes repository-scoped incomplete evidence instead of +being allowed to invert current/obsolete classification. A pull-request +response with incomplete head/base identity retains one bounded retry after a +one-second delay. + +Queue age for a fetched job is measured from that job's own `created_at`, not +the parent run's, so a later job in an already in-progress run (for example one +gated by `needs:`) that only just became eligible is not measured against the +whole run's age and does not trigger a false capacity-breach alert. Every row +exports both `queue_age_started_at` and `queue_age_source` (`job_created_at` or +`run_created_at`) so consumers can reproduce the reported `queue_age_seconds`. +Requested, pending, and queued runs intentionally use run-level evidence when +GitHub has not supplied job detail. + +Two bounded active-status sweeps run in opposite orders and must agree before +the snapshot is accepted. This prevents historical completed runs from +exhausting the bound while rejecting evidence that changes between partitioned +reads. Each status read is capped at one 50-run page, limiting collection to ten +run-list calls per repository; exceeding the cap is reported as incomplete +evidence. Current-head `in_progress` and `waiting` runs make the additional jobs +API read needed to distinguish concrete runner assignment from an environment +or deployment approval wait. + +List endpoints use collector-controlled GitHub API pagination with at most 20 +explicit page reads; the collector never asks GitHub CLI to download an +unbounded page set and never requests page 21. Pull-request and job lists use +pages of 100 records; workflow-run lists use pages of 50 so a large Actions +queue does not require one oversized response. An incomplete, malformed, or +larger response is recorded as repository-scoped incomplete evidence and the +collector continues with the remaining allowlisted repositories; it never +silently claims that the visible page is the whole queue. The JSON and HTML +reports expose each collection error explicitly. + +Every external `gh api` read has a 30-second subprocess timeout, and the +collector job has a 30-minute execution ceiling. A timeout is typed as +incomplete queue evidence rather than success. Repository names reject `.` and +`..` path segments. Offline snapshots also reject duplicate repository entries +before counting runs so repeated input cannot inflate the reported queue. + +The default queue-age SLO is 900 seconds. A current-head job that remains +unassigned beyond that limit produces a warning and an explicit manual action +to inspect runner capacity, billing, runner-group policy, environment approval, +and concurrency saturation. The workflow intentionally remains read-only and +fail-closed when GitHub API or runner evidence is unavailable. Paged API reads +are not atomic; changing totals are retained only when the collected records +cover the largest observed total, and the report remains explicitly an +observation rather than a merge decision. + +Implementation ownership is intentionally split without duplicate collector +copies: `actions_queue_health_core.py` owns the shared bounded parsing and +reporting primitives, while the executable `actions_queue_health.py` entrypoint +owns stable pull/workflow identity and audit-provenance reconciliation. Tests +load the executable boundary used by the scheduled workflow. + +The allowlist is deliberately explicit in +`config/actions_queue_health_repositories.json`; adding a repository requires +review of its governance and data boundary. This first slice does not claim +that a queued run is obsolete or safe to cancel. + +## References + +GitHub. (n.d.). *REST API endpoints for workflow runs*. Retrieved August 20, +2026, from https://docs.github.com/en/rest/actions/workflow-runs + +Internet Engineering Task Force. (2022). *HTTP semantics* (RFC 9110). +https://www.rfc-editor.org/rfc/rfc9110 + +OWASP Foundation. (n.d.). *Path traversal*. Retrieved August 20, 2026, from +https://owasp.org/www-community/attacks/Path_Traversal diff --git a/docs/doctoring/actions-schedule-run-records-20260917.md b/docs/doctoring/actions-schedule-run-records-20260917.md new file mode 100644 index 0000000000..d82ab19568 --- /dev/null +++ b/docs/doctoring/actions-schedule-run-records-20260917.md @@ -0,0 +1,52 @@ +# Doctoring record: scheduled workflows still enqueue run records under saturation; coalesce tick had none (2026-09-17) + +- **Date:** 2026-09-17 +- **Subject:** After org-wide Actions saturation (~01:32Z), operators observed queued + schedule runs sitting for 3+ hours and believed no new schedule records were being + created. The coalesce tick workflow (`opencode-review-coalesce-tick.yml`, id + `360129488`) showed zero runs while `OPENCODE_REVIEW_COALESCE_ENABLED=false`. +- **Decision record:** none — diagnostic plus a step-scoped gate repair for the tick + workflow. + +## Measured evidence + +REST sample gathered 2026-09-17 (`repos/ContextualWisdomLab/.github/actions/runs`): + +| Observation | Evidence | +|---|---| +| Schedule runs still created after 01:32Z | `35170930384` Repository Metadata Reconcile at `2026-09-17T01:32:08Z` (queued); `35182924821` Daily Review Recovery at `04:42:31Z` (queued); `35183563151` PR Auto Rebase at `04:52:33Z` (queued) | +| Six schedule runs currently queued | `status=queued&event=schedule` → `total_count=6` | +| Coalesce tick zero runs | `actions/workflows/opencode-review-coalesce-tick.yml/runs` → `total_count=0` | +| Coalesce flag off | repo variable `OPENCODE_REVIEW_COALESCE_ENABLED=false` | + +The org-wide stall is therefore **runner admission under the plan concurrent-job ceiling** +(`docs/doctoring/actions-plan-concurrency-ceiling-20260903.md`), not GitHub ceasing to +create schedule run records entirely. New schedule records continue to arrive; they +queue behind thousands of other jobs and rarely reach `in_progress`. + +## Coalesce tick zero-run root cause + +The tick workflow used a **job-level** `if: vars.OPENCODE_REVIEW_COALESCE_ENABLED == 'true'`. +When the variable is `false`, GitHub does not enqueue a workflow run for that schedule +event at all — confirmed live: zero runs since merge at `f9863d941` even though the +five-minute cron has elapsed many times. That made the tick invisible in the Actions UI +and prevented `recent_coalesce_tick_completed()` from ever observing a completed tick, +which would have blocked review dispatch indefinitely had coalescing stayed enabled without +the scheduler fail-open repair. + +## Repair + +1. **Scheduler fail-open** (`scripts/ci/pr_review_merge_scheduler_core.py`): when + coalescing is enabled but no tick completed within `600s` (2× the cron interval), + `dispatch_opencode_review()` dispatches immediately instead of returning `coalescing`. +2. **Tick observability** (`opencode-review-coalesce-tick.yml`): move the flag gate from + job scope to step scope so every cron produces a run record; only the substantive steps + are skipped when the variable is false. + +## Audit trail + +- `/tmp/gh-cache-lead/schedule-runs-all.json`, `/tmp/gh-cache-lead/schedule-queued.json` + (REST, 2026-09-17). +- `repos/ContextualWisdomLab/.github/actions/workflows/opencode-review-coalesce-tick.yml/runs`. +- `docs/doctoring/actions-capacity-root-cause-20260917.md`, + `docs/doctoring/actions-plan-concurrency-ceiling-20260903.md`. diff --git a/docs/doctoring/all-self-hosted-runner-routing-20261003.md b/docs/doctoring/all-self-hosted-runner-routing-20261003.md new file mode 100644 index 0000000000..5eebc87357 --- /dev/null +++ b/docs/doctoring/all-self-hosted-runner-routing-20261003.md @@ -0,0 +1,48 @@ +# All-self-hosted workflow routing — staged rollout + +## Authority and scope + +On October 3, 2026, the user directed this SDP workstream: “self hosted runner로 모두 전환”. This change covers every runner declaration in the central `.github` repository (73 jobs) and SDP (4 jobs). It does not claim migration of unrelated ecosystem repositories or deployment of runner infrastructure. + +## Routing and isolation + +Existing restricted control, CodeQL and OpenCode groups stay intact. Existing exact-main workflow predicates and repository predicates stay intact; only their GitHub-hosted fallback changes. Ordinary jobs and non-main fallbacks require the dedicated `CWL CI isolated` runner group and all labels `self-hosted`, `linux`, `x64`, `cwlab-ci-isolated`. Both constraints are mandatory: GitHub routes a group selector through the group's repository access boundary and then matches every label inside that group. The label remains a provisioning contract, not a claim that a suitable runner already exists. Never add a privileged control/review runner to this group or apply the isolation label merely to release queued PR jobs. + +PR code must execute on disposable, job-isolated machines without production credentials, privileged Docker sockets, internal-network access or state shared with trusted control jobs. Rebuild the job environment after each job. Secret-bearing trusted jobs need separate disposable instances from untrusted PR jobs. Labels are mutable metadata and GitHub does not validate that the `linux` and `x64` labels match the runner machine; label-only routing therefore cannot prove the authority boundary. The dedicated group is the repository-access boundary, while the labels express the required platform and isolation capability inside it. Keep current token permissions, environment approvals, immutable artifact checks, exact-head review and required merge gates. This patch declares the required group but does not widen runner-group allowlists, register runners, alter secrets or bypass protection. + +R reusable jobs use Linux/x64-only admission: every requested leg must name `ubuntu-latest` or `ubuntu-24.04`. R versions and other fields remain unchanged for an admitted matrix. Any unsupported or malformed leg stops the complete matrix with STOP / nonzero, including mixed and unsupported-only matrices; no requested check is dropped to manufacture success. Admission and R execution both use the fixed `CWL CI isolated` group with `self-hosted`, `Linux`, `X64`, and `cwlab-ci-isolated` labels. Windows, macOS, ARM and unknown images are not platform coverage, and stopped legs have no queued R check. Linux instances still need compatible userspace and CI dependencies; labels do not install packages or attest the image. + +The first group-scoped R selector encoded its runner object with unescaped +literal JSON braces inside GitHub's `format()` expression. GitHub requires +literal braces in a format template to be doubled; the unescaped opening brace +was parsed as a replacement field and stopped the matrix before runner +admission. RED `f9211898d83f039cdf7ff82c8f93c72c9c10dceb` +renders the exact workflow template for Linux, Windows, macOS and an unsupported +OS and reproduces the failure. GREEN +`7fc1e2e8ad5091adff6c56b501e93d1d4ac2f211` doubles only the outer braces, +retains `{0}` and `{1}`, and passes all eight focused contracts both normally +and with `GITHUB_ACTIONS=true`. This proves selector serialization only; it does +not prove that the runner group, capacity, cleanup or operating-system images +exist. + +## Actual admission observation and rollout prerequisite + +The complete organization runner inventory read during preparation had 9 runners: 7 online Linux x64 and 2 offline. None carried `cwlab-ci-isolated`; no registered Windows or macOS runners were observed, and no verified `CWL CI isolated` group/capacity record was available. Existing groups contain privileged or workflow-restricted runners. Therefore this configuration is not runtime-ready and must not be enabled or merged before the authorized runner operator creates the dedicated group, limits its repository access, and provides disposable isolated capacity. The OpenCode group's sole runner `cwlab-s1-04` was offline; changing fallback routing does not repair it. + +Before promotion, the operator must return real runner identity/group/access records, prove that `CWL CI isolated` excludes privileged pools and admits only the intended repositories, demonstrate isolated cleanup and tool availability, and produce a successful canary job plus every exact-head required gate. Provisioning and runtime canaries are not completed by local YAML parsing or pytest. Preserve existing current-head executions; do not cancel or duplicate model reviews. Ship through normal reviewed protected PRs only, after prerequisites are satisfied. + +## Verification and rollback + +Permanent regression tests enumerate declarations, reject every GitHub-hosted fallback, require the dedicated group whenever `cwlab-ci-isolated` is selected, and keep PR-executing jobs outside privileged pools. Existing workflow-contract assertions are migrated rather than deleted. A structured YAML inventory verifies all 77 central/product job declarations; normal test suites and immutable source comparisons are separate evidence. Rollback is an ordinary reviewed revert of the migration PRs, not a protection change, group-access expansion or runner relabeling. + +## Primary sources (APA 7) + +GitHub. (n.d.). Choosing the runner for a job. GitHub Docs. Retrieved October 3, 2026, from https://docs.github.com/en/actions/how-tos/write-workflows/choose-where-workflows-run/choose-the-runner-for-a-job + +GitHub. (n.d.). *Expressions: format*. GitHub Docs. Retrieved October 3, 2026, from https://docs.github.com/en/actions/reference/workflows-and-actions/expressions#format + +GitHub. (n.d.). Self-hosted runners reference. GitHub Docs. Retrieved October 3, 2026, from https://docs.github.com/en/actions/reference/runners/self-hosted-runners + +GitHub. (n.d.). Managing access to self-hosted runners using groups. GitHub Docs. Retrieved October 3, 2026, from https://docs.github.com/en/actions/how-tos/manage-runners/self-hosted-runners/manage-access + +GitHub. (n.d.). Using self-hosted runners in a workflow. GitHub Docs. Retrieved October 3, 2026, from https://docs.github.com/en/actions/how-tos/manage-runners/self-hosted-runners/use-in-a-workflow diff --git a/docs/doctoring/central-coverage-owner-stack-2521.md b/docs/doctoring/central-coverage-owner-stack-2521.md new file mode 100644 index 0000000000..412899d94e --- /dev/null +++ b/docs/doctoring/central-coverage-owner-stack-2521.md @@ -0,0 +1,46 @@ +# Central coverage owner stack RCA + +## Incident and ownership + +`ContextualWisdomLab/.github#2521` owns the repository-wide Python statement +and branch coverage repair. Concurrent PRs carried valid prerequisites rather +than competing implementations: #2530 owns the hash-pinned parser inputs used +by complete test collection, and #2532 owns explicit closure of file-like +GitHub `HTTPError` responses. Both were preserved as ordinary merge parents; +neither failure was treated as a reason to discard a PR. + +## Root causes + +The first warning-fatal integrated run separated three causes: + +1. the predecessor tree lacked parser dependencies required during collection; +2. synthetic GitHub redirect/error paths leaked response objects under Python + 3.14 and therefore failed when `ResourceWarning` was promoted to an error; +3. four central scripts retained executable paths that their tests did not + reach, while two tests invoked a live `gh` boundary or asserted nothing. + +The remaining coverage work was test-first and behavior-bound. It exercises +queue admission/cancellation, bounded repository scans, exact Git blobs, +Cargo workspace and development-lock identity, runtime receipt architecture, +release fanout limits, and Python 3.10 TOML parser fallback. The runtime archive +prescreener's final repeated count check was proven unreachable because its +earlier exact-cardinality, uniqueness, and per-archive validation already +reject every false case; only that redundant branch was removed. + +## Evidence and acceptance + +- RED integrated evidence: 5,196 passed, 6 skipped; 90 statements missed and + 29 partial branches across the four remaining owner modules. +- Focused release dependency evidence: 950 passed with warnings treated as + errors. +- GREEN combined-successor evidence after re-fetching and ordinarily merging + current #2530 and #2521 heads: 5,291 passed, 5 skipped, 40 subtests passed; + 18,729/18,729 production statements and 7,642/7,642 branches covered, with + production Docstring coverage at 100%. +- No warning, security result, or fail-closed input validation was suppressed. + +This local result is not merge authority. Acceptance requires publication to +the re-fetched #2530 successor branch without force, fresh hosted Checks bound +to that exact commit, qualifying independent review, and ordinary protected +merge. The #2521 predecessor remains open until that protected merge and +complete tree carryover are verified. diff --git a/docs/doctoring/central-dedicated-runner-routing-20260927.md b/docs/doctoring/central-dedicated-runner-routing-20260927.md new file mode 100644 index 0000000000..aa84465589 --- /dev/null +++ b/docs/doctoring/central-dedicated-runner-routing-20260927.md @@ -0,0 +1,133 @@ +# Central dedicated runner routing + +## Status + +Proposed workflow change; organization runner groups and five S1 runners are +already deployed. This document does not assert protected-main adoption. + +## Context + +The 2026-09-27T09:41:06.945544+00:00 collection recorded 610 unique queued central runs. +Trusted-main dispatch queues included 61 CodeQL and 31 OpenCode runs; three +control workflows had 18 main-branch runs. Older runs may be stale, so these +counts are allocation evidence, not exact-head merge evidence. + +An exact-rational linear relaxation plus exhaustive integer allocation selected +one additional runner for each lane under a four-core host CPU-quota budget, +32 GiB additional guest RAM and 160 GiB sparse-disk budget. Existing two runners +were preserved. Historical successful job duration sums excluded queue waits. +The forecast is sensitive to service times; measured latency improvement remains +unverified. The detailed calculation is retained in the system-management task's +`central-runner-optimization-20260927.md` and JSON input/output receipts. + +## Decision + +Declare dedicated routing in the five workflow files: + +| Workflow | Runner selection | +| --- | --- | +| CodeQL scan dispatch | Group `CWL central CodeQL`, labels `self-hosted`, `linux`, `x64` | +| OpenCode review dispatch | Group `CWL central OpenCode`, labels `self-hosted`, `linux`, `x64` | +| Agent mention router | Group `CWL central control`, labels `self-hosted`, `linux`, `x64` | +| Hourly review recovery | Group `CWL central control`, labels `self-hosted`, `linux`, `x64` | +| PR review merge scheduler | Central caller: self-hosted Linux X64 with `cwlab-control`; other callers: `ubuntu-24.04` | + +Groups 4/5/6 allow only the central repository and their selected workflow paths +at `refs/heads/main`. Keep those restrictions and external contributor approval. +The control runner has the `cwlab-control` label. A reusable workflow inherits +the caller's repository context, so its consumer branch must retain hosted access. + +The OpenCode guest exposes four virtual CPUs and 20 GiB RAM to satisfy the +existing four-CPU/14-GiB Docker sandbox, with host CPUQuota 100%. CodeQL has two +CPUs/eight GiB; control has one CPU/four GiB. Existing guests remain running; +their group excludes future OpenCode dispatch because their three visible CPUs +cannot satisfy that Docker request. A real container resource check passed on +the new OpenCode guest. Do not change model deadlines, providers, permissions, +concurrency, or protected review requirements to compensate for admission delay. + +## Consequences and alternatives + +Explicit selectors keep long reviews separate from short control work. Native +organization group restrictions remain the trust boundary. A missing dedicated +runner now queues the central job instead of silently choosing a hosted runner. + +Generic Ubuntu labels alone served existing queued jobs, but did not express +durable lane selection in source. Expanding central groups to every consumer +repository would weaken their access boundary; caller-aware scheduler routing +avoids that expansion. No new manual dispatch trigger or PR-branch group access +is added. + +## Verification + +Check workflow syntax, runner-selection contracts, the independent OpenCode +workflow blob pin, and existing affected contracts. Native assignment receipts +already show CodeQL dispatch and OpenCode review execution on the new runners +and a successful control queue job; they do not prove this proposed source has +landed or that all required review gates pass. + + +## Noema control admission follow-up + +At 2026-09-27 10:38 UTC the organization API listed five online runners, +while the central repository still listed 455 queued runs. Group 6's control +runner was idle in the subsequent group-membership observation; these are +point-in-time observations, not a measured capacity forecast. + +Noema's admission, changed-scope, closed-run cleanup, and post-failure +re-dispatch jobs now select `self-hosted`, `Linux`, `X64`, `cwlab-control` +only in the central repository. The concurrent #2421 allocation for contextual-orchestrator consumers is preserved; other consumer repositories retain Ubuntu 24.04. +The concurrent #2421 model-review allocation to the MCP remediation pool is preserved; this change allocates short +control work and does not assert compatibility of a model sandbox with the +one-core/four-GiB control guest. + +Deployment requires group 6's existing trusted-main workflow allowlist to +include `ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main`. +Preserve every existing allowlist entry, repository restriction, and external +contributor approval. Do not allow a feature-branch ref. Until that grant is +verified, the source change is not an operational routing repair. + + +## Issue 1565 review admission follow-up + +The SDK and naruon consumer Noema jobs still selected hosted Ubuntu after the +initial runner rollout. Extend the existing repository allowlist to +`ContextualWisdomLab/cwl-telemetry` and `ContextualWisdomLab/naruon`, only when +`github.workflow_ref` is exactly the central Noema workflow at `refs/heads/main`. +Metadata and continuation use the control pool; model review uses MCP remediation. +PR-authored workflow refs retain hosted execution and existing fork admission, +credentials, review publication, concurrency and inference-time policy remain. + +At 2026-09-27 12:12 UTC, group 3 repository membership was verified after two +repository-specific PUT requests. Its selected-workflow restrictions remain; +group 6 already allows repositories subject to its selected-workflow restrictions. +This is runner admission, not approval or evidence of a completed model review. +Existing queued runs retain their original workflow revision and may still wait +until event-driven current-head recovery creates a new run. + +The allocation calculation from the initial rollout is reused; no new host +capacity or independent service-time measurement justifies another solver. +The routing regression fails against the unchanged baseline. Workflow syntax +and affected contracts passed: 238 passed, 2 skipped with `GITHUB_ACTIONS=true`; +`actionlint` and `git diff --check` passed. + + +## fast-mlsirm Strix control admission + +Current fast-mlsirm PR #2220 head `4eaeb799a6647ea29f3f4902d9ca79a1377e795c` +queued Strix admission job `108617323217` with `ubuntu-24.04`, despite the +self-hosted rollout. Route only changed-scope, current-head admission, +superseded-run cleanup and manual status publication through group 6 when +the source is exactly central `strix.yml@refs/heads/main` and the caller is +the central repository or fast-mlsirm. These jobs do not check out PR code. +The model scan keeps its existing hosted image and all evidence, credentials, +fork handling and live-head validation remain intact. + +Reuse the deployed allocation; no new service-time or capacity measurement +justifies a different solver result. Deployment requires adding only central +`strix.yml@refs/heads/main` to group 6's selected workflows, preserving all +existing restrictions and grants. Old queued jobs keep their original source. + +The routing test failed on the unmodified workflow. The affected runner, +changed-scope and dependency-hash tests passed (21 tests); actionlint and +diff whitespace checks passed. This is local source proof, not completed +consumer gate evidence. diff --git a/docs/doctoring/central-review-handoff-and-runner-20261009.md b/docs/doctoring/central-review-handoff-and-runner-20261009.md new file mode 100644 index 0000000000..551beb914f --- /dev/null +++ b/docs/doctoring/central-review-handoff-and-runner-20261009.md @@ -0,0 +1,84 @@ +# Central review handoff and runner repair + +## Scope + +This change repairs existing review control paths. It does not claim that the +organization's hosted workload migration, LiteLLM onboarding, or product +validation is complete. + +- Central Noema handoff/base binding is already owned by + ContextualWisdomLab/.github#2111. A duplicate implementation was removed + from this change; this patch does not claim that repair is deployed. +- OpenCode's immediate post-approval scheduler invocation is dry-run, + merge-disabled, auto-merge-disabled, and branch-update-disabled. A formal + review is evidence, not permission for that job to mutate the reviewed PR. +- The coalescing tick is a trusted central-main schedule with no PR checkout. + It selects the already-admitted `CWL MCP remediation` group with + `self-hosted`, `linux`, and `x64` labels. The existing job-level enable flag + still prevents inert ticks from obtaining a runner. +- The coalescing invocation explicitly selects `--merge-mode disabled` as + well as disabling auto-merge and branch updates. Disabling auto-merge alone + does not disable a direct-merge mode. + +The regular review-event and scheduled merge scheduler paths are unchanged. +This patch does not establish an end-to-end no-merge guarantee for a PR that +those separately authorized paths inspect. + +## Current operational evidence + +GitHub check-run annotations for +`ContextualWisdomLab/psychometrics-commons#165`, head +`bb9028714d996871ed2588ea9a3aa83fd581af84`, reported that the hosted job could +not start because the account was locked due to a billing issue. The jobs +had runner id `0` and no executed steps. They are not executed test failures. + +The product fix was previously verified locally, including branch coverage +1724/1724. That evidence does not replace GitHub required checks, independent +review, or an admitted isolated self-hosted build lane. + +An organization runner-group readback showed `CWL MCP remediation` already +admitted the coalescing workflow at central `refs/heads/main`, with two online +runners. Runtime availability is re-read before dispatch; this observation is +not a permanent capacity claim. + +## Remaining prerequisites + +- Product PR execution must use a verified isolation boundary; do not send + untrusted builds to shared metadata/review runners. +- The scheduler recognizes only the canonical GitHub-authenticated Noema + App as an exception to blanket bot exclusion. It requires the real review + commit to match the exact head, a coherent publisher head/result/actor/App + credential receipt, and non-author identity after REST/GraphQL normalization. + The latest relevant formal review is resolved before receipt validation: + revocations or malformed latest approvals cannot resurrect older approvals. + This establishes independent-review eligibility only; GitHub's aggregate + approval policy, checks, security evidence, threads, and merge freshness + remain separate and unchanged. +- Current review configuration remains `orchestrator/free`; it is not a + literal `auto` model setting. An authenticated LiteLLM onboarding and + explicit gateway contract/policy update are required before claiming the + requested `auto` route. +- The public LiteLLM schema at `https://litellm.poinnetworks.net/openapi.json` + declares authentication for `/key/generate`; no unauthenticated key was + requested or issued. A dedicated inference credential must enter the + gateway's approved secret boundary, not source, logs, or review text. +- No hosted security or product-validation workflow was disabled by this + patch. Mandatory unavailable evidence stays non-passing until an equivalent + isolated replacement is verified. + +## Verification + +Regression tests enforce assessment-only post-publication scheduler arguments +and pin the trusted coalescer's runner selection. Workflow shell syntax and +actionlint validate the modified callers. Two pre-existing CodeQL tests now +freeze their default-clock input so September fixtures do not expire under +October's real wall clock; production freshness thresholds are unchanged. + +## References + +- GitHub. (n.d.). *Secure use reference*. + https://docs.github.com/en/actions/reference/security/secure-use +- GitHub. (n.d.). *Events that trigger workflows*. + https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows +- LiteLLM. (n.d.). *Self serve*. + https://docs.litellm.ai/docs/proxy/self_serve diff --git a/docs/doctoring/co-noema-control-allocation-20260927.md b/docs/doctoring/co-noema-control-allocation-20260927.md new file mode 100644 index 0000000000..d9a5922f6f --- /dev/null +++ b/docs/doctoring/co-noema-control-allocation-20260927.md @@ -0,0 +1,26 @@ +# Contextual Orchestrator Noema control allocation + +## Structure and gap + +After #2420, the four metadata-only Noema jobs used the control group only for +`.github`. Contextual Orchestrator still placed admission and scope detection +in the two-runner model pool. On 2026-09-27, run 36314265013 had both jobs queued +with no runner assignment while both remediation runners were busy. All five +organization runners were online; the idle CodeQL runner was workflow-restricted. + +## Allocation + +Apply the existing control allocation to both already admitted repositories. +Keep the trusted-main guard, hosted fallback, exact-head admission, permissions, +and model job unchanged. The control group permits all repositories but limits +execution to explicit central workflows at main; its allowlist already includes +Noema. These four jobs do not check out PR code. No optimizer or broader runner +access is needed for this fixed eligibility partition. + +## Verification and limits + +The five focused runner, queue, admission and Noema contract files completed +222 tests locally. Actionlint passed. Hosted current-head execution and actual +queue drainage must be checked after deployment; local tests do not establish +runner capacity or independent model approval. Rollback restores only the four +runner expressions from parent revision efe71f4. diff --git a/docs/doctoring/coalesce-tick-inert-runner-queue-20260917.md b/docs/doctoring/coalesce-tick-inert-runner-queue-20260917.md new file mode 100644 index 0000000000..0427c80675 --- /dev/null +++ b/docs/doctoring/coalesce-tick-inert-runner-queue-20260917.md @@ -0,0 +1,82 @@ +# Doctoring record: coalesce tick run 35219385415 queued 3h+ for an inert job (2026-09-17) + +- **Date:** 2026-09-17 +- **Subject:** Why `OpenCode Review Coalesce Tick` run `35219385415` stayed + `status=queued` for more than two hours on `ContextualWisdomLab/.github`, + whether a lighter job or concurrency change is warranted, and the repair. +- **Decision record:** none — diagnostic plus a workflow gate correction. +- **PR:** this commit's pull request. + +## Live evidence (gathered 2026-09-17) + +| Observation | Evidence | +|---|---| +| Stuck run | `35219385415`, event `schedule`, created `2026-09-17T12:07:50Z`, still `status=queued` / `conclusion=null` at `15:32Z` (~3h25m) before operator cancel | +| Head at enqueue | `130ce425f74c` (post-`#2235`; coalesce tick workflow already on `main` via `#2232`) | +| Workflow inventory | Only **2** runs total for workflow id `360129488`: pre-gate `35191169833` and the stuck `35219385415` | +| Pre-`#2232` contrast | `35191169833` at `06:44:23Z` → `completed`/`skipped` by `06:44:24Z` (1s; job-level gate era) | +| Coalesce flag | repo variable `OPENCODE_REVIEW_COALESCE_ENABLED=false` (updated `2026-09-17T04:21:48Z`) | +| Runner label | workflow `runs-on: ubuntu-24.04` (not floating `ubuntu-latest`) | +| Concurrency | group `opencode-review-coalesce-tick`, `cancel-in-progress: false` | +| Org ceiling context | Prior same-day census: ~48 org-wide `in_progress` vs ~1,911 `queued` (`docs/doctoring/actions-queue-24h-remeasurement-20260917.md`); plan concurrent-job ceiling ~60 (`docs/doctoring/actions-plan-concurrency-ceiling-20260903.md`) | + +Operator cancel of `35219385415` at `15:36:29Z` reached `completed`/`cancelled` so the concurrency group no longer holds a forever-pending inert tick. + +## Root cause + +Not a missing runner label, not a hung step, and not a defect in the +org-wide GraphQL / scheduler loop (those steps never started). + +`#2232` moved `OPENCODE_REVIEW_COALESCE_ENABLED` from a **job-level** `if:` to +a **step-level** gate so that every five-minute cron would still produce a +visible run record while coalescing stayed off. That succeeded at producing +records, but it also forced GitHub to **admit the job into the shared runner +queue** even when the only work would be an inert `echo` and `exit 0`. + +Under the org's plan concurrent-job ceiling the inert job sits behind ~10³ +other queued runs. `cancel-in-progress: false` is correct for an in-flight +org-wide dispatch (do not cut mid-repository), and with at most one active + +one pending member it also explains why the five-minute cron did not +accumulate unbounded stacked run records while `35219385415` remained the +active waiter. + +The earlier claim that a job-level `if:` "suppressed every run record" +(`docs/doctoring/actions-schedule-run-records-20260917.md`) does not hold +against `35191169833`, which is a completed/`skipped` schedule run from the +job-level-gate era. Skipped jobs still create run records; they simply do not +wait for a runner. + +## What is / is not warranted + +| Lever | Verdict | +|---|---| +| Lighter job when flag is false | **Yes** — restore job-level `if:` so disabled ticks skip before runner admission | +| Change `cancel-in-progress` to `true` | **No** — would cancel an in-flight org-wide dispatch mid-repository; does not shorten admission wait for the active waiter | +| Different `runs-on` label | **No** — already pinned to `ubuntu-24.04`; hosted labels share the same plan ceiling | +| Plan-tier / more concurrent jobs | Owner-only; still the only way to make an *enabled* tick admit quickly under saturation | +| Scheduler fail-open (`#2233`) | Keep — when the flag is on and a real tick queues for hours, dispatch must not defer forever | + +When coalescing is later enabled, a real tick still competes for the same +ceiling; that is accepted. `recent_coalesce_tick_completed()` must treat only +`conclusion=success` as a healthy tick so a disabled-era `skipped` run cannot +be mistaken for proof that coalesce dispatch is alive after the flag flips on. + +## Repair + +1. Restore job-level `if: vars.OPENCODE_REVIEW_COALESCE_ENABLED == 'true'` on + `coalesce-tick` and drop the step-scoped inert/echo gate. +2. Require `conclusion == "success"` in `recent_coalesce_tick_completed()`. +3. Cancel the stuck inert run (`35219385415`) so it no longer occupies the + concurrency group (done live during this investigation). + +## Audit trail + +- `repos/ContextualWisdomLab/.github/actions/runs/35219385415` +- `repos/ContextualWisdomLab/.github/actions/runs/35191169833` +- `repos/ContextualWisdomLab/.github/actions/workflows/360129488/runs` +- `repos/ContextualWisdomLab/.github/actions/variables/OPENCODE_REVIEW_COALESCE_ENABLED` +- Prior related records: + `docs/doctoring/actions-schedule-run-records-20260917.md`, + `docs/doctoring/actions-queue-24h-remeasurement-20260917.md`, + `docs/doctoring/actions-plan-concurrency-ceiling-20260903.md`, + `docs/doctoring/actions-capacity-root-cause-20260917.md` diff --git a/docs/doctoring/coalesce-tick-post-2242-live-verify-20260917.md b/docs/doctoring/coalesce-tick-post-2242-live-verify-20260917.md new file mode 100644 index 0000000000..9cd0f5d240 --- /dev/null +++ b/docs/doctoring/coalesce-tick-post-2242-live-verify-20260917.md @@ -0,0 +1,149 @@ +# Doctoring record: post-#2242 coalesce tick live verify + re-enable criteria (2026-09-17) + +- **Date:** 2026-09-17 +- **Subject:** After `#2242` restored the job-level + `OPENCODE_REVIEW_COALESCE_ENABLED` gate, confirm the next schedule ticks + finish as `completed`/`skipped` (or later `success` when enabled) within + seconds — not multi-hour `queued` — and state when the flag may safely be + flipped back to `true`. +- **Decision record:** none — live verification plus recommended re-enable + criteria. Flipping the repo variable remains an explicit operator action. +- **PR:** this commit's pull request. + +## Preconditions verified on `main` + +| Check | Evidence | +|---|---| +| `#2242` merged | `3449d0020ffac86315ecccfb9d5a1dd3bf421834` at `2026-09-17T16:14:00Z` | +| Job-level gate restored | `origin/main:.github/workflows/opencode-review-coalesce-tick.yml` has `if: vars.OPENCODE_REVIEW_COALESCE_ENABLED == 'true'` on `coalesce-tick`; no step-scoped inert `echo`/`exit 0` gate | +| Flag still off | repo variable `OPENCODE_REVIEW_COALESCE_ENABLED=false` (unchanged since `2026-09-17T04:21:48Z`) | +| Stuck inert waiter cleared | run `35219385415` reached `completed`/`cancelled` at `15:36:29Z` (concurrency group no longer holds a forever-pending inert tick) | +| Prior healthy skip baseline | run `35191169833` (job-level-gate era) `06:44:23Z` → `completed`/`skipped` by `06:44:24Z` (1s) | + +Repair intent (from `#2242` / `docs/doctoring/coalesce-tick-inert-runner-queue-20260917.md`): +disabled ticks must skip **before runner admission** so they do not compete for +the plan concurrent-job ceiling. + +## Live observation window (post-merge) + +Sampled via +`repos/ContextualWisdomLab/.github/actions/workflows/opencode-review-coalesce-tick.yml/runs` +(workflow id `360129488`). + +| Sample time (UTC) | `total_count` | Newest run | Status / conclusion | Notes | +|---|---|---|---|---| +| `16:36Z` (~22m after merge) | 2 | `35219385415` | completed / cancelled | No post-`#2242` schedule run yet | +| `16:43Z` | 2 | same | same | Still only the pre-merge pair | +| `16:48Z` (~34m after merge) | 2 | same | same | Still no `queued` and no `skipped` successor | +| `17:06Z` (~52m after merge) | 3 | **`35249460935`** | **completed / skipped** | First post-`#2242` delivery | + +### Reading + +1. **No multi-hour `queued` inert tick has reappeared after `#2242`.** That is the + failure mode `#2242` fixed. Under the step-scoped gate, the first post-`#2232` + schedule delivery created `35219385415` and left it `queued` for ~3.5h. After + the job-level gate returned, the next delivered tick never entered `queued`. +2. **Schedule delivery still lags the `*/5` cron under saturation.** The first + post-merge delivery arrived at `16:54:29Z` (~40m after merge), consistent with + earlier same-day gaps (`06:44Z` → `12:07Z`, ~5.5h) documented in + `docs/doctoring/actions-queue-24h-remeasurement-20260917.md`. Missed intervals + are not backfilled as a stack of five-minute runs. +3. **Positive confirmation landed.** Run `35249460935` matches `#2242`'s + acceptance check: flag still `false`, `conclusion=skipped`, wall time 1s, + `head_sha=3449d0020ffa` (the `#2242` merge). + +### First post-`#2242` tick + +| Field | Value | +|---|---| +| Run id | `35249460935` (run_number 3) | +| Created / updated | `2026-09-17T16:54:29Z` → `2026-09-17T16:54:30Z` | +| Conclusion | `skipped` | +| Elapsed | **1s** | +| Head SHA | `3449d0020ffa` (`#2242` merge) | +| URL | https://github.com/ContextualWisdomLab/.github/actions/runs/35249460935 | + +## Recommended `OPENCODE_REVIEW_COALESCE_ENABLED` re-enable criteria + +Do **not** flip the variable to `true` until all of the following hold. These are +operator criteria, not code changes. + +### Must-have (gate health) + +1. **Post-`#2242` disabled-tick proof.** At least **one** (preferably **two**) + schedule runs on workflow `360129488` with + `conclusion=skipped`, wall time ≤ ~10s, and `head_sha` containing the + job-level gate (`≥ 3449d0020`). **Met** by `35249460935` (1s skip on + `3449d0020`); a second skipped delivery remains preferred before flip but + is not blocking once capacity criteria (#4–#5) are accepted. +2. **Job-level gate still on `main`.** + `if: vars.OPENCODE_REVIEW_COALESCE_ENABLED == 'true'` remains on the + `coalesce-tick` **job**, not moved back to a step. Contract: + `tests/test_opencode_review_coalesce_tick.py`. +3. **Fail-open still present.** `recent_coalesce_tick_completed()` still requires + `conclusion == "success"` and the scheduler still fail-opens when no fresh + successful tick exists (`#2233`). Skipped/cancelled ticks must never count as + coalesce liveness. + +### Should-have (capacity / blast radius) + +4. **Org admission headroom or accepted fail-open.** A live census of + org-wide `in_progress` vs plan concurrent-job ceiling (~60; see + `docs/doctoring/actions-plan-concurrency-ceiling-20260903.md`) and queued + depth (`docs/doctoring/actions-queue-24h-remeasurement-20260917.md`). + - Prefer enable when `in_progress` is clearly below ceiling and queued depth + is not on the order of 10³, **or** + - Explicitly accept that enabled ticks may still sit `queued` for hours and + that `#2233` fail-open will temporarily bypass coalesce deferral until a + `success` tick completes. Enabling under deep saturation without that + acceptance recreates "reviews never dispatch" rather than "inert ticks + clog the queue." +5. **Operator watch on the first enabled ticks.** After flipping the variable, + watch the next 2–3 schedule deliveries until each reaches + `conclusion=success` (or a documented fail-open dispatch path fires). Do not + walk away after only seeing `queued`. +6. **No concurrent experiment that reintroduces step-scoped observability.** + Run-record hunger must not override the admission-skip contract. + +### Explicit non-criteria + +- **Do not** treat schedule-delivery lag (hours between cron fires) as a reason + to widen the tick job or move the gate to steps again. +- **Do not** set `cancel-in-progress: true` to "fix" admission delay — that + cancels mid-org dispatch (`docs/doctoring/coalesce-tick-inert-runner-queue-20260917.md`). +- **Do not** enable solely because `#2242` merged; merge proves the code path, + not live schedule behavior under today's queue. + +### Suggested flip procedure + +```text +1. Confirm ≥1 post-#2242 skipped tick (table above filled). +2. Re-sample org in_progress / queued; decide accept-fail-open vs wait-for-relief. +3. gh variable set OPENCODE_REVIEW_COALESCE_ENABLED --body true -R ContextualWisdomLab/.github +4. Watch next ticks for conclusion=success; confirm recent_coalesce_tick_completed path. +5. If ticks queue for hours, leave flag on only if fail-open is observed healthy; else set false again. +``` + +## Verdict (as of `17:06Z`) + +| Claim | Status | +|---|---| +| `#2242` on `main` with job-level skip-before-admission | **Confirmed** | +| No post-merge multi-hour inert `queued` tick | **Confirmed** | +| Next tick completes `skipped` in seconds | **Confirmed** — `35249460935` in 1s | +| Must-have #1 (post-`#2242` disabled-tick proof) | **Met** | +| Safe to set `OPENCODE_REVIEW_COALESCE_ENABLED=true` now | **Not yet** — still need should-have capacity/fail-open acceptance (#4–#5); prefer a second skipped tick if schedule delivers one before flipping | + +## Audit trail + +- `ContextualWisdomLab/.github#2242` merge `3449d0020` @ `2026-09-17T16:14:00Z` +- `repos/ContextualWisdomLab/.github/actions/workflows/360129488/runs` +- `repos/ContextualWisdomLab/.github/actions/runs/35249460935` (post-`#2242` skipped) +- `repos/ContextualWisdomLab/.github/actions/runs/35219385415` +- `repos/ContextualWisdomLab/.github/actions/runs/35191169833` +- `repos/ContextualWisdomLab/.github/actions/variables/OPENCODE_REVIEW_COALESCE_ENABLED` +- Prior: + `docs/doctoring/coalesce-tick-inert-runner-queue-20260917.md`, + `docs/doctoring/actions-queue-24h-remeasurement-20260917.md`, + `docs/doctoring/actions-schedule-run-records-20260917.md`, + `docs/doctoring/actions-plan-concurrency-ceiling-20260903.md` diff --git a/docs/doctoring/codeql-ghas-analyses-pagination.md b/docs/doctoring/codeql-ghas-analyses-pagination.md new file mode 100644 index 0000000000..ad647f7d62 --- /dev/null +++ b/docs/doctoring/codeql-ghas-analyses-pagination.md @@ -0,0 +1,78 @@ +# Complete GHAS analyses pagination + +Status: local source repair on base `37b10243cec3d160ecc9c1be75c71428b160a703`; +protected integration, hosted security checks and independent review remain required. + +## Root cause and ownership + +`list_codeql_analyses()` returned only the first `per_page=100` response and +ignored `Link`. An identity first encountered on a later page could therefore +be absent from the evidence supplied to the existing base/head pairing rule. +The central standalone helper owns this retrieval defect, not a consumer +repository's CodeQL workflow or lifecycle policy. + +The read-only overlap survey collected 383 open central PRs and examined the +exact committed helper source in all 11 file-overlap candidates. Ten retained +the single-request implementation; #2205's head has no helper source. +In particular, #2532's HTTP-error-response closure is separate +work: it is not complete-history pagination and is not implemented here. +No duplicate pagination implementation was found in those candidate heads. + +## Contract + +- Preserve the public `list_codeql_analyses()` arguments and list return shape, + the optional ref filter, analysis order, and existing identity pairing policy. +- Follow GitHub's `next` Link only after validating canonical HTTPS + `api.github.com`, the exact repository analyses path, unchanged decoded + `ref`/`tool_name`/`per_page`, and the next sequential page number. GitHub's + numeric `/repositories//code-scanning/analyses` alias is accepted only + after an authenticated, redirect-free read of the original owner/name + repository metadata verifies that exact positive integer ID. Cache this + binding within one collection; never infer it from an analysis row. +- Reject ambiguous/malformed Link relations, query changes, cycles or skips + before creating another authenticated request. Continue refusing redirects. +- Collect at most **1000 pages**, each with integer `per_page` in **1..100**. + Exceeding that resource bound raises an incomplete-evidence error; no prefix + is returned as complete history. +- HTTP/transport errors, non-list JSON, invalid JSON/UTF-8 and empty HTTP bodies + abort the entire collection. A valid empty JSON array remains supported. +- Do not retire, filter or rewrite configuration identities; do not upload SARIF, + synthesize security status, weaken a gate or change lifecycle approval policy. + +The transport's `_request_json()` still returns JSON to its original callers; +its optional output-header mapping exposes Link to traversal. Repeated physical +Link fields are combined in response order using `get_all("Link")` before the +existing relation grammar is validated; later next relations cannot be hidden, +and duplicates/malformed fields fail closed before another authenticated request. +Dict-shaped test responses retain their single-value lookup compatibility. Its +pre-existing empty-body behavior remains compatible when that mapping is not +requested. +The module remains stdlib-only and executable as a standalone fetched script. + +## Regression and validation + +`tests/test_codeql_ghas_analyses_pagination.py` drives the actual opener seam +with synthetic HTTP responses, never live credentials or invented GHAS state. +A three-page, 201-row regression keeps an active identity on page three and +proves it still blocks a head missing that identity. Negative cases exercise +poisoned next targets (with no second bearer request), cycles, malformed Link, +later-page errors and the independent literal 1000-page boundary. Positive +cases cover no ref, PR refs, escaped ref characters, query ordering, single +pages, valid empty arrays and the pre-existing non-dict-row behavior. + +Validate the helper and existing authority/credential contracts in normal and +`GITHUB_ACTIONS=true` modes, with 100% statements, branches and docstrings. +Run the dispatch workflow contracts too. On macOS, nine unchanged workflow +fixtures fail because they explicitly invoke `/bin/bash` 3.2 with Bash 4+ +`${value,,}` syntax; the same failures reproduce in a clean archive of the +exact base. Do not reinterpret those baseline failures as passing CI. + +## Primary references + +- [GitHub REST pagination](https://docs.github.com/en/rest/using-the-rest-api/using-pagination-in-the-rest-api) + specifies following response Link relations rather than assuming one page. +- [List code scanning analyses](https://docs.github.com/en/rest/code-scanning/code-scanning?apiVersion=2022-11-28#list-code-scanning-analyses-for-a-repository) + documents the paginated analyses endpoint, ref filter and page-size limit. + +This API-contract repair adds no new scientific inference or model invocation; +there is no paper PDF to redistribute. diff --git a/docs/doctoring/codeql-ghas-configuration-identity-2133.md b/docs/doctoring/codeql-ghas-configuration-identity-2133.md new file mode 100644 index 0000000000..699d163c8d --- /dev/null +++ b/docs/doctoring/codeql-ghas-configuration-identity-2133.md @@ -0,0 +1,62 @@ +# GHAS CodeQL configuration identity continuity (#2133) + +## Symptom + +Wardnet PR `ContextualWisdomLab/wardnet#129` at exact head +`2cedf7098723cd12f59125e72f4354226165a112` completed its central current-head +CodeQL dispatch successfully while the GitHub Advanced Security CodeQL +comparison check was still terminal **neutral** with: + +> Code scanning cannot determine the alerts introduced by this pull request, +> because 1 configuration present on `refs/heads/main` was not found. +> Missing: `Default setup /language:rust`. + +Dispatch completion alone is therefore incomplete differential-analysis +evidence. + +## Root cause + +Protected bases that use GitHub CodeQL Default setup publish identities of the +form: + +```text +(analysis_key=dynamic/github-code-scanning/codeql:analyze, category=/language:) +``` + +GHAS pairs each such base identity with the same tuple on the PR head. Default +setup often finishes a fast language (for example `actions`) minutes before a +slower one (for example `rust`). The GHAS comparison can settle after the first +language lands and report `configuration not found` for every base language not +yet present on the head — even though the slower analysis later appears under +the correct identity on the same exact SHA. + +The central `#2106` handler stack correctly keeps `github/codeql-action/analyze` +at `upload: false` while Default setup owns code-scanning uploads (Default setup +blocks advanced CodeQL API uploads). Advanced uploads also use a different +`analysis_key`, so they cannot satisfy a Default setup base identity. The +central producer therefore cannot "impersonate" Default setup; it must prove +continuity of the identities Default setup already publishes. + +## Repair + +`scripts/ci/codeql_ghas_configuration_identity.py` is the executable pairing +contract: + +- positive: exact base/head SHAs sharing Default setup `/language:` pair; +- negative: base Default setup rust with only actions on the head fails closed; +- advanced-setup analysis keys are not interchangeable with Default setup. + +`.github/workflows/codeql-scan-dispatch.yml` fetches that script beside the +SARIF gate and, after the Medium+ gate, waits (bounded poll) until the scanned +language's base identity is present on the exact head before publishing the +`codeql-dispatch/` status. Least privilege stays `security-events: +read` for this verification; no leaf Default setup disablement and no synthetic +GHAS status. + +## Ownership boundary + +Cross-repository CodeQL evidence identity remains owned by the organization +central producer/handler/settlement layer (`codeql-pr.yml` → +`codeql-scan-dispatch.yml`, coordinated with `#2106` / `#2040` / `#1929`). Do +not copy CodeQL workflows into consumer repositories or reinterpret a transient +neutral GHAS comparison as GREEN. diff --git a/docs/doctoring/codeql-metadata-admission-bound-20260927.md b/docs/doctoring/codeql-metadata-admission-bound-20260927.md new file mode 100644 index 0000000000..60c9f5f9f2 --- /dev/null +++ b/docs/doctoring/codeql-metadata-admission-bound-20260927.md @@ -0,0 +1,53 @@ +# CodeQL metadata job admission bound + +On 2026-09-27, central Strix admission job108624881622 was queued with the +correct self-hosted/cwlab-control labels while all three control runners were +busy. Contextual-orchestrator CodeQL job108620193038 occupied cwlab-s2-01 in +`Read current-head CodeQL dispatch verdict`. This establishes a shared control +lane and live metadata work; it does not prove which individual API call stalled. + +The central codeql-pr jobs detect languages, read verdicts, or coordinate +dispatch, with no job execution bound. Language detection checks out source +for trusted classification; it does not execute PR-authored code. A stalled gh call can +therefore occupy a control slot for the platform default six hours. Add the +existing operational budgets: five minutes for detection/dispatch and ten +minutes for verdict reads, as used by control cleanup. +The separately dispatched scan and model inference keep their own contracts; +no elapsed inference time becomes a model-failure verdict. A timed-out metadata +job remains non-passing and cannot authorize a merge. + +The new assertion fails against unchanged source. The CodeQL and runner +contracts pass in local and GITHUB_ACTIONS=true modes (36 each); actionlint +and whitespace checks pass. Runner access, source-ref guards, permissions, +head revalidation, concurrency and authenticated verdict checks are unchanged. +Existing runs retain their original source and were not cancelled. Native +post-merge execution is needed to prove slot recovery and queue latency. + +## Terminal and idle-runner revalidation + +At 2026-09-27 14:46 UTC, job108620193038 was verified terminal: started +13:16:43, completed 13:23:14, failure. Its verdict-read step succeeded from +13:16:48 to 13:23:06 (378 seconds), then its enforcement step failed. It is +not a currently stuck slot, nor proof of a particular stalled API call. The +initial five-minute proposal would interrupt this observed orderly path; +only the verdict-reader budget is therefore revised to the existing ten-minute +control budget. This is an operational bound, not a calibrated latency optimum. + +Strix job108624881622 remains queued with cwlab-control labels while group6 +reports an online idle cwlab-s1-05. Its run36321072667 has no pending deployment +approval. The selected-workflow allowlist includes trusted-main Strix. This +contradicts treating every wait as simply all control runners being busy; +workflow eligibility, concurrency and organization admission still require +current evidence. These observations do not authorize cancellation or runner +access expansion. Some REST reads succeed while run-list reads return quota +errors, so no complete active-job census or current global-ceiling claim is made. + +## Current-main integration, 2026-09-28 + +Replayed only the three job budgets onto central main `5b0024a9`. +Existing trusted-source routing, current-head validation and scan contracts remain. +The CodeQL workflow, runner-image and required-queue contract suites pass: +112 tests locally and 112 with `GITHUB_ACTIONS=true`. Actionlint (ShellCheck +disabled) and `git diff --check` pass. These checks establish local source +contracts; native queue recovery remains unverified. Earlier runner observations +above are historical and do not describe current occupancy. diff --git a/docs/doctoring/codeql-obsolete-pr-verdict.md b/docs/doctoring/codeql-obsolete-pr-verdict.md new file mode 100644 index 0000000000..82b27beae1 --- /dev/null +++ b/docs/doctoring/codeql-obsolete-pr-verdict.md @@ -0,0 +1,75 @@ +# Closed and superseded CodeQL compatibility shards + +## Incident and root cause + +ContextualWisdomLab/fast-mlsirm#2172 merged at 2026-09-26 11:05:49 UTC. +The actions compatibility shard in [run 36237658142](https://github.com/ContextualWisdomLab/fast-mlsirm/actions/runs/36237658142/job/108414341704) +started its live PR read at 19:47 UTC. It correctly observed the closed PR and +returned without requesting a scan. The next step saw a successful read with +an empty verdict and failed with `CodeQL shard has no authenticated current-head +verdict or dispatch receipt.` The same producer/consumer mismatch existed when +the live open PR head differed from the event head. + +The queue delay exposed this bug; delay itself does not explain the failed +verdict contract. The missing output is the causal defect. + +## Repair and boundaries + +The live read now emits `verdict=obsolete` for a closed PR or a live head proven to descend from the event head. +Enforcement accepts that state without asserting a successful scan and without +publishing a security status. A lagging or diverged head, failed/incomplete comparison, malformed SHA, or unknown PR state fails +before retirement. Open PRs at the event head still require the existing +trusted terminal verdict; pending, failed, missing and unauthenticated evidence +remain failures. No permissions, security severity or required gates change. + +This repairs the required workflow compatibility layer. It does not replace +#2382's separate dispatch-handler stale-run repair or change an already-recorded +historical check result. + +## Verification + +Tests execute the actual workflow shell blocks with a stubbed GitHub API. +Closed and superseded targets reproduce the missing-output failure on the +baseline and pass with the repair. Unknown states, malformed SHAs and unproven forward ancestry fail in +both the read and enforcement steps. Existing exact-head verdict tests cover +trusted failure, spoofed success, missing evidence and terminal dispatch receipts. + +## Primary platform basis + +GitHub. (n.d.). *Workflow commands for GitHub Actions: Setting an output parameter*. +https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-output-parameter + +GitHub. (n.d.). *Contexts reference: Steps context*. +https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#steps-context + +## Existing security baseline repaired with the consumer + +The repository's open Dependabot alerts 11–13 identify AnyIO 4.14.0 in +`requirements-strix-ci-hashes.txt`. The Critical and High advisories are +GHSA-82r6-8w77-94w6 and GHSA-3w57-8xmc-8v26; the patched version is 4.14.2. +The source pin, two release hashes and source/lock parity test are reused from +#2385 at `372f5b8bb1ae1bb32ab29e9afbe363d81aed81e3`, without claiming that PR's +other changes or checks have been inherited. Both release digests were verified +against PyPI's version-specific JSON. This removes the known vulnerable lock +entry while preserving the repository-wide security gate. + +GitHub. (2026). *AnyIO: TLSStream IDNA 2003 host name encoding enables potential +TLS certificate spoofing* (GHSA-82r6-8w77-94w6). +https://github.com/advisories/GHSA-82r6-8w77-94w6 + +Python Package Index. (2026). *AnyIO 4.14.2*. +https://pypi.org/project/anyio/4.14.2/ + +## Dedicated control admission + +The five-runner allocation already documented in +[central dedicated routing](central-dedicated-runner-routing-20260927.md) separates +heavy CodeQL scans, long OpenCode reviews, and small control work. Compatibility +language detection, verdict reads, and dispatch coordination use the control +lane when `github.workflow_ref` identifies this exact trusted main workflow. +PR-authored revisions retain hosted execution. The existing control group adds +only this main workflow path to its allowlist; no PR ref or repository access +is broadened. Heavy scans continue using the dedicated CodeQL group. + +GitHub. (n.d.). *Using self-hosted runners in a workflow: Using labels and groups*. +https://docs.github.com/en/actions/how-tos/manage-runners/self-hosted-runners/use-in-a-workflow diff --git a/docs/doctoring/codeql-pr-private-consumer-read-permissions.md b/docs/doctoring/codeql-pr-private-consumer-read-permissions.md new file mode 100644 index 0000000000..fc0cf62d7e --- /dev/null +++ b/docs/doctoring/codeql-pr-private-consumer-read-permissions.md @@ -0,0 +1,26 @@ +# CodeQL required workflow denies private consumers a read they need — 2026-09-13 + +## Symptom + +Private consumer `ContextualWisdomLab/late-life-anxiety-reanalysis` PR #10 (head `a1cd5bc6783c6510dfcf937f523c733366e82213`, run `34700410434`) failed both org-required `.github/workflows/codeql-pr.yml` jobs at their first API call, each with `gh: Resource not accessible by integration (HTTP 403)`. `CodeQL compatibility analysis (python)` (job `103571590442`), step "Read current-head CodeQL dispatch verdict", calls `gh api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"` under `GH_TOKEN: ${{ github.token }}`; the runner printed effective token permissions of Contents: read, Metadata: read only (declared: `contents: read`, `id-token: write`). `Dispatch current-head CodeQL scan` (job `103571810868`) makes the same GET, then later reads `repos/${TARGET_REPOSITORY}/commits/${PR_HEAD_SHA}/statuses`, under `contents: read`, `id-token: write`, `actions: read`. Public consumers (fast-mlsirm, pg-erd-cloud, naruon, html4tree) pass the identical workflow only because GET on a *public* repository needs no fine-grained grant; the defect is specific to private repositories. + +## Root cause + +Neither job declared the fine-grained read permissions GitHub's REST contract requires for these calls on a private repository: "Get a pull request" needs `pull-requests: read`; "List commit statuses for a reference" needs `statuses: read`. Missing both, the minted `GITHUB_TOKEN` had no read access to pull-request or status data on a private repo, and testing against public consumers never exercised the gap because anonymous-equivalent GETs on public repository resources are always permitted. + +## Repair + +Added `pull-requests: read` and `statuses: read` to the `analyze-head` and `dispatch-current-head` job `permissions:` blocks in `.github/workflows/codeql-pr.yml`, preserving declaration order (contents, id-token, [actions], pull-requests, statuses). No write permission is added anywhere; `actions: write` remains absent, still guarded by the existing `test_codeql_required_workflow_does_not_gain_actions_write` regression test. + +## Local evidence + +New test `test_codeql_pr_jobs_hold_read_grants_private_consumers_need` in `tests/test_codeql_pr_workflow_contract.py` slices both permission blocks the same way the neighboring `actions: write` guard does and asserts each holds exactly `pull-requests: read` and `statuses: read` with no `actions: write`. RED: 1 failed (`assert [] == ['read']`). GREEN: 1 passed. Combined focused run across the five CodeQL/required-workflow contract test files: 149 passed. Full repository suite and `actionlint` result are recorded in the pull request description. + +## Hosted acceptance still required + +This repair is unverified against GitHub's live permission enforcement. A newly loaded central SHA carrying this change must still pass both `analyze-head` and `dispatch-current-head` on the private consumer's exact current head before the defect is resolved end-to-end. Separately, the later `repository_dispatch` POST from `dispatch-current-head` to `ContextualWisdomLab/.github` using the OpenCode app token has not yet been exercised from a private consumer at all, and may surface a distinct scoping issue of its own once this read-permission blocker is cleared. + +## References + +- GitHub REST, "Get a pull request": https://docs.github.com/en/rest/pulls/pulls#get-a-pull-request (fine-grained permission: `pull-requests: read`) +- GitHub REST, "List commit statuses for a reference": https://docs.github.com/en/rest/commits/statuses#list-commit-statuses-for-a-reference (fine-grained permission: `statuses: read`) diff --git a/docs/doctoring/codeql-terminal-proof-2352.md b/docs/doctoring/codeql-terminal-proof-2352.md new file mode 100644 index 0000000000..cfd2b356a3 --- /dev/null +++ b/docs/doctoring/codeql-terminal-proof-2352.md @@ -0,0 +1,66 @@ +# CodeQL terminal-proof settlement (#2352) + +## Incident + +On `.github#2352@f1a8dc813e6dba4e4905bf3e1b770b6d44344944`, required CodeQL +run `35805450471` initially failed pending and was later rerun. Attempt 2 jobs +`107353895415` (Actions) and `107353895562` (Python) became GREEN by reading +the successful `Enforce CodeQL Medium+ SARIF gate` step from producer run +`35841640640`. + +The producer jobs were nevertheless terminal failures: the later +`Verify GHAS base/head CodeQL configuration identity` step received HTTP 403. +The gate-only fallback therefore hid the exact credential/permission defect +tracked by `#2275` and `#2276`. + +## Root cause and boundary + +The required receiver and settlement contract treated one successful SARIF +gate step as terminal success even when a later mandatory proof failed. This +was originally allowed so a wake-only API failure could not invalidate an +otherwise complete scan, but the contract did not distinguish that harmless +late failure from GHAS identity or SARIF-preservation failure. + +A clean result recovered from a producer job whose overall conclusion is +failure now requires the same three proof units in both paths: + +1. `Enforce CodeQL Medium+ SARIF gate` succeeds; +2. `Verify GHAS base/head CodeQL configuration identity` succeeds; and +3. `Preserve CodeQL SARIF evidence` succeeds. + +A later failure confined to waking the exact required job remains outside the +scan verdict and may still be reconciled. A Medium+ gate failure remains a +terminal security failure and does not require a successful GHAS identity +step. A producer job whose overall conclusion is success remains authenticated +terminal proof because GitHub completed its non-optional steps successfully. +Missing, duplicate, skipped, cancelled, or failed proof on the failed-job clean +fallback stays fail-closed. + +An independent review found a second boundary defect before merge: the +required receiver and coordinator trusted the legacy +`codeql-dispatch/` commit status using only head SHA and publisher. +GitHub retains statuses on a commit, so the same head could reuse a success +from an earlier base, required run, or producer protocol after a PR retarget. +The current producer and consumers now use the v2 receipt exclusively: + +- context: `codeql-dispatch//`; +- description: exact head SHA, required run ID, workflow identity, and live + merge-source SHA; and +- publisher: the existing allowlisted app identity. + +The coordinator dispatches `codeql-scan-v2` with the versioned `pr_head` +envelope and live merge source. A legacy or otherwise stale status is ignored, +so the exact run performs or reuses only its own base/source-bound scan. + +## Verification and ownership + +Executable regressions reproduce the direct receiver and run-wide settlement +false-GREEN surfaces plus stale trusted-status reuse. They are RED on protected +`main` and GREEN with the proof contract. Focused workflow tests pass 91/91; +the complete repository suite passes 3,372 tests with 28 skips and 40 subtests. + +The central `.github` workflow remains the canonical owner. Do not copy the +workflow into a consumer, synthesize a status, accept clean SARIF alone, or +weaken the GHAS identity proof. `#2275`/`#2276` still own the real credential +and target permission repair; this change prevents that missing authority from +being mislabeled as a successful required check. diff --git a/docs/doctoring/codeql-verdict-history-scope.md b/docs/doctoring/codeql-verdict-history-scope.md new file mode 100644 index 0000000000..c25f395608 --- /dev/null +++ b/docs/doctoring/codeql-verdict-history-scope.md @@ -0,0 +1,21 @@ +# CodeQL verdict history scope + +## Structure and gap + +Required CodeQL shards consume an authenticated status or an exact completed dispatch bound to target repository, PR, head, base and required run ID. The fallback previously paginated the complete central dispatch history. On 2026-09-27 the public workflow API reported 10,311 runs; contextual-orchestrator#1031 Python verdict job 108609837545 was executing the lookup on cwlab-s1-05. This proves the lookup workload, not that it alone caused all queue delay. + +## Repair and invariant + +Read the canonical required run creation timestamp with Actions read permission. Fail closed if it is missing or malformed. Query repository_dispatch runs created at or after that timestamp, retaining pagination and exact identity and terminal gate checks. A producer bound to the required run cannot exist before the required run. No elapsed-time model verdict, synthetic approval, runner-group relaxation or security exemption is introduced. + +The same live API query with created >= 2026-09-27T11:08:00Z returned 3 runs. This is query cardinality evidence, not deployed latency or completed CodeQL proof. + +## Verification + +Real extracted Bash verdict scripts retain successful completed-dispatch recovery, later-page recovery, stale base/run rejection, unknown-state rejection and no-dispatch pending behavior. Added invalid timestamp failure coverage. The focused contract suite passed 29 tests before the explicit Actions read grant. Final combined verification is recorded in the PR. + +The original extended runner-image oracle expected three literal ubuntu-24.04 jobs while protected main routes trusted workflow jobs to the central control group. The exact oracle failed on unmodified base c3e86141c. It now requires all three jobs to compare the exact trusted main workflow ref, select the control group with self-hosted/linux/x64 labels, and retain the explicit ubuntu-24.04 fallback for other refs. The six runner-image tests pass locally; combined final receipt is recorded in the PR. + +## Reference + +GitHub. (n.d.). *REST API endpoints for workflow runs*. Retrieved September 27, 2026, from https://docs.github.com/en/rest/actions/workflow-runs#list-workflow-runs-for-a-workflow . The created filter uses date-time search syntax; per_page supports 100. Filtered searches return up to 1,000 runs; overflow cannot authorize a false success because exact receipt matching remains mandatory. diff --git a/docs/doctoring/codeql-versioned-handler-bootstrap-20260912.md b/docs/doctoring/codeql-versioned-handler-bootstrap-20260912.md new file mode 100644 index 0000000000..47b4482c9a --- /dev/null +++ b/docs/doctoring/codeql-versioned-handler-bootstrap-20260912.md @@ -0,0 +1,96 @@ +# CodeQL versioned handler bootstrap — 2026-09-12 + +## Status + +Proposed repair from protected `main@691fb78932eff5fbe52db69077848134b0b4e053`. +No merge or production claim is made here. The complete consumer successor is +PR #2040, revalidated at current head +`6476b919d3febf79cc53e71d6d60f15d7e83ced4`. + +## Exact live evidence + +PR #2040 predecessor head `a9b18b4b24980c7ceb8b8cc0d143a24db20c90bf` +had successful Runtime Quality run `34684155351` (3,127 passed, 1 skipped, +21 subtests; 100% statement, branch, and public-doc coverage), Security run +`34684356405`, SAST run `34684356377`, and Python Security run `34684356416`. +It had no unresolved review threads. The later current head `6476b919...` +moves replay-guard tests without changing this handler source, but historical +hosted results are not inherited. The current head is Draft and had no +associated pull-request workflow runs in the connector snapshot. It therefore +remains unmergeable through ordinary protection. + +Protected handler run `34684228601` is the smallest causal trace. Its Actions +and Python scan, SARIF gate, artifact preservation, and status paths reached +terminal completion. The Actions shard then woke the shared required run. The +Python shard's independent wake received HTTP 403 because that run was no +longer in the terminal-failed state. Same-repository/PR handler runs +`34684373526`, `34684458709`, `34684518320`, and `34684575249` were then +cancelled by the stable concurrency group while retries kept dispatching. In +`34684575249`, Python produced clean scan evidence while its sibling and wake +path did not converge. The repeated consumer symptom was a dispatched success +with a pending terminal verdict. + +## Root cause + +The protected handler woke the required run independently from each matrix +scan job. The first wake changed the run state before the second language +could validate and mutate it. In addition, a candidate stronger consumer +could not prove itself against protected `main`: the old handler lacked its +source-bound title and base-bound receipt, while replacing the handler in one +step would reject the still-protected legacy producer. Re-running either side +alone reproduces the dependency cycle. + +## Repair contract + +One existing handler accepts `codeql-scan` legacy v1 and `codeql-scan-v2`. +The event type is the explicit protocol version, avoiding an eleventh +top-level `client_payload` property. v1 retains the current title, payload, +and status context byte-for-byte at the boundary, while rejecting all v2-only +identity fields. v2 requires the exact source/base/head evidence implemented +by #2040. Both use the same scan implementation and one post-matrix settlement +writer. No scan shard has `actions:write`. + +The bridge removal condition is executable policy: remove v1 only after a +protected v2 producer is live, every in-flight v1 required run is terminal, +and a caller inventory finds zero `codeql-scan` producers. Until then, v1 is a +bounded compatibility port, not production authority for v2 consumers. + +## Verification and next action + +The bootstrap contract executes both payload shapes, rejects v2-to-v1 +downgrade fields, verifies one actions writer after the matrix, and preserves +the legacy and base-bound contexts separately. The full repository suite and +hosted exact-head checks must pass before ordinary merge. After bootstrap +merge, #2040 must non-force absorb protected main, change only its producer +event to `codeql-scan-v2`, and generate new end-to-end evidence; existing +failed or queued runs are not inherited. + +PR #2106 review then exposed a credential-fallback contamination edge case: +`gh api` may emit an HTTP error body to stdout before returning nonzero, so a +failed credential's JSON could precede the later credential's successful +response. A generic `{"message":"Forbidden"}` body was already discarded by +the current `jq` projections and therefore was not RED. The corrected RED +fixture emits `{"state":"closed"}`, a field the PR validator consumes: before +the repair it is concatenated with the authorized response and rejects that +valid fallback. `run_api` now captures each attempt and emits its body only +after that exact attempt succeeds, preserving stderr diagnostics and the +existing credential order without a temporary-file lifecycle. + +The overlapping predecessor PR #2105 retained two additional fail-closed +guards that the first #2106 tree did not carry. Nested rerun authority now +requires the exact string schema `"1"`; missing, numeric, and unknown schemas +are rejected before checkout or mutation. The single settlement writer also +validates the required run's positive integer `run_attempt` and stops before +mutation when it reaches 48. GitHub documents that `run_attempt` begins at 1 +and increments for every re-run, while one workflow run permits at most 50 +re-runs; the cutoff therefore preserves attempts 49–51 for human recovery +rather than consuming the native allowance automatically. The structured +failure records the run, attempt, schema, languages, and handler identity. +This integrates the valid #2105 delta into the backward-compatible legacy/v2 +bridge rather than choosing either incomplete branch unchanged. + +GitHub. (2026). *Re-running workflows and jobs*. +https://docs.github.com/en/actions/how-tos/manage-workflow-runs/re-run-workflows-and-jobs + +GitHub. (2026). *Variables reference*. +https://docs.github.com/en/actions/reference/workflows-and-actions/variables diff --git a/docs/doctoring/dependency-review-fail-closed-permission-envelope.md b/docs/doctoring/dependency-review-fail-closed-permission-envelope.md new file mode 100644 index 0000000000..2f4dca0cc2 --- /dev/null +++ b/docs/doctoring/dependency-review-fail-closed-permission-envelope.md @@ -0,0 +1,114 @@ +# Dependency Review fail-closed authority and caller permission envelope + +## Incident boundary + +Protected `ContextualWisdomLab/.github` main introduced the central reusable Dependency Review workflow through #1724. Subsequent exact-head evidence exposed three owner-contract defects that are repaired together in #1725 because all belong to the canonical dependency-review admission boundary. + +### Defect A — ambiguous HTTP status normalized to success + +The initial reusable preflight treated Dependency Graph compare HTTP 403/404 as proof that the feature was unavailable, wrote `available=false`, and let the pull-request gate finish successfully. That inference is unsafe: an authorization/policy denial can present the same status shape. The repair keeps the exact base/head compare request but authorizes the action only on HTTP 200; every other pull-request response is blocking and reports its status. + +Test-first evidence: + +- RED `cb07b8bb28ef9d3a147cc966a0c70654d132da1d` makes 403/404-as-unavailable, the fallback note, and any non-explicit fail-closed response illegal. +- Production `31f60e532e135008cabd09fcddd46a53062b0ea0` removes the 403/404 success branch and fallback note, preserving the pinned action, inputs, and non-`pull_request` skip boundary. + +### Defect B — thin callers lost required `GITHUB_TOKEN` permissions + +The original repository-local workflows carried read permission envelopes, but the migration examples/thin callers did not preserve them uniformly. GitHub reusable workflows cannot elevate the caller token. Consequently the called workflow's own `permissions: contents: read, pull-requests: read` declaration is only a ceiling; it cannot grant `pull-requests: read` when the caller omitted it. + +Live immutable-pin evidence isolates this from mutable-ref resolution: + +| Consumer | Exact head | Run | Result before caller repair | +| --- | --- | --- | --- | +| `ContextualWisdomLab/newsdom-api#784` | `1623977e6c37c78cb1a94a7a48c48f6d02cac86c` | `33622976911` | `startup_failure`, zero jobs; referenced workflow resolved to `.github@0bcd22d8bb07650aafb0a8f116e4c2bbb8744f03` | +| `ContextualWisdomLab/mightyETL#330` | `65efdf7b4064df5b9811c0403defb707e6efbc02` | `33623035969` | `startup_failure`, zero jobs | + +The consumer-side repair explicitly restores: + +```yaml +permissions: + contents: read + pull-requests: read +``` + +Fresh heads then materialized Dependency Review runs instead of failing before job creation: + +- newsdom-api `9a798d5ac7b9b295a1accb2327fc76611352290f`, run `33623818000`; +- mightyETL `4576f863ede9fca0673d6cce5ae8a4093246f5ab`, run `33623854807`; +- scopeweave `db8b8ed6d36a6dc6cc1d07255a7a9a86bc88bf4f`, run `33623761776`; +- Argos #557 `ee4c5dd326977407435b0f2425fdecebc34a810f`, run `33623867278`. + +Central test-first repair for this second defect: + +- RED `ee0f1ce544965772775b590050e40476df4ea8f6` adds `test_example_caller_preserves_required_permission_envelope()` without changing production/example workflow text. Against its parent it fails because the canonical caller example has no `permissions:` block. +- GREEN production `ca3bdbd210de988ccd31f7fb96d3a97adfdb9bff` adds the least-privilege caller envelope, explains the reusable-workflow permission ceiling, and replaces the mutable `@main` example with ``. + +### Defect C — compare identity was trusted before transport + +The earlier bundled Security Scan accepted the event-provided repository/base/head strings without first proving they were exact immutable Git object identities and one legal `owner/name` repository identity. That left the preflight contract weaker than the evidence it claimed to authorize. + +The predecessor owner lane #1643 added fail-before-transport validation and a temporary A/B canary. Its exact-head canary run `33589436750`, job `100120235906`, executed on `2026-09-02` and produced decisive evidence for the same immutable pair: + +- repository: `ContextualWisdomLab/.github`; +- base: `bb14b014eee31e6abdb5d2fffbb805aa29420eac`; +- head: `a6a2759640e6aa1d1e1219e1cd7aacdeffef32c0`; +- anonymous compare: HTTP `404`, curl exit `0`; +- job-token compare with `contents: read` and `pull-requests: read`: HTTP `200`, curl exit `0`. + +This proves that anonymous status is not an availability authority and that the least-privilege job token can establish the exact comparison. The temporary canary itself is diagnostic-only and is not part of the publishable successor contract. + +#1725 carries the durable part of that predecessor delta into the canonical writer: + +- both the reusable workflow and bundled Security Scan reject non-exact base/head revisions before curl; +- repository identity must be exactly one non-dot `owner/name` pair; `ContextualWisdomLab/.github` remains legal; +- the compare request uses the job token and only HTTP 200 authorizes the pinned Dependency Review action; +- named refs, malformed identities, 403/404 and all other non-200 outcomes fail closed. + +### Defect D — HTTP text could hide a failed curl transfer + +The reusable preflight appended `|| true` to curl and then authorized solely on +the printed HTTP status. A partial body transfer can print `200` and still exit +nonzero, so the old branch could publish `available=true` for incomplete +evidence. The executable regression makes the fake transport print `200` and +exit `18`; the preflight must remain nonzero and must not publish availability. +The minimal repair captures curl's exit status and requires both exit zero and +HTTP 200. The bundled Security Scan already enforced both conditions and was +left unchanged. + +Test-first evidence: RED `fe9d193343d12dbd229815a18ce85b494b3acff9` adds only the executable transport +contract and fails on the inherited preflight; its ordinary child carries the +minimal production and documentation repair. + +## Security invariants + +1. Pull-request Dependency Review executes only after curl completes successfully and an exact base/head compare returns HTTP 200. +2. Base/head revisions must be exact 40- or 64-character lowercase hexadecimal Git object IDs before transport. +3. Repository identity must be exactly one non-dot `owner/name` pair before transport. +4. No anonymous response, HTTP 403/404, or other non-200 response is translated into a successful "unavailable" state. +5. OSV-Scanner, Scorecard, and the separate Security Scan path remain independent controls; they do not satisfy a failed Dependency Review gate. +6. The called workflow and each caller use only `contents: read` and `pull-requests: read` for this path. No write permission is introduced. +7. Product callers pin the central workflow to an immutable protected-main commit after merge. `@main`, PR heads, and branch URLs are not production authority. +8. A non-`pull_request` event may skip because it lacks the PR base/head identity required for the comparison. + +## Verification and merge boundary + +The focused owner contracts are: + +```bash +PYTHONPATH=. pytest -q \ + tests/test_dependency_review_reusable_workflow_contract.py \ + tests/test_dependency_review_bundled_scan_identity_contract.py +``` + +The repository's normal exact-current-head required Checks, full coverage evidence, security scans, and independent reviews remain authoritative. #1725 stays Proposed/Draft while those gates are non-terminal or any substantive finding is unresolved. Queue saturation does not authorize bypass of a startup, permission, provenance, review, or security defect. + +After #1725 reaches protected main through ordinary protection, each consumer must bump its immutable reusable-workflow pin to that protected-main SHA and regenerate exact-head Dependency Review evidence. No consumer should return to `@main`. + +## References + +GitHub. (n.d.-a). *Reusing workflow configurations*. GitHub Docs. https://docs.github.com/actions/using-workflows/reusing-workflows + +GitHub. (n.d.-b). *Use GITHUB_TOKEN for authentication in workflows*. GitHub Docs. https://docs.github.com/actions/security-guides/automatic-token-authentication + +GitHub. (n.d.-c). *REST API endpoints for the dependency graph*. GitHub Docs. https://docs.github.com/rest/dependency-graph diff --git a/docs/doctoring/fmls-preflight-readiness-20260927.md b/docs/doctoring/fmls-preflight-readiness-20260927.md new file mode 100644 index 0000000000..3b65f3f68b --- /dev/null +++ b/docs/doctoring/fmls-preflight-readiness-20260927.md @@ -0,0 +1,160 @@ +# fast-mlsirm review-gate continuation: readiness repair + +## Original intent and boundaries + +Independently review ContextualWisdomLab/fast-mlsirm#2114 and +ContextualWisdomLab/fast-mlsirm#2120, then report shared CI failures to the +main or owning coordinator. Those PRs were merged on 2026-09-24. The previous +session stopped after the five-item report and correction of Cargo ownership. +This continuation preserves the numerical formulas, source transcript, +other agents' dirty worktrees, and protected review/security gates. It makes +no merge, tag, release, provider-availability, or hosted-acceptance claim. + +## Current scope + +DOI URL tests are now in ContextualWisdomLab/fast-mlsirm#2171 at +`cffb90fb742d0ebcb9c5aa49c8323ce349138eab`; six focused tests pass on that head +and after an isolated conflict-free merge with main `6dd48140`. + +Existing central repairs were independently checked and received scoped +COMMENT reviews: + +| Repair | Exact reviewed head | Local evidence | +| --- | --- | --- | +| #2360, committed Rust roots via `cargo vendor --sync --locked` | `fc9c8d2c8537e9a0582299d5b26eef31d6309710` | 26 passed; three real Cargo integrations excluded | +| #2385, proven target GHAS-read credential selection | `372f5b8bb1ae1bb32ab29e9afbe363d81aed81e3` | 59 focused tests passed | +| #2387, Noema retry dispatch credential separation | `33b9028318ddd0cf6c34d1816b09b94e95346c10` | 85 focused tests passed | + +Those local results do not prove hosted credentials, provider capacity, +whole-PR approval, or deployment. The Noema repair addresses the observed +eligible HTTP 504 followed by integration HTTP 403 in run `36237188317`. +The Strix binder successor remains separately owned by #2291. The original +CodeQL-status permission and live governance requirements must be evaluated +from terminal producer evidence, not inferred from the GHAS-read test. + +## New root cause and primary evidence + +[Strix run 36237188327](https://github.com/ContextualWisdomLab/fast-mlsirm/actions/runs/36237188327), +job `108408520367`, artifact `10919666896` (`strix-reports`): discovery ends +at 19:06:31Z on 2026-09-26. Two OpenRouter probes return 429; gemma-3 probes +on both NVIDIA accounts return 404. Later probes complete (the next sequential +invocation demonstrates completion). `nvidia_nim` llama-3.2-90b begins at +19:06:49.173Z without any later outcome before hosted cancellation at +01:00:56.653Z on 2026-09-27. This is about 5h54m before readiness, gateway +preflight, or scanning. The preflight JSON is empty; sanitized stderr retains +the route invocation. ZIP SHA-256: +`7bfd559ac1abda65c150fc3d5ec99562d8c83fca1a8d9dc7b444f7de6a4304e7`. + +The executed shared base is `e6334e229581a918e2f22de18733b76fa65d7e71`, +vendoring contextual-orchestrator `767e67fbc6b881a452761f32abb69b9971b9b03b`. +That runtime intentionally removed the 90-second inference deadline. +The sequential readiness walk consequently prevents later eligible routes +from being checked while a provider remains pending. Historical ADR-0029 +wall-time bounds no longer describe that pin. + +An event-controlled check against the exact base launcher confirmed that a +pending first call prevents all eight later readiness calls. An initial +concurrent implementation with only eight outstanding calls reproduced the +same obstruction with eight pending candidates, so the final scheduling uses +the existing sixteen-base-probe budget to bound outstanding calls as well. +It processes available completions before scheduling more work, preserves +catalog priority among admitted routes, and shares the four escalation +reservations across all probes and fallback. Pending calls continue without +admission or a synthetic failure verdict. + +## Verification and remaining acceptance + +The original runtime preflight suite and eight new concurrency checks pass: +143 tests with warnings as errors, both locally and with `GITHUB_ACTIONS=true`. +The event tests hold one or eight calls pending and require eight subsequent +ready routes to be admitted before releasing the pending calls. Other checks +cover all-429 failure, escalation bounds, deferred-route admission, fallback +ordering, fault propagation, and production wiring. Ruff and diff whitespace +checks pass. No live provider credential or model API was used in tests. + +After this repair lands through normal protection, a fresh exact-head review +must show completed readiness and a valid reviewer receipt. Pools without +enough responding routes can still wait; hosting loss and durable resumption +remain distinct concerns. The snapshot's pending rows are startup evidence, +not final outcomes of those model calls. More simultaneous calls may expose +provider rate limits sooner, within unchanged total request budgets. + +Organization-wide evidence found 51 assigned running jobs (30 Strix, 19 Noema, +two compatibility), while the #2171 OpenCode coverage and CodeQL producer jobs +were unassigned. This establishes occupancy, not the exact concurrency ceiling. +The Actions budget does not halt usage. Five oldest sampled review runs still +matched open current-head PRs; no stale cancellation was justified. + +Owner report: [fast-mlsirm #2171 comment](https://github.com/ContextualWisdomLab/fast-mlsirm/pull/2171#issuecomment-5853298639). +Repair tracking: #2408, Project #1 In Progress. Hosted current-head acceptance +and qualifying independent review remain required. + +## 2026-09-27 security prerequisite integration + +Noema #2387's hosted pip-audit job `108414598334` is a real shared-lock +failure: `requirements-strix-ci-hashes.txt` still selects AnyIO 4.14.0. +The audit lists CVE-2026-63374, CVE-2026-64847 and CVE-2026-63349, each with +4.14.2 as the patched version. This is separate from the startup scheduling +failure and the retry-dispatch credential defect. + +The canonical dependency repair is #2278 at +`8a5251bf409fe84b3dd0cba1e48992f5b8d9eda5`. Its complete three-dot delta +against protected main is exactly the six-line AnyIO pin/hash change. The +current-head requested-changes review cites failed coverage and contains no +source-backed lock finding; there are no inline review comments. That review +is retained, and no approval or main merge is inferred from the dependency +verification. + +An ordinary two-parent integration carries the canonical owner's exact commit +into this isolated repair branch. The integration changes only that lockfile; +it does not modify the owner's branch or copy unrelated foundation repairs. +The release wheel and sdist were downloaded from PyPI's official distribution +host and their actual SHA-256 bytes matched both committed hashes: + +- wheel: `9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494` +- sdist: `cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f` + +A hash-pinned pip-audit 2.10.1 installed in an isolated project venv audited +all 106 distributions listed in the original and repaired Strix lock, with +`--strict --disable-pip --no-deps --format json`. The original returns exit 1 +with exactly those three AnyIO findings; the repaired lock returns exit 0 +with zero findings. Both JSON results contain 106 dependencies and zero +skipped entries. Target dependencies were not installed or executed. HTTP +cache entries that could not be decoded were ignored by the tool; the audit +completed. This establishes the changed lock's advisory result, not the +security of every repository input or a live Strix run. + +Primary advisory basis: [AnyIO process-pool stderr advisory](https://github.com/agronholm/anyio/security/advisories/GHSA-5p39-cfhj-2xmp) +and [supplementary-group advisory](https://github.com/agronholm/anyio/security/advisories/GHSA-3w57-8xmc-8v26). +The existing gate and its severity/ignore policy remain intact. + +## Current governance audit and correction of the historical diagnosis + +Live ruleset `18156473` still requires seven `.github@main` workflows, +including `codeql-pr.yml`. That CodeQL entry is intentional: the protected +rollout document's 2026-09-04 correction restored a dispatch-safe entrypoint +that does not directly invoke `github/codeql-action`. The earlier transcript's +claim that its presence disagreed with the removal policy is superseded by +that correction. The July inventory warning still described removal/native +setup as the current posture; this continuation repairs that stale wording +without changing a required gate. + +The live organization payload also reveals a separate approval-policy +mismatch. Its approving-review count is one and last-push approval is false; +protected main's audit contract and July 23 rollout evidence require two and +true. Running the existing auditor on the actual payload returns exactly those +two errors. All seven workflow identities, required source ref, exclusions, +stale-review dismissal, review-thread resolution, and branch protection rules +pass that audit. The stacked ruleset `21732164` separately passes its audit. +Code-owner review remains false as the maintainer requires. + +An unapplied candidate changing only those two approval fields passes the +existing auditor. The current payload's SHA-256 is +`d6e6efd8c67027ae4a3625753c0e90198f8f92c67c691a0857231bd81d8ce412`. +The audit-log endpoint returned HTTP 404, so the reason or authority for the +live approval settings cannot be established from that endpoint. The user has +been asked which approval policy is intended before changing organization-wide +merge conditions or the repository contract. No live ruleset has been changed. +This mismatch does not explain an unassigned CI runner; job +`108568126406` and the original OpenCode coverage job `108521250487` are +separately confirmed queued with runner_id zero and no executed steps. diff --git a/docs/doctoring/fmls-release-sidecar-runtime-adoption-20260928.md b/docs/doctoring/fmls-release-sidecar-runtime-adoption-20260928.md new file mode 100644 index 0000000000..fa68aac4ed --- /dev/null +++ b/docs/doctoring/fmls-release-sidecar-runtime-adoption-20260928.md @@ -0,0 +1,7 @@ +# Release sidecar runtime adoption + +The immutable release helper at 4b0c6b75 predates the shared-runtime fix from #2468. Updating central main or rerunning an old immutable workflow cannot make that checkout consume the fix. + +All three release helper checkouts and their identity guards now pin protected-main ancestor e45f1b144aef900d734ff4c900f9e0010fd5a32d. The scripts/ci tree is 7f902df89a925f89c4fae69a842508406cd0207c; the requirements blob remains eb83beda177c9d2e4ca9b7e2888a1ccb55a123ac. The entire scripts delta from the prior helper is the six-line shared Python-library binding. Origin, commit, tree, clean-file and sibling checks remain intact. + +Independent guest-02 probes confirmed the selected 3.12.14 executable loaded the 3.12.3 runtime under inherited paths; the exact patched prefix selects 3.12.14 and passes logging/asyncio imports. The shared sidecar contracts passed 31 tests locally and in CI mode before #2468 merged; its whole merged tree matched the tested tree. This adoption does not claim hosted Noema acceptance, source grant clearance, release publication or a twelve-wheel verdict. The fast caller must separately adopt this callee revision. diff --git a/docs/doctoring/fmls-reviewed-release-helper-adoption-20260928.md b/docs/doctoring/fmls-reviewed-release-helper-adoption-20260928.md new file mode 100644 index 0000000000..5eca9ef7df --- /dev/null +++ b/docs/doctoring/fmls-reviewed-release-helper-adoption-20260928.md @@ -0,0 +1,9 @@ +# Reviewed release helper adoption + +The release callee continued checking out helper `5a29e0a5` after central #2457 and #2465 were merged. Advancing the fast caller workflow alone would therefore not execute the reviewed artifact recognition or complete libfuzzer source-obligation checks. + +All three callee jobs now pin protected-main ancestor `4b0c6b754fc30a0d0bf77f9c41650e1451476c26`. Their identity guards require scripts tree `f1b96f0a0af5cc30f8c8f2bb662727d41129f7dd`; requirements blob remains `eb83beda177c9d2e4ca9b7e2888a1ccb55a123ac`. Foreign origin, dirty/missing files, incorrect commit/tree and caller-controlled sources remain rejected. + +Reviewed #2465 source d0abbd63 integrated on a2ba7972: full merge tree `5b92f72696aae71cfe35ce5d765bf280f4f7d504` exactly equals merged 4b0c6b75. Independently fetched 59 immutable source/grant bodies with exact hash/size agreement; full LLVM modern/legacy terms and CREDITS were read. The 957 affected license tests pass locally and under GITHUB_ACTIONS=true. Adoption workflow/identity suites pass 58 tests in each mode; actionlint (ShellCheck disabled) and diff checks pass. + +The scripts-tree delta also includes separate Noema/materializer changes; these are not release-gate entry points. The release sidecar delta enables fatal stack-location diagnostics without frame locals. Strix requirements are unchanged. This is fixed-helper adoption only: fast caller adoption, native execution, original HOLD clearance, and published 12-wheel acceptance remain distinct requirements. diff --git a/docs/doctoring/full-suite-parser-locks-20260930.md b/docs/doctoring/full-suite-parser-locks-20260930.md new file mode 100644 index 0000000000..8454481059 --- /dev/null +++ b/docs/doctoring/full-suite-parser-locks-20260930.md @@ -0,0 +1,81 @@ +# Full-suite parser dependency admission + +## Incident + +At protected `main@37b10243cec3d160ecc9c1be75c71428b160a703`, the common +OpenCode quality lock did not install the parsers imported by repository test +collection. Agent Mention Router Quality run +[36443475158](https://github.com/ContextualWisdomLab/.github/actions/runs/36443475158) +and source-repair run +[36443475290](https://github.com/ContextualWisdomLab/.github/actions/runs/36443475290) +reported `ModuleNotFoundError: No module named 'defusedxml'` and 13 collection +errors. The newer main additionally has four workflow tests importing `yaml`. +These are environment failures before test execution, not product findings or +passing coverage measurements. + +## Repair boundary + +The shared tooling input reuses `requirements-noema-document-ci.txt` for the +existing `defusedxml==0.7.1` pin and adds the existing security-tooling +`PyYAML==6.0.3` pin. The generated lock is rebuilt only with +`./scripts/ci/compile_opencode_review_lock.sh` (Python 3.14, Linux target, +`--upgrade`, hashes). The required upgrade also resolves Hypothesis 6.168.3 +instead of 6.168.0. No hashes are edited by hand. + +Installing the entire Bandit lock alongside this lock is not the repair: their +Pygments pins differ. The common quality environment instead resolves its own +complete, consistent dependency closure. Hash checking remains mandatory. +PyYAML's repository reports MIT. GitHub reports NOASSERTION for defusedxml, +so its installed hash-pinned 0.7.1 wheel's LICENSE was read directly and confirms +Python Software Foundation License Version 2; a missing SPDX detection is not +interpreted as missing permission. + +A contract regression requires both parser packages in the installed lock. +RED: one failing test because both packages were absent. GREEN: 36 tests passed +and two optional document-format tests skipped after hash-locked installation. +The full repository suite is a separate verification step, not implied by that +focused result. On code head `41e95af9dd1ac95dea716d53c44df1eacbd81d9d`, the final +isolated run finished with 5,165 passed, five skipped and 40 subtests passed in +356.08 seconds. It used only the common quality lock plus the project-pinned +pip seed, disabled incidental pytest plugin autoload and cleared live event/token +environment variables. An inherited ResourceWarning from a synthetic HTTPError +fixture remains visible; it is not suppressed or treated as a product failure. + +## Verification environment + +The first YAML-present full run on source-repair head `deb47f8db` finished with +5,232 passed, seven failed, five skipped and 40 subtests passed. Two failures +were a locally unseeded virtualenv lacking pip; five used pytest temporary +paths beneath the host home, which the sandbox correctly refuses. Installing +project-pinned pip 26.2.1 and using an isolated `/tmp` basetemp made all seven +reproductions pass. These local environment corrections do not weaken sandbox +validation and are not changes to production code. + +An initial clean-lock run had one further environment failure: macOS inherited +`/tmp` group 0, which this user does not belong to, and silently cleared the +setgid bit when the test requested mode 2600. A direct mode probe observed 600, +while a staff-owned directory retained 2600. The final run used a unique +staff-owned parent outside the host home; the security check itself was not +changed. Both the targeted permission test and the complete suite passed there. + +Source-repair's own two scripts separately measured 100% statement/branch +coverage and 100% docstrings. Neither those measurements nor the parser repair +constitute hosted exact-head approval or merge authorization. + +## Consumers and limits + +Quality workflows install `requirements-opencode-review-ci-hashes.txt` directly. +The central OpenCode coverage image also consumes it in +`.github/workflows/opencode-review-dispatch.yml`, together with the existing +Noema document lock. No exact-head fast-mlsirm Noema HTTP 400 or Strix report +scope failure is claimed resolved by this dependency change. Those incidents +require their own current-head transport/report evidence and independent review. + +## References + +Python Packaging Authority. (n.d.). *Secure installs*. Retrieved September 30, +2026, from https://pip.pypa.io/en/stable/topics/secure-installs/ + +The pip documentation requires every dependency in hash-checking mode to be +pinned and hashed. The repair preserves that contract rather than adding an +unhashed installation fallback. diff --git a/docs/doctoring/github-api-http-error-response-lifecycle.md b/docs/doctoring/github-api-http-error-response-lifecycle.md new file mode 100644 index 0000000000..e26110c0b8 --- /dev/null +++ b/docs/doctoring/github-api-http-error-response-lifecycle.md @@ -0,0 +1,86 @@ +# GitHub API HTTP error response lifecycle + +Status: Proposed source repair; exact-head hosted security Checks and independent review remain mandatory. + +## Incident and exact owner + +Protected `ContextualWisdomLab/.github` `main` at +`37b10243cec3d160ecc9c1be75c71428b160a703` reproduced eight failures in +`tests/test_github_api_url_boundary.py` under Python 3.14.7 with warnings promoted +to errors. Both authenticated GitHub REST clients correctly refused a synthetic +302 without sending a second request, but the resulting `HTTPError` response was +left open and Python reported `ResourceWarning: Implicitly cleaning up +`. + +The exact RED command was: + +```console +GITHUB_ACTIONS=true python -m pytest tests/test_github_api_url_boundary.py -q -W error +``` + +Result on the protected revision: `8 failed, 26 passed`. The same result in a +clean protected-revision worktree proves this is a pre-existing central supplier +defect rather than a consumer or `.github#2040` branch-only failure. + +## Root cause and repair + +Python documents `urllib.error.HTTPError` as both an exception and the same kind +of file-like response returned by `urlopen()`, with a readable error-body file +pointer. The clients converted the exception into their domain error but did not +close that response object. Success responses already used context managers; the +HTTP error path violated the same lifecycle boundary. + +The repair keeps the existing fail-closed redirect policy and error mapping. +The first exact-tree verification also exposed the same lifecycle defect in +other central HTTP boundaries, so the owner repair now covers every observed +path rather than leaving warning-fatal failures for a successor: + +- `codeql_ghas_configuration_identity._request_json` reads at most 400 bytes of + diagnostic body and closes the `HTTPError` in `finally`, including decode/read + failures; +- `strix_evidence_binding.default_github_opener` snapshots the status code, + closes the response, and then raises `EvidenceBindingError`; +- `noema_review_gate.call_llm` extracts only bounded allowlisted gateway + telemetry and then closes the response in `finally`; +- the Pingora artifact client closes JSON and raw-blob HTTP errors after + preserving its existing 404/domain-error mapping; +- the review preflight closes provider HTTP errors after recording only the + safe status and retry fields; +- Pages publication and sandbox readiness probes close rejected redirects + before returning their existing fail-closed result; +- the production-opener regression now asserts that the single synthetic 302 + response is closed as well as proving no redirected bearer request occurs, + and each newly discovered caller has an equivalent close contract. + +The 400-byte intake bound matches the pre-existing 400-character public +diagnostic limit without reading an arbitrarily large untrusted response first. +No warning filter, security suppression, redirect allowance, timeout, or check +threshold changed. + +## Acceptance + +1. The protected-revision RED becomes GREEN under Python 3.14.7 and `-W error`. +2. CodeQL identity, Strix evidence, Noema, Pingora, review-preflight, Pages, + sandbox readiness, and URL-authority suites remain GREEN with warnings + fatal. +3. Changed production behavior retains contract coverage and public docs. +4. Hosted Semgrep, Bandit, CodeQL, dependency, and independent review Checks pass + on the exact PR head before ordinary protected merge. +5. `.github#2040` then merges the protected repair normally and regenerates its + own exact-head evidence; stale predecessor failures are not rerun as proof. + +Local repair evidence on Python 3.14.7 is `35 passed` for the URL-authority +suite after adding the bounded-intake regression (`1 failed` before the repair), +`220 passed` for the original eight-file CodeQL/Strix impact suite, `69 passed` +for the sandbox E2E suite, and `5161 passed, 10 skipped, 40 subtests passed` +for the complete warning-fatal test tree. The original two production modules +each report 100% statement/branch coverage, while the repository-wide report +remains at its pre-existing 99% because unrelated production files outside this +delta retain uncovered branches. Hosted exact-head Checks, rather than this +local evidence, remain the merge authority. + +## Primary reference + +Python Software Foundation. (2026). *urllib.error — Exception classes raised by +urllib.request* (Python 3.14.7 documentation). +https://docs.python.org/3.14/library/urllib.error.html diff --git a/docs/doctoring/github-api-published-lineage-authority.md b/docs/doctoring/github-api-published-lineage-authority.md new file mode 100644 index 0000000000..5f6a363848 --- /dev/null +++ b/docs/doctoring/github-api-published-lineage-authority.md @@ -0,0 +1,28 @@ +# GitHub API evidence published-lineage authority + +Status: Proposed repair evidence for `.github` PR #2279. Hosted exact-head security and independent review remain mandatory. + +## Finding + +The first published-lineage contract checked that the documentation named intended replacement SHAs and omitted two known unreachable candidates. That established expected spelling but not repository reachability. A 40-hex identifier can satisfy those assertions while referring to no commit published in the repository, so the contract did not make G-17's evidence lineage independently reconstructable. + +Current-head review identified that gap and required the G-17 evidence identifiers themselves to resolve and belong to the current published branch ancestry. + +## RED → repair + +- Structural RED `c37db5405142da1d0fa2ae972cbacab28563c370` factors a G-17 evidence validator and adds a mutation control that substitutes the first evidence commit with the all-zero, commit-shaped identifier. The intentionally shape-only validator accepts that mutation, so the regression fails instead of giving false assurance. +- Minimal repair `b339370ed1e032527e504ca3500a2f0ca825ff77` keeps validation in the existing GitHub API authority contract. For every full SHA named in the single G-17 row it now requires both `git cat-file -e ^{commit}` and `git merge-base --is-ancestor HEAD` to succeed. The negative mutation therefore fails closed, while the documented published evidence must be resolvable in current history. + +The repair does not change either production HTTP client, credential handling, redirect policy, workflow threshold, or the standalone `$RUNNER_TEMP` CodeQL materialization boundary. It strengthens only executable evidence traceability. + +## Invariants + +1. G-17 has exactly one gap-register row. +2. Every full commit SHA named by that row resolves as a commit in the checked-out repository. +3. Every such evidence commit is an ancestor of the exact checked-out head; detached or unreachable object-store artifacts are not accepted as published lineage. +4. A syntactically valid but unreachable 40-hex identifier fails the contract. +5. Exact-head hosted CI/security gates and independent review remain distinct from this focused local invariant. + +## Rejected alternatives + +Checking only SHA syntax was rejected because it proves formatting rather than publication. Checking only that expected strings occur in Markdown was rejected because unreachable objects can still be named. GitHub API lookups were unnecessary for the repository-local invariant and would add network/credential authority to a test whose evidence is already in Git history. diff --git a/docs/doctoring/github-api-url-authority-2248.md b/docs/doctoring/github-api-url-authority-2248.md new file mode 100644 index 0000000000..01db8f1f17 --- /dev/null +++ b/docs/doctoring/github-api-url-authority-2248.md @@ -0,0 +1,73 @@ +# GitHub REST URL authority boundary for central CI clients + +Status: Proposed repair for `.github` issue #2248; exact-head hosted security and independent review remain mandatory. + +## Problem + +Protected `.github/main` at `64aa08d7fa487deacd41c761c36277ca68cab6c9` contains two central CI HTTP clients: + +- `scripts/ci/codeql_ghas_configuration_identity.py` for CodeQL analyses; +- `scripts/ci/strix_evidence_binding.py` for pull-request changed-file evidence. + +The whole-tree Semgrep gate reported `python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected` at both original dynamic `urlopen` sites, and Bandit B310 reported the same class. A comment-only suppression would not prove the security premise that bearer-authenticated requests stay inside GitHub REST authority. + +The first repair made the initial URL predicate executable, but exact-head CodeRabbit review then identified a second authority transition: Python's default `HTTPRedirectHandler` can construct a redirected request from the already-authorized request and preserve request headers, including `Authorization`. Validating only the first `https://api.github.com/...` URL therefore did not prevent a 3xx response from redirecting the bearer token to another authority. + +## Initial URL RED → repair + +Structural RED `4732f3e29ab8cd0b88506beecd4e70bdfaafb8da` requires both clients to reject, before network/file opener execution: + +- `http://api.github.com/...`; +- `https://api.github.com.evil.example/...`; +- `https://api.github.com@evil.example/...`; +- `https://api.github.com:443/...` because the canonical authority is exact; +- an otherwise canonical URL carrying a fragment; +- `file:///etc/passwd`. + +The production predicate requires scheme exactly `https`, network authority exactly `api.github.com`, an absolute path, and no fragment. The positive control proves exact `https://api.github.com/...` reaches the injected opener and decodes JSON normally. + +A temporary shared helper candidate was removed because `codeql-scan-dispatch.yml` materializes `codeql_ghas_configuration_identity.py` into `$RUNNER_TEMP` and executes it as a standalone file. The CodeQL helper therefore keeps its small fail-closed transport boundary self-contained instead of gaining a repository-local import dependency that the workflow does not materialize. + +## Redirect RED → repair + +CodeRabbit's current-head review of `9ba43f284da51bfa6aaa389d3fb67f8b232fbba5` correctly rejected the initial-only guard: default `urllib` redirect handling can create a new request after the first authority check and carry the bearer header to the new target. + +Structural redirect RED `7a00442cbfd01408068a060c2bebba84041a33eb` adds hostile redirect targets for a lookalike HTTPS host, `http://api.github.com/...`, and `file:///...`. The contract requires both clients' redirect handlers to return no redirected request while the original request retains its bearer header; the repair also blocks same-authority redirects so there is no unreviewed second authority transition at all. + +Production repair lineage: + +- `a2e9126416c96bb8c5fa1e00190a8eca45758883` replaces CodeQL's default `urlopen` transport with a local `OpenerDirector` whose `_RejectRedirects` handler refuses every redirect; +- `4c7bcbeb06e421b98b0992b62cac06eaae45a98c` applies the same fail-closed boundary to the Strix evidence client; +- `e06b6dd84b012db9c3fafc09d417a85f4aaeff4c` adds direct-handler hostile cases, canonical opener positive controls, and same-authority redirects to the refusal contract; +- `57477289ebec5631b0c48f0bc419f336dbe19deb` closes the remaining executable-binding gap: both actual module-level production openers receive a synthetic 302 through their real HTTPS open/response chains, and the regression proves transport sees exactly the original canonical request plus bearer and never receives a redirected request. + +The redirect repair removes the two dynamic `urlopen` sinks rather than broadening a Semgrep/Bandit suppression. A 3xx response now terminates as the opener's HTTP error path; no second request object is created and the bearer credential cannot be forwarded by redirect machinery. The executable proof patches only the actual opener's bounded HTTPS transport slot for a synthetic response; it does not replace `open()`, call the redirect handler directly as its oracle, or contact a network endpoint. + +## Production opener-chain RED → evidence repair + +Current-head review found that the direct `_RejectRedirects.redirect_request(...)` unit cases would remain green if either production `_GITHUB_API_OPENER` were accidentally rebuilt with Python's default redirect handler. Commit `57477289ebec5631b0c48f0bc419f336dbe19deb` therefore drives each public client path through its actual module-level opener. A synthetic HTTPS transport returns `302 Location: https://api.github.com/repos/ContextualWisdomLab/redirected`; the contract requires the client-specific HTTP error and exactly one transport call containing the original bearer header. + +Mutation RED temporarily replaced both `build_opener(_RejectRedirects())` constructions with `build_opener()`. Both new tests failed on the forbidden second request and recorded `Authorization='Bearer test-token'` at that redirect target. Restoring the production constructors made the complete authority file GREEN (`31 passed`, including malformed-authority parse failures for both clients and all four redirect target classes). This binds the executable claim to the production handler chain without adding network I/O, sharing runtime helpers, or changing the standalone CodeQL module. + +The broader focused run then exposed four pre-existing Strix fixtures still patching the removed module-level `urlopen` symbol: HTTP error, URL error, malformed JSON, and success. Their RED result was `2 failed, 77 passed` because monkeypatch setup stopped before those cases reached production. They now patch `binding._GITHUB_API_OPENER.open`, matching the real call path; the three-file CodeQL/Strix/authority suite passes in both normal and `GITHUB_ACTIONS=true` modes (`87 passed` each), with 100% statement and branch coverage across the two affected production modules. + +A clean worktree at predecessor `25f83aaee9eb97e423f6ef2467e722035bc2e362` reproduced those two Strix failures in the full suite (`2 failed, 3354 passed, 28 skipped, 40 subtests`) and the repository-wide pre-existing 98% coverage gate (`262` missed statements). The repair removes the two causal suite failures and all misses in the two affected production modules; it does not claim to close unrelated coverage debt in `actions_queue_health*`, Rust materialization, Noema document handling, or scheduler code. + +## Alternatives rejected + +Broad Semgrep/Bandit suppression, path exclusion, or threshold weakening were rejected because they hide unrelated findings. Revalidating only the final response URL was rejected because the unauthorized network contact would already have occurred. Preserving redirects while stripping only `Authorization` was rejected because the client would still contact a target outside the stated GitHub REST authority. A custom redirect-following policy was unnecessary for these CI reads; blocking redirects entirely is the smaller authority surface. + +## Evidence and acceptance + +Primary scanner rule inspected at [semgrep/semgrep-rules revision `40b8c63f75dc7c22c8a77482d73bfb864b146f7e`](https://github.com/semgrep/semgrep-rules/commit/40b8c63f75dc7c22c8a77482d73bfb864b146f7e): `python/lang/security/audit/dynamic-urllib-use-detected.yaml`. Python stdlib `HTTPRedirectHandler` behavior was inspected during review because redirect construction is the second network-authority decision that the original source predicate did not control. + +Acceptance requires all of the following on the exact PR head: + +1. `tests/test_github_api_url_boundary.py` passes initial hostile-authority, direct-handler redirect-refusal, actual-production-opener synthetic-302, and canonical positive-control cases for both clients; +2. existing CodeQL GHAS identity and Strix evidence-binding suites remain green; +3. Semgrep and Python/Bandit no longer report the #2248 baseline findings and introduce no replacement Medium+ finding; +4. no security rule, path, threshold, or required check is weakened; +5. independent current-head review confirms redirects cannot create a second request carrying the bearer token; +6. the standalone `$RUNNER_TEMP` CodeQL materialization contract remains intact. + +Hosted exact-head evidence is mandatory. Source inspection, structural RED/repair lineage, and review comments are not substitutes for repository/security GREEN. diff --git a/docs/doctoring/k-csap-skills-central-quality.md b/docs/doctoring/k-csap-skills-central-quality.md new file mode 100644 index 0000000000..f33f0e9b69 --- /dev/null +++ b/docs/doctoring/k-csap-skills-central-quality.md @@ -0,0 +1,66 @@ +# K-CSAP 중앙 품질 실행 상태 + +기준일: 2026-10-09 KST. 최종 갱신: 2026-10-13 KST. 관련 issue: 중앙 #2612. 이 문서는 실행 성공 또는 출시 승인이 아니다. + +## 2026-10-13 갱신 + +- canonical `.github/actionlint.yaml`에 `k-csap-isolated`를 등록했다. 기본 actionlint 호출 exit 0이다. +- 리뷰 지적에 따라 workflow-scope `concurrency`(같은 PR의 이전 head 취소, 비PR은 run id 분리)를 추가했다. +- `git diff --check`는 깨끗한 checkout에서 빈 결과만 내므로 빈 tree 대비 HEAD 전체를 검사하도록 바꿨다. 실제 git 저장소로 커밋된 공백의 거부를 시험했다. +- job 전용 임시 디렉터리를 공유 `RUNNER_TEMP`가 아닌 run-owned `.ci-tools/tmp` 아래에 만든다. +- 아래의 workflow SHA-256 `9c4cc914…`, `2b14845e…` 등은 당시 기록이다. 현재 head의 workflow 바이트는 이후 수정되었으므로 그 리뷰를 현재 head의 승인으로 재사용하지 않는다. +- 실제 Linux runner 실행·App 승인은 여전히 미검증이다. + +## 현재 단계 + +2026-10-10 admission 수리본의 독립 fresh review는 `CONDITIONAL_SOURCE_ACCEPTANCE`다. 부모가 report SHA-256 `dbc0d58d24a098c68f33268fcd8dc1a6de00f57bfe729aa0dd0272fd4e29ff62` 및 workflow `2b14845ebf761652ca78ecdaac86c6e851ea41dc573312f41ade2695b0a719b7`, test `36bbf733ea0bec7e0cd3b03cb07d6c466cf19fffb578f16114e820a9d69c3737`를 직접 대조했다. 실제 admission shell 35개와 SHA 소비자 4개 사례가 기대대로 동작했고 고정본 계약 테스트 28개가 통과했다. PR head/event SHA 및 state를 분리하며 admission 출력 SHA를 checkout/HEAD 검증이 함께 소비한다. Flatback의 이전 head 독립 재현은 2 FAIL/3 PASS로 보존한다. 이전 아래 해시는 역사 증거이며 새 수리본의 승인으로 재사용하지 않는다. + +setup-uv 사설 저장소/checksum 수정은 실제 pinned dist의 macOS arm64 Node24 실행, 잘못된 checksum 거부, cache-disabled post 실행으로 검토했다. report SHA-256 `2159eec4f6343b82250124b78af57ed8925deaa8a63dc007a6f7e15a9670da83`를 대조했으며 admission 변경과 해당 setup/cleanup 블록의 바이트가 동일함을 fresh reviewer가 확인했다. Linux X64/Node20 실제 Actions, Python runtime 무결성, same-UID 공격 및 강제 종료 격리는 미검증이다. job expression 검증은 로컬 합성 입력이며 원격 runner 배정 증거가 아니다. scratch custom-label actionlint는 canonical CI vocabulary 수락을 대체하지 않는다. 새 소스 출판은 허용되지만 Draft 해제·운영 활성화·병합은 아직 수락하지 않았다. + +초안의 독립 리뷰가 FAIL이다. 원본 workflow SHA-256은 `1743d6c788606bdbd6caa2006a2de47a22029ed5696d2a9a705085542224fa45`, 테스트는 `29e2d825a666332c6bf8a3272359d72c743856606f5d940c3f6920869e354db6`이다. 리뷰 report SHA-256은 `a67ed03a6136c0bdc98b48201b68720489f10a68c10a7eaa8eaa5e72f5546fd9`이다. 초안의 9개 계약 테스트와 actionlint 통과는 이 FAIL을 대체하지 않는다. + +독립 재리뷰는 동일 workflow/test 해시에서 `PASS_WITH_ACTIVATION_LIMITATIONS`를 반환했다. 부모가 report SHA-256 `923b518cf61b072d3fb8ca7e03e5a1825faaca3c248942dd3048d2fbc2a2a378` 및 두 파일 해시를 직접 대조했다. 리뷰의 focused pytest 21개, custom-label actionlint exit 0, 별도 harmless cleanup probe 11/11은 로컬 계약 증거다. 초기 FAIL report는 역사 기록으로 보존한다. setup-uv 도구 상태는 private cleanup 범위 밖이며 외부 action 내부 cleanup, immutable download 무결성, 실제 원격 실행/App 승인은 미검증이다. + +현재 수리본은 job-level `if`를 runner 배정 전 gate로 추가하고, repo-scoped `k-csap-isolated` label을 사용한다. 기존 step admission은 checkout 전 방어층으로 유지했다. exact head, protected master ref, 일회용 source path, Python 3.11.14 및 uv/private venv/cache를 좁은 테스트로 검증했다. 초안 FAIL을 역사 기록으로 유지하며 수리본의 독립 재리뷰는 로컬 소스 한정 조건부 통과다. 수리 과정의 위임 writer 2회는 provider transport 실패로 결과 없이 종료했으며 부모가 테스트 RED를 확인하고 직접 수리했다. 수정본 로컬 `pytest` 21개 통과, scratch custom-label actionlint와 `git diff --check` exit 0; workflow SHA-256 `9c4cc914b57f919da06f434527670d34597ac4a8c1fc4c24008ce36440fdc5c9`, 최종 test SHA-256 `64ae572dfee0063e85ec4850969c02018105aceeb237d20abb1dedc7fe68d2c9`, 최종 staged patch SHA-256 `03a8a6260ae18aa887aab3151dcf8848c0fbe2f3c28cd47b6808699bf579968e`다. 이는 실 runner에서의 실행/격리를 입증하지 않는다. + +## 리뷰 사실관계 구분 + +리뷰는 “checkout도 admission 전에 실행된다”고 기록했으나 frozen YAML은 첫 step admission, 다음 step checkout 순서다. 이 서술은 현재 파일로 지지되지 않는다. 반면 job runner 배정은 첫 step보다 앞서므로 **step admission만으로 배정 이전 신뢰 경계가 생기지 않는다는 지적은 유효하다.** 원본 FAIL을 삭제하거나 PASS로 덮어쓰지 않는다. 완료한 재리뷰 `923b518c…`에서 이 두 결론을 구분했다. + +PR exact head 검증은 기존 PR #30의 exact-head 증거 계약을 이어받은 의도다. GitHub merge candidate를 검증했다는 주장은 하지 않는다. push/manual 호출은 protected master로 제한하는 수리 계약이다. + +## 원격 activation + +직접 API 조회 결과: + +- 조직 group 13 `CWL CI isolated`는 `visibility=selected`, `restricted_to_workflows=false`이다. +- 허용 repository 목록에 K-CSAP이 없다. 소속 runner `orgmetra-ci-01`, `keyverse-ci-01`, `calendarweave-ci-01`은 모두 offline이다. +- 제품 repo-scoped `k-csap-isolated-linux`는 online/idle이다. 조직 group 13의 runner가 아니다. group 구성원이란 이유 없이 옮기거나 relabel하지 않는다. +- 중앙 global PR #2565의 head는 `ef65b2fdc2036ccaca8d19c1db88dc8075ae9164`, OPEN이다. 기존 owner source는 수정하지 않는다. + +중앙 reusable job은 caller에 제공된 repo-scoped runner도 사용할 수 있다는 소유권 교정을 반영했다. group 13 ACL 추가는 이 경로의 선행조건이 아니다. 현재 labels가 맞는 runner의 등록 상태만 확인했고 실제 reusable job 배정과 host isolation은 미검증이다. 동일 UID의 적대적 동시 writer, 잔류 프로세스, 강제 종료와 tool download 무결성은 activation gate에 남는다. 사용자 요구에 따라 hosted fallback은 추가하지 않는다. 실제 execution evidence가 없으면 unavailable로 남긴다. + +## AI 리뷰 및 키 경계 + +중앙 #2560 key/relay custody와 Remora shared review owner를 유지한다. `LLM_GATEWAY_API_KEY` Secret metadata updated `2026-10-09T05:07:41Z`와 `LLM_GATEWAY_MODEL=auto`를 확인했다. 비밀 값은 읽지 않았고 중복 발급/덮어쓰기는 하지 않았다. 사용자가 지정한 `https://litellm.poinnetworks.net`의 실제 authenticated auto inference, private-data route 적격성, author-distinct App의 exact-head APPROVED는 아직 이 lane에서 검증하지 않았다. + +## 재현 가능한 테스트 환경 + +새 전용 테스트 lock `requirements-k-csap-quality-tests-hashes.txt`는 `requirements-k-csap-quality-tests.in`에서 uv 0.12.5의 실제 compile로 생성했다. 공유 manifest/lock은 수정하지 않았다. private scratch CPython 3.11.16 환경에서 hash-required binary-only sync로 pytest 9.1.1, PyYAML 6.0.3 및 4개 전이 의존성을 설치하고 21개 계약 테스트를 실행해 통과했다. YAML 텍스트에서 기대값을 합성하는 대안 parser는 폐기했으며 실제 PyYAML로 구조를 읽는다. `subprocess.run(check=False)`를 명시하여 실패 exit 검증을 유지했다. 최종 delta 재리뷰 report SHA-256 `0d36a3d66c8d6ae435128cfaeda8d0926b3fa0721a50ddc3c7b489e4d157095d`가 이 테스트/lock/문서 범위를 조건부 통과로 바인딩했다. runtime workflow의 Python pin은 3.11.14로 유지되어, 이 실행은 workflow 원격 runtime 실행 증거가 아니다. + +## 다음 gate + +1. 로컬 소스와 최종 delta 독립 검토 완료. 운영 제한은 미수락이다. +2. 중앙 Draft PR #2615를 출판했다. 초도 head는 `9996a21600b3751491842de20ca4c87e1cc257a8`이며 아직 병합하지 않았다. +3. 실제 required checks와 formal App 검토를 확인하고 제한을 해결한 뒤 일반 병합으로 callee를 보호 브랜치에 출판한다. 소비자 caller에 가짜 SHA나 mutable branch를 넣지 않는다. +4. caller·runner ACL·capacity를 승인된 경계에서 연결하고 run ID와 job.runner_name을 검증한다. +5. 실제 Noema/OpenCode auto inference 및 조건부 App 승인을 별도 확인한다. 단독 개발자에게 가상의 다른 인간 리뷰어를 요구하지 않는다. +6. 초도 head에서 확인한 두 실패는 별개다. CodeQL `Analyze (javascript-typescript)`는 billing account lock 때문에 시작하지 않았다. Noema는 Draft라 model inference를 skip한 뒤 Artifact storage quota로 upload에 실패했다. GraphQL annotations로 원인을 확인했으며 REST log 조회 quota 403은 별도 조회 실패다. `coverage-evidence`/`coverage-source-tree` SUCCESS와 `opencode-review` SKIPPED는 실 모델 승인 증거가 아니다. 이 실패 이력은 현재 head의 검증 결과로 승계하지 않는다. + +## 2026-10-11 다음 병합 준비 단계 + +- bootstrap 독립 plan review는 `CHANGES_REQUESTED`, 보고서 해시 `5eaa615dc1f6ac143f526329ae46d68f95284452114cada4a83c77e3391586b1`다. Ready는 리뷰 요청이며 merge 승인과 분리한다. 승인된 reviewer 경로가 준비되면 소스 및 live preflight 후 Ready만 전환할 수 있다. 실제 callee 실행은 oscar가 승인된 임시 exact-SHA caller로 병합 전에 확인한다. 실행·literal auto·App 승인·필수 check가 미충족이면 병합하지 않는다. +- 실제 inline shell을 permissive `umask 000` 아래 실행하면 source와 `.ci-tools`가 mode0777로 생성되는 RED 2개를 확인했다. 각 생성 단계의 `umask 077` 수리 후 30개 계약 테스트가 통과했다. 다른 UID 접근 제한만 확인하며 같은 UID의 비협조 writer 격리나 forced-kill cleanup을 증명하지 않는다. +- 이전 scratch Python 3.11 환경과 actionlint config가 없어 최초 호출은 setup 실패했다. 새 검증은 기존 hash-locked Python 3.14.7 scratch 환경을 사용했다. workflow의 Python 3.11.14 runtime 통과가 아니다. +- canonical PyYAML 누락 제안은 #2565 comment `6098771884`로 전달했다. 최신 remote shared head `b78007bfc976faaa4d416d2b6578b114e2725a82`의 canonical lock에 PyYAML pin이 있음을 확인했다. 보호된 main 배포와 실제 설치는 별도다. 그 head의 canonical lint label에는 `k-csap-isolated`가 없다. diagnostic label config의 성공은 canonical CI 수락이 아니다. +- oscar의 PR32 caller 후보 packet을 읽었다. 미게시 2파일 변경이며 base는 `9b0b1a202ea23b96ba2d3fe71ac1d86b0ca6bc36`, 기존 producer pin은 `c0d6235bd996159fa8c2bb2d176dce2d1983b38b`다. 후보의 로컬 테스트는 소유자 보고이며 Actions 실행이 아니다. 새 producer bytes를 게시하면 caller 소유자가 pin을 다시 검토해야 한다. diff --git a/docs/doctoring/law-ai-agent-central-ci.md b/docs/doctoring/law-ai-agent-central-ci.md new file mode 100644 index 0000000000..4bb159440f --- /dev/null +++ b/docs/doctoring/law-ai-agent-central-ci.md @@ -0,0 +1,145 @@ +# Law AI Agent 중앙 CI 이관 기록 + +## 범위와 상태 + +- 2026-10-05 14:02 KST 사용자 승인. 중앙 소유자는 `ContextualWisdomLab/.github`다. +- 중앙 기준본: `main@37b10243cec3d160ecc9c1be75c71428b160a703`. +- 검증한 소비 기준본: `ContextualWisdomLab/law-ai-agent@301800bd9e2f1c0876ff3466b0de8013474c7d26`. +- 이 변경은 새 workflow, shell helper, 중앙 계약 테스트, 이 기록의 네 파일만 소유한다. + 기존 중앙 control/security/scanner와 소비 source는 수정하지 않는다. +- AGENTS, CLAUDE, master context, product directive, Project protocol, gap baseline, + `ContextualWisdomLab/naruon#974` spec을 읽었다. Project #1은 조회 당시 503개 항목이었다. + 조회한 앞 100개는 전체 인벤토리가 아니다. 이 작업의 통합 담당자는 Coordinator다. +- 소스 이관과 runner 운영은 다른 증거다. 보호된 병합, GitHub job 성공, 출시를 주장하지 않는다. + +## 신뢰 경계 + +`workflow_call`만 허용한다. 중앙 public PR 이벤트는 private 소비 code를 checkout하지 않는다. +첫 inline admission은 checkout 전에 exact caller repository, 이벤트와 SHA를 검사한다. +허용 이벤트는 같은 저장소의 main 대상 PR, main push, main 수동 실행이다. +Fork, 다른 저장소, `pull_request_target`, mutable 중앙 ref, 잘못된 SHA는 실패한다. +Fork를 성공한 skip으로 처리하지 않는다. + +소비 checkout은 이벤트의 PR head 또는 github.sha다. 중앙 helper checkout은 +`job.workflow_repository`와 `job.workflow_sha`다. `github.workflow_sha`를 중앙 SHA로 쓰지 않는다. +공식 문서는 job 필드가 “workflow file that defines the current job”을 가리킨다고 설명한다 +(GitHub, n.d.-a). 두 checkout의 실제 HEAD를 검사한다. 경로는 `source`와 `central`로 분리한다. +`persist-credentials: false`, `contents: read`, cache 비활성화를 적용한다. 별도 secret과 +`secrets: inherit`은 없다. 소비 concurrency prefix와 중앙 `law-ci` prefix는 달라야 한다. + +검증한 action 객체: + +- checkout commit: `fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09`. +- 소비 setup-uv의 `94527f2e458b27549849d47d273a16bec83a01e9`는 annotated tag 객체다. + REST git tag 응답에서 commit `37802adc94f370d6bfd71619e3f0bf239e1f3b78`을 확인했다. + 중앙은 이 commit을 pin한다. uv 버전은 0.12.5다. + +Runner selector는 group `CWL law CI`, labels `self-hosted/Linux/X64/law-ai-agent-ci`다. +Python 3.12/3.14 matrix는 `max-parallel: 1`, 각 job은 20분이다. +Reusable workflow의 runner 접근과 hosted billing은 caller context를 따른다 +(GitHub, n.d.-b). 중앙 소스 이관으로 private caller의 전역 admission 문제가 해결됐다는 주장은 하지 않는다. +격리 runner의 운영 권한·자원·network 검증은 Coordinator의 별도 영수증이 필요하다. + +기존 중앙 workflow에는 Harden-Runner가 있다. 이 lane에는 효과가 없는 action을 복사하지 않았다. +공급자 문서는 self-hosted runner에 Enterprise 구독과 runner infrastructure agent가 필요하며, +구독 없이 action만 추가하면 no-op이라고 설명한다 (StepSecurity, n.d.). Egress 강제와 +host 격리는 runner provisioning 경계다. 이 기록은 해당 제어의 실제 활성화를 증명하지 않는다. + +## 실행 계약 + +Helper는 BASH_SOURCE로 자기 경로를 찾는다. CWD에 의존하지 않는다. +정확한 consumer commit을 `git archive`로 새 scratch source에 복원한다. +이전 checkout의 dist, venv, cache를 사용하지 않는다. + +- native PostgreSQL은 provisioned `pg_config --bindir`에서 찾는다. 런타임 apt/sudo 설치는 없다. +- RUNNER_TEMP의 짧은 `mktemp` immediate-child root, uid 소유권, mode 0700, + socket 0700, TCP off, test database만 사용한다. 모든 경로 component의 symlink를 거부한다. +- `LAW_AGENT_ROOT`와 소비 안전 코드가 실제 읽는 `LAW_AGENT_POSTGRES_ROOT`는 같은 scratch다. +- 서버와 연결의 statement 10초, lock 2초 제한을 유지한다. 실제 consumer 승인 함수 + `connect(..., test_only=True)`를 사용하여 migration 001/002를 commit한다. + committed baseline fixture가 suite 앞부분에서 필요하므로 전체 pytest 전에 초기화한다. +- uv locked sync, ruff, format, mypy, 전체 pytest/coverage90/subprocess coverage를 유지한다. + JUnit의 모든 suite에서 skip/failure/error 0과 test count 양수를 확인한다. +- 새 outdir에서 정확히 wheel 1개와 sdist 1개를 요구한다. uv의 자동 `.gitignore`는 + `--no-create-gitignore`로 막는다 (Astral, n.d.). +- 새 lane-local cache를 online prime한 뒤 다른 새 venv에 실제 offline wheel 설치를 한다. + source 밖에서 `python -I`로 installed prefix 안의 module, 두 migration resource와 CLI help를 확인한다. +- 생성 직후 trap을 설치한다. 기존 state marker는 덮어쓰거나 삭제하지 않는다. + child 실패 상태를 보존하고 cleanup 실패를 성공으로 바꾸지 않는다. + 항상 실행하는 workflow cleanup은 private uid/mode와 canonical immediate-child 범위를 다시 검사한다. + +Shell은 Python interrogate 대상이 아니다. 모든 shell function 앞의 역할·실패 경계 주석과 +모든 embedded Python block의 module docstring을 이 파일의 문서화 계약으로 사용한다. +이는 전체 조직의 100% coverage/docstring gate가 실행됐다는 주장이 아니다. + +## 실제 검증 + +2026-10-05 KST, macOS 로컬의 실제 native PostgreSQL 18.6와 uv 0.12.5에서 실행했다. +소비 source는 위의 정확한 committed SHA다. 소비 작업 중 추가된 미커밋 테스트는 포함하지 않는다. +요청 당시 343개와 달리 현재 기준본의 실제 collection은 **345개**다. + +| 실행 | 실제 결과 | +|---|---| +| Python 3.12.14 전체 소비 suite | 345 passed, skipped 0, coverage 98.95% | +| Python 3.14.7 전체 소비 suite | 345 passed, skipped 0, coverage 98.87% | +| 두 Python의 ruff / format / mypy | 통과 | +| 두 Python의 fresh wheel/sdist + offline 새 venv 설치 | 통과 | +| 두 Python의 source 밖 isolated resource / CLI help | 통과 | +| 두 Python의 native cluster stop와 scratch 제거 | 통과 | +| 중앙 admission TDD | 15 RED → 15 GREEN | +| 중앙 helper evidence TDD | 7 RED → 23 GREEN | +| 실행 계약 TDD | 2 RED → 25 GREEN | +| 실제 빌드 `.gitignore` 회귀 | RED 확인 후 수정·두 lane 재실행 통과 | +| 독립 리뷰 cleanup/trap 회귀 | nested/public root와 trap 순서 3 RED → 31 GREEN | + +Synthetic tool fixture는 child exit 37의 전파, stop 실패, 재cleanup만 검사한다. +실제 DB 검증으로 계산하지 않는다. 실제 DB 결과는 위의 두 native 실행뿐이다. +독립 리뷰 뒤 강화한 최종 helper bytes는 아래 최종 checkpoint에서 다시 검증한다. + +`actionlint 1.7.12`는 공식 `job.workflow_*` 필드를 아직 모른다. 처음 실행은 이 필드와 +custom label에서 실패했다. 임시 custom-label config와 이 세 필드의 exact error regex만 +ignore한 실행은 통과했다. Workflow expression 검사가 완전히 지원된다는 주장은 하지 않는다. +공식 context 문서와 admission runtime 테스트를 별도 근거로 남긴다. +Shellcheck와 `bash -n`은 통과했다. 중앙 full-suite/100% coverage는 별도 실행 상태를 기록한다. + +## 최종 로컬 checkpoint — 14:42 KST + +독립 리뷰 수정 뒤 동일 helper bytes를 두 Python에서 다시 실행했다. +3.12.14는 345 passed / skip 0 / 98.95%, 3.14.7은 345 passed / skip 0 / 98.87%다. +양쪽 offline 실설치, prefix/resource/CLI help와 cleanup을 포함하여 exit 0이다. +중앙 subset은 일반 환경과 `GITHUB_ACTIONS=true`에서 각각 **33 passed**다. +Shellcheck, bash 구문, 6개 embedded Python block compile, 제한적 actionlint와 diff check는 통과했다. +중앙 전체 suite와 100% coverage/interrogate는 이 worker에서 실행하지 않았다. + +최종 core 파일 SHA-256: + +| 파일 | SHA-256 | +|---|---| +| `.github/workflows/law-ai-agent-ci.yml` | `3aac714edfb5438c1f7c0eb6420fcff2f43b39692324cd6d7e86c4c103cfc164` | +| `scripts/ci/law_ai_agent_ci.sh` | `7ee3d7d4f7083d87b921530f4ee3c7ce7dd8041e9611f4429390aa58f9e4e935` | +| `tests/test_law_ai_agent_ci.py` | `8afd78b82f0c2a270375192eb35454735b1bb192b573bc89027d951b50900b35` | + +이 문서 자체의 hash는 Coordinator handoff에서 별도로 전달한다. +다른 agent의 runner image/provisioning/review 파일은 shared worktree에 있지만 이 worker는 수정하지 않았다. + +## 남은 통합 확인 + +1. 독립 reviewer에게 final 네 파일 SHA-256을 전달한다. +2. Coordinator가 격리 Linux runner에서 final helper exact bytes를 실행한다. +3. 소비 thin SHA caller의 exact commit과 중앙 protected commit을 연결한다. +4. GitHub current-head required checks, 독립 승인, protected merge를 따로 검증한다. +5. 활성 기존 writer/cron의 재개는 Coordinator가 소유한다. 이 worker는 스케줄을 변경하지 않는다. + +## References (APA 7) + +Astral. (n.d.). *Commands: uv build*. Retrieved October 5, 2026, from +https://docs.astral.sh/uv/reference/cli/ + +GitHub. (n.d.-a). *Contexts reference*. Retrieved October 5, 2026, from +https://docs.github.com/en/actions/reference/workflows-and-actions/contexts + +GitHub. (n.d.-b). *Reusing workflow configurations*. Retrieved October 5, 2026, from +https://docs.github.com/en/actions/reference/workflows-and-actions/reusing-workflow-configurations + +StepSecurity. (n.d.). *Harden-Runner*. Retrieved October 5, 2026, from +https://github.com/step-security/harden-runner diff --git a/docs/doctoring/law-central-ci-independent-review.md b/docs/doctoring/law-central-ci-independent-review.md new file mode 100644 index 0000000000..becd4bc6cc --- /dev/null +++ b/docs/doctoring/law-central-ci-independent-review.md @@ -0,0 +1,89 @@ +# Law central CI — independent bounded review + +검토일: 2026-10-05 KST. 담당 dispatch: `ctx_03ac2f3ed053`. + +## 판정 + +**BOUNDED PASS — writer 완료 뒤 frozen 6파일 bytes와 전달 SHA256이 일치했다. 원격 gate, 소비 caller pin과 출시의 PASS가 아니다.** + +Coordinator는 writer_done `msg_87a489827bae`, outcome succeeded와 14:44 KST byte freeze를 확인했다. 검토자는 freeze SHA256 6개를 현재 파일에서 직접 다시 계산하고 일치시켰다. 조기 발견한 root 경계와 trap-before-marker 결함은 final helper에서 수정됐다. 현재 승인된 source/design 검토 범위에서 미해결 blocking 결함은 발견하지 않았다. + +검토 대상은 중앙 신규 reusable workflow, shell helper, 계약 테스트와 운영 설명이다. 제품 원문을 중앙에 복사하지 않는다. 검토자는 제품 소스, 자격 증명, 다른 private 자료를 읽지 않았다. 소스 수정, Git 변경, 빌드, 전체 테스트, 컨테이너 실행과 등록을 하지 않았다. 이 문서만 검토자가 소유한다. + +## 조기 발견 — 수정 중인 bytes 기준 + +1. **Cleanup 경로 경계** — 최초 helper의 `cleanup_root`는 최종 root의 디렉터리/비-symlink 여부와 basename만 검사했다. `cleanup_state`는 marker의 regular/non-symlink 여부와 lexical prefix만 검사했다. marker/root owner, mode, 정규화된 즉시 자식과 parent symlink 검증이 없었다. 삭제를 실행하지 않고 Bash 조건만 평가했다. `/scope/lawci.parent/lawci.child`와 `/scope/lawci.parent/../lawci.child` 모두 lexical prefix 조건에 통과했다. 기존 정리 경계 안에서 owner/mode와 canonical direct-child 경계를 확인하는 수정을 coordinator에 전달했다. +2. **Marker 생성 이전 실패** — 최초 helper는 `mktemp` 이후 state 부재 검사와 state 쓰기를 완료한 다음 EXIT trap을 설치했다. state 충돌/쓰기 실패 시 새 scratch 정리가 실행되지 않는다. 기존 marker와 이번 실행 marker의 소유 여부를 구분하면서 trap을 일찍 설치하는 좁은 수정이 필요하다. +3. **실행 증거의 강도** — 최초 helper는 PostgreSQL 도구 존재를 검사하나 16.15 runtime version을 검사하지 않았다. matrix Python은 uv 선택값이며 실행 interpreter version 검사가 없었다. 설치 wheel의 import는 source 밖임만 확인하고 이번 installed prefix 안임은 확인하지 않았다. image/runtime 영수증과 최종 수정 확인이 필요하다. 재현 없이 실제 image가 잘못됐다고 주장하지 않는다. + +## 현재 직접 관측 + +- 14:31 KST GitHub API: runner group `11`, `CWL law CI`, visibility `selected`, `allows_public_repositories=false`, 선택 repository ID는 `[1404539192]` 한 개다. workflow restriction은 설정되지 않았다. 이것은 선택된 private 저장소의 접근 범위이며 특정 중앙 workflow만 허용하는 ACL은 아니다. +- 같은 조회: runner `1134186`, Linux, `online`, `busy=false`; labels는 `self-hosted`, `Linux`, `X64`, `law-ai-agent-ci`다. 재등록, 영구 재사용 또는 컨테이너 폐기의 증거는 아니다. +- 최초 workflow는 `workflow_call`만 받는다. contents 권한은 read다. 두 checkout은 persist-credentials=false이며 consumer head와 called workflow SHA를 따로 검증한다. admission은 owned same-repository PR, main push, main dispatch만 인정하고 fork와 pull_request_target을 거부한다. +- `bash -n scripts/ci/law_ai_agent_ci.sh`는 exit 0이었다. 검토 Python은 `/Users/seonghobae/.pyenv/versions/3.14.5/bin/python3`, version `3.14.5`였다. 이것은 runner Python 3.12/3.14 실행 증거가 아니다. +- 중앙 기존 tracked 파일 수정은 최초 `git status --short`에 없었다. 신규 workflow/helper/test 3개가 untracked였다. 다른 writer가 작업 중이므로 이 관측을 최종 상태로 재사용하지 않는다. + +## 공식 설계 근거 + +- GitHub 2026-09-03 Changelog, “GitHub Actions: Early September 2026 updates”: `job.workflow_ref`, `job.workflow_sha`, `job.workflow_repository`, `job.workflow_file_path`가 해당 job을 정의한 workflow의 identity를 제공한다. github.workflow_*와 달리 reusable called workflow를 가리킨다. GitHub Enterprise Server에는 제공되지 않는다. +- GitHub Docs, “Contexts reference”, job context: 위 called-workflow identity 의미가 현재 reference에도 있다. +- GitHub Docs, “Reusing workflow configurations”, access/runner behavior: private caller는 public reusable workflow를 사용할 수 있다. Actions 허용 설정은 별도 전제다. 같은 조직 called workflow는 caller에게 제공된 조직 self-hosted runner를 사용할 수 있다. GitHub-hosted runner 배정과 billing은 caller context다. 중앙 이전을 billing 또는 현재 StartupFailure 해소로 판정하지 않는다. + +공식 페이지는 검토 세션에서 직접 검색했다. 운영 API 관측은 문서 일반론과 구분한다. + +## 중간 수정 확인 — 14:37 KST, 아직 freeze 전 + +- `validate_owned_root`가 marker/root uid와 0600/0700 mode, immediate-child, parent symlink 검증을 추가했다. 기존 lexical-only 문제는 읽은 revised bytes에서 해소됐다. +- trap이 marker 생성보다 먼저 설치되고 `state_owned`가 이전 marker 삭제를 막는다. 조기 lifecycle 문제는 읽은 revised bytes에서 해소됐다. +- source Python major/minor runtime 검사와 wheel import의 `sys.prefix` 내부 검사가 추가됐다. PostgreSQL 16.15는 여전히 Dockerfile의 major-only 패키지 지정과 실제 image receipt를 구분해야 한다. +- 4번째 파일은 `.github/runner-images/law-ai-agent/Dockerfile`이다. 공식 base digest는 dispatch와 일치한다. build 중 root는 apt 설치에만 사용하고 최종 USER는 runner다. 실제 배포 image와 final Dockerfile bytes 동일성은 미확인이다. + +## 소비 계약 테스트 — 명시적으로 승인된 한 파일 + +Coordinator가 `/Users/seonghobae/orca/projects/law-ai-agent-pg-moa/tests/test_central_ci_caller.py`만 읽도록 추가 승인했다. 42줄의 해당 파일만 읽었다. private product source와 실제 caller workflow는 읽지 않았다. + +- immutable 40-hex 중앙 참조 1개, 중앙 실행 중복 금지, contents read, events와 concurrency literal을 검사한다. +- 테스트 보강 권고: YAML 구조로 `uses`가 job-level인지, push와 pull_request 양쪽 branch 범위가 main인지 확인한다. 현재 branch literal 한 번만 있으면 통과한다. 이는 실제 caller 결함 판정이 아니라 테스트의 증명 범위다. +- consumer test의 현재 RED3/pass1은 coordinator 관측이며 검토자가 실행하지 않았다. 중앙 SHA pin을 쓰기 전 결과를 최종 gate로 주장하지 않는다. + +## 최종 frozen bytes와 직접 bounded probe + +14:44–14:46 KST, 다음 SHA256을 full bytes에서 계산했다. 모두 Coordinator freeze receipt와 일치했다. + +| 파일 | SHA256 | +|---|---| +| `.github/workflows/law-ai-agent-ci.yml` | `3aac714edfb5438c1f7c0eb6420fcff2f43b39692324cd6d7e86c4c103cfc164` | +| `scripts/ci/law_ai_agent_ci.sh` | `7ee3d7d4f7083d87b921530f4ee3c7ce7dd8041e9611f4429390aa58f9e4e935` | +| `tests/test_law_ai_agent_ci.py` | `8afd78b82f0c2a270375192eb35454735b1bb192b573bc89027d951b50900b35` | +| `docs/doctoring/law-ai-agent-central-ci.md` | `08f02306e9aee3d9ca2a4d5ada6b0cc50a492831de08e4712479eec13772e6e5` | +| `.github/runner-images/law-ai-agent/Dockerfile` | `17596881141c761c5e1a55a81d1b2b76559f6fac3aa8b66e1b705f4990808694` | +| `docs/doctoring/law-ci-runner-provisioning.md` | `c798d85cc1db6b92113b2463dbbdb9b12b7115c23dbdf02f7fc0937f041af281` | + +직접 실행 결과: + +- 실제 workflow admission parsed Python body: 정상 main push 허용, mutable 중앙 ref/target event/중앙 caller/uppercase SHA 거부, **5개 PASS**. +- 실제 helper의 root validator parsed Python body: owned direct-child 허용, nested root/symlink root/0644 marker 거부, **4개 PASS**. validator만 실행했고 stop/remove는 실행하지 않았다. +- embedded Python **6개 compile PASS**, `bash -n` exit **0**. +- 승인된 소비 테스트 44줄을 다시 읽었다. Coordinator가 event별 main branch와 job-level uses regex를 추가했음을 확인했다. 실제 caller는 중앙 commit 전이므로 별도 후속 검토다. + +## 실행 증거의 경계와 다음 단계 + +- writer 문서의 macOS native 두 lane/33 central tests 성공은 writer 관측이다. 검토자가 재실행하지 않았다. +- Coordinator는 final helper `7ee3d7d…`와 consumer baseline `301800bd…`를 사용한 Linux 격리 image에서 3.12.3: 345 pass, skip 0, coverage 98.95%; 3.14.7: 345 pass, skip 0, coverage 98.87%, full quality/offline wheel/cleanup exit 0을 직접 확인했다고 전달했다. 이는 producer 관측이며 검토자 자신의 실행과 구분한다. +- image SHA, container limits/mounts와 sudo 거부는 provisioning 문서와 Coordinator producer 영수증이다. 문서는 egress 방화벽 미검증, VM 수준 격리 아님, apt 재빌드 결과 가변, ephemeral 이후 새 등록 필요를 명확히 기록했다. 현재 source 검토가 이를 보안 인증으로 확대하지 않는다. +- inherited PGOPTIONS는 helper에서 제거된다. native server timeout 10s/2s를 유지한다. 단일 job 20분은 품질 lane이며 중앙 모델 검토 timeout 변경이 아니다. +- 실제 소비 thin SHA caller, 원격 current-head required checks, ordinary protected merge/publish가 남았다. 중앙 기존 review/security gate는 변경하지 않았다. 중앙 이관은 billing/permission gate 우회가 아니다. + + +## 후속 공백 경로 수리 — scratch 검증, 미게시 + +위 14:44–14:46의 helper `7ee3d7d…` 및 test `8afd78b8…` 값은 당시 frozen source의 역사적 receipt다. 이후 a55 source의 migration inventory·ancestor admission 수리 bytes와 같다는 주장이 아니다. 당시 producer의 DB/wheel 관측을 이 후속 후보의 실행으로 전이하지 않는다. + +현재 scratch는 a55 head의 source에 공백 경로용 두 줄 수정과 별도 회귀 테스트를 추가했다. helper SHA-256은 `67b7b11968eed60745537d9e000a5c834f1cfbc22409d8321f16191ac25355f6`이며, 기존 test SHA-256은 `cbc5fa07cb6f004ff32f4f8a89f91d16dbcb4e944f9850af14d64c10e7311a25`, 추가 test SHA-256은 `736eb442735e1d40744edb554a65c087f825079eb91f5dc7650a679d6d3b8800`다. + +- RED: 실제 helper에서 추출한 pg_ctl 호출과 conninfo 할당을 실행했다. 공백이 든 path를 shlex token seam으로 확인한 2개 assertion이 실패했다. +- 최소 GREEN: pg_ctl server option의 socket 값을 double quote하고 conninfo host 값을 single quote했다. 같은 두 assertion은 2 passed, Bash syntax exit 0이다. +- pg_ctl executable은 합성 capture stub이다. shlex token seam은 실제 libpq parser 또는 native PostgreSQL startup이 아니다. 현재 host PATH에는 PostgreSQL 실행 파일이 없어 실제 native startup은 NOT_RUN이다. +- 이 후보는 공백 경로만 검증했다. quote/backslash 등 별도 경로 문자에 대한 전체 escaping acceptance를 주장하지 않는다. 실제 native startup과 독립 검토 전 canonical adoption·게시·App 승인으로 확대하지 않는다. +- 새 base362fed와의 조합은 아직 실행하지 않았다. a55의 이전 full gate를 이 수정이나 새 base의 결과로 전이하지 않는다. diff --git a/docs/doctoring/law-ci-runner-provisioning.md b/docs/doctoring/law-ci-runner-provisioning.md new file mode 100644 index 0000000000..1fa421ab41 --- /dev/null +++ b/docs/doctoring/law-ci-runner-provisioning.md @@ -0,0 +1,57 @@ +# Law CI 전용 runner 제공 기록 + +## 목표와 책임 + +ContextualWisdomLab/law-ai-agent#1의 품질 실행을 중앙 reusable workflow로 옮긴다. +중앙 issue는 ContextualWisdomLab/.github#2580, GitHub Project #1은 In Progress다. +사용자는 2026-10-05 KST에 중앙 .github 이관과 self-hosted 전환을 명시했다. +제품 source와 source 기록은 private consumer에 유지한다. 중앙 public repo에 원문을 복사하지 않는다. + +## 2026-10-05 14:25 KST 실측 + +- 새 group 11, `CWL law CI`: selected repository는 `ContextualWisdomLab/law-ai-agent` (1404539192) 하나. +- public repository 허용 false, 기존 control/security/signing/GPU/health group 변경 0. +- 새 runner 1134186 `law-ai-agent-ci-isolated-20261005`: Linux X64, label `law-ai-agent-ci`, online, busy false. +- 별도 컨테이너: non-root UID1001, mounts [], cap-drop ALL, no-new-privileges, memory2147483648, nanoCPUs2000000000, pids512. +- host Docker socket, host home/credential, production database volume, published port, host network 사용 0. +- 컨테이너에서 sudo는 no-new-privileges 때문에 거부된다. 이는 helper에 sudo install이 없다는 전제와 일치한다. +- immutable base: `ghcr.io/actions/actions-runner@sha256:e5496277be5d09bc968b3d64911b74e219ac4a3f2edce956a3ecf9271bea1ef4`. +- 로컬 제공 image SHA256 `7eef6dcf639e556ab28c8970978f30b668dc9d21099b8c8d206d3c8d863f7068`. +- `.github/runner-images/law-ai-agent/Dockerfile`의 SHA256 `17596881141c761c5e1a55a81d1b2b76559f6fac3aa8b66e1b705f4990808694`. +- native PostgreSQL 16.15와 pg_config bindir `/usr/lib/postgresql/16/bin`를 직접 확인했다. +- runner 2.337.0과 `Listening for Jobs`를 확인했다. GitHub job 성공 증거는 아니다. +- 이미지 apt 해석 결과는 위 image에 고정돼 있다. Dockerfile의 apt package는 재빌드 시 바뀔 수 있다. registry 배포/이미지 서명은 수행하지 않았다. + +## 재현 운영 계약 + +이미지 build는 provisioning 때만 수행한다. CI job에서 sudo/apt 설치하지 않는다. +전용 image는 DB 바이너리를 제공하고 workflow가 uv0.12.5와 Python matrix를 제공한다. +각 runner는 `--ephemeral --disableupdate --runnergroup 'CWL law CI' --labels law-ai-agent-ci`로 등록한다. +단일 job 종료 후 등록이 해제된다. 다음 matrix job은 새 컨테이너와 새 등록이 필요하다. +등록 token은 승인된 GitHub API로 구하고 stdin으로 config.sh에 전달한다. +`ACTIONS_RUNNER_INPUT_TOKEN`은 등록 child에만 적용하며 stdout·로그·파일·명령 인자에 직접 넣지 않는다. +서버 .credentials 등의 generated credential 파일은 컨테이너 내부에만 존재한다. 컨테이너 제거 시 함께 회수한다. + +컨테이너 생성 옵션은 `--restart=no --cap-drop ALL --security-opt no-new-privileges --memory=2g --cpus=2 --pids-limit=512`다. +이미지 writable layer는 job 전용이다. host mount를 추가하거나 host 네트워크로 전환하지 않는다. +같은 명칭의 busy runner는 제거하지 않는다. API의 id/name/group/busy와 컨테이너 owner label을 대조한 뒤 owned idle resource만 회수한다. +초기 detached stdin 전달은 대기 상태로 남았다. 원인은 detached container의 stdin attachment였다. +owned unregistered container를 제거하고 `docker exec -i` 등록 → 별도 `docker exec -d` 실행으로 수리했다. +등록 성공, online, workflow 해석, job 시작, source checkout, 품질 verdict를 별도 확인한다. + +## 신뢰 한계와 실제 다음 gate + +이 개발용 컨테이너를 VM 수준 격리 또는 hostile workload 보안 인증으로 표현하지 않는다. +network는 GitHub/toolchain 접근을 위해 존재한다. host private network에 대한 별도 egress 방화벽은 이 기록에서 검증하지 않았다. +일반 CI가 signing/security runner나 운영 자격을 공유하지 않도록 repo별 group을 사용한다. +reusable workflow의 runner access와 billing은 caller context다. 중앙 파일 이관은 caller의 계정 admission을 자동 해결하지 않는다. +issue ContextualWisdomLab/law-ai-agent#2의 정책 정상화 증거와 실제 runner-bound job가 있어야 ordinary merge를 승인한다. +공급된 runner만으로 성과를 만들지 않는다. 실제 Linux helper/full-suite 기록과 exact-head GitHub job receipt를 별도로 남긴다. + +## 공식 근거 (APA 7) + +GitHub. (2026, September 3). *GitHub Actions: Early September 2026 updates*. GitHub Changelog. https://github.blog/changelog/2026-09-03-github-actions-early-september-2026-updates/ + +GitHub. (n.d.). *Reusing workflow configurations*. GitHub Docs. Retrieved October 5, 2026, from https://docs.github.com/en/actions/reference/workflows-and-actions/reusing-workflow-configurations + +GitHub. (n.d.). *Reuse workflows*. GitHub Docs. Retrieved October 5, 2026, from https://docs.github.com/en/actions/how-tos/reuse-automations/reuse-workflows diff --git a/docs/doctoring/law-ci-test-portability-fix.md b/docs/doctoring/law-ci-test-portability-fix.md new file mode 100644 index 0000000000..bb15ee74be --- /dev/null +++ b/docs/doctoring/law-ci-test-portability-fix.md @@ -0,0 +1,60 @@ +# Law CI test portability fix + +## 범위 + +2026-10-05 KST. Dispatch `ctx_df60f6521590`. +기준 HEAD는 `9e482e14c3d31a09e9167af2fafb2edab26fbf12`다. +수정 파일은 `tests/test_law_ai_agent_ci.py`와 이 새 보고서뿐이다. +Git commit, push, 원격 게시, runner 설정, 제품 helper 변경은 하지 않았다. + +GitHub에서 PR #2581의 head/base와 CodeRabbit comment `4181096101`을 직접 읽었다. +Finding은 유효했다. 기존 fixture는 `~/.hermes/cache/scratch`가 이미 있어야 했다. +해당 부모 디렉터리가 없으면 두 parametrize case 모두 child 실패 주입 이전에 실패했다. +CodeRabbit의 `/tmp` 고정 제안은 적용하지 않았다. 현재 TMPDIR와 canonical 경로 검증을 유지했다. + +## RED → GREEN + +실행 interpreter는 `/Users/seonghobae/.pyenv/versions/3.14.5/bin/python3`, Python **3.14.5**다. +환경 snapshot의 3.14.7과 구분한다. 실제 TMPDIR는 +`/var/folders/vq/nxtc622x2lg1xq3jpdktymdw0000gn/T/`였다. + +1. 새 subprocess 회귀 테스트를 먼저 추가했다. 빈 HOME, 긴 RUNNER_TEMP, 짧은 TMPDIR에서 + 기존 실제 `test_synthetic_child_failure_and_cleanup_are_not_success[False/True]`를 각각 실행했다. +2. `python3 -m pytest tests/test_law_ai_agent_ci.py -q -k without_hermes_home`는 + **2 failed, 33 deselected**였다. 두 child 모두 `.hermes/cache/scratch/lf.*`에서 + **FileNotFoundError**였다. 이는 추정이나 별도 mock 재현이 아니다. +3. 테스트 전용 `short_temp_root()`를 추가했다. `tempfile.gettempdir()`의 resolved 경로를 우선한다. + `l.` + 8자리 임시 이름과 `/lawci.XXXXXX`를 포함한 24자 예산을 적용한다. + 생성될 helper scratch는 기존 계약인 **65자 미만**을 유지한다. +4. 기본 temp 경로가 길면 HOME 아래 `.cache/law-ci-tests`를 canonical 경로로 선택한다. + 길이를 먼저 검사하고 mode 0700으로 생성한다. 기존 경로의 uid와 private mode도 검사한다. + 긴 HOME까지 허용하려고 `/tmp` alias나 제품 validator를 우회하지 않는다. +5. GREEN 회귀 child는 `--basetemp`를 지정해 pytest 자체 임시 파일과 helper scratch를 분리한다. + 두 case는 child exit **37**, stop 호출, stop 실패 시 보존, 재cleanup을 실제 helper로 확인한다. + 새 `keep` sentinel은 cleanup 이후에도 내용 `unrelated`를 유지한다. + 테스트 종료 시 TemporaryDirectory가 이번 실행 디렉터리만 제거한다. + +## 최종 검증 + +| 명령/계약 | 실제 결과 | +|---|---| +| 원래 collection | 33 tests | +| 두 no-Hermes-HOME subprocess + 기존 두 failure case | 4 passed, 31 deselected | +| `python3 -m pytest tests/test_law_ai_agent_ci.py -q` | **39 passed, 10.18s** | +| `GITHUB_ACTIONS=true python3 -m pytest tests/test_law_ai_agent_ci.py -q` | **39 passed, 10.88s** | +| `git diff --check` | exit 0 | +| 추가 portability 계약 | short temp, symlink alias canonicalization, long temp fallback, TMPDIR 없는 default | + +기존 33개와 새 6개가 모두 통과했다. Synthetic PostgreSQL 도구 결과를 실제 DB 실행으로 계산하지 않는다. +전체 중앙 suite/coverage는 중복 실행하지 않았다. Coordinator의 진행 중 baseline 결과와 별도다. +중앙 provider/model review timeout은 변경하지 않았다. 독립 리뷰와 보호된 원격 gate는 Coordinator가 소유한다. + +## Exact bytes + +| 파일 | SHA-256 | +|---|---| +| `tests/test_law_ai_agent_ci.py` | `e36b38d67befd4f591c2362fe2306430af8843d8028d7d13e3deb5429bfece79` | +| `scripts/ci/law_ai_agent_ci.sh` (변경 없음) | `7ee3d7d4f7083d87b921530f4ee3c7ce7dd8041e9611f4429390aa58f9e4e935` | +| `.github/workflows/law-ai-agent-ci.yml` (변경 없음) | `3aac714edfb5438c1f7c0eb6420fcff2f43b39692324cd6d7e86c4c103cfc164` | + +보고서 자체의 SHA-256은 handoff에서 따로 전달한다. diff --git a/docs/doctoring/maturin-download-response-lifecycle-20261001.md b/docs/doctoring/maturin-download-response-lifecycle-20261001.md new file mode 100644 index 0000000000..b6765daae8 --- /dev/null +++ b/docs/doctoring/maturin-download-response-lifecycle-20261001.md @@ -0,0 +1,44 @@ +# Maturin download response-lifecycle RCA + +## Incident + +Trusted uv Materializer run `36811202519`, job `110206427182`, checked out +`.github#1653@5cd141ec2c33b631d164af936cd1c9de70e4c9a4`. All 5,314 tests passed, +but the complete repository gate reported 99% coverage. The only incomplete +owner was `scripts/ci/verify_release_maturin_tool_assets.py`: five statements +and two branches in its non-200 and `HTTPError` response paths. + +## Root cause and boundary + +The bounded downloader belongs to the central `.github#2530` successor, not +the #1653 label-taxonomy delta. Its nullable `response` finalizer encoded a +false branch that cannot fall through: when `opener.open()` raises, control +re-raises from the `HTTPError` handler before the code following the finalizer. +That made honest 100% branch evidence impossible even though successful +responses were closed. The Trusted uv workflow path filter also omitted this +verifier and its tests, so fixing the canonical owner would not itself request +the complete gate that originally exposed the defect downstream. After the +path repair, owner head `8cf2ea5f73976d47b2267fb52ac28284323404b7` +still produced no gate because the workflow admitted only PRs whose base was +`main`, while #2530 is correctly stacked on canonical owner #2531. + +## Repair + +The downloader now closes every returned response in one unconditional nested +`finally` block. An opener-raised `HTTPError` remains independently closed by +its handler. Tests assert closure for both an HTTP 503 response and an HTTP 502 +exception. A workflow contract now requires both verifier paths in pull-request +and protected-branch triggers, and a separate contract admits stacked PR bases +while the push trigger remains restricted to protected `main`. The repair does +not change admitted hosts, the one-hop redirect +contract, credentials, request timeout, byte bounds, digests, or error mapping. + +## Evidence and remaining gates + +- Hosted RED: 5,314 passed, 5 skipped, 40 subtests; 18,775 statements with 5 + missing, 7,652 branches with 2 partial; total 99%. +- Local owner GREEN: 17 focused tests; 107/107 statements and 34/34 branches; + `git diff --check` clean. +- Required before acceptance: complete exact-head hosted suite, security and + CodeQL verdicts, qualifying independent approval, ordinary owner integration, + then ordinary merge-forward into #1653 and fresh consumer Checks. diff --git a/docs/doctoring/noema-central-transport-continuation.md b/docs/doctoring/noema-central-transport-continuation.md new file mode 100644 index 0000000000..f53277998d --- /dev/null +++ b/docs/doctoring/noema-central-transport-continuation.md @@ -0,0 +1,34 @@ +# Noema central transport continuation + +## Failure + +The 429-capacity continuation sent repository dispatch to the product repository. +Organization-required workflows do not supply a local repository-dispatch handler +there. A central review of another repository also failed its same-repository +origin guard, even though the review itself had admitted that target. + +## Repair + +Send the existing `noema-review` event to the central `.github` handler. Preserve +its target repository, PR, exact head and retry count. Permit only the central +origin or the target repository's own required workflow. Re-fetch an open PR and +require matching head, base, base repository and head repository before sending. +Fork, stale, closed and unrelated-origin continuations retire or fail closed. + +The central handler can dispatch with its repository-scoped GitHub token. +A consumer continuation requires the existing `PR_REVIEW_MERGE_TOKEN` to read the +product PR and create a central repository dispatch; absence or insufficient +permission fails explicitly. This change does not assert that every consumer has +that credential. Native trusted-main runner restrictions, independent review, +publication fencing and the existing post-failure retry bound remain unchanged. +No model inference deadline is added. + +## Evidence + +The shell regression fails on the baseline because the dispatch endpoint is the +consumer repository. It executes the actual workflow step against a fake API, +checks the central endpoint and preserved payload, permits central-origin retry, +rejects unrelated origin and fork or changed-base evidence, and proves a rejected +POST cannot report successful continuation. The unchanged reviewer contracts +are run alongside this regression. Live provider recovery and approval still +require successful current-head hosted execution. diff --git a/docs/doctoring/noema-document-reader-dependency-baseline-20261001.md b/docs/doctoring/noema-document-reader-dependency-baseline-20261001.md new file mode 100644 index 0000000000..4e723f2040 --- /dev/null +++ b/docs/doctoring/noema-document-reader-dependency-baseline-20261001.md @@ -0,0 +1,33 @@ +# Noema document-reader dependency baseline + +## Incident + +Security Scan run +[`36773087489`](https://github.com/ContextualWisdomLab/.github/actions/runs/36773087489), +Trivy job `110084194330`, found three medium-severity vulnerabilities in the +generated Noema document-reader lock at exact PR head +`a99784219305d1b6e14cf76f0acea30c5ee45e21`: + +- `fast-uri` 3.1.7: CVE-2026-86472 / GHSA-hrr3-gc8f-f4qj; +- `ip-address` 10.7.0: CVE-2026-101911 / GHSA-h3mg-xc3c-68pw; and +- `ip-address` 10.7.0: CVE-2026-101912 / GHSA-j6r3-76f7-8jcv. + +The upstream fast-uri advisory fixes the 3.x line in 3.1.8. The ip-address +advisories fix both findings in 10.7.1; this lock selects the first patched 10.7.1 +release within the existing `^10.2.0` transitive range. + +## Ownership and decision + +The vulnerable file is owned by the central `.github` Noema document-reader +runtime, so the repair stays in that owner instead of patching consumer +repositories. The direct manifest is unchanged. `npm update` regenerated only +the two transitive lock entries, including registry URL and integrity digest. + +## Verification and remaining gate + +`tests/test_noema_document_reader_runtime_dependencies.py` first failed on +3.1.7 and 10.7.0, then passed with exactly one safe entry for each package. +The focused Python context suite passed 14 tests with 2 skipped, `npm ci +--ignore-scripts` reproduced the lock, and `npm audit --omit=dev +--audit-level=moderate` reported zero vulnerabilities. Hosted Security Scan, +CodeQL, independent approval, and ordinary protected merge remain required. diff --git a/docs/doctoring/noema-draft-before-sidecar.md b/docs/doctoring/noema-draft-before-sidecar.md new file mode 100644 index 0000000000..a0452db11e --- /dev/null +++ b/docs/doctoring/noema-draft-before-sidecar.md @@ -0,0 +1,60 @@ +# Noema live draft check before sidecar provisioning + +Date: 2026-09-26 +Repository: `ContextualWisdomLab/.github` +Workflow: `.github/workflows/noema-review.yml`, job `noema-review` + +## Root cause + +`Provision contextual-orchestrator review sidecar` (`scripts/ci/contextual_orchestrator_review_sidecar.sh`) +takes 10-13 minutes. Only afterwards did `Prepare Noema model verdict` run +`.github/actions/noema-review/two_phase.py --prepare-verdict-file`, whose `prepare_verdict` reads the +live pull request and returns early with `PR is draft; Noema verdict preparation skipped.` without an +envelope, so publication was skipped. Every draft run therefore held a hosted runner for ~13 minutes to +reach a decision that was available from one API call. Observed examples: newsdom-api job +108077744310 (2026-09-25) and `.github` job 106665379126 (2026-09-22). With organization Actions +concurrency saturated, that runner time delays other required checks. + +## Repair + +- New step `Check live pull request draft state before sidecar provisioning` (`id: live_draft`), + placed after `Validate current pull request head` and `Resolve Noema target repository visibility`, + reads `repos//pulls/` with the same selected reviewer token and REST lookup the validate + step already uses, and writes `live_draft=true|false`. +- `Provision contextual-orchestrator review sidecar`, `Provision local reviewed HWP document reader`, + and `Prepare Noema model verdict` are gated on `steps.live_draft.outputs.live_draft != 'true'`. +- Fail open: a lookup error, malformed body, or any `draft` value other than JSON `true` yields + `live_draft=false`, which is exactly today's path; `two_phase.py` keeps its own draft check. +- Downstream steps are unchanged. They already require `steps.noema_prepare.outputs.prepared == 'true'` + (publication token refresh, publish) or `failure()` (transport re-dispatch, sidecar evidence upload), + so unset prepare outputs mean "publication skipped", the state a draft already produced. The job + concludes success for drafts, as before. + +## Why ruleset repositories are unaffected + +The organization ruleset launches this required workflow in other repositories only for +opened/synchronize/reopened, never `ready_for_review`. The repair therefore adds no trigger-level or +event-payload draft filter; it moves the existing runtime live-PR draft decision earlier. A draft +never produced a Noema verdict at runtime, and a ready PR follows the identical path. + +One narrow timing difference remains: a PR that was draft when the check ran but was marked ready +during what used to be the 10-13 minute provisioning window would previously have been reviewed by +that same run; it now needs the next run (a `ready_for_review` event here, or the next push in a +ruleset repository). + +## Regression coverage + +`tests/test_noema_draft_admission_before_sidecar.py` pins step ordering, the gate on each +model-heavy step, the live REST lookup and token reuse, the unchanged trigger list, the absence of +`github.event.pull_request.draft`, and executes the step with a fake `gh` for draft, ready, lookup +failure, and malformed/non-boolean `draft` bodies. + +## Complete response parsing repair — 2026-09-27 + +Independent exact-head review of `dfa41ab4` found that jq can print `true` before +returning a nonzero status on trailing malformed input. A stdout-only comparison +therefore skipped review for an invalid response. Draft admission now requires a +successful complete slurped parse containing exactly one object with boolean +`draft: true`. Invalid trailing bytes and a second JSON value keep the full review +path. The actual workflow shell regression failed before the fix and passes after +it; normal Draft/Ready behavior and bounded startup continuation remain covered. diff --git a/docs/doctoring/noema-self-hosted-node-bootstrap.md b/docs/doctoring/noema-self-hosted-node-bootstrap.md new file mode 100644 index 0000000000..3a6d6943f4 --- /dev/null +++ b/docs/doctoring/noema-self-hosted-node-bootstrap.md @@ -0,0 +1,18 @@ +# Noema document-reader runtime on self-hosted workers + +The exact-head Noema job for .github#2373, run 36256598579 job 108504745563, +terminated before model review on 2026-09-27 with exit 127: the local HWP reader +version probe could not find `node`. This is a runtime prerequisite failure, +not provider capacity exhaustion or a product review verdict. + +The workflow now provisions Node.js 22.23.3 through the exact-pinned setup-node +v4 action before provisioning the gateway sidecar. The reader already accepts +Node 20 or 22 and uses its reviewed local npm lock with lifecycle scripts +disabled. This removes an implicit hosted-image prerequisite without modifying +providers, model deadlines, reader dependencies, review sufficiency, or retry +limits. It also avoids occupying a runner for gateway discovery before learning +that the local document reader cannot start. + +The regression asserts the pinned version, action revision and preparation +order. A local workflow contract pass is not proof of hosted review approval; +a fresh exact-head run still must execute the reader and publish a real verdict. diff --git a/docs/doctoring/opencode-coverage-cargo-fixtures-20260928.md b/docs/doctoring/opencode-coverage-cargo-fixtures-20260928.md new file mode 100644 index 0000000000..1b5d0b65fd --- /dev/null +++ b/docs/doctoring/opencode-coverage-cargo-fixtures-20260928.md @@ -0,0 +1,55 @@ +# OpenCode coverage Cargo fixture intake (2026-09-28) + +## Incident + +The current-head `.github#1026` OpenCode dispatch run `36348910783`, job +`108722448709`, reached the isolated coverage sandbox. Its full suite reported +four failures in `test_materialize_base_rust_dependencies.py` and +`test_maturin_offline_build_contract.py`, each before the tested behavior at +`cargo generate-lockfile` (3,538 passed, 4 failed, 4 skipped). The PR does not +change either test file. Both files construct registry-backed crates (`itoa`, +`ryu`, and PyO3) during the test, while the sandbox runs with `--network=none` +and its base-repository Rust materializer finds no Cargo lock in this PR's +validated base tree. + +With a fresh `CARGO_HOME` and `CARGO_NET_OFFLINE=true`, the two representative +tests failed at the same command. A direct `cargo generate-lockfile` on the +`itoa` fixture reported `no matching package named itoa found` in the offline +crates.io index. This establishes missing registry fixture input, rather than +a product-code assertion failure. The original hosted test helper captures +Cargo stderr, so the hosted log alone does not identify the missing crate. + +## Repair and trust boundary + +A trusted, lockfile-pinned fixture manifest covers the three registry crates +used by these tests. The networked coverage image build fetches that exact +closure with `cargo fetch --locked`. The PR tree never enters that build +context. The later untrusted test container still has `--network=none` and +receives only the trusted image's cached registry artifacts, copied into its +isolated `CARGO_HOME` after removing any PR-supplied sandbox home. Its trusted +Cargo config enables offline registry resolution, so a lockfile generated by a +test cannot attempt an index refresh against the disconnected network. Tests +that intentionally create a separate Cargo home can still resolve local Git +fixtures before their own offline build step. Existing base-repository Rust +vendor configuration and the sandbox's credentials and network restrictions +remain in force. + +The image build fails if the trusted fixture files are absent, symbolic links, +or cannot be fetched against their checksummed lock. It does not turn a failed +test into a pass. + +## Verification boundary + +An initial PyO3 extension build exceeded its 600-second limit on a loaded +macOS host. A later run with the project-local pinned maturin completed that +test in 32 seconds. With a fresh `CARGO_HOME` populated only by the locked +fixture and its trusted offline config, the complete two-file Rust dependency +and PyO3 suite passed all 29 tests with `GITHUB_ACTIONS=true`. The first +attempt at global `CARGO_NET_OFFLINE=true` failed one local Git dependency +fixture; scoping offline resolution to the default trusted Cargo home fixed +that failure. A terminal hosted rerun is still required before claiming the +coverage gate repaired. The targeted workflow contract suite passed 77 tests +with `GITHUB_ACTIONS=true` after this change. +The fixture crate's own `cargo llvm-cov --all-features --fail-under-lines 100` +run passed locally with one test and 100% line coverage, exercising its cached +`itoa` and `ryu` dependencies. diff --git a/docs/doctoring/opencode-coverage-daemon-cpu-capacity.md b/docs/doctoring/opencode-coverage-daemon-cpu-capacity.md new file mode 100644 index 0000000000..2eda65469f --- /dev/null +++ b/docs/doctoring/opencode-coverage-daemon-cpu-capacity.md @@ -0,0 +1,20 @@ +# OpenCode coverage sandbox의 Docker CPU 용량 결속 + +## 실제 실패 + +공개 target `.github#2617@b3983b467c2d7f1440b5024acb6a3860eb3e043d`의 중앙 run `38443671498`, coverage job `115456508271`은 `orgmetra-ci-01` / group 13에서 실행됐다. 공개 exact source 확보와 tool image build는 성공했다. 이후 고정 `--cpus 4`가 CPU 2개인 Docker daemon에서 거부됐다. sandbox exit 125로 출력 파일이 생성되지 않았고 테스트·coverage 측정·모델 추론은 실행되지 않았다. diagnostic `COMMENTED` 리뷰는 정식 승인으로 인정하지 않는다. + +## 좁은 수리와 검증 + +Docker daemon의 기존 `docker info --format '{{.NCPU}}'` 결과를 양의 정수로 검증한다. sandbox CPU limit는 `min(4, NCPU)`로 설정한다. 조회 실패·빈 값·0·부호·소수·줄 삽입은 실행 전 거부한다. host의 `nproc`를 daemon 용량으로 대체하지 않는다. + +메모리 14g, PID limit, network none, capability set, mount, 출력 인증, cleanup 및 100% coverage 기준은 변경하지 않는다. CPU 수리를 메모리 적합성이나 실제 runner 실행의 성공으로 주장하지 않는다. 원래 실행은 고정 workflow source를 유지하므로 수리 소스를 소비하는 fresh 지원 경로가 별도로 필요하다. 새 dispatch나 reviewer identity 우회는 이 소스 수리에 포함하지 않는다. + +실제 YAML의 launch shell을 추출하고 synthetic Docker CLI만 치환하여 argv를 검증한다. 첫 harness는 동일 cleanup 문자열의 첫 위치를 잘못 선택해 실패했다. 위치를 바로잡은 후 원래 `4 != 2` assertion 실패를 관측했고, 최소 수리 후 CPU/거부 입력 15개가 통과했다. 이는 실제 Docker daemon이나 Actions 실행의 검증은 아니다. + +## 공식 문서 + +- Docker. (2026). *docker system info*. https://docs.docker.com/reference/cli/docker/system/info/ +- Docker. (2026). *Resource constraints*. https://docs.docker.com/engine/containers/resource_constraints/ + +부모의 실제 문서 조회는 첫 구현 및 15개 GREEN 이후인 2026-10-14에 수행했다. 이를 구현 전 조회로 소급하지 않는다. `--cpus`는 container CPU 사용의 상한이며 테스트 수나 정확성 기준을 바꾸는 옵션이 아니다. diff --git a/docs/doctoring/opencode-exact-vcs-dependency-evidence.md b/docs/doctoring/opencode-exact-vcs-dependency-evidence.md index a9b26ae474..45a8831b6e 100644 --- a/docs/doctoring/opencode-exact-vcs-dependency-evidence.md +++ b/docs/doctoring/opencode-exact-vcs-dependency-evidence.md @@ -37,10 +37,15 @@ product's current-head tests passing. hook, or dependency lifecycle script runs while the network is available. - The source repository must be publicly fetchable without credentials, expose the normalized top-level import package directly or under `src`, and remain a - pure-Python leaf dependency. Private repositories, environment-marked VCS - requirements, namespace/layout aliases, installed-distribution metadata, + pure-Python leaf dependency. Private repositories, namespace/layout aliases, + installed-distribution metadata, entry points, compiled extensions, and registry packages that require the VCS distribution fail closed instead of expanding the secret-free build boundary. + An environment marker is accepted only when it is a single + `python_full_version` lower bound already met by the fixed Python 3.14 coverage + image. False or more expressive markers remain unsupported, so erasing the + proven-true lower bound cannot expose a source that the coverage interpreter + would omit. - The checkout roots and path file are explicitly world-readable so the later networkless coverage container can run as UID 65532 independently of the image builder's umask. diff --git a/docs/doctoring/opencode-infrastructure-review-state.md b/docs/doctoring/opencode-infrastructure-review-state.md new file mode 100644 index 0000000000..eb54c2fe6e --- /dev/null +++ b/docs/doctoring/opencode-infrastructure-review-state.md @@ -0,0 +1,28 @@ +# OpenCode infrastructure failures and review state + +Status: Proposed; protected delivery and downstream exact-head review are unverified. + +## Causal evidence + +CO #1223 at 90911687cb1ee49325ddd1f2bdfd29284a526028 was returned to Draft +because older OpenCode reviews remained CHANGES_REQUESTED. Their bodies explicitly +reported no source-backed product finding. Central run 36081670283, coverage job +107989271158, failed its trusted Docker image build before any CO test executed: +requirements-noema-document-ci-hashes.txt was missing from the build context. +That independent source defect is owned by ContextualWisdomLab/.github#2286 and #2385. + +The fallback publisher unconditionally used REQUEST_CHANGES to satisfy the formal +receipt gate. This promoted missing infrastructure evidence into a product verdict. +The correction publishes COMMENT instead and retains COVERAGE_BLOCKED separately. +COMMENTED still fails the formal receipt gate, never grants approval, and never +satisfies merge acceptance. Real source-backed model findings retain REQUEST_CHANGES. +Old reviews are not dismissed. After infrastructure delivery, a fresh model review +and required checks must settle against the exact current target head and base. + +## Verification + +The new event regression fails on unmodified main (1 failed, exit 1). Focused +publisher, coverage identity, receipt, pinned-workflow and toolchain contracts pass: +88 passed, 1 skipped, exit 0. actionlint and git diff --check pass. No hosted Docker +build or protected merge is claimed. The deterministic fallback source body is +unchanged; only its GitHub event is diagnostic rather than a fabricated verdict. diff --git a/docs/doctoring/opencode-private-artifact-destination.md b/docs/doctoring/opencode-private-artifact-destination.md new file mode 100644 index 0000000000..e3a64770ff --- /dev/null +++ b/docs/doctoring/opencode-private-artifact-destination.md @@ -0,0 +1,22 @@ +# OpenCode private source의 artifact 수신 경계 + +## 요구와 근거 + +private PR source archive를 공개 review 저장소의 Actions artifact로 발행하지 않는다. GitHub 공식 안내는 workflow artifact의 다운로드 권한을 해당 저장소의 read access에 결속한다. private 원본 저장소의 ACL이나 `retention-days: 1`은 공개 수신 저장소의 다운로드 권한을 제한하지 않는다. + +- GitHub. (2026). *Downloading workflow artifacts*. https://docs.github.com/en/actions/how-tos/manage-workflow-runs/download-workflow-artifacts (확인: 2026-10-14). +- 근거 소스: `acb8b34f9e7b8649fa1478139fa21c2217bdfbc7`의 `.github/workflows/opencode-review-dispatch.yml`. + +## 좁은 수리 + +검증된 `steps.validate.outputs.is_private`와 현재 수신 저장소의 GitHub REST metadata를 비교한다. 이벤트 시점의 `github.event.repository.private`는 사용하지 않는다. private source는 token exchange·source fetch·archive 전에 수신 저장소를 조회하고, upload 직전에 같은 검사를 다시 실행한다. 응답의 `full_name`이 실제 수신 저장소와 같고 `private`이 Boolean true인 경우만 허용한다. API 실패·잘못된 JSON·여러 JSON 응답·누락·문자열 Boolean·public은 nonzero로 거부한다. public source는 이 추가 API를 호출하지 않는다. 새 허가 Boolean이나 정책 variable은 추가하지 않는다. + +public source의 credential-free, artifact-free 경로는 유지한다. private 수신 저장소의 기존 artifact 경로와 1일 보존값도 유지한다. 이 수리는 공개 중앙 저장소로의 private archive 전달을 금지하는 경계이며, 암호화 전송 구현이나 private 실행 활성화가 아니다. 실제 private 데이터 전송이나 과거 유출이 있었다고 주장하지 않는다. + +## 검증과 남은 조건 + +실제 YAML의 guard 위치·환경 binding을 검사하고 inline Bash를 합성 visibility 입력으로 실행한다. 정상 public 및 private destination을 별도 양성 대조로 유지한다. 원본 artifact-free helper의 실제 로컬 Git 회귀도 함께 실행한다. `tests/test_opencode_public_source_transport.py`는 기존 quality CI의 실행 목록에 포함되어 있다. + +GitHub REST `GET /repos/{owner}/{repo}`의 현재 metadata를 사용한다. 전송 전과 upload 직전 조회는 atomic ACL 보장이 아니며, 마지막 조회 후 또는 artifact 보존 중 저장소 공개 전환을 방지하지 않는다. private artifact 경로 활성화에는 별도의 수신 ACL·보존 정책이 필요하다. 현재 공개 중앙 저장소에서는 private source 전달이 거부된다. + +모델 비용·retention admission, 실제 source transport, 독립 App의 exact-head 리뷰, 원격 Actions 및 병합은 별도 gate다. private target의 나머지 경로를 이 guard만으로 모두 안전하다고 선언하지 않는다. guard를 우회하거나 정책 attestation을 조작해 실행하지 않는다. diff --git a/docs/doctoring/opencode-public-source-artifact-free.md b/docs/doctoring/opencode-public-source-artifact-free.md new file mode 100644 index 0000000000..95133f7da5 --- /dev/null +++ b/docs/doctoring/opencode-public-source-artifact-free.md @@ -0,0 +1,29 @@ +# OpenCode 공개 coverage source 전달 + +상위 목표: ContextualWisdomLab/.github#2560. 하위 목표: ContextualWisdomLab/.github#2638. + +## 원인과 수리 범위 + +공개 PR의 검증된 merge-tree archive도 metadata job에서 GitHub artifact로 업로드했다. storage quota에 도달하면 metadata job이 실패하여 격리된 coverage 실행과 실제 OpenCode 모델 리뷰가 시작되지 않았다. + +공개(`is_private == 'false'`) source는 isolated coverage job에서 고정 HTTPS Git origin과 정확한 base/head 객체로 다시 구성한다. private(`'true'`) source는 기존 인증된 acquisition과 archive upload/download를 그대로 사용한다. unknown은 명시적으로 실패한다. 공개 경로가 실패해도 인증·private artifact·moving ref로 fallback하지 않는다. + +이 수리는 공개 **source 전달**만 artifact에 의존하지 않게 한다. private artifact, 다른 증적 업로드, scanner, release, runner capacity, 모델/API availability를 해결했다고 주장하지 않는다. + +## 신뢰와 실행 계약 + +- coverage helper는 PR checkout이 아니라 중앙 immutable `github.workflow_sha`의 trusted checkout에서 실행한다. moving main fallback 및 전체 `toJSON(github)` 환경 전달은 coverage resolver에서 제거했다. +- trusted checkout bootstrap과 public Git child는 새 HOME/XDG, config/system/global 차단, empty init template, hooks/credentials/extraheader/redirect/proxy 차단을 사용한다. TLS 검증은 유지한다. +- 공개 repo slug, 두 lowercase40hex SHA, run/attempt를 검증한다. full history와 `.git`를 보존하며 기존 base detach → head merge `--no-ff --no-edit` 의미를 유지한다. 합성 merge commit SHA가 아닌 tree와 base/head가 동일성 기준이다. +- 정상 파일과 공백 포함 파일명을 허용한다. symlink/gitlink/non-regular, traversal, `.git` lookalike 및 control path는 checkout 전 거부한다. +- output은 validated RUNNER_TEMP 아래 독점 run/attempt 경로만 생성한다. 기존 output은 덮어쓰거나 삭제하지 않는다. 실패 시 처음 생성한 inode가 그대로인 owned directory만 정리한다. +- helper의 Git subprocess에는 Actions/App/Git 환경 자격증명이 전달되지 않는다. 이것은 실행중 Actions worker ancestor 또는 동일 UID의 경쟁 프로세스에 대한 sandbox 증명이 아니다. 기존 isolated job과 제품 Docker sandbox를 유지한다. +- 기존 syntax/replay/coverage identity, 100% 품질 기준, distinct App actor, 정확한 head/base 재검증과 실제 모델 verdict 조건은 변경하지 않는다. + +## 검증과 운영 상태 + +새 helper와 workflow 회귀는 RED를 실행한 뒤 수리했다. actual-shell helper는 실제 Git fixture와 정상 merge/history 대조를 사용한다. 공개 HTTPS acquisition의 별도 smoke도 실행했다. 이 결과는 제품 테스트나 원격 Actions/App 승인 대신이 아니다. + +`agent-review-runtime-quality-ci.yml`은 trigger·affected suite 선택·명령 목록 모두 새 회귀와 helper coverage100을 호출한다. 로컬 전체 suite, actionlint, 독립 구현 검토, 원격 exact-head 실행, 실제 모델 리뷰, App 승인은 각각 별도 receipt와 상태로 기록한다. 실패·skip·미실행을 PASS로 바꾸지 않는다. + +기존 Noema production permission test는 prepare/read-only metadata와 최종 publication의 역할을 구분하도록 현재 계약과 일치시켰다. production Noema workflow는 바꾸지 않았다. read-before-write와 prepared envelope 조건을 검증하며 reviewer 권한을 줄이거나 가장하지 않는다. diff --git a/docs/doctoring/opencode-rust-coverage-runtime-boundary.md b/docs/doctoring/opencode-rust-coverage-runtime-boundary.md index 8adc68d0ea..04213ba398 100644 --- a/docs/doctoring/opencode-rust-coverage-runtime-boundary.md +++ b/docs/doctoring/opencode-rust-coverage-runtime-boundary.md @@ -18,9 +18,35 @@ declarations and the isolated container's `docker run --env` arguments. If that workflow changes, its `REVIEW_DISPATCH_BLOB_SHA` pin must change with it; this does not rewrite the review-agent key system. +### Base-pinned Rust releases (2026-09-30) + +Debian's `rustc` (1.85) is older than the MSRV of repositories that pin a newer +release; fast-mlsirm pins 1.97.1 and its dependency graph needs at least 1.90, +so `maturin build --offline` could never build `_core` and coverage evidence +failed for every fast-mlsirm pull request. When the live-validated **base** +commit carries `rust-toolchain.toml` (or `rust-toolchain`), +`scripts/ci/resolve_base_rust_toolchain.py` selects its exact `1.x.y` channel; +`stable`, `nightly-*`, custom `path` toolchains and unparsable files select the +central pin instead. The pull-request head's pin is never read. The image build +(network allowed, before any PR content is mounted) then downloads +`rustup-init` 1.29.1 from `static.rust-lang.org`, verifies its SHA-256, and +installs only that release with `--profile minimal --component +llvm-tools-preview`; rustup checks each component against the hashes in the +channel manifest. `cargo` and `rustc` are symlinked straight to the toolchain +binaries, so no rustup proxy runs inside the `--network=none` sandbox, and the +second reviewed LLVM pair becomes: + +- `LLVM_COV=/usr/local/libexec/opencode-rust/llvm-cov` +- `LLVM_PROFDATA=/usr/local/libexec/opencode-rust/llvm-profdata` + +Those tools ship with the same rustc release, so they match its profile format. +Repositories without a base pin keep the Debian image unchanged, and if the +pinned layer fails to build, the job rebuilds the Debian image rather than +losing coverage for that repository. + The runtime MUST NOT fall back to unversioned `llvm-cov` or `llvm-profdata`, a -host-runner tool, a pull-request-selected path, or a dynamically downloaded LLVM -binary. Missing, changed, or non-executable reviewed paths are coverage-evidence +host-runner tool, a pull-request-selected path, or an LLVM binary downloaded at +PR runtime. Missing, changed, or non-executable reviewed paths are coverage-evidence failures rather than reasons to measure a different toolchain. NIST SP 800-218 PW.4.1 covers acquiring and maintaining third-party software diff --git a/docs/doctoring/opencode-vcs-python-source-root.md b/docs/doctoring/opencode-vcs-python-source-root.md new file mode 100644 index 0000000000..1ad7cc95b2 --- /dev/null +++ b/docs/doctoring/opencode-vcs-python-source-root.md @@ -0,0 +1,37 @@ +# OpenCode immutable VCS `python/` source-root RCA + +Status: **Source repaired on protected `main` via #2123 (`ebc69a401`); image-path resolver extracted and contract-proven under #2157 follow-up.** Hosted consumer `coverage-evidence` past docker step #17 remains the issue-closure gate when a post-merge run is linked. + +## Incident and user-visible failure + +On 2026-09-12 UTC, central OpenCode dispatch [run 34701472466](https://github.com/ContextualWisdomLab/.github/actions/runs/34701472466) validated `ContextualWisdomLab/contextual-orchestrator#1149` at exact head `684cf28fa59e800c0db4886a08f25dd2edd156fc`. Its `coverage-source-tree` job succeeded, but `coverage-evidence` job `103574547257` failed while building the trusted tool image, before any pull-request test or coverage command ran. OpenCode therefore published only a non-approving COMMENTED review, and the required receipt remained fail-closed. + +The failing dependency was the exact VCS pin `fast-mlsirm@09f762ded35786dd1078222a4577ff09d649816f` from the consumer's validated `pyproject.toml`. That commit contains `python/fast_mlsirm/__init__.py`; it does not expose the import package at repository root or under `src/`. + +## Root cause and boundary + +`opencode-review-dispatch.yml` enumerated only four trusted candidates: `src/`, `src/.py`, ``, and `.py`. The materializer had already authenticated the target repository, bound the dependency to an immutable commit, fetched that commit without tags, and verified `FETCH_HEAD` and `HEAD`; the failure was solely an incomplete source-layout contract in the central owner. + +#2123 added only `python/` and `python/.py`, then mapped a match to the repository's `python/` directory. It preserved the invariant that exactly one candidate may exist and continued to reject symlinked/namespace imports, any symlink layout, compiled extensions, installed distribution metadata, and ambiguous roots. It did not infer arbitrary paths from untrusted packaging metadata and did not execute dependency lifecycle code. + +The #2157 follow-up extracts that same admission logic into +`scripts/ci/resolve_opencode_base_vcs_import_root.sh`, which the coverage Dockerfile +`COPY`s and executes. Offline fixtures in +`tests/test_opencode_vcs_python_source_root_contract.py` prove the `python/` layout +succeeds, `src/` and root layouts still succeed, and missing/ambiguous/namespace/compiled +trees still fail closed — so the image-path algorithm no longer depends solely on an +untested HEREDOC. + +Rejected alternatives were: changing the consumer's valid immutable dependency pin; copying `fast-mlsirm` into the consumer; adding the whole repository to `PYTHONPATH`; recursively searching for a matching directory; or weakening/bypassing the OpenCode coverage gate. Each would move ownership, admit ambiguity, or hide the central defect. + +## RED → repair → verification gate + +- RED commit `b1fe97c477b56e148afbeeaed9a6b74338994b6b` requires both package and single-module `python/` candidates in the published workflow contract. +- Repair commit `af04581cea4ffc038c881c6ad101ea3e5842a664` adds those candidates and the corresponding `python_root` mapping. +- Hosted Runtime Quality [job `103581110552`](https://github.com/ContextualWisdomLab/.github/actions/runs/34704176931/job/103581110552) then failed the independent pairing contract because the changed workflow blob `f315683208d57ba89a2942502c525abe7355e2fd` no longer matched the reviewed predecessor pin. Commit `683cb053b3c6f1c7b3f293a74263ac9b13e9bdf1` advances only that exact pin; no hash check is removed or relaxed. +- #2123 merged to protected `main` as `ebc69a401` (2026-09-13). +- #2157 follow-up moves the resolver into `scripts/ci/resolve_opencode_base_vcs_import_root.sh` with executable fixtures and re-pins `REVIEW_DISPATCH_BLOB_SHA`. + +## Follow-up + +Rerun only consumer failures whose cause changed, beginning with `contextual-orchestrator` PRs that previously died at step #17. Verify that the trusted image builds from the same `fast-mlsirm` commit, the PR sandbox remains networkless and credential-free, coverage/docstring evidence executes, and a substantive exact-head review is published. Link that `coverage-evidence` job on #2157 before closing the issue. If any additional conventional source root is needed, add it through its own immutable fixture and one-root regression rather than generalized path discovery. diff --git a/docs/doctoring/org-queue-sweep-rotation.md b/docs/doctoring/org-queue-sweep-rotation.md index 03784d0b7e..51a640443a 100644 --- a/docs/doctoring/org-queue-sweep-rotation.md +++ b/docs/doctoring/org-queue-sweep-rotation.md @@ -1,8 +1,16 @@ # Org-queue-sweep review-dispatch rotation +> **Superseded for queue hygiene.** The organization sweep no longer inventories +> or cancels repository-wide queued/in-progress Actions runs. That duplicate +> queue-hygiene path was removed by `.github#1878` (`1b65dbc35e7183722ad77894e2d80b39993be90d`), +> and current-head coalescing was later integrated into the merge scheduler. +> The rotation material below remains historical evidence for the former +> review-dispatch fairness mechanism, not a description of current stale-run +> ownership. + ## Problem -`org-queue-sweep` in `pr-review-merge-scheduler.yml` walks every organization +Historically, `org-queue-sweep` in `pr-review-merge-scheduler.yml` walked every organization repository once per 15-minute tick and consumes bounded, organization-wide review-dispatch budgets across that entire walk. Default-base work uses `ORG_SWEEP_REVIEW_DISPATCH_LIMIT` (default `1`); stacked work uses the separate @@ -23,6 +31,15 @@ required review. ## Decision +The current ownership boundary is: + +- `pr-review-merge-scheduler` owns review, merge, and branch-update state. +- The integrated current-head coalescer owns same-PR stale-run cleanup. +- The organization sweep does not repeat an Actions inventory per repository. + +The rotation decision below is retained as historical operational evidence for +the former review-dispatch fairness implementation. + Rotate the sweep's repository walk order by a rotation index before applying the unchanged organization-wide budgets. `rotation_offset = rotation_index % repository_count`; the walk starts at that offset and wraps. This spreads each diff --git a/docs/doctoring/persistent-runner-workspace-reuse-20260927.md b/docs/doctoring/persistent-runner-workspace-reuse-20260927.md new file mode 100644 index 0000000000..18914b4fd6 --- /dev/null +++ b/docs/doctoring/persistent-runner-workspace-reuse-20260927.md @@ -0,0 +1,44 @@ +# Persistent runner workspace reuse + +## Observed failures + +On 2026-09-27, CodeQL scan job 108599677231 on `cwlab-s1-03` +failed with `remote origin already exists` before analysis. The manual Git +initialization reused a repository from an earlier job. Anonymous OpenCode +coverage materialization used the same pattern with `trusted-source` and +also failed a real local repeated-workspace regression. + +The subsequent cross-repository status publication returned HTTP 403. +Live organization installation metadata shows `opencode-agent` has only +`statuses: read`. Adding `statuses: write` to the workflow cannot elevate +that installation permission. Existing successful-scan artifact settlement +remains the supported authenticated fallback; this repair does not invent +a trusted status author or widen application permissions. + +Scheduler job 108601462359 failed with `invalid UTF-8 string` on the first +GraphQL page. Its query is ASCII; the next same-source job 108601744765 +succeeded, and a current read-only request for the same 25-PR page succeeds. +No encoding mutation is justified by this non-reproducing observation. + +## Repair + +Use the repository's pinned native checkout action for CodeQL's validated +repository and exact head, with cleanup and without persisting credentials. +For anonymous OpenCode coverage bootstrap, discard only the current job +workspace contents after verifying it is not a symlink, matches the physical +current directory, and is directly under the runner-provided job directory. +The directory itself remains. Old Git hooks, configuration, and untracked +files cannot survive this anonymous bootstrap; child symlink targets and +sibling directories remain untouched. The Git fetch stays anonymous and +pinned to the validated trusted source ref. + +## Verification + +Before repair, three focused regressions failed: repeated anonymous checkout, +linked workspace refusal, and the native CodeQL checkout contract. +After repair, the focused real-Git regressions and existing CodeQL, OpenCode +shell, coverage toolchain, and paired workflow blob contracts report +**105 passed, 1 skipped**. Actionlint validates the two modified workflows +with ShellCheck and Pyflakes disabled; no claim about those engines is made. +Hosted execution and downstream CodeQL analysis are separate acceptance +steps. No active runner is restarted and no unrelated working copy is cleaned. diff --git a/docs/doctoring/pg-erd-central-quality-20261008.md b/docs/doctoring/pg-erd-central-quality-20261008.md new file mode 100644 index 0000000000..9470213a66 --- /dev/null +++ b/docs/doctoring/pg-erd-central-quality-20261008.md @@ -0,0 +1,95 @@ +# pg-erd-cloud central quality workflow — candidate + +## Owner requirement + +The October 8, 2026 instruction requires central ContextualWisdomLab/.github +ownership, self-hosted execution with no GitHub-hosted fallback, and genuine +Noema/OpenCode exact-head review and independent bot approval. Model selection +is `auto`. This change owns only the missing product validation reusable workflow; +it does not duplicate the all-workflow routing candidate in #2565, the review +reliability lane in #2574, or their secrets and publication handlers. + +## Implementation + +`.github/workflows/pg-erd-cloud-quality.yml` has fixed backend and frontend jobs. +It declares only `workflow_call`, no caller-provided executable strings, no +inherited secrets, and `contents: read`. Both jobs target `CWL CI isolated` and +`[self-hosted, linux, x64, cwlab-ci-isolated]`. The first inline step rejects +foreign repositories, forks, pull_request_target, unsupported events and +non-main push/dispatch before any action or checkout. Same-repository PRs remain +admitted, including stacked PRs. Actions use immutable SHA pins; checkout selects +the caller head and disables persisted credentials. + +Backend keeps Python 3.10, both product hash locks, automation contracts, mypy +and pytest. A unique RUNNER_TEMP directory holds its venv, is cleaned on EXIT, +and avoids installing candidate dependencies into shared runner Python. Frontend +keeps Node 26, npm ci including dev dependencies, typecheck, tests, production +build, and explicit NODE_ENV test/production. All phases remain failure-gating. + +Keep concurrency in the caller only. The eventual thin caller has: + +- name `ci`; +- push on main and same-repository PR triggers; +- contents read; +- `ci-${{ github.repository }}-${{ github.event.pull_request.number || github.run_id }}` + concurrency with PR-only cancellation; +- one reusable job calling + `ContextualWisdomLab/.github/.github/workflows/pg-erd-cloud-quality.yml@` + followed by a real reviewed/published 40-character central commit SHA; +- no `secrets: inherit` and no arbitrary inputs. + +Do not activate a mutable branch caller or invent its not-yet-published SHA. +Reusable invocation can change displayed required check names. Reconcile those +through the protection owner without removing checks merely to unblock merge. + +## Executed acceptance + +Tests first failed for the missing workflow. The next test failed because the +initial port installed Python dependencies globally. Job-private venv repair +followed. Final local focused result: 24/24 new contracts plus 72/72 existing +queue contracts, 96 passed. Actual admission shell was executed for 18 accepted +and rejected tuples. The actual backend shell was exercised with synthetic tool +executables for success, mypy failure and pytest failure: exit propagation, +owned temporary cleanup and unrelated-directory preservation passed. This is +shell/contract execution, not backend dependency installation or runner evidence. + +Actionlint passed with the exact custom-label vocabulary supplied by #2565's +`.github/actionlint.yaml` in a separate scratch config; that PR-owned file was +not duplicated or edited. Default actionlint without custom-label config failed +on the isolation label, and that failure must not be hidden as schema success. + +## Fresh capacity observation — October 8, 2026 + +Group 13, `CWL CI isolated`, contains three runners: orgmetra-ci-01, +keyverse-ci-01 and calendarweave-ci-01, all offline at the observed GET. Its five +selected repositories are .github, CalendarWeave, fast-mlsirm, keyverse and +Orgmetra. pg-erd-cloud is not selected. Other online runners do not establish +this group's capacity and must not be relabeled or silently borrowed. + +The existing operator lane is ContextualWisdomLab/linux-cluster-ops#326. Its +isolation, custodian acceptance, runner registration, tool availability and +cleanup canary remain required. This source change neither provisions a runner +nor modifies ACLs. No remote job execution, model-backed approval, merge or +hosted-only retirement is established by these local tests. + +## Review/gateway boundary + +Protected central workflows currently route Noema/OpenCode through the vendored +contextual-orchestrator sidecar and `orchestrator/free`. The sidecar explicitly +rejects auto in its current admission. Workflow model-string substitution alone +is therefore incorrect. External LiteLLM routing needs a separately reviewed +configuration/provider contract, least-privilege key, budget and private-target +ZDR protection while preserving the existing independent bot publisher and +exact-head verdict checks. The LiteLLM UI requested login; the secure credential +save was declined, so no key was issued or read and no inference claim is made. + +The implementing account must not self-approve. Only a real passing independent +model verdict plus current-head publication authority permits formal approval. + +## Primary technical reference + +GitHub Docs, *Reuse workflows* and *Using self-hosted runners in a workflow*. +Check the live documentation before activation. Group restriction and label +matching are routing/access constraints, not proof of isolation. Local proposed +configuration, central publication, consumer adoption, actual runner execution, +review approval and ordinary protected merge are distinct acceptance states. diff --git a/docs/doctoring/pingora-hwpx-evidence-admission.md b/docs/doctoring/pingora-hwpx-evidence-admission.md new file mode 100644 index 0000000000..a8a0f9e98d --- /dev/null +++ b/docs/doctoring/pingora-hwpx-evidence-admission.md @@ -0,0 +1,40 @@ +# Pingora edge policy admits HWPX evidence documents (#2116) + +`scripts/ci/pingora_edge_policy.py` rejected the consumer's HWPX evidence +attachment before a merge verdict. `BINARY_DOCUMENT_MAGIC` only knew +`.pdf`/`.png`, and `_is_binary_documentation_asset` only admitted a +`doc`/`docs`/`documentation` directory, so the ZIP-based `.hwpx` under +`evidence/` matched neither rule and fell through to the strict UTF-8 +decode every other candidate gets. + +Source baseline: `fb17ef556f94f673234aa557254ae52779e9a7b0`. Consumer +evidence: ContextualWisdomLab/late-life-anxiety-reanalysis#10, head +`a1cd5bc6783c6510dfcf937f523c733366e82213`, run `34700409497`, job +`103571044859`. Reported failure: "Pingora edge policy could not establish +complete evidence: Runtime policy candidate +evidence/reviewer_response_draft.hwpx is not valid UTF-8". + +The repair adds `.hwpx` (`PK\x03\x04`) to `BINARY_DOCUMENT_MAGIC` and lets +`_is_binary_documentation_asset` also admit an `.hwpx` under an `evidence` +path segment, gated on a bounded HWPX container check: unprefixed ZIP, +exact EOCD record, unique members with `mimetype` first, a stored (not +deflated) `mimetype` entry exactly `application/hwp+zip`, and a non-empty, +unencrypted `Contents/content.hpf` manifest. Format evidence only -- no +document rendering or malware inspection. The runtime-path guard and the +Nginx-runtime-text fallback scan for disguised/malformed archives are +retained unchanged. + +Test evidence (offline, this branch): RED (test-only apply) 3 failing / 19 +passing in `tests/test_pingora_hwpx_evidence.py`; GREEN (full patch) 90 +passing across that file plus `tests/test_pingora_edge_policy.py` and +`tests/test_pingora_edge_workflow_contract.py`. Branch coverage of the +touched module: 100% (388 statements, 174 branches, 0 missed). +`interrogate scripts/ci -q`: 100.0% docstrings. Full suite passes, no new skips or warnings. + +Hosted acceptance still requires a newly loaded central source SHA to +re-run the consumer's exact head bootstrap; local tests prove the declared +classification and container logic, not a hosted admission outcome. + +## Reference + +Hancom. (n.d.). *한/글 문서 파일 형식: HWPX 포맷 구조 살펴보기*. https://tech.hancom.com/hwpxformat/ diff --git a/docs/doctoring/product-gap-baseline-source-integrity-20261001.md b/docs/doctoring/product-gap-baseline-source-integrity-20261001.md new file mode 100644 index 0000000000..a009f92bbb --- /dev/null +++ b/docs/doctoring/product-gap-baseline-source-integrity-20261001.md @@ -0,0 +1,28 @@ +# Product gap baseline source-integrity repair + +## Incident + +Repository Metadata Reconcile run `36777030872`, job `110097412873`, ran +against `.github#2530@61a3f2ebbf6adf9c237a647552ccffeb52b00f47`. +Five tests failed because `docs/product-technical-gap-baseline.md` began with +a connector display warning and contained only 433 lines. The previous complete +artifact contained 3,703 lines and 361,021 bytes. + +## Root cause + +Commit `bd3cfe8645844ec7b140a1b73d2b339f8e4283dc` attempted to change one +dependency-floor row, but persisted a truncated connector rendering instead of +the Git blob. This removed G-17 publication evidence, the 107-row live +inventory, APA 7th references, and buyer-facing PRD/TRD/UML/Gap evidence. + +## RED to GREEN + +- RED `c8d41f52962814d080a1c4947acbff2f3f9bb81e` rejects the connector warning + and output-line banner at the artifact boundary. +- GREEN restores the complete parent blob and reapplies only the intended + `ip-address` 10.7.1 dependency-floor row. +- The focused integrity, gap-baseline, and published-lineage tests must pass + before the full warnings-fatal repository suite is accepted. + +Hosted exact-head Metadata, authenticated agent verdicts, and independent +approval remain required; this record is not merge authorization. diff --git a/docs/doctoring/psychometrics-commons-central-ci.md b/docs/doctoring/psychometrics-commons-central-ci.md new file mode 100644 index 0000000000..b02521f9aa --- /dev/null +++ b/docs/doctoring/psychometrics-commons-central-ci.md @@ -0,0 +1,60 @@ +# Psychometrics Commons central Runtime CI candidate + +## Scope and source + +The reusable `psychometrics-commons-ci.yml` preserves the three Runtime CI +checks from `ContextualWisdomLab/psychometrics-commons` PR #165 head +`bb9028714d996871ed2588ea9a3aa83fd581af84`. Its commands/image/tool versions +also match product main `e81441ce70c676992470afe2be469dd891ad3eb5`; their +trigger/concurrency differences remain caller-owned. The unavailable temporary +product checkout was replaced by immutable GitHub source reads, not an assumed +local snapshot. + +The workflow exposes only `workflow_call`, without executable inputs or +inherited secrets. Every job admits only an open, non-Draft, same-repository +PR targeting `main` in the fixed `ContextualWisdomLab/psychometrics-commons` +repository before runner allocation. The first step then validates event +identity, positive PR number, exact merge ref, and canonical head before +checkout. Checkout uses the supplied PR head only and verifies actual HEAD; +there is no fallback to an event SHA. + +## Execution and unchanged gates + +All jobs select `CWL CI isolated` with `self-hosted`, `linux`, `x64`, and +`cwlab-ci-isolated`. No shared control/review runner or hosted fallback exists. +Rust 1.97.1, nightly-2026-08-18, cargo-llvm-cov 0.8.6 and the product's exact +PostgreSQL 18 image digest are unchanged. Stable commands explicitly use the +pinned toolchain. Formatting, all-target compile/test/Clippy, committed lock, +Python coverage contracts, warning-free rustdoc, complete production line and +branch coverage, and failure diagnostics remain required. + +Each job has its own GitHub-managed PostgreSQL service/network and ephemeral +test password. Docker allocates a random host port; connection setup consumes +that service's reported port instead of binding shared host port 5432. Service +cleanup belongs to the job lifecycle, not recursive deletion of runner storage. + +## Admission and evidence limits + +This is a source candidate, not runtime-ready evidence. Central main +`58d12a9f1b46d611f75cfcf3e6c5a3fe19a55f96` defines the isolated group contract, +but labels and YAML do not prove disposable guest isolation or safe execution. +The parent previously observed that group 13 did not admit psychometrics. +An authorized operator must verify consumer access, disposable job isolation, +Docker/service capabilities, clean teardown, pinned tool availability and +actual exact-head runs. Do not relabel privileged runners to release queued +jobs. No grants, registrations, restarts, secret changes, dispatch, remote +push, merge or product caller edits were performed for this candidate. + +Only PR events are admitted in this slice. Main pushes and manual callers are +not silently interpreted as PR validation; they would need a separately +verified admission contract. Job skips are not quality success. Preserve all +three exact check names when a thin consumer caller is later pinned to an +integrated immutable central commit, and keep consumer concurrency there. + +## Local verification + +The focused tests parse the actual workflow, verify job-level admission and +exact checkout, execute the defensive shell with allowed and rejected synthetic +metadata, and bind toolchain/image/coverage threshold commands. Actionlint and +whitespace checks validate the candidate. These tests do not execute product +Rust tests, PostgreSQL services, GitHub runners or formal App review. diff --git a/docs/doctoring/quarantine-sandbox-runtime-self-hosted-ci.md b/docs/doctoring/quarantine-sandbox-runtime-self-hosted-ci.md new file mode 100644 index 0000000000..96c1fdde62 --- /dev/null +++ b/docs/doctoring/quarantine-sandbox-runtime-self-hosted-ci.md @@ -0,0 +1,206 @@ +# Quarantine sandbox runtime: central self-hosted CI + +## Status and ownership + +This is a future routing definition, not working runner capacity or release evidence. +The operator-requested dedicated group `CWL QSR hostile workload` is +**NOT_PROVISIONED** under this handoff's current group-absence baseline, pending +ContextualWisdomLab/.github#1590. It must not be interpreted as a deployed group. +This writer did not query or change runner inventory, registration, labels or ACL. +The parent retains the inventory receipt and activation decision. + +ContextualWisdomLab/.github#1590 owns dedicated disposable runner capability and +custody. ContextualWisdomLab/.github#2083 owns reusable positive-LSM publication +and operational acceptance. Both issue bodies were read on 2026-10-08; neither +is closed by this source-only extraction. Existing central control, CodeQL, +OpenCode, isolated and remediation groups are not fallback capacity. + +Owned QSR implementation consists of the reusable workflow, its Python contract +tests and this record. Source integration also declares the QSR custom labels in +`.github/actionlint.yaml` and binds the named group in the organization runner +contract. When merging the K-CSAP successor, keep its existing label and exact +workflow/job selector, and preserve the combined label set in its config test. +These source-only changes do not register runners or change ACLs or thresholds. +Independent source review is separate from a formal App review; this writer +supplies no self-approval. + +## Fixed interface and candidate binding + +`.github/workflows/quarantine-sandbox-runtime-ci.yml` accepts **workflow_call +only**, with no inputs, declared secrets or outputs. In particular, there is no +`candidate_sha`, `lane`, runner selector or test-selector input. One aggregate +call contains four eligible product jobs plus explicit admission and terminal +acceptance jobs. Terminal acceptance requires all five dependencies to report +success; missing, skipped, failed or cancelled dependencies are nonpassing. The separate product caller must +pin a reviewed immutable central commit SHA; a mutable branch is not publication. +Do not pass `secrets: inherit` or an environment carrying credentials. + +The GitHub context is the caller's context. A dedicated `admission` job runs before +any checkout or product command. Each of the four product jobs also executes a +trusted inline caller/event guard before its own checkout; this rejects a wrong +repository, fork, unsupported event, empty head, or non-lowercase/non-40-character +head. PR events require the raw `pull_request.head.sha`; push events require +`github.sha`. The admitted raw SHA must equal the effective checkout SHA. A missing +PR head cannot be accepted through the `github.sha` fallback, even if that fallback +is valid. The separate admission job alone is not treated as per-job protection. +It must fail for repositories other than +`ContextualWisdomLab/quarantine-sandbox-runtime`, fork pull requests and +unsupported events. The retained product jobs still require either push or a +native same-repository pull_request. Other repositories, fork PRs, +pull_request_target, workflow_dispatch and repository_dispatch do not qualify. +The original positive push/same-repository guard is retained and narrowed by the +repository/event gate. **Native fork PR product execution: NOT_RUN; admission: +FAILED**. No skipped product job is acceptance evidence. + +The candidate is derived solely from +`github.event.pull_request.head.sha || github.sha`. Each job checks out that +candidate with the original SHA-pinned checkout action, `persist-credentials: +false`, then compares `git rev-parse HEAD` against the event-derived SHA before +installing tools or executing product code. The workflow grants only +`contents: read`, contains no publication/write commands, credential inheritance +or write permission, and uploads only the original coverage evidence artifacts. +Artifact upload is not a repository-write permission grant. + +Reusable concurrency is workflow-scoped and namespaced `qsr-central-`, then +workflow/repository/PR number with run-ID fallback and cancellation enabled. +The caller must use a different group namespace to avoid self-cancellation. +There is no central helper-code checkout and no new `job.workflow_*` expression. + +## Retained lanes and lost capability + +All jobs require group `CWL QSR hostile workload` with labels `self-hosted`, +`linux`, `x64`, `cwl-hostile-workload`; positive LSM additionally requires `selinux`. +The organization runner policy contract admits this static group only with +`cwl-hostile-workload`; actionlint label declarations are source validation only. +There is no GitHub-hosted fallback or user-selected runner routing. + +| Child job | Preserved mechanics | +| --- | --- | +| `verify` | Rust 1.97.1; locked metadata/test/clippy; repository policy; coverage-parser unit tests; both product-owned caller contract modules; fmt; rustdoc with warnings denied | +| `coverage` | Rust 1.97.1 + llvm-tools-preview; cargo-llvm-cov 0.8.6 locked install; workspace lib/tests JSON and strict production parser | +| `branch-coverage` | nightly-2026-07-01 + llvm-tools-preview; cargo-llvm-cov 0.8.6 locked install; branch JSON, missing-line report and `--require-branches` parser | +| `podman-e2e-positive-lsm` | Rust 1.97.1; exact Podman 5.8.4; nonempty runner name; rootless and SELinux boolean assertions; immutable pre-pull; real service isolation/cleanup witness; always-run zero container/network leak assertions | + +The fixture remains +`docker.io/library/python@sha256:94457973ea8a27a799f0b8ea1fe3e3147fcbebaed63497a8af63b28195a08108`. +All action SHAs, product commands, environment values and coverage artifacts are +retained from the actual product source, with one independently reproduced +cleanup repair: enumerate networks first and fail on enumeration error before +checking names. The old pipeline's `|| true` incorrectly accepted query failure +as zero leaks. Container-query errors and visible container/network leaks remain +nonpassing. Synthetic shell controls verify these exits, not real isolation. Production statement/function/region/branch +coverage requirements remain exact **100%** where the parser exposes them; no +threshold, warning, isolation or cleanup requirement is relaxed. + +`podman-e2e-negative-rootless-apparmor` is deliberately **DISABLED** and omitted. +The hosted Ubuntu 24.04 / Podman 4.9.3 producer's rootless unavailable-effective-LSM +rejection-and-cleanup witness is lost. It is not migrated to an unverified +self-hosted AppArmor profile and is not represented as passed by positive SELinux, +unit tests or skipped checks. Restoring a negative producer needs separately owned +capability and real unchanged-candidate evidence. + +## Caller contract enforcement + +The `verify` job executes both product-owned caller modules after checking the +candidate HEAD, using `python3 -B -m unittest scripts/test_central_ci_caller.py +scripts/test_central_ci_caller_sensitivity.py`. The invocation is unconditional; +a missing module or either module's failure fails this job. There is no optional +skip, alternate path, or failure masking. The caller's reviewed full-SHA oracle +must be updated together with its workflow pin when adopting a successor. +Running this producer against a predecessor without these modules fails closed. +Their tests do not replace host prelease isolation or actual execution evidence. + +## Check-name migration is not branch protection compatibility + +An aggregate reusable call changes published child check names. If the product +caller job ID is `ci`, the expected mapping to verify in a real Actions run is: + +| Previous direct job | Expected nested check name | +| --- | --- | +| New admission rejection | `ci / admission` | +| New terminal evidence result | `ci / acceptance` | +| `verify` | `ci / verify` | +| `coverage` | `ci / coverage` | +| `branch-coverage` | `ci / branch-coverage` | +| `podman-e2e-positive-lsm` | `ci / podman-e2e-positive-lsm` | + +A different caller ID/name changes this prefix. These are planning mappings, +not observed check-run names. Existing required-check names and branch protection +have not been queried or changed by this writer. Do not claim compatibility: +the parent must inspect actual published contexts and obtain the policy owner's +reviewed migration before treating these checks as replacements. The terminal +`ci / acceptance` check must also be enforced; requiring only filtered product +jobs is insufficient. No branch protection mutation was performed. Calling this +all-job interface three times would duplicate all four jobs, not select lanes. +A QSR #151-based caller does not automatically include QSR #150 static-analysis +lineage. Do not claim #150 coverage/adoption without an explicit rebase/merge and +separate verification. The peer's candidate_sha/lane oracle is not acceptance +of this no-input interface; a new independent verification must bind these final +source hashes, actual published central SHA and observed check-name mapping. +The caller must retain its existing immutable pin until a reviewed successor is +published. A local integration or a new main revision does not update that pin. + +## Admission and evidence still missing + +Same-repository PR identity is **not security review authorization**. Exact HEAD, +read-only token and labels do not provide a safe prelease boundary. Product build +scripts, tests, parsers and dependency hooks execute hostile-capable code directly +on the selected host, even in the non-Podman jobs. There is no fabricated safe +prelease guard and no assertion that the product's container boundary protects +the CI host from its build process. + +Before activation, #1590's owner must establish dedicated disposable custody, +absence of provider/production credentials, isolation from shared control planes, +and reviewed repository plus selected-workflow ACL restrictions bound to the +approved immutable central workflow. Do not register, relabel or expand existing +groups to make this definition run. Missing group/capability remains blocked or +queued, never a hosted fallback or operational success. + +#2083 is broader than this source baseline: its production command timeout +witness +`production_gated_command_execution_kills_and_reports_a_command_that_exceeds_its_timeout` +and `x86_64-unknown-linux-musl` target are absent from the inspected source job. +This extraction does not invent either or claim complete #2083 acceptance. The +product owner must supply their reviewed mechanics through a subsequent exact +source change. Current-head positive service and command evidence, required +security/SBOM/provenance gates, independent review and protected integration +remain unresolved. No VM, compiler, cargo suite, container E2E or hosted execution +was run in this implementation task. + +## Local source verification + +Tests use PyYAML BaseLoader (preserving `on`) and a frozen in-test oracle from +the actual product CI bytes, not a runtime sibling-checkout dependency. Sequential +RED/GREEN receipts cover missing workflow, preserved mechanics, exact candidate, +read-only token, dedicated routing, admission, concurrency and this gap record. +Verification uses the QSR contract module plus the adjacent runner-policy and +K-CSAP integration contracts. Actionlint runs against the QSR workflow with its +normal external scanners and the committed custom-label config; a scoped pass is +not a repository-wide actionlint pass. Neither static tests nor lint establishes +runner availability, operational isolation, qualifying App approval or release readiness. + +## Exact positive-LSM execution witness + +`LSM-TEST-001` addresses the zero-test-success finding on central PR #2601: +`--exact` alone does not require a matching test. The trusted inline wrapper +executes the immutable named ignored test with libtest pretty output, one test +thread, no color and stdout capture enabled. It preserves a nonzero Cargo exit +and requires exactly one running-count line, exactly the named passing-test line, +and exactly one summary with one pass and zero failures, ignored or measured +tests. Empty, renamed, ignored, duplicate or inconsistent results fail closed. + +The transcript stays in the executing wrapper rather than a reusable fixed file; +Python isolated mode prevents candidate-root module imports by this wrapper. +Synthetic Cargo-output controls test admission, not Podman or kernel isolation. +This check detects absent/renamed tests, not a malicious candidate that forges +libtest output or weakens the test body. Code review and disposable host custody +remain necessary. Fresh source/tool/evidence directories (SRC03) and actual +positive-LSM execution are separate, unfinished activation work. + +Primary platform references: GitHub Docs, **Secure use reference** (self-hosted +PR-code risk), **Reusing workflow configurations** (caller context/concurrency), +and **Reuse workflows** (immutable reusable references and permission boundaries). +Product source baseline SHA-256: +`1386ffe186ed9a903f5490289705bc7ee84a0bc3a9db62a1caf9b00238747813`. +The parent handoff carries frozen file hashes, full diff hash and raw local gate +outputs; none is promoted to successful operational evidence. diff --git a/docs/doctoring/readme-template-authoring-contract.md b/docs/doctoring/readme-template-authoring-contract.md new file mode 100644 index 0000000000..8afaa97563 --- /dev/null +++ b/docs/doctoring/readme-template-authoring-contract.md @@ -0,0 +1,70 @@ +# README authoring template and delivery contract + +## Problem + +The README mission requests a reusable writing template, but the existing standard +at `bd56a9cc599e60be1b5d2a9729dea36b0e215e9f` explicitly excluded a template. +It also described a remaining external blocker as an alternative completion +condition. A checklist was useful, but neither sentence satisfied the requested +writing-and-integration outcome. + +## Decision + +Keep the existing standard and its product-owned language, provenance, licensing, +and ordinary-merge controls. Add `docs/templates/repository-readme-template.md` +as an adaptable authoring scaffold. Require command working-directory, runtime, +lock, result, side-effect and stop/recovery evidence. An unexecuted example must +be identified as such; an external blocker leaves delivery incomplete. + +The scaffold supplies a title/promise, task-oriented introduction, quick start, +example, integration context, status/verification, document navigation, support +and license section. It supplies no real package, command, endpoint, badge, +benchmark, source license or customer claim. Authors replace placeholders only +from repository evidence and omit irrelevant sections. Nothing automatically +copies the template into product repositories or changes their runtime contracts. + +## Alternatives and risks + +A checklist alone was rejected because it leaves the requested reusable writing +structure absent. A mass README generator was rejected because repository-specific +product authority, release state and third-party obligations are not interchangeable. +A blocked PR must retain its ownership and valid delta rather than be reported +complete or closed on a title/ancestry match. + +The main misuse risk is publishing the scaffold literally or interpreting static +template tests as proof of a product's runtime behavior or license rights. The +instructions prohibit both. Real command execution, visual review when relevant, +source/provenance assessment, and exact-head integration evidence remain separate. + +## Verification + +The pre-change standard bytes matched Git blob +`3d3d5895ead0b4dfc8a94d7b651d6d7de68bb427`. The new unittest module was exercised +against that standard before the repair: template presence, discoverability, +execution fields and incomplete-blocker assertions failed. After the documentation +change, all four test methods pass locally under Python 3.13.5: + +```sh +python -m unittest discover -s tests -p test_readme_template_contract.py -v +``` + +This is structural documentation evidence, not full repository CI, semantic +review, release evidence, or a sibling-product conformance claim. Protected +integration still requires the unchanged current head's applicable checks and +reviews. The existing `.github` MIT source grant is unchanged; this work adds no +third-party source, dependency, asset, or license exception. + +## References + +GitHub. (n.d.). *About READMEs*. GitHub Docs. Retrieved September 12, 2026, from +https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-readmes + +GitHub. (n.d.). *Licensing a repository*. GitHub Docs. Retrieved September 12, +2026, from +https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/licensing-a-repository + +GitHub's README guidance supports purpose, getting-started, help and maintainer +navigation. Its licensing guidance supports keeping a real source grant distinct +from repository visibility. The stronger commercial-intake, exact-head and +complete-delta-delivery requirements here come from the repository owner's +explicit CWL mission, not an assertion that GitHub requires those policies. diff --git a/docs/doctoring/release-build-artifact-identity.md b/docs/doctoring/release-build-artifact-identity.md new file mode 100644 index 0000000000..bf00920ee3 --- /dev/null +++ b/docs/doctoring/release-build-artifact-identity.md @@ -0,0 +1,62 @@ +# Immutable same-run build artifact intake + +Base: 1916e95a8ee3b0dbd1c84011d88fc580700430e3. Previously the dependency +gate selected the caller's build artifact by name only. The gate now requires +`build_artifact_id` and `build_artifact_digest` as well as the expected name. +The producer must pass its upload result ID and `sha256:`-prefixed digest. +Missing values cannot fall back to name selection. + +The shipped shell reuses the existing exact-artifact-sbom-attestation workflow's +same-repository/same-run metadata comparison and the same pinned download action. +It also checks the returned ID explicitly and requires canonical positive +decimal ID and sha256 digest input. Metadata identity, name, digest, run and +unexpired status must agree before download. An API error blocks consumption. +No second generic verifier, new token, or helper revision is introduced. + +The gate requests only the additional `actions: read` permission required by +the metadata API. The caller must grant it; a called workflow cannot elevate +the caller's token permissions. Existing name-only callers must provide both +new required inputs when adopting this revision. Repository-local inspection +finds no executable caller of this reusable workflow; external caller adoption +is not exhaustively verified. FMLS's trusted matrix aggregation remains unwired. + +## Pinned download action contract, source inspection only + +The actual pinned revision is +`actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c`. +Its action.yml lines42–46 declare `digest-mismatch: error` as the default; +this candidate explicitly selects error. Its src/download-artifact.ts +lines94–136 select immutable IDs from current-run artifacts, lines171–183 pass +the selected artifact's digest as expectedHash, and lines215–231 throw and fail +the action on mismatch. A single requested ID avoids the multi-ID partial-match +warning path. A single selected artifact uses the existing destination root. + +Primary sources opened directly: +https://raw.githubusercontent.com/actions/download-artifact/3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c/action.yml +https://raw.githubusercontent.com/actions/download-artifact/3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c/src/download-artifact.ts + +The pre-download API comparison is not an independent hash of downloaded +bytes. Byte verification relies on this pinned action's implementation; its +download library/bundled execution and hosted transport are not executed in +the local tests. No warning-only revision is treated as equivalent. The API +record and downloaded record must refer to the same immutable ID; this does +not establish a release-source build proof or trusted gate success by itself. + +## Scoped evidence and remaining holds + +Tests execute the actual shell with inert gh output and installed jq. They +cover a valid record, same-name different ID, different run, absent/modified +digest, expiry, absent ID, invalid expected digest, different repository and +API failure. Rejected cases cannot reach the next-step marker. Static checks +bind download to ID and error-on-digest-mismatch; no real download occurs. +The first test run failed10 cases because its declaration extractor split at +child indentation; after anchoring the next input key correctly, the same +cases execute the shipped shell. This is a test harness repair, not acceptance +of the failed run. + +Source/control equality, fixed helper00c655, full licence/Strix policy, +platform closure, same-run trusted success aggregation, resource bounds, and +final R5 HOLD remain unchanged. API rate exhaustion prevents a fresh broad +external ownership/Project census; no inference of absent external callers or +approval follows. CodeGraph indexed38 workflow files with0nodes/edges, so all +workflow call paths are inspected as source rather than graph completeness. diff --git a/docs/doctoring/release-fixed-helper-source.md b/docs/doctoring/release-fixed-helper-source.md new file mode 100644 index 0000000000..f93ef9f0c1 --- /dev/null +++ b/docs/doctoring/release-fixed-helper-source.md @@ -0,0 +1,34 @@ +# Fixed helper source for release gates + +The three trusted checkouts use literal repository `ContextualWisdomLab/.github` +and reviewed helper revision `00c6551183cca101cfc97c43656a17cc2491c1b4`. +This is an independent helper revision, not an assertion that helper and called +workflow revisions are equal. A future workflow change does not silently update +these helper bytes. Updating the pin and content identities requires review. + +All three checkouts materialize `scripts/ci/` and +`requirements-strix-ci-hashes.txt`, preserving helper siblings. Before executing +helpers they verify checkout HEAD, canonical origin URL, the scripts tree +`bf26d3eefdb71fe79b855d941ffb46eb432b2f76`, lock blob +`9e705850b5ce53c7fe836bc3df3a18771151e3f6`, tracked-file cleanliness and required +entrypoints. Missing, foreign or mismatched source rejects. The step reports +helper repository/SHA separately from caller workflow SHA. The latter is only +provenance context, never a checkout selector or authorization input. + +GitHub's [current context reference](https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#job-context) +documents called-job workflow identity fields, but actionlint 1.7.12 and the +inspected upstream main strict schema do not yet support them. This alternative +uses neither those expressions nor an ignored diagnostic or permissive schema. +It changes the contract from called-self checkout to an explicitly adopted +fixed helper snapshot. The earlier called-self candidate remains separate. + +Local synthetic guards exercise valid identity, another caller SHA, missing +git source, foreign origin, wrong HEAD/tree, dirty tracked files and a missing +entrypoint. They do not perform checkout or network access. Hosted checkout and +attestation behavior remain unexecuted. + +`SOURCE_SHA == GITHUB_SHA`, full licence/Strix authorization, twelve-platform +closure and complete resource intake budgets remain unresolved independently. +No condition is relaxed by this source-selection fix. The pinned snapshot +retains its existing licence/tool-dependency limitations; exact-byte provenance +does not imply policy acceptance. diff --git a/docs/doctoring/release-license-archive-binding-20260924.md b/docs/doctoring/release-license-archive-binding-20260924.md new file mode 100644 index 0000000000..745e843727 --- /dev/null +++ b/docs/doctoring/release-license-archive-binding-20260924.md @@ -0,0 +1,64 @@ +# Archive-bound license evidence candidate + +Base: `a78b1c9f788d1a89fd7c8ab39d6347152b7e3065`. + +## Reproduced defect + +`/private/tmp/pr2347-archive-binding-repro.py` exercises the real capture, license +gate and install-lock binder. Its synthetic wheel contains academic-only terms, +while the separately supplied raw `licenses/LICENSE` contains the reviewed pytest +MIT text. The wheel and lock SHA-256 both equal +`659169bde33b6d27bf4cf927c01d69418cc97241bf728627448542f781c2771d`. +The base gate returns PASS and the binder writes that restrictive archive's hash. +This is a synthetic exploit, not a claim about any upstream package. +The raw receipt is `/private/tmp/pr2347-archive-binding-repro.md`. + +## Candidate contract + +- Raw capture retains `source.archive` for both wheel and crate inputs. +- One bounded byte read supplies both the archive digest and in-memory license + extraction. No archive extraction or package execution occurs in this helper. +- Conventional license/notice names at every depth and declared custom + `License-File` / Cargo `license-file` members are inspected. Missing declared + members, duplicate normalized paths, traversal, archive links, invalid text + and malformed archives refuse the capture. +- Separate raw license sidecars no longer supply the license decision. +- The gate reopens the retained archive and compares the source digest, full + license text mapping and raw member SHA-256 mapping against the evidence. +- The install binder verifies those member hashes against the collected wheel + and repeats the license decision on its actual member bytes before writing + the pinned install lock. Forged permissive report fields cannot authorize + an unrecognized restrictive body. + +## Verification + +The existing eight targeted test files plus +`tests/test_release_dependency_archive_binding.py` produce **296 passed, +1 skipped**, raw exit **0**, in 4.67 seconds. The skip is the existing GNU-find +Linux capture integration case; it is not a new skip. + +The 16 added cases cover Python/Cargo sidecar forgery, evidence alteration, +archive replacement, archive absence, duplicate archive members, nested/raw-byte +hash preservation, custom wheel license paths and missing declarations, plus +two real shell install-binder refusals. A fake pip recorder establishes zero +install calls in both new install-negative cases. No real install or download +is used. Existing fixture archives now carry the same source-bound full texts +used by their license tests; one formerly permissive missing-archive capture +expectation changes to an explicit refusal. + +`git diff --check` and shell syntax checking also return 0. This is a selected +regression result, not full-suite, coverage, hosted execution or release approval. + +## Remaining boundaries + +Raw shell capture still performs its existing extraction/native/hook collection +before assembly. Those scanners and separate metadata are not all reconstructed +from the retained snapshot by this patch. This candidate closes the demonstrated +license-sidecar binding defect; it does not certify every captured evidence field +or shell extraction as safe. The final install trusts the protected workflow +workspace to prevent mutation between binding and pip's hash-checked read. + +Only the previously reviewed exact full texts are recognized. Unsupported texts, +MPL/BSL complex provenance, NumPy bundle questions, tools/sidecar pre-install +coverage and complete dependency closure remain held. No license exception or +legal conclusion follows from this candidate. diff --git a/docs/doctoring/release-license-fixture-recovery-20260924.md b/docs/doctoring/release-license-fixture-recovery-20260924.md new file mode 100644 index 0000000000..ed010bf912 --- /dev/null +++ b/docs/doctoring/release-license-fixture-recovery-20260924.md @@ -0,0 +1,52 @@ +# 기존 18실패의 원문 근거 복원 + +기준 `4fe66efdcee6bb6b68e5a6c386feea7280ecea8d`에서 새 브랜치 `codex/pr2347-source-fixture-recovery-20260924`를 사용한다. 기존 여섯 원문의 JSON 값과 provenance 첫 여섯 행은 Git blob 대조로 불변을 확인한다. + +공통 fixture는 Python MIT→실제 pytest 전체 MIT, Cargo Apache→실제 atheris 전체 Apache로 연결한다. 패키지 자체에 atheris라는 이름을 붙이거나 metadata 누락을 고친다는 주장이 아니라, synthetic gate fixture의 동일 SPDX 본문을 검증 가능한 전체 원문으로 복원한다. Cargo를 MIT로 바꾸지 않는다. 원문 hash는 기존 provenance에 있다. + +## 18개 before/after 기대와 근거 + +아래 이름은 `test_release_dependency_license_text_evidence.py` 기준이다. P=Python 원문, C=Cargo 원문이다. 기존 실패의 별도 C UNVERIFIED는 실제 Apache 전체 원문으로 해소한다. 검사의 핵심 실패 사유를 삭제하지 않는다. + +|번호|사례|before expected|after expected 및 변경 근거| +|---|---|---|---| +|1|no_bundled_text|MISSING 1개|동일. C만 전체 Apache로 복원| +|2|unrecognizable[unknown]|UNVERIFIED 1개|동일. UNKNOWN 입력 유지| +|3|unrecognizable[commercial-prohibited]|UNVERIFIED 1개|동일. 상업 금지 입력 유지| +|4|unrecognizable[empty]|UNVERIFIED 1개|동일. 빈 원문 유지| +|5|unrecognizable[pointer]|UNVERIFIED 1개|동일. 링크-only 입력 유지| +|6|unrecognizable[all-rights-reserved]|UNVERIFIED 1개|동일. 권리 유보 입력 유지| +|7|recognized_text_contradicts_declaration|DISAGREEMENT 1개|동일. MIT 선언에 실제 전체 Apache를 넣음| +|8|denied_title_disagreement|DISAGREEMENT 1개|동일. 정상 LICENSE는 실제 MIT, 별도 GPL COPYING 유지| +|9|matching_declaration|실패 없음|동일. 전체 pytest MIT 사용| +|10|permissive_family[Apache]|실패 없음|동일. 전체 atheris Apache 사용| +|11|permissive_family[BSD3]|실패 없음|동일. 전체 colorama BSD3 사용| +|12|permissive_family[ISC]|실패 없음|동일. 전체 libloading ISC 사용| +|13|permissive_family[MPL]|실패 없음|제목-only 원문은 UNVERIFIED. unsupported_title_only[MPL]로 목적을 명시하며 동일 입력을 유지. 실제 hypothesis 복합 적용 범위는 아래 별도 HOLD| +|14|permissive_family[BSL]|실패 없음|제목-only 원문은 UNVERIFIED. unsupported_title_only[BSL]로 목적을 명시하며 동일 입력을 유지. 기존 cache 인벤토리에 BSL 원문 mapping이 없음| +|15|permissive_family[Unlicense]|실패 없음|동일. memchr의 실제 전체 UNLICENSE 사용| +|16|dual_selection_disagreement|DISAGREEMENT 1개|동일. BSD/GPL 선언·BSD 선택에 실제 전체 MIT를 넣어 불일치 유지| +|17|recognizer_positive_half|MIT 포함|동일. 전체 MIT를 사용하고 UNKNOWN 반환 검사는 유지| +|18|install_binding cargo_only_release|실패 없음, lock digest 없음|동일. C에 전체 Apache를 연결. lock/hash 구현은 변경하지 않음| + +BSD 선택 양성과 sealed SBOM 선택 rationale 테스트도 동일 colorama BSD3 전체 원문으로 복원한다. 앞 후보의 부분 Apache 거부 테스트는 공통 fixture 변경에 영향받지 않도록 그 테스트에서 부분 Apache를 명시한다. 기존 음성을 정상으로 변경하지 않는다. + +## 추가 실제 원문과 보류 + +memchr2.8.3 `.crate` SHA256 `cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98`, member `memchr-2.8.3/UNLICENSE`, 원시 `7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c`, 정규화 `2069c208cba553e43cd0b730df8a0c10bf1b1101b96f661e2f1307c73b9722e3`다. 전체 본문에서 copy/modify/publish/use/compile/sell/distribute, commercial or non-commercial, public-domain dedication 및 면책을 직접 읽는다. 다른 추가 조건을 발견하지 않는다. 이 파일만 registry에 추가하며 crate의 COPYING pointer·MIT/Unlicense 선택 전체를 자동 수용하지 않는다. + +hypothesis6.156.6 wheel SHA256 `b4e66aaa7385538a5d617174d47c198ee807f06de99e282a67c6cb724c69340d`, member `hypothesis-6.156.6.dist-info/licenses/LICENSE.txt`, raw `ac89037bac63550644dce8cf32c6765e5fab9dc1a1ce94b89f8a805f341a6750`이다. 전체 1–10절과 Exhibits A/B를 읽는다. 앞부분은 명시된 예외 외 MPL 적용, 다른 프로젝트 코드의 원래 license와 수정 dual license를 설명한다. METADATA의 License-Expression=MPL-2.0/License-File=LICENSE.txt이며, archive의 license/copying/notice 이름 member는 이 파일 하나다. 개별 코드의 다른 원래 라이선스 적용 범위는 이 한 파일로 확정되지 않아 후속 mapping 검토가 필요하다. + +1.12절의 GPL/LGPL/AGPL 명칭은 Secondary License 정의다. 그 이름만으로 실제 금지 의존성이나 선택된 copyleft라고 판정하지 않는다. 기존 `scan_license_text`가 이 명칭에서 거부하는 문제는 의미 구분이 없는 별도 한계다. 이번 원문은 `unsupported-hypothesis.json`에 원시 hash와 함께 보존하고 recognizer UNKNOWN을 확인하며, keyword 거부를 실제 GPL 확정 판정으로 승인하지 않는다. 이번 범위에서 scanner 예외를 새로 허용하지 않는다. + +## 실행 + +``` +PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python3 -B -m pytest --noconftest -p no:cacheprovider -o addopts= tests/test_release_dependency_license_text_evidence.py tests/test_release_dependency_install_binding.py tests/test_release_dependency_install_ordering.py tests/test_release_dependency_full_text_contract.py tests/test_release_dependency_reviewed_artifact_texts.py tests/test_spdx_license_policy.py tests/test_release_dependency_gate.py tests/test_release_dependency_gate_capture_and_seal.py -q --tb=short +280 passed, 1 skipped in 2.94s +raw exit 0 +``` + +최초 실행은 sealed SBOM 사례의 REVIEWED_TEXTS import 누락으로 1 failed/279 passed/1 skipped/exit1이다. 실제 해당 함수 import를 고쳐 위 결과를 얻는다. 기존 skip은 macOS의 GNU find capture 경로이며 새 skip을 추가하지 않는다. 검사 삭제 없이 MPL/BSL 두 parameter를 별도 UNKNOWN 음성으로 유지하고, 실원문과 추가 제한·다중 파일·미선언 거부 회귀를 함께 실행한다. 마지막 unused import 제거는 실행 경로와 무관하다. + +`git diff --check` exit0. 기존6개 텍스트/provenance 불변 대조 true. 전체 suite·coverage·hosted·tooldeps·현재 release closure는 이번 소형 성공으로 수용하지 않으며 HOLD다. diff --git a/docs/doctoring/release-license-six-artifact-texts-20260924.md b/docs/doctoring/release-license-six-artifact-texts-20260924.md new file mode 100644 index 0000000000..c477bcb52c --- /dev/null +++ b/docs/doctoring/release-license-six-artifact-texts-20260924.md @@ -0,0 +1,30 @@ +# 실제 artifact 원문 여섯 개의 증분 인식 + +이 후보는 `4329ebb84ddac1752e5c4149fd3c94731b262f2e` 뒤에 여섯 전체 원문 hash를 추가한다. 일반적인 라이선스 판별기나 전체 배포 closure 승인으로 확대하지 않는다. production 변경은 기존 `_VERIFIED_LICENSE_TEXT_DIGESTS` 추가뿐이다. + +## 출처와 직접 읽은 범위 + +자료는 기존 로컬 FMLS license evidence archive다. artifact filename/SHA256, 내부 member, 원시 SHA256, 정규화 SHA256, SPDX 대응은 `tests/fixtures/release_license_texts/provenance.json`에 기록한다. 원문은 기억에서 재작성하지 않고 archive member를 UTF-8로 읽는다. 전체 본문을 줄 생략 없이 확인한다. `texts.json`의 각 문자열을 UTF-8로 인코딩한 바이트가 원시 member hash와 일치하는지 테스트한다. `fixture` 필드는 이 JSON 안의 키다. 끝 개행이 없는 원문도 그대로 보존한다. + +|실제 원문|읽은 허용·조건과 경계| +|---|---| +|pytest9.1.1 LICENSE / MIT|전체 grant에 use/copy/modify/merge/publish/distribute/sublicense/sell과 without restriction이 있다. copyright·permission notice 보존 및 보증 면책을 읽는다. 정확한 Holger Krekel 머리말도 hash에 포함한다. 다른 MIT 머리말을 자동 인정하지 않는다.| +|atheris3.1.0 LICENSE / Apache-2.0|1–9절과 적용 부록 전체다. 2절 copyright grant, 3절 patent grant·소송 종료조건, 4절 재배포·수정·고지, 5절 contribution, 6절 trademark, 7–9절 보증·책임을 읽는다. 별도 NC/학술 전용 부속문구는 없다. 이는 파일의 인식이며 실제 atheris의 metadata 선언 누락은 계속 HOLD다.| +|Rust numpy0.29.0 LICENSE / BSD-2-Clause|source·binary 재배포 허용, 두 고지 보존 조건, 전체 면책을 읽는다. PyPI NumPy 복합 원문과 다른 파일이다.| +|colorama0.4.6 LICENSE.txt / BSD-3-Clause|source·binary 재배포 허용, 고지 보존 두 조건, 이름을 허가 없이 endorsement에 사용하지 않는 세 번째 조건과 면책을 읽는다. 추가 상업 이용 금지는 없다.| +|libloading0.8.9 LICENSE / ISC|any purpose with or without fee의 use/copy/modify/distribute 허용, copyright·permission notice 보존, 전체 면책을 읽는다. Simonas Kazlauskas 머리말을 포함한다.| +|foldhash0.2.0 LICENSE / Zlib|any purpose including commercial applications, alter/redistribute 허용과 출처 오인 금지·변형 표시·고지 제거 금지 세 조건을 읽는다. 전체 면책도 포함한다.| + +모두 저장된 실제 소스에 대한 인식 지원이다. 상용 제품의 모든 법적 의무 충족을 확정하는 판단이 아니다. 선택된 OR의 pointer 문서, PyPI NumPy의 GPL/LGPL 복합 원문, upstream 16개, 다른 copyright/원문 변형, 수집 누락은 별도 HOLD다. 추가 파일마다 기존 consumer의 검사가 계속 적용된다. + +## 검사와 실패 보존 + +명령 공통 환경: `PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python3 -B -m pytest --noconftest -p no:cacheprovider -o addopts=`. + +- `tests/test_release_dependency_reviewed_artifact_texts.py tests/test_release_dependency_full_text_contract.py tests/test_spdx_license_policy.py -q`: 99 passed, raw exit0. 여섯 실제 원문의 hash·정상 consumer, 앞/뒤/중간 추가 조건, 별도 NOTICE 제한, atheris 선언 누락 유지가 포함된다. +- 첫 fixture 생성은 끝 개행을 추가해 원시 hash 6건이 실패(6 failed/93 passed)하고, 첫 보정은 개행 없는 2개 파일 끝 문자를 훼손해 4 failed/95 passed다. 원문 문자열을 JSON에 그대로 보존하는 방식으로 보정하고 모든 원시 hash를 다시 확인한다. 정규화 hash만 일치한다는 이유로 이 실패를 무시하지 않는다. +- 기존 비교군 `test_release_dependency_license_text_evidence.py`, `test_release_dependency_install_binding.py`, `test_release_dependency_install_ordering.py`: 18 failed/27 passed/1 skipped, raw exit1. 이전과 같은 미지원 부분 원문 기대값 실패를 보존한다. 기존 fixture 수정은 없다. + +이전18건 중 잘못된 양성 기대는 짧은 MIT/Apache/BSD/ISC/MPL/BSL/Unlicense 제목·일부 grant를 완전 원문으로 취급하는 부분이다. 정상 corpus를 바꾸려면 그 라이선스의 실제 전체 자료와 hash를 같은 SPDX로 연결하는 별도 diff가 필요하다. Cargo Apache 제목-only fixture도 Apache 전체 원문으로 복원해야 하며, MIT로 바꾸는 방식은 허용하지 않는다. 이번 변경은 그 기대값을 편의상 고치지 않는다. + +전체 suite·coverage·hosted·tooldeps 검사와 publish는 미실행·HOLD다. 여섯 원문 추가로 범위 전체를 수용하지 않는다. diff --git a/docs/doctoring/release-license-whole-text-candidate-20260924.md b/docs/doctoring/release-license-whole-text-candidate-20260924.md new file mode 100644 index 0000000000..ffa9db7855 --- /dev/null +++ b/docs/doctoring/release-license-whole-text-candidate-20260924.md @@ -0,0 +1,34 @@ +# PR2347 전체 원문 확인 중간 후보 + +기준은 `48caafec7160dd0cb9bafc58b28a884dc4c35cbb`이다. 원문 제목/부분 문자열만으로 허용하는 P1을 닫는 로컬 후보이며, 전체 의존성 정책 구현 완료나 병합·배포 수용을 뜻하지 않는다. + +## 근거와 지원 경계 + +직접 읽은 저장소 `LICENSE` 전체를 근거로 삼는다. 원시 SHA256은 `08f1fd81fb120bc468b69dc3e58ea0dc23c216305c766e45e107f56c76559e3f`이다. ASCII 공백·탭·CR·LF만 연속 공백 하나로 정규화한 전체 본문 SHA256은 `f5ac0308cf2b3f96a0f49a8c0c9e4a2a02c483afc72a646af8de1f356983de06`이다. + +검증 지원은 이 MIT 원문 한 개이며 Copyright 문구까지 포함한다. 다른 저작권자 머리말도 아직 UNKNOWN이다. 임의 머리말·추가 조건·접미사·유니코드 제어 문자를 지우지 않는다. SPDX 선언, 제목, 허용 구절만으로 확인된 원문이 되지 않는다. 이 레지스트리는 새로운 의존성을 라이선스 이름만으로 승인하는 수단이 아니다. + +실제 closure에 필요한 BSD, Apache, CC0 및 다른 라이선스 원문·변형 지원은 미완료다. 각 원문과 전체 일치 계약을 독립 검토한 뒤 별도 증분으로 추가해야 한다. 현재 인벤토리 전체 PASS는 불가능하다. 설치 전 도구 의존성 검사는 별도 미해결이다. + +`recognize_license_text`의 production caller는 `release_dependency_gate.evaluate_dependency_license`다. CodeGraph는 해당 트리에 index가 없다고 반환하며, 소스 참조를 직접 대조한다. caller가 파일마다 판정하므로 허용 LICENSE와 별도 제한 NOTICE를 함께 넣어도 거부한다. + +## 검증 + +모든 명령은 이 별도 작업 트리에서 실행한다. 환경은 `PYTHONDONTWRITEBYTECODE=1 PYTEST_DISABLE_PLUGIN_AUTOLOAD=1`, 공통 인자는 `python3 -B -m pytest --noconftest -p no:cacheprovider -o addopts=`다. + +1. `tests/test_release_dependency_full_text_contract.py tests/test_spdx_license_policy.py -q`: **68 passed, raw exit 0**. 최초 실행은 새 테스트에서 상수 소유 모듈을 잘못 적어 1 failed/67 passed/exit1이다. `policy.LICENSE_TEXT_DISAGREEMENT`를 실제 소유자 `gate`로 고친 뒤 위 결과를 얻는다. +2. 기존 소형 비교군 `tests/test_release_dependency_license_text_evidence.py tests/test_release_dependency_install_binding.py tests/test_release_dependency_install_ordering.py -q --tb=no`: **18 failed, 27 passed, 1 skipped, raw exit 1**. 기존 fixture는 수정하지 않는다. +3. `git diff --check`: exit0. + +새 회귀는 실제 gate의 MIT 추가 상업 제한과 CC0/NonCommercial 반례를 거부하고, 완전한 확인 MIT 원문은 동일 dependency caller에서 통과시킨다. gate 전체의 정상 Python 사례에서도 기존 Cargo Apache fixture가 UNKNOWN으로 남아 전체 통과를 주장하지 않는다. GPL 별도 파일, 추가 NOTICE, 본문 변조·앞뒤 조건·NUL·zero-width suffix도 확인한다. + +### 보존하는 기존 실패 분류 + +- 원문 누락/UNKNOWN/별도 GPL 등 기존 원인 자체는 계속 거부한다. 같은 capture의 Cargo Apache 제목-only fixture가 추가 `LICENSE_TEXT_UNVERIFIED`를 내므로 기존 exact-single-failure 기대와 다르다. +- MIT·Apache·BSD·ISC·MPL·BSL·Unlicense 부분 원문을 정상으로 기대한 사례와 recognizer 직접 호출의 부분 MIT 기대는 더 이상 충족하지 않는다. +- 기존 Apache-vs-MIT 제목-only 불일치는 확인된 Apache가 아니므로 UNKNOWN으로 분류한다. 지원하지 않는 본문에서 라이선스 종류를 확정하지 않는다. +- Cargo-only binding 테스트는 그 Apache fixture 원문의 미확인 때문에 실패한다. lock hash 결속 구현의 변경은 아니다. + +실제 gate 분류 재확인: Python 원문 없음은 MISSING + Cargo UNVERIFIED, Python UNKNOWN/부분 MIT/부분 Apache는 각각 Python UNVERIFIED + Cargo UNVERIFIED다. 합성 Cargo를 MIT로 바꿔 실패를 숨기지 않는다. + +전체 suite·coverage·hosted CI·Linux capture·원문 수집 완전성·tooldeps 설치 전 검사는 이번 수용 밖이며 HOLD다. 이 후보는 공개 push 없이 다른 reviewer의 검토에 인계한다. diff --git a/docs/doctoring/required-review-control-runner.md b/docs/doctoring/required-review-control-runner.md new file mode 100644 index 0000000000..c7bb9a3987 --- /dev/null +++ b/docs/doctoring/required-review-control-runner.md @@ -0,0 +1,29 @@ +# Required review control-runner admission + +## Cause and scope + +On 2026-09-27, CO #1222 at `048d90b3715f792bd6a779d0b013c665fdb01385` still had queued required review entrypoints although five organization self-hosted runners were online. Central #2385 and #2417 are merged. Their scanner and scheduler routing does not change the required OpenCode/Noema entrypoints: these still explicitly request hosted Ubuntu. Existing queued attempts retain their original workflow configuration. + +## Constrained assignment + +Let x_j be 1 when an eligible admission job uses the control pool and 0 when it uses hosted capacity. Minimize sum(1 - x_j) over the six jobs, subject to 0 <= x_j <= 1, trusted central-main workflow identity, no PR-source execution, and separation of model/scanner work from control. For the exact central-main workflow identity, the unique admissible self-hosted pool is `CWL central control`; its one registered worker permits at most one executing job at a time, which GitHub enforces natively. Setting all six x_j to 1 attains the lower bound zero hosted admission jobs. This is a direct linear assignment, not an estimated optimum for completion time: model durations and historical queue positions are not reliable cost coefficients. No solver dependency or learned-policy claim is introduced. + +Non-main and unrecognized workflow identities retain hosted Ubuntu 24.04; the selected-workflow group must not strand PR/branch-ref validation jobs. All six OpenCode entrypoint jobs read metadata, retain required context names, dispatch, or clean superseded runs. They never checkout PR code. Noema control admission is independently owned by #2420. This PR leaves its worker and transport continuation unchanged. + +## Runner policy and rollout + +Group 6 must preserve `visibility=all`, `allows_public_repositories=true`, and `restricted_to_workflows=true`, retaining all existing selected workflows and adding only this exact central-main path: + +- `ContextualWisdomLab/.github/.github/workflows/opencode-review.yml@refs/heads/main` + +Read the live group immediately before PATCH and include the complete policy so omitted fields cannot erase existing restrictions. Read it back after mutation. The permission remains limited to jobs defined by these trusted central workflows, rather than arbitrary consumer workflows. + +Tests pin the six assignments and absence of PR checkout, and retain the docs-only and exact-head dispatch contracts. Run affected contracts both normally and with `GITHUB_ACTIONS=true`, then actionlint. The maintainer explicitly authorized bypass merge for this CI admission repair; missing hosted checks must remain recorded as missing evidence. + +After merge, verify a new targeted review/scheduler attempt uses `cwlab-s1-05`. Old queued runs are not deployment proof. A new workflow event or trusted central dispatch is needed to adopt the new configuration. Rollback restores the runner selectors and removes only the added OpenCode group path after verifying no dependent jobs need them. + +## References + +GitHub. *Choosing the runner for a job*. https://docs.github.com/en/actions/how-tos/write-workflows/choose-where-workflows-run/choose-the-runner-for-a-job + +GitHub. *Managing access to self-hosted runners using groups*. https://docs.github.com/en/actions/how-tos/manage-runners/self-hosted-runners/manage-access diff --git a/docs/doctoring/reusable-scheduler-control-runner.md b/docs/doctoring/reusable-scheduler-control-runner.md new file mode 100644 index 0000000000..a5e2aef4f4 --- /dev/null +++ b/docs/doctoring/reusable-scheduler-control-runner.md @@ -0,0 +1,21 @@ +# Reusable scheduler control runner + +## Cause and assignment + +On 2026-09-27, fast-mlsirm#2199 still had 21 queued checks despite five online self-hosted runners. Four runners were busy; cwlab-s1-05 was idle. The central control group admitted only the .github repository, while the reusable scheduler explicitly sent consumer repositories to hosted Ubuntu. Adding runners alone did not remove either access/routing constraint. + +The assignment minimizes hosted admission for trusted scheduler work subject to one dedicated control runner and separation from long model, scanner, and PR build execution. With one eligible control pool, the assignment is direct; no optimizer dependency or speculative duration weights are needed. Existing CodeQL/OpenCode pools and live inference are preserved. + +## Change and trust boundary + +The reusable scheduler uses group `CWL central control` and labels `[self-hosted, linux, x64]` for every caller. Runner group `CWL central control` must grant organization repository access while retaining `restricted_to_workflows=true` and exactly the three existing central `@refs/heads/main` workflow paths: agent-mention-router, hourly-review-repair, and pr-review-merge-scheduler. This permits only jobs directly defined in trusted central workflows, not arbitrary caller jobs. The scheduler materializes only the immutable central workflow source; PR source execution is unchanged. Security gates, review verdicts, provider policy, and model duration remain unchanged. + +## Verification + +Run the scheduler runner-image contract, required-workflow queue contracts, and actionlint. After integration, inspect the actual runner name of a targeted dry-run dispatch; config acceptance is not execution proof. No skipped/queued checks are represented as successful tests. User explicitly authorized bypass merge for blocked CI on this task. + +## References + +GitHub. (n.d.). *Managing access to self-hosted runners using groups*. https://docs.github.com/en/actions/how-tos/manage-runners/self-hosted-runners/manage-access + +GitHub. (n.d.). *Reusing workflow configurations*. https://docs.github.com/en/actions/reference/workflows-and-actions/reusing-workflow-configurations diff --git a/docs/doctoring/review-failure-taxonomy.md b/docs/doctoring/review-failure-taxonomy.md new file mode 100644 index 0000000000..45c064d337 --- /dev/null +++ b/docs/doctoring/review-failure-taxonomy.md @@ -0,0 +1,51 @@ +# Review failure taxonomy: gateway routing, scanner tooling, dispatch admission + +On 2026-09-21 the central review pipeline looked like a provider outage. It was not. +Three unrelated failures were being read as one. + +## What the hosted evidence showed + +Every run reached the vendored contextual-orchestrator sidecar and every run reported +`provider secrets present: 5 of 5`, including runs that predate any credential change. +The gateway answered its own preflight with `status: ready` and `finish_reason: stop`. +Provider credentials were never the blocker. + +## 1. OpenCode: gateway routing, reported as `Error: not found` + +The sidecar exports `CONTEXTUAL_ORCHESTRATOR_BASE_URL` as a bare `scheme://host:port`. +Noema and Strix append `/v1/chat/completions` themselves. OpenCode's +`@ai-sdk/openai-compatible` provider appends only `/chat/completions`, so it posted to an +unprefixed path. The gateway serves `/v1/chat/completions` and answers anything else with +`route_not_found`, whose message is the bare string `not found` — which OpenCode printed +verbatim as `Error: not found`, half a second after its banner had already resolved the +agent and model. + +Reproduced locally against a stub gateway with the installed OpenCode CLI: an unprefixed +`baseURL` produced `POST /chat/completions`, and `{env:CONTEXTUAL_ORCHESTRATOR_BASE_URL}/v1` +produced `POST /v1/chat/completions`. The `/v1` belongs in the OpenCode provider options, +never in the sidecar export — moving it there would double-prefix Noema and Strix. + +The banner is the tell: once `> · ` has printed, agent and model already +resolved, so a later `not found` is a transport answer, not configuration lookup. + +## 2. Strix: scanner tooling, previously folded into the provider verdict + +A failing scan emitted Caido GraphQL errors (`Invalid HTTPQL query`, `Failed to parse +cursor`, `TransportQueryError`) while the gateway was healthy. Genuine provider rate +limits appeared in the same log, so the single `STRIX_PROVIDER_UNAVAILABLE` notice was not +wrong — it was incomplete, and it hid a scanner defect behind an infrastructure label. +`strix.yml` now emits `STRIX_TOOLING_ERROR` on its own whenever a tooling signature +appears. Both notices can appear together. Neither changes the exit code: an incomplete +scan stays non-passing. + +## 3. Dispatch admission: never a gateway outcome + +`repository_dispatch authorization rejected` and `repository_dispatch metadata does not +match the live pull request` fire before the sidecar is provisioned. Counting them as +review-pipeline outages inflates the apparent provider failure rate. + +## Rule + +Attribute a review failure to the provider only after the gateway request itself failed. +Name the gateway's served path, the scanner's own errors, and admission gates as separate +classes. `tests/test_review_failure_taxonomy_contract.py` pins all three. diff --git a/docs/doctoring/scheduler-status-read-permission.md b/docs/doctoring/scheduler-status-read-permission.md new file mode 100644 index 0000000000..82e7b0cbf3 --- /dev/null +++ b/docs/doctoring/scheduler-status-read-permission.md @@ -0,0 +1,38 @@ +# Scheduler commit-status read permission (#2120) + +The organization-required scheduler selected `github.token` for same-repository +reads but omitted `statuses` from `scan-pr-queue.permissions`. The private +consumer's combined-status GET consequently failed with HTTP 403 before a merge +verdict. `checks: read` does not grant classic commit-status access. + +Source baseline: `fb17ef556f94f673234aa557254ae52779e9a7b0`. +Consumer evidence: ContextualWisdomLab/late-life-anxiety-reanalysis#10, +head `3d1e3ae56e3ef6ca0a995b6082c4f4a13629e0f6`, run `34698738407`, +job `103566634488` (2026-09-12). The reported failing endpoint is +`GET /repos/{repository}/commits/{head}/status`. + +The repair adds only `statuses: read` to the existing scan job. Workflow defaults, +mutation credentials, cross-repository credential selection and fail-closed API +errors remain intact. It adds no status publication or App installation grant. +The existing credential-contract test now requires exactly `read` in that job's +permission block; its RED revision is `9521b6771`. + +Validation uses the existing pytest workflow/credential/status suites and +Actionlint's workflow validation. Local tests prove the declared contract, not a +hosted permission grant. After protected integration, validate a newly loaded +central source SHA and the consumer's exact current head: the combined-status +request must succeed, and missing checks or substantive failures must still block +merge. For reusable callers, every caller permission ceiling must also admit +status reads; the inspected consumer PR head has no `.github` tree, so do not +invent a repository-local caller or modify App permissions to compensate. + +Next integration review: 2026-09-13, because this prevents the current private +consumer's mandatory scheduler from evaluating status evidence. #2116's HWPX +classification remains a separate bootstrap repair. Reverting this one-line grant +restores the pre-existing 403 behavior; it is not a viable consumer repair. + +## Reference + +GitHub. (n.d.). *REST API endpoints for commit statuses: Get the combined status +for a specific reference*. Retrieved September 12, 2026, from +https://docs.github.com/en/rest/commits/statuses#get-the-combined-status-for-a-specific-reference diff --git a/docs/doctoring/shared-security-baseline-pyjwt-pyo3-20260930.md b/docs/doctoring/shared-security-baseline-pyjwt-pyo3-20260930.md new file mode 100644 index 0000000000..d0f1cbbb2b --- /dev/null +++ b/docs/doctoring/shared-security-baseline-pyjwt-pyo3-20260930.md @@ -0,0 +1,157 @@ +# Shared PyJWT and PyO3 security baseline repair + +## Status and decision + +**Proposed; release HOLD.** The central `.github` repository owns both affected +dependency surfaces, so the repair belongs on a protected-main foundation PR. +It must not be copied into PR #1026, which changes neither lock. The owner repair +must pass exact-head review and hosted security Checks, merge ordinarily, and +then reach #1026 through a non-force merge from protected `main`. + +## Exact incident evidence + +PR [#1026](https://github.com/ContextualWisdomLab/.github/pull/1026) was observed +at exact head `6f645a73502e159d5a229805afa34868ad9bb851` against protected +`main@37b10243cec3d160ecc9c1be75c71428b160a703`. + +- [Security Scan run 36495499815](https://github.com/ContextualWisdomLab/.github/actions/runs/36495499815), + job `109380628690`, reported PyO3 `0.22.6` in + `tests/fixtures/coverage-cargo/Cargo.lock`: GHSA-36hh-v3qg-5jq4 (High, 8.0) + and GHSA-chgr-c6px-7xpp (Medium, 5.5). Both advisories fix the defect in + PyO3 `0.29.0`; this repair selects and locally verifies `0.29.2`. +- [Python Security run 36495499871](https://github.com/ContextualWisdomLab/.github/actions/runs/36495499871), + job `109380725819`, reported PyJWT `2.13.0` in + `requirements-strix-ci-hashes.txt` as affected by CVE-2026-102274. PyJWT + `2.14.0` is the fixed release. +- The exact #1026 diff changes neither vulnerable file. The same lock bytes + were present on protected `main`, establishing a shared baseline defect rather + than a PR-specific regression. + +## Root cause and operational scenarios + +PyJWT was only a transitive MCP dependency, so the generated Strix lock could +select a newly vulnerable release without an explicit reviewed source pin. A +malformed RSA JWK can raise a plain `ValueError` and abort processing of the +whole JWK Set. An operator can therefore lose otherwise valid signing keys and +fail authentication or review-agent startup because one untrusted key is bad. + +The offline Rust coverage fixture intentionally pins exact crate releases, but +its PyO3 pin was not advanced when the two 2026 advisories were published. One +defect permits an out-of-bounds read from iterator methods; the other omits a +required `Sync` bound and permits a data race. Even though this is a test +fixture, the central scanner correctly treats its lock as executable supply +chain material. + +## RED to GREEN contract + +RED commit `cd84d887` introduced two fail-closed contracts: + +1. `requirements-strix-ci.txt` must explicitly select PyJWT `2.14.0`, and the + generated hash lock must contain the same version. +2. The Rust coverage fixture manifest and lock must both select PyO3 `0.29.2`. + +The implementation adds the direct PyJWT input, regenerates the Python 3.13 +manylinux hash lock with the repository command, advances the exact PyO3 +manifest pin, and regenerates the Cargo lock with Rust `1.97.1`. The +cryptography override remains single-purpose; it does not become a general +security-version overlay. + +## Ownership, release, and failure recovery + +The fixed-source release workflows consume `requirements-strix-ci-hashes.txt` +from immutable central revisions. This PR repairs the canonical owner bytes but +does not rewrite those workflows to an open branch. After ordinary protected +merge, a separate consumer change must advance their exact source commit and +rerun API/schema, security, SBOM, and provenance evidence. If any exact-head +scanner, build, or independent review fails, the PR remains HOLD and the root +cause is repaired here; no bypass or mutable source reference is permitted. + +## Local verification on the repaired tree + +- Focused dependency, fixed-source, Maturin asset, and Rust toolchain contracts: + 48 passed, 1 skipped. +- Repository regression suite: 5,160 passed, 11 skipped, 40 subtests passed. +- Rust `1.97.1` `cargo check --locked`: passed for the coverage fixture. +- Python lock regeneration with `uv 0.12.18`, seeded with the existing reviewed + output, was byte-identical. Input SHA-256 values were `c3812261…` for + `requirements-strix-ci.txt` and `3b745514…` for the override; the output was + `8f8318d4…`. A hash-enforced installation loaded PyJWT `2.14.0`. A fresh + unseeded solve is intentionally not claimed to be byte-identical because it + may select newer allowed transitive releases. +- `pip-audit`: no known vulnerabilities in the Strix lock. OSV's direct + `pyo3@0.29.2` query returned no vulnerability records. + +No production Python module changes in this repair. The repository-wide +coverage and docstring commands expose separate protected-main baseline debt: +coverage is 99% (178 statements missing) and `interrogate scripts/ci` is 97% +(43 docstrings missing). Those failures are not waived or called green here; +they require their own bounded owner repair before the repository can claim the +100% global gates. + +## References + +GitHub. (2026, June 12). *Out-of-bounds read in PyO3 iterator methods* +(GHSA-36hh-v3qg-5jq4). GitHub Advisory Database. +https://github.com/advisories/GHSA-36hh-v3qg-5jq4 + +GitHub. (2026, June 12). *PyO3 missing Sync bound can lead to a data race* +(GHSA-chgr-c6px-7xpp). GitHub Advisory Database. +https://github.com/advisories/GHSA-chgr-c6px-7xpp + +Open Source Vulnerabilities. (2026). *CVE-2026-102274: PyJWT RSA JWK Set +availability failure*. +https://osv.dev/vulnerability/CVE-2026-102274 + +## 2026-10-01 urllib3 audit follow-up + +Python Security run `36733279716`, job `109949358063`, found two newly +published vulnerabilities in urllib3 2.7.0: CVE-2026-97687 permits target TLS +policy to weaken or replace HTTPS proxy TLS policy, and CVE-2026-97689 permits +an unbounded chunk-size line to consume memory in streaming clients. Both are +fixed in urllib3 2.8.0. The same vulnerable transitive pin appeared in the +pip-audit and Strix hash locks, so this remains one central security-owner +repair rather than two consumer workarounds. + +The repair adds urllib3 2.8.0 to both source inputs and regenerates both locks +with their recorded uv commands. A contract requires exactly one 2.8.0 row in +each source input and generated lock. Comparison against exact predecessor +`d1aa3659fca527a6c7330151f3ab4df3d7578391` shows no unrelated package-version +movement. Repeated compilation produced identical SHA-256 digests, and +pip-audit 2.10.1 reported no known vulnerabilities for either generated lock. +These local results are not merge authority: exact-head hosted security Checks, +terminal authenticated CodeQL evidence, independent approval, and ordinary +protected merge remain required. + +## 2026-10-01 PyJWT recursion denial-of-service follow-up + +Security Scan run [36741151937](https://github.com/ContextualWisdomLab/.github/actions/runs/36741151937) +found GHSA-42vr-xj54-vc7v / +CVE-2026-101918 in PyJWT 2.14.0. Dependency Review job `109975641239` and OSV +job `109975641271` both rejected that shared Strix lock. An attacker-controlled, +deeply nested unsigned JWT payload can exhaust Python recursion during unverified +payload parsing in `PyJWKClient.get_signing_key_from_jwt`, before key lookup, +and raise an uncaught request-level exception; +the available evidence does not establish a process crash or authentication +bypass. PyJWT 2.15.0 contains the upstream fix. + +The canonical-owner repair advances the explicit source pin and generated lock +to 2.15.0. Lock regeneration changes only the PyJWT version and its wheel/sdist +hashes; urllib3 2.8.0, PyO3 0.29.2, and the single-purpose cryptography override +remain unchanged. The existing parity test was first changed to require 2.15.0 +and failed against the 2.14.0 source and lock before implementation. Hosted +exact-head Security, CodeQL, independent approval, ordinary protected merge, +and immutable consumer-pin advancement remain release gates. + +Local verification used the hosted-workflow Python 3.12 line. The focused +source/lock contract passed 5 tests and the warnings-as-errors repository suite +passed 5,167 tests, 6 skips, and 40 subtests. Repeating the recorded `uv 0.12.18` +compile command was byte-identical at lock SHA-256 `76443a3300d0…`; a +hash-enforced, no-dependency installation loaded PyJWT 2.15.0 and urllib3 2.8.0. +`pip-audit 2.10.1` reported no known vulnerabilities. The repository's existing +97% docstring baseline remains a separate HOLD and is not represented as green. +An independent review found no remaining Critical, Important, or Minor finding +after correcting the advisory's attack-vector wording. + +GitHub. (2026). *PyJWT has a denial of service vulnerability via maliciously +crafted JWT token with deeply nested payload* (GHSA-42vr-xj54-vc7v). +https://github.com/jpadilla/pyjwt/security/advisories/GHSA-42vr-xj54-vc7v diff --git a/docs/doctoring/shared-security-litellm-credential-exfiltration-20261001.md b/docs/doctoring/shared-security-litellm-credential-exfiltration-20261001.md new file mode 100644 index 0000000000..fc573fc8e1 --- /dev/null +++ b/docs/doctoring/shared-security-litellm-credential-exfiltration-20261001.md @@ -0,0 +1,53 @@ +# Shared Strix LiteLLM credential-exfiltration RCA + +## Incident binding + +On 2026-10-01, `.github` PR #2531 exact head +`516471fbe7d4e93a50c7bbba20402447f06f8d8b` failed Python Security run +`36799069276`, job `110169140365`. The unmodified +`requirements-strix-ci-hashes.txt` selected LiteLLM 1.94.1, and `pip-audit` +reported CVE-2026-84377 / GHSA-3cv6-jpf6-8222. The advisory describes an +authenticated request-body routing override that can redirect an upstream call, +exfiltrate configured provider credentials, and reach internal services. + +This is a canonical control-plane dependency defect, not a product-PR finding +and not an audit-service transient. The same exact head also failed Trivy on the +Noema document reader's `fast-uri` 3.1.7 and `ip-address` 10.7.0. Stacked PR +#2545 already owns and proves the minimal Node transitive repair, so its ordinary +commit is preserved in the repaired #2531 ancestry instead of being copied or +reimplemented. + +## Test-first repair + +The RED contract +`test_strix_litellm_security_pin_is_an_explicit_lock_input` first failed because +the source input did not own a LiteLLM pin. The minimal repair: + +1. selects `litellm==1.94.3` in `requirements-strix-ci.txt`, the first patched + release in the retained 1.94 line; +2. regenerates `requirements-strix-ci-hashes.txt` with the repository's declared + `uv pip compile --generate-hashes` command; +3. requires exact source/lock parity so a future resolver run cannot silently + restore an affected release; and +4. preserves #2545's `fast-uri==3.1.8` and `ip-address==10.7.1` source overrides, + lock, and nested-copy regression contract as unchanged ancestry. + +No scanner finding is ignored or suppressed. `pip-audit` over the repaired +hash lock reports no known vulnerabilities. At repaired exact head +`fe879f7b7f48f729f757e03851bf61149470ccb5`, Python Security run `36800615364`, +Security Scan run `36800615435`, SAST run `36800615456`, and runtime-quality run +`36800615444` are terminal GREEN. CodeQL run `36800615319` remains fail-closed: +both language jobs recorded `verdict=pending` while the exact-head dispatch job +succeeded. A qualifying independent approval and ordinary protected merge are +still required; local or partial hosted evidence is not merge authorization. + +## References + +BerriAI. (2026, August 26). *Authenticated SSRF and provider-credential +exfiltration via unvalidated request-body routing parameters* +(GHSA-3cv6-jpf6-8222) [Security advisory]. GitHub. +https://github.com/BerriAI/litellm/security/advisories/GHSA-3cv6-jpf6-8222 + +National Institute of Standards and Technology. (2026). *CVE-2026-84377*. +National Vulnerability Database. +https://nvd.nist.gov/vuln/detail/CVE-2026-84377 diff --git a/docs/doctoring/sidecar-python-shared-library-20260928.md b/docs/doctoring/sidecar-python-shared-library-20260928.md new file mode 100644 index 0000000000..70b928440e --- /dev/null +++ b/docs/doctoring/sidecar-python-shared-library-20260928.md @@ -0,0 +1,48 @@ +# Sidecar CPython shared-library binding + +## Status + +Proposed common startup repair; protected hosted acceptance remains unverified. + +## Evidence and root cause + +Naruon #1795 Noema and Strix both exited 139 in the offline gateway fixture on +`cwlab-s1-02`, before live provider calls, at pinned orchestrator +`01bf92a3ec67a0e1f9b68978eb16b60301e985fd`. +Their selected executable was toolcache Python `3.12.14/x64/bin/python`. +The composite action intentionally uses `update-environment: false`; Strix +retained the consumer `3.13.15/x64/lib` in `LD_LIBRARY_PATH`. + +A read-only runtime comparison on that same guest on 2026-09-27 UTC +found that the selected executable, without its matching library path, reported +Python **3.12.3** while reading the toolcache 3.12.14 standard library and +`_asyncio` extension. The complete offline fixture, exact source and binary-only +hash-pinned dependencies installed in a new owned environment, passed under +system Python 3.12.3. Keeping those dependency bytes and switching to the +selected toolcache executable reproduced SIGSEGV at `logging.LogRecord`, +`asyncio.current_task()` in the HTTP request thread. Only `_cffi_backend` was +listed as an external extension in the fatal trace; this is not evidence of a +provider or fast-mlsirm defect. + +Prepending the selected toolcache's `lib` directory made the entire fixture +pass. The actual patched shell selection also passed with the stale consumer +3.13 library path supplied. No shared installation, service, runner registration, +group grant or existing job was modified. + +## Repair and verification + +Resolve the selected executable (including symlinks and PATH lookup), then +prepend its adjacent library directory only when `libpython3.12.so.1.0` exists. +Keep the existing library search path as a suffix and keep this environment +inside the sidecar shell process. Python 3.12 validation, dependency hashes, +all offline assertions, provider discovery and review gates remain enforced. + +The behavioral regression fails on unmodified main and passes after the repair; +it covers symlink resolution, matching-library precedence and absent-library +fallback. Local sidecar contracts: 31 passed. With `GITHUB_ACTIONS=true`, warnings +as errors and pytest plugin autoload disabled: 168 sidecar, runtime-preflight +and composite-action contracts passed in 16.68 seconds. Bash syntax, Ruff and +`git diff --check` pass. Linux real-fixture comparison additionally exercised +imports, server creation, rejection logging, large request, tool descriptions +and shutdown. Hosted exact-head review and full live-provider acceptance are +still required. diff --git a/docs/doctoring/sidecar-venv-persistent-temp-20260929.md b/docs/doctoring/sidecar-venv-persistent-temp-20260929.md new file mode 100644 index 0000000000..94b1fc1803 --- /dev/null +++ b/docs/doctoring/sidecar-venv-persistent-temp-20260929.md @@ -0,0 +1,51 @@ +# Review sidecar venv on persistent runner temp + +## Symptom + +From 2026-09-27T17:47Z every `opencode-review` job that reached +"Provision contextual-orchestrator review sidecar" on `cwlab-s1-04` failed +within a minute: + +``` +.../_temp/contextual-orchestrator-review/.venv/bin/python: No module named pip.__main__; 'pip' is a package and cannot be directly executed +``` + +A 60-run GraphQL sample of dispatch runs from 2026-09-25 to 2026-09-29 shows +the step succeeding up to 2026-09-27T10:36Z and failing in all twelve runs +after that. No OpenCode verdict was published in that window, so every +required `opencode-review` check across the organization failed closed. + +## Cause + +#2437 (2026-09-27T12:40Z) made the sidecar create +`$RUNNER_TEMP/contextual-orchestrator-review/.venv` with `python -m venv`. +The script already wipes its vendored source directory before each run but +reused this one. The deterministic failure after that merge shows the +environment directory survived between jobs on `cwlab-s1-04`; the stock +runner normally empties `_temp` at job start, and why it did not here +(for example, a deletion error it only logs) was not observed. +`python -m venv DIR` over an existing environment whose `pip` package lost +`__init__.py`/`__main__.py` exits 0 and leaves `pip` as a namespace package, +which matches the production message. The first job that damaged the +environment was not identified: the provisioning-time cancellations found +between the last success and the first failure were dispatched before #2437 +and ran the previous script. + +Reproduced on Ubuntu 24.04 amd64 with the actions/python-versions +3.12.14 toolcache build at the self-hosted path: the damaged-then-rerun +case prints the exact error above; `--clear` restores a working pip. + +## Repair + +Create the environment with `python -m venv --clear`. The regression test +builds a real venv, removes pip's `__init__.py` and `__main__.py`, reruns the +script's venv line, and requires `python -m pip --version` to succeed. It +fails on unmodified main with the production message and passes after the +change. + +## Not addressed here + +Queue latency is a separate capacity problem: one OpenCode runner serves +three `needs`-chained jobs, and about half of sampled dispatches were stale +by the time they ran (median queue wait 2.4 h). This repair only restores +verdict publication. diff --git a/docs/doctoring/startup-failure-and-strix-concurrency-20260904.md b/docs/doctoring/startup-failure-and-strix-concurrency-20260904.md index e710eb5d1f..5ba354947a 100644 --- a/docs/doctoring/startup-failure-and-strix-concurrency-20260904.md +++ b/docs/doctoring/startup-failure-and-strix-concurrency-20260904.md @@ -43,6 +43,35 @@ another. Workflow-level concurrency was deliberately not used because GitHub applies it before any live-head admission job can run and does not guarantee concurrency ordering. +**Amendment (2026-09-05).** "nor one another" no longer holds for `push` +events on the same branch. Measured at 14:27Z in `.github`: nine `push`/`main` +Strix runs were outstanding at once — five holding runner slots under the +shared 60-job ceiling (jobs started 12:31-14:25Z, one already past two hours) +and four more waiting in the queue behind them, which occupy no slot until a +runner is assigned — against a 10-30 minute normal scan. The run-id fallback +in the workflow-level group made every main push its own group, so no newer +main head ever retired an older scan. The workflow-level group now scopes +`push` events as `push-`: a newer head of the same protected branch +supersedes the older scan exactly as a newer PR head does. What a retired scan +gives up is its own report, not the gate's inputs: a push scan covers the whole +tree (`STRIX_TARGET_PATH` is `./` outside PR scope) and publishes no `strix` +commit status, so the newest head's scan is a complete scan *of the current +tree*. It is not a record of every earlier commit: code that entered and left +`main` between two heads, and findings a retired run never uploaded, are absent +from the newest report, and report collection preserves only runs that reach +it. A per-commit evidence-retention guarantee would need a separate, +verifiable preservation contract; this change does not provide one. `schedule` +and PR-less `repository_dispatch` runs still receive a unique run id. The +`pr_number=${GITHUB_RUN_ID}` admission output is unchanged. + +Tradeoff, stated so a later reader of the security dashboard is not +surprised: with `main` moving roughly every 30 minutes against a 10-30 minute +scan, "main is scanned after every merge" becomes "the latest `main` is +scanned once merging pauses for at least one scan duration". During a merge +burst each new head cancels the previous scan; the burst's final head is +scanned, and the weekly full-tree `schedule` scan (unique run id, never +cancelled) is the floor under a sustained burst. + ## Verification - `python -m pytest -q tests/test_pr_review_merge_scheduler.py -k 'startup_failures or startup_failure'` diff --git a/docs/doctoring/strix-evidence-binding-2159-2168.md b/docs/doctoring/strix-evidence-binding-2159-2168.md new file mode 100644 index 0000000000..2e6151a8ce --- /dev/null +++ b/docs/doctoring/strix-evidence-binding-2159-2168.md @@ -0,0 +1,50 @@ +# Strix evidence binding for PR-delta and remediation claims + +Status: accepted 2026-09-17 + +## Incidents + +### #2159 — PR-delta vs repository baseline + +Required Strix review on `.github#2106` reported source findings against +`scripts/ci/pingora_edge_policy.py` and +`scripts/ci/contextual_orchestrator_review_policy.py` even though both blobs +were base-identical across the authenticated PR tuple. The PR review lane +treated those observations as if they were introduced by the PR. + +### #2168 — false "already applied" remediation + +LineageWeave Strix run `34746057545` completed SUCCESS with a valid Medium +finding, but the report claimed the fix was "already applied" and +"syntax-verified" after `apply_patch` failed with +`WorkspaceReadNotFoundError` / `ApplyPatchFileNotFoundError` against +`/workspace/backend/app/main.py` instead of the materialized scan workspace. + +## Decision + +1. `scripts/ci/strix_evidence_binding.py` classifies each finding against an + authenticated changed-file inventory (including renames and hunk lines) as + `pr_delta`, `repository_baseline`, `context_dependency`, or `unmapped`. +2. The Strix gate labels blocking PR intersections as `evidence_scope=pr_delta` + and unchanged-path continuations as `evidence_scope=repository_baseline`. +3. After each attempt, the gate sanitizes report artifacts through the binder + so an `apply_patch` miss cannot remain summarized as "already applied". +4. Remediation states distinguish `finding_confirmed`, `fix_proposed`, + `fix_applied_in_scan_workspace`, `fix_validated`, + `fix_committed_to_source`, and `remediation_failed`. A fix is never marked + applied without workspace-byte proof or an exact source commit receipt. + +## Evidence and rollback + +Contract tests in `tests/test_strix_evidence_binding.py` cover changed-source, +base-identical, context-dependency, rename, stacked-base, stale-head, and +apply_patch-miss RED fixtures. Gate wiring is pinned by +`assert_strix_evidence_binding_contract` in +`scripts/ci/test_strix_quick_gate.sh`. Roll back only with an equivalent +fail-closed evidence binder; do not restore false PR-delta attribution or +false remediation claims. + +## References + +- ContextualWisdomLab/.github#2159 +- ContextualWisdomLab/.github#2168 diff --git a/docs/doctoring/strix-preflight-capacity-continuation-20260927.md b/docs/doctoring/strix-preflight-capacity-continuation-20260927.md new file mode 100644 index 0000000000..8e44924901 --- /dev/null +++ b/docs/doctoring/strix-preflight-capacity-continuation-20260927.md @@ -0,0 +1,36 @@ +# Strix all-429 startup continuation — 2026-09-27 + +## Evidence and cause + +Current-head late-life-anxiety-reanalysis #257 (`3936039d8406275e754fc518f70fefa491d3d8bb`) +Strix job `108504580446` and #269 (`78617f3160cfe8fbf7a4dae2ca3f3fdaca44db8e`) +job `108303563901` failed before sidecar health. Sanitized producer evidence reported +`strix-plain-chat-preflight-v2`, ready=0, rejected=3, probed=3, and HTTP 429 +for all selected routes. This was startup capacity loss, not a completed security review. + +At main `23f36cd56fbe245a06e7a9727cb28d9511154645`, Strix's model retry +lives after sidecar startup and cannot recover this failure. PR #2440 repaired +Noema's corresponding boundary; this change reuses its stdlib-only classifier. + +## Proposed boundary + +The failed scan exports only typed capacity evidence. A separate job with minimal +Contents write permission sends at most two automatic `strix-scan` continuations. +It has no checkout, model inputs, or provider secrets. It rechecks the live open, +Ready PR's repository, head SHA, base SHA, and base ref after bounded scheduling +jitter, and retires if any changed. The payload includes all identity fields needed +by the existing Strix dispatch validator. The scan remains failed and its existing +status publication is unchanged. Cancellation cannot start a continuation. + +Missing, malformed, non-429, linked, or oversized preflight reports are ineligible; +malformed retry counters exhaust the shared budget. Stale reports are removed +before startup. Private-target ZDR, free-route policy, gateway failover, and model +inference time limits are unchanged. + +## Verification boundary + +The actual dispatch shell is exercised with local GitHub/sleep stubs. It verifies +one exact payload for a valid Ready PR and zero dispatches for moved head/base ref, +Draft, malformed Draft, closed state, or invalid attempts. Existing classifier and +Strix sidecar contract tests also pass. This is local evidence only; fresh hosted +review and an observed same-head continuation are still required after deployment. diff --git a/docs/doctoring/trusted-review-archive-transient-retry.md b/docs/doctoring/trusted-review-archive-transient-retry.md new file mode 100644 index 0000000000..341f79323d --- /dev/null +++ b/docs/doctoring/trusted-review-archive-transient-retry.md @@ -0,0 +1,37 @@ +# Trusted review archive transient retry + +## Incident + +Bounded Noema continuation run +[`36760921156`](https://github.com/ContextualWisdomLab/.github/actions/runs/36760921156), +job `110043067331`, resolved the trusted workflow source to protected +`main@37b10243cec3d160ecc9c1be75c71428b160a703` and then failed before model +setup. The GitHub archive API returned HTTP 502 while the workflow executed a +single `curl -fsSL` request. The source revision, target pull request, and model +route were not the cause. + +## Decision + +The three central workflows that materialize the same immutable trusted archive +use `curl --retry 3 --retry-all-errors --retry-delay 1 --retry-max-time 30`. +This is a bounded transport repair at the canonical `.github` owner. It does +not select a mutable ref, bypass archive extraction checks, change credentials, +or convert a terminal failure into success. + +## Alternatives + +- Manual rerun was rejected because it would not repair the repeated control + path. +- A custom retry helper was rejected because native `curl` already provides the + bounded behavior and another abstraction would enlarge the trusted surface. +- An unbounded loop was rejected because it could occupy review capacity without + a terminal result. + +## Verification and remaining gate + +`tests/test_trusted_archive_retry_behavior.py` runs the production command from +each workflow against a local server that returns one 502 followed by a 200. +The test was RED on the predecessor and GREEN after the bounded retry options. +Hosted exact-head Checks, unresolved-thread review, qualifying independent +approval, and ordinary protected merge remain required. A local GREEN result is +not merge authorization. diff --git a/docs/doctoring/zdr-feed-model-id-route-keys.md b/docs/doctoring/zdr-feed-model-id-route-keys.md new file mode 100644 index 0000000000..f219a97c56 --- /dev/null +++ b/docs/doctoring/zdr-feed-model-id-route-keys.md @@ -0,0 +1,25 @@ +# OpenRouter ZDR feed route keys used the wrong field — 2026-09-13 + +## Symptom + +`noema-review` and `strix` both failed closed on `ContextualWisdomLab/late-life-anxiety-reanalysis#10` (head `a1cd5bc6783c6510dfcf937f523c733366e82213`, runs `34700409452`/job `103571267389` and `34700409446`/job `103571829483`) against central `fb17ef556f94f673234aa557254ae52779e9a7b0`, both exiting with `PolicyError: no attested ZDR model route is available with the ZDR policy; orchestrator/free would fail closed`. Every private/internal consumer runs `--require-zdr` (ADR-0003), so this is a hard boot failure, not a degraded catalog. + +## Root cause (feed schema) + +`_load_zdr_endpoints` (`scripts/ci/contextual_orchestrator_review_policy.py`) built route keys from `endpoint.get("model_name")`. On the real `https://openrouter.ai/api/v1/endpoints/zdr` feed (see OpenRouter's ZDR docs, https://openrouter.ai/docs/guides/features/zdr), `model_name` is a human display string (e.g. "DeepSeek: DeepSeek V4.1 Flash") while `model_id` is the slug contextual-orchestrator discovery reports as `model` (e.g. `inclusionai/ling-3.0-flash-vl:free`). No live-feed key ever matched `is_zdr_model(...)`, so the catalog was always empty under `--require-zdr`. The three fixtures in `tests/test_contextual_orchestrator_review_policy.py` put slugs into `model_name`, which is why this was invisible to tests since the keying was introduced in 17052a7ca (#1360, 2026-08-27). + +## Repair + +`_load_zdr_endpoints` now keys on `endpoint.get("model_id")`; there is no fallback to the display name, and rows missing `model_id` or `provider_name` are still skipped. The three fixtures were corrected to carry the real feed schema (`model_id` slug + a display-string `model_name`). `is_zdr_model` and `zdr_policy.py` are unchanged. + +## Offline reproduction (before / after) + +Discovery: a 60-row consumer snapshot (20 each openrouter/nvidia_nim/nvidia_nim_sub free rows). Feed: the live 859-entry `/api/v1/endpoints/zdr` response fetched 2026-09-13, carrying three matching `inclusionai/ling-3.0-flash-*:free` openrouter routes served by `Novita`. + +- Before: `--require-zdr --pool free` exits 1 with the `PolicyError` above. +- After: exits 0, `zdr_selected_count: 3`, selecting exactly `openrouter/inclusionai/ling-3.0-flash-{vl,sante,fin}:free`, all `zdr: true`. +- Without `--require-zdr`: `zdr_selected_count: 3` and those three routes rank first in the 12-route free catalog. + +## Hosted acceptance still required + +This is an offline fix against a static discovery/feed snapshot. It does not prove a newly loaded central SHA boots the sidecar on the private consumer's exact head, and it does not change how the gateway itself requests ZDR routing from OpenRouter — that remains a separate contextual-orchestrator (CO)-side check. diff --git a/docs/noema-sidecar-evidence-1218.md b/docs/noema-sidecar-evidence-1218.md new file mode 100644 index 0000000000..555c5218b4 --- /dev/null +++ b/docs/noema-sidecar-evidence-1218.md @@ -0,0 +1,45 @@ +# Noema startup evidence for contextual-orchestrator PR #1218 + +On 2026-09-27, run `36025318452`, attempt 3, job `108389560765` +was inspected at consumer head `2fed942d378cd959250f648a16b35276279c9603`. +The job used gateway pin `767e67fbc6b881a452761f32abb69b9971b9b03b`. +Dependencies installed successfully. At 2026-09-26T12:37:08Z the sidecar +started; no health/preflight-ready confirmation followed. The job was cancelled +at 18:35:14Z, approximately six hours after admission. This does not establish +that a Noema review request or any particular provider attempt occurred. + +Artifact `10877250808` was created on 2026-09-25T17:04:14Z and belongs to an +earlier attempt. It must not be attributed to attempt 3 merely because the +workflow run ID and consumer head are equal. + +The workflow uploaded its two existing sanitized evidence files only under +`failure()`. Using `always()` preserves those same files after successful, +failed and normally cancelled execution, without collecting raw provider logs. +The pinned uploader, file allowlist, five-day retention and absent-file behavior +remain intact. A hard runner timeout may prevent cleanup/upload entirely; this +change cannot recover the missing attempt-3 evidence or prove its internal +startup cause. Gateway inference timeouts must not be invented to hide it. + +Verification: the changed workflow contract fails on the previous source; +the Noema workflow contract suite and actionlint verify the revised step. +Hosted execution and independent review remain required before integration. +# Startup progress follow-up for CO #1083 + +ContextualWisdomLab/contextual-orchestrator#1209 run `36138543702`, job +`108153123179`, logged sidecar start at 19:11:54Z on 2026-09-25, then runner +shutdown at 23:12:37Z without readiness confirmation. The run's artifact API +returned no artifacts. This proves loss of startup evidence, not a particular +provider deadlock or a model failure. + +The shared readiness loop now logs every 60 failed health polls whether its +discovery, catalog, policy, and preflight report files are nonempty. These are +presence observations only: no report content, provider response, or credential +is printed. Poll count is not elapsed time and does not impose an inference +deadline. A successful health check still ends the loop, and sidecar process +exit retains the existing failure handling. + +The executable regression runs the real health loop with absent, partially +completed, and completed report stages; verifies exact output and secret +non-disclosure; and verifies readiness can succeed after the diagnostic. It +does not establish that the unknown startup cause is repaired. A fresh hosted +run after protected integration is still needed to locate that cause. diff --git a/docs/org-required-workflow-rollout.md b/docs/org-required-workflow-rollout.md index 88f6cc4deb..b7804b65bc 100644 --- a/docs/org-required-workflow-rollout.md +++ b/docs/org-required-workflow-rollout.md @@ -136,21 +136,19 @@ gate only when they do not compete to upload the same SARIF. The central native dispatch handler analyzes the target head without making the target repository's default-setup upload path its source of truth. -### Repository-local CodeQL inventory (2026-07-04) — HISTORICAL, superseded 2026-09-03 - -**This entire subsection describes a plan that did not work and is not -current guidance.** It assumed `codeql-pr.yml` would become a functioning -central required check once ruleset `18156473` included it; the "Correction -(2026-09-03)" note under "Code scanning required workflow posture" above -explains why that assumption was wrong — `codeql-action` cannot run inside a -required workflow at all, so `codeql-pr.yml` was removed from the ruleset, -not fixed. "Centralizing through `codeql-pr.yml` fixes every inherited -repository in one ruleset change" (below) never happened and never could. -Coverage for repositories without a local CodeQL workflow now comes from -GitHub's native `code-scanning/default-setup` instead (see the 2026-09-03 -"Evidence from this rollout" entry) — do not read the table below as -"repositories still needing the ruleset update to land"; treat it only as a -2026-07-04 point-in-time snapshot of which repositories had a local `codeql.yml`. +### Repository-local CodeQL inventory (2026-07-04) — HISTORICAL + +**This subsection records the original July rollout, not current guidance.** +That plan invoked `github/codeql-action` directly inside a required workflow, +which GitHub does not support. The old entrypoint was removed on 2026-09-03. +The 2026-09-04 correction above restores a different, dispatch-safe +`codeql-pr.yml`: it sends the scan to a native workflow and consumes an +app-authored exact-head status. This restored entrypoint is in the current +seven-workflow ruleset. Native default setup is a repository-local safety net, +not a replacement for that central gate. The table below remains only the +2026-07-04 snapshot of repositories with a local `codeql.yml`; it does not +identify present-day adoption gaps. + Org audit of default-branch workflow files as of 2026-07-04. @@ -225,6 +223,16 @@ mints a short-lived installation token restricted to the target repository; the App has read-only Actions/checks/contents/status/code-scanning/Dependabot access and write access only to pull-request reviews. +The scheduler's independent-review gate recognizes this canonical App only +when GitHub authenticates its bot actor, the attached review commit is the +exact head, and the publisher's head/result/actor/App-source receipt agrees. +The PR author cannot satisfy this condition, including alternate REST and +GraphQL spellings of the same bot. Latest malformed formal approvals or +revocations block older receipts; comments do not replace formal policy state. +Other automation remains excluded. This reviewer eligibility does not replace +GitHub approval policy, required checks, security evidence, resolved threads, +or fresh head/base validation before a head-guarded merge. + The PydanticAI `ReviewAgent` product in `ContextualWisdomLab/noema` (`reviewer/noema_reviewer`, `ContextualWisdomLab/noema#9`) is the target standalone judgement plane, while the central Python gate remains the deployed fail-closed reviewer. The diff --git a/docs/policies/PINGORA_EDGE_POLICY.md b/docs/policies/PINGORA_EDGE_POLICY.md index 619374a13d..f7a6e6a5ee 100644 --- a/docs/policies/PINGORA_EDGE_POLICY.md +++ b/docs/policies/PINGORA_EDGE_POLICY.md @@ -63,6 +63,61 @@ This is a bounded binary-evidence classifier, not a general image renderer; visual fidelity and optional ancillary-chunk semantics are outside this gate. Other binary files remain unavailable evidence and fail closed. +## Declared research/data artifact paths + +The scanner's binary exemption is otherwise shaped by path only (`doc`/ +`docs`/`documentation`, plus the `evidence`/`figures` publication +directories). A research repository whose raw data and fitted-model +artefacts live elsewhere by deliberate, owner-approved design -- SPSS +`.sav` files, serialized model objects, compressed numeric arrays -- can +opt in without relocating that data under `docs/`. + +Add `.github/edge-policy-artifact-paths.txt` at the repository root: one +explicit relative path prefix per non-blank line, no globs or wildcards. +For example: + +``` +local +evidence/raw +``` + +**Security property.** `evaluate_pull_request` resolves this file only +from the pull request's *base ref* -- never its head. A pull request that +adds or widens the declaration is not self-authorizing: it gets no benefit +from that change until the change itself is reviewed and merged into the +base branch. This mirrors how the required workflow already treats every +other piece of policy evidence -- current-head content only, no +pull-request-controlled trust. + +**What the declaration replaces, and what it does not.** A file under a +declared prefix is admitted on exactly the same evidence documentation +paths already require: `_runtime_path_rule` matches (`Dockerfile`, +`nginx.conf`, service files, and the like) are rejected inside a declared +prefix exactly as inside `docs/` today, and any file that decodes as valid +UTF-8 is still fully content-scanned, never silently admitted. A file whose +suffix has a known magic byte (`.hwpx`, `.pdf`, `.png`) is verified by that +format's structural evidence; a file with no known magic entry (most +research-data formats) is admitted only on the stricter combination of "no +diff patch", "the fetched bytes are not valid UTF-8", and "the +replacement-decoded content contains no prohibited runtime pattern". A text +file cannot be mistaken for a binary artefact merely by sitting under a +declared prefix, and a stray invalid byte cannot hide a readable runtime +directive. + +**Bounds.** The declaration is capped at 64 entries and 8 path segments of +depth per entry (`MAX_DECLARED_ARTIFACT_PREFIXES` / +`MAX_DECLARED_ARTIFACT_PREFIX_DEPTH` in `scripts/ci/pingora_edge_policy.py`) +-- parsing-safety bounds, not a product limit on how many locations a +repository may declare. An absolute path, a `..` traversal component, a +bare `.`/`/`, or a glob character in any entry is a hard `PolicyError` +naming the offending entry; a repository with no declaration file behaves +identically to before this feature existed. When a declared prefix admits a +file, the required workflow logs a `::notice::` naming the prefix and the +base ref the declaration was read from, so a reviewer can trace the +admission back to the reviewed declaration it relied on. + +Refs #2193, #2149, #2116. + ## Exception process There is no standing Nginx exception. A temporary exception requires a public ADR diff --git a/docs/pr-review-and-merge-procedure.md b/docs/pr-review-and-merge-procedure.md index e34c3957cb..5a2dc88ad7 100644 --- a/docs/pr-review-and-merge-procedure.md +++ b/docs/pr-review-and-merge-procedure.md @@ -63,6 +63,15 @@ by the OpenCode GitHub App. A GitHub Actions-authored review is not OpenCode approval evidence. The separate scheduler also listens for that App review, waits for the publishing OpenCode check to finish, and then retries direct merge outside the review job when repository auto-merge is unavailable. +The OpenCode job's own post-publication scheduler is assessment-only: +`--dry-run`, `--merge-mode disabled`, `--no-enable-auto-merge`, and +`--no-update-branches`. Review publication never grants mutation authority to +that follow-up. Separately authorized review-event and scheduled scheduler +runs retain their existing policy; these flags do not disable those runs. + +Central Noema handoff and live base binding are tracked separately in +ContextualWisdomLab/.github#2111. This assessment-only follow-up change does not +claim that handoff is deployed. A dispatch is neither review nor approval. Every merge keeps `--match-head-commit`. It prefers squash and retries with a merge commit only when the target repository explicitly reports that squash diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 1cc9e20313..bacdd4c25e 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -1,12 +1,143 @@ # Product and Technical Gap Baseline +## 2026-10-09 reusable R matrix conservation + +| Gap | Exact evidence | Action | Status | +|---|---|---|---| +| `.github#2565@6e924f32a2dbaa0950709c6d8f1391fc1058ef43` printed `STOP` for unsupported R platform legs but returned exit 0 whenever at least one Ubuntu leg remained; the workflow then expanded only the filtered output, so a required macOS, Windows, ARM, or unknown check could disappear while the reusable workflow passed | RED changed the existing executable contract to require the mixed Ubuntu+macOS+Windows+ARM matrix to fail; exact-parent execution failed because the script returned 0 and emitted only Ubuntu. The inline workflow copy had identical behavior | **PRD:** requested required checks are conserved or the request fails; partial silent execution is forbidden. **TRD/RCA:** reject the entire matrix on the first unsupported leg and return the original rows only when every row is admitted; keep the owner script byte-equivalent to the workflow inline copy. **Context Map:** central `.github` owns reusable R admission; product callers consume the released contract. **Flow:** `caller matrix → validate every row → all admitted → fixed isolated Linux/x64 execution`, otherwise `STOP / nonzero`. DB/UI/ERD changes are N/A | **Proposed / focused RED→GREEN complete; full exact-tree verification, hosted exact-head Checks, isolated-capacity canary, and qualifying independent approval remain required** | + +## 2026-10-07 security scanner runner isolation + +| Gap | Exact evidence | Action | Status | +|---|---|---|---| +| `.github#2565@3b35d3d427d17f71472d3bf52ba179338011c17a` restored the pinned Trivy binary cache on one self-hosted runner but could not execute `trivy`, while the completed OSV comparison was reclassified as failed only because optional debug-artifact storage quota was exhausted | Security run `37242578328`; Trivy job `112314054734` logged cache hit followed by `entrypoint.sh: line 88: trivy: command not found` (exit 127). OSV job `112314054619` found no PR-introduced issue and uploaded SARIF before `Upload OSV debug artifacts` failed with `Artifact storage quota has been hit` | **PRD:** a buyer-visible security verdict must represent scanner findings, not runner-local cache state or optional retention quota. **TRD/RCA:** disable `trivy-action` binary caching across the heterogeneous isolated runner group and make only OSV debug retention non-blocking; the base/head reporter and SARIF path remain fail-closed. **Context Map:** central `.github` owns security workflow execution; product repositories consume the released required-workflow contract. **Flow:** `exact head checkout → pinned scanner install → finding gate → SARIF → optional debug retention`. DB/UI/ERD changes are N/A. RED `cc42969b…` failed 2/2 contracts; GREEN `26f993c0…` passes focused tests normally and under `GITHUB_ACTIONS=true`, plus 5,488 passed, 10 skipped, 40 subtests repository-wide | **Proposed / source and contract repair published; fresh exact-head hosted security evidence, isolated-capacity canary, unresolved-thread zero, and qualifying independent approval required** | + +## 2026-10-03 isolated self-hosted runner authority boundary + +| Gap | Exact evidence | Action | Status | +|---|---|---|---| +| `.github#2565@d6ba56d04e22476b2ea41bfe7bb26e2cbc98f98f` routed ordinary jobs and non-main fallbacks by the mutable `cwlab-ci-isolated` label alone, although the PR's own doctoring stated that a generic label does not attest separation; GitHub does not validate OS/architecture label truth, so a privileged or persistent runner with matching labels and repository access could receive untrusted work | RED contract `test_isolated_label_is_scoped_to_dedicated_runner_group` failed first at `.github/workflows/actions-queue-health.yml`; the reviewed tree contained 63 affected `runs-on` declarations across direct, conditional, CodeQL and R-matrix selectors | At the central `.github` owner, require both the dedicated `CWL CI isolated` runner group and existing labels for every isolated selector; preserve trusted-main groups and repository predicates; update exact workflow oracles and rollout doctoring; keep Draft/HOLD until the group, access policy, disposable capacity, cleanup proof, canary and exact-head Checks exist | **Proposed / local RED→GREEN complete; operator capacity, exact-head hosted Checks and qualifying independent approval required** | +| `.github#2565@cd49a60f1299fb8f7984de8d8ba223db673f7b84` wrapped the reusable R runner JSON in unescaped literal braces inside GitHub `format()`, so expression evaluation treated `"group"` as a replacement field and failed before any matrix job could request a runner; the prior test asserted that defective source literal instead of rendering it | RED `f9211898d83f039cdf7ff82c8f93c72c9c10dceb` added a behavior contract that renders and parses the exact template for Linux, Windows, macOS and an unsupported OS; focused result was 1 failed / 7 passed with `KeyError: '"group"'`. GREEN `7fc1e2e8ad5091adff6c56b501e93d1d4ac2f211` doubles only the outer JSON braces and passes 8/8 normally and with `GITHUB_ACTIONS=true` | Preserve `{0}`/`{1}` substitutions and the original matrix OS label, render one valid group-scoped selector for every supported/unsupported branch, and keep the PR Draft/HOLD until the independent runner-group prerequisites and hosted exact-head gates are satisfied | **Proposed / local RED→GREEN complete; no runner capacity or merge authority claimed** | +| `.github#2565@f0f7ed988fba5d94eda6513f35d171f23c4bd9a5` inserted caller-controlled `matrix.config.os` inside a quoted JSON string; a quote-bearing label could add duplicate `group` and `labels` members whose last values replaced the fixed isolation boundary | Exact-head RED updated the source contract to require an unquoted `{1}` JSON value plus `toJSON(matrix.config.os)`; before source repair the focused result was 2 failed / 6 passed, after repair 8/8 passed. The behavioral matrix includes benign Linux/Windows/macOS/unsupported labels, a duplicate-key injection string, and backslash/newline data. Full warning-fatal verification is 5,439 passed, 10 skipped, 40 subtests with 18,830/18,830 statements and 7,684/7,684 branches covered. Exact-head public-doc coverage initially exposed two inherited Maturin helper gaps; adding their public contracts restored 100%, and compileall/diff checks pass | **PRD:** untrusted reusable-workflow input must remain data and cannot select privileged capacity. **TRD/RCA:** serialize the OS label exactly once at the GitHub expression boundary, then parse one selector with fixed group and mandatory labels. **Context Map:** central `.github` owns runner admission; callers supply labels through the released workflow contract only. **Flow:** `r_matrix JSON → matrix.config.os → toJSON → format value slot → fromJSON → fixed runner group`. DB/UI/ERD changes are N/A. Require hosted exact-head security/quality Checks, isolated capacity canary, cleanup proof, and qualifying approval | **Proposed / local RED→GREEN and full verification complete; hosted/operator gates pending** | + +## 2026-10-01 Maturin response-lifecycle coverage closure + +| Gap | Exact evidence | Action | Status | +|---|---|---|---| +| `.github#1653@5cd141ec2c33b631d164af936cd1c9de70e4c9a4` passed all 5,314 tests but failed the complete branch gate because the canonical Maturin downloader left five error-path statements and two branches unexecuted; the owner workflow omitted both verifier paths and stacked PR bases | Trusted uv Materializer run `36811202519`, job `110206427182`; `verify_release_maturin_tool_assets.py` 95%, missing lines 104 and 106-112 plus branch 114→116; no owner run at #2530 predecessor `8cf2ea5f73976d47b2267fb52ac28284323404b7` because its base was #2531 rather than `main` | Repair canonical successor `.github#2530`: exercise non-200 and opener-raised `HTTPError` closure, replace the impossible nullable-response finalizer with one unconditional response-owned close scope, add source/test and stacked-PR trigger contracts to the complete gate while retaining protected-main push scope, preserve all network and fail-closed boundaries, then ordinary-merge the accepted owner head into #1653 | **Proposed / hosted RED reproduced; focused verifier coverage GREEN locally; path and stacked-admission contracts RED→GREEN; exact-head hosted full-suite, security, CodeQL, and independent approval required** | + +## 2026-10-01 bounded Maturin release downloader SAST closure + +| Gap | Exact evidence | Action | Status | +|---|---|---|---| +| The canonical Maturin asset verifier replaced a suppressed dynamic `urlopen` call with direct `HTTPSConnection`, but Semgrep's low-confidence certificate-validation audit still rejects every use of that low-level API | `.github#2531@e33d97d022e1c1a26b35d51f9fa4b695fe969547`; SAST run `36802256836`; job `110178968784`; rule `python.lang.security.audit.httpsconnection-detected.httpsconnection-detected` | At source-repair head `eede925e71e0f1526560a305f5219a13c6631227`, replace the low-level connection with a standard-library opener whose redirect handler admits one credential-free HTTPS hop only from the fixed GitHub release path to the exact release-assets host; preserve bounded reads and terminal response closure; prohibit scanner suppressions | **Proposed / RED scanner-contract reproduction and 31 tests plus 4 subtests GREEN locally; source-repair exact-head Security, Python Security, SAST, and runtime-quality GREEN; terminal CodeQL and independent approval required** | + +## 2026-10-01 gap baseline source integrity + +| Gap | Exact evidence | Action | Status | +|---|---|---|---| +| A connector display truncation was committed as the baseline source, removing 3,270 lines of PRD/TRD/UML, Gap-register, APA 7th, and live-inventory evidence | Repository Metadata Reconcile run `36777030872`; job `110097412873`; exact broken head `.github#2530@61a3f2ebbf6adf9c237a647552ccffeb52b00f47`; RED `c8d41f52962814d080a1c4947acbff2f3f9bb81e` | Restore the last complete 3,703-line source blob, reapply only the intended first-patched dependency-floor row, and reject future connector truncation banners in the repository quality gate | **Proposed / local RED→GREEN complete; exact-head hosted Metadata and independent approval required** | + +## 2026-10-01 Noema document-reader transitive security baseline + +| Gap | Exact evidence | Action | Status | +|---|---|---|---| +| The generated Noema document-reader lock selected `fast-uri` 3.1.7 and `ip-address` 10.7.0 after CVE-2026-86472, CVE-2026-101911, and CVE-2026-101912 were published | Security Scan run `36773087489`; Trivy job `110084194330`; exact predecessor `.github#2530@a99784219305d1b6e14cf76f0acea30c5ee45e21` | At the central `.github` owner, regenerate only the two transitive entries to `fast-uri` 3.1.8 and the first patched `ip-address` 10.7.1 release, scan every hoisted or nested lock entry in a regression contract, reproduce with `npm ci`, and require a zero-vulnerability npm audit | **Proposed / local RED→GREEN and audit complete; exact-head hosted security and independent approval required** | + +## 2026-10-01 shared Strix LiteLLM credential-exfiltration closure + +| Gap | Exact evidence | Action | Status | +|---|---|---|---| +| The shared Strix hash lock selected LiteLLM 1.94.1, which is affected by CVE-2026-84377 / GHSA-3cv6-jpf6-8222 and can expose configured provider credentials through authenticated routing overrides | Failing predecessor `.github#2531@516471fbe7d4e93a50c7bbba20402447f06f8d8b`, Python Security run `36799069276`, job `110169140365`; repaired exact head `fe879f7b7f48f729f757e03851bf61149470ccb5`, Python Security run `36800615364`, Security Scan run `36800615435`, SAST run `36800615456`, and runtime-quality run `36800615444` | Preserve stacked #2545's Noema document-reader transitive repair, add a direct `litellm==1.94.3` source floor, regenerate the complete hash lock, bind source and lock with a RED-to-GREEN regression, and require authenticated CodeQL verdict evidence plus independent approval | **Proposed / dependency, security, SAST, and runtime-quality Checks GREEN; CodeQL run `36800615319` fail-closed pending authenticated verdicts; independent approval required** | + +## 2026-10-01 trusted review archive transient transport + +| Gap | Exact evidence | Action | Status | +|---|---|---|---| +| Required Noema resolved the correct immutable trusted source but a single GitHub archive API HTTP 502 exhausted materialization before model setup | `.github` run `36760921156`, job `110043067331`; protected source `37b10243cec3d160ecc9c1be75c71428b160a703`; combined successor predecessor `.github#2530@5b3a76ea71d7ae2fa9b1719f4f82df8d52e98082` | At the central `.github` owner, add bounded native transport retries to Noema, OpenCode, and merge-scheduler trusted archive downloads; prove the actual commands against a deterministic 502→200 server without changing exact-SHA, credential, extraction, or fail-closed contracts | **Proposed / local RED→GREEN complete; exact-head hosted Checks and independent approval required** | + +## 2026-10-01 PyJWT recursion denial-of-service closure + +| Gap | Exact evidence | Action | Status | +|---|---|---|---| +| The shared Strix source and hash lock retained PyJWT 2.14.0 after GHSA-42vr-xj54-vc7v / CVE-2026-101918 disclosed an unauthenticated recursion DoS | Security Scan run `36741151937`; dependency-review job `109975641239`; OSV job `109975641271`; dependent `.github#2540@612d8e77cf13eba84782a22587a72d3ffb4b6c6e`; canonical owner PR #2531 predecessor `dde3ea7876ceb1569db717975cc74f44cc8d18f9` | In canonical owner PR #2531, advance source and lock to 2.15.0 without unrelated package movement, preserve exact source/lock parity, and merge-forward dependent branches only after owner acceptance | **Proposed / exact-head Checks and independent approval required** | + +## 2026-10-01 shared urllib3 security closure + +| Gap | Exact evidence | Action | Status | +|---|---|---|---| +| pip-audit and Strix locks retained urllib3 2.7.0 after CVE-2026-97687 and CVE-2026-97689 were published | Python Security run `36733279716`, job `109949358063`; exact predecessor `d1aa3659fca527a6c7330151f3ab4df3d7578391` | In canonical owner PR #2531, pin urllib3 2.8.0 in both source inputs, regenerate both hash locks without unrelated version movement, and bind all four files with one contract | **Proposed / exact-head Checks and independent approval required** | + 작성 기준일: **2026-08-26 10:35 KST** 대상: **ContextualWisdomLab/.github** 중앙 거버넌스·자동화 레포지터리와 이를 소비하는 naruon 생태계 현재 보호된 `main`: `826b92394c63deb6981c3a8d16a724d71f85a0d7` -현재 열린 PR 수: **107** (아래 표에 이 스냅샷의 전체 목록 포함; live API 재수집) +현재 열린 PR 수: **107** (아래 기준선 인벤토리 행 수; 이 스냅샷의 기계 계약) + +2026-10-07 delta: 그날 관측한 보호된 `main`은 `7554587c2e3106a388998bcad048a3d7121de25e`였고, live connector 조회는 반환 상한 100개를 모두 채웠으므로 열린 PR은 **최소 100개**였다. 이 관측값은 위 2026-08-26 스냅샷을 대체하지 않으며, 전체 수로 외삽하거나 아래 기준선 행 수를 재작성하지 않는다. 이 문서는 제품·기술·운영 Gap을 현재 문서와 현재 GitHub 상태에 묶어 두는 기준선이다. 새 작업은 먼저 이 문서의 Gap ID를 PR 설명과 테스트 증거에 연결하고, PR의 정확한 exact HEAD·Checks·리뷰를 다시 수집한 뒤 구현한다. 표의 상태는 작성 시점의 관측값이므로, 병합 판단에는 재사용하지 않는다. 이 인벤토리는 스냅샷이며 merge authorization이 아니다. +### 2026-10-07 Strix report-identity boundary delta + +| Gap ID | 상태 | exact-tree evidence | causal owner / next gate | +|---|---|---|---| +| CONTROL-STRIX-REPORT-PATH-IDENTITY-01 | **Source repaired on Draft `.github#2574`; protected integration pending** | RED `d8aeb759f780d58b13c8692fe52dd39cd4f60d80` proved that both `scripts/ci/strix_quick_gate.sh:backup` and its private-scope-root form were accepted as the changed file. GREEN `983e31ff828baeca9873042d8c3faf1b949f2df8` restricts colon suffixes to numeric `path:line[:column]` evidence. Focused evidence is 39 passed with 55/55 statements and 26/26 branches covered. Protected `main@7554587c2e3106a388998bcad048a3d7121de25e` was integrated by ordinary two-parent merge `d1cd94627279e1bcb9b5cd5fddbad9a0056d6e1a`. | Central `.github` owns the Strix report-to-changed-source identity boundary. Keep the PR Draft until its final exact head has hosted source-executing security/runtime Checks, no unresolved actionable review, and qualifying independent current-head approval. Do not treat zero-step runner failures as source evidence or use bypass. | + +### 2026-09-30 central coverage owner stack delta + +| Gap ID | 상태 | exact-head evidence | causal owner / next gate | +|---|---|---|---| +| CONTROL-CENTRAL-COVERAGE-OWNER-01 | **Proposed — complete local integration GREEN; hosted exact-head acceptance pending** | Current coverage owner `.github#2521@61fb469a…`, parser/security/response integration successor `.github#2530@2510618f…`, GitHub API response-lifecycle owner `.github#2532@9d3ec75d…`를 ordinary two-parent merge로 보존했다. 첫 integrated warning-fatal run은 `5196 passed, 6 skipped` 뒤 queue/Strix/release prescreen/release dependency의 실제 미실행 분기 90개와 partial branch 29개를 드러냈다. Dummy/live-CLI tests를 bounded behavior contracts로 교체하고, exact Git blob·Cargo development lock·runtime receipt·final fanout cap·Python 3.10 TOML fallback을 검증했으며, 앞선 필수조건 때문에 도달 불가능했던 prescreener postcondition만 제거했다. 두 live head를 재수집·일반 병합한 combined successor는 `5291 passed, 5 skipped, 40 subtests passed`, owned production `18729/18729` statements 및 `7642/7642` branches, Docstring 100%, warning 0이다. | Canonical owner는 중앙 `ContextualWisdomLab/.github`이며 source delta는 ordinary merge ancestry로만 통합한다. #2530과 #2521의 live head 이동을 재수집해 force 없이 merge했고 새 integrated tree 전체를 재검증했다. 게시된 #2530 exact head의 hosted security/quality Checks 및 qualifying independent approval을 새로 확인한다. queued/skipped/pending을 성공으로 간주하지 않고 #2521/#2530/#2532를 단순 Close하지 않는다. [RCA와 검증 근거](doctoring/central-coverage-owner-stack-2521.md). | + +### 2026-09-30 full-suite parser-lock incident delta + +| Gap ID | 상태 | exact-head evidence | causal owner / next gate | +|---|---|---|---| +| CONTROL-QUALITY-FULL-SUITE-PARSER-LOCK-01 | **Proposed — `.github#2530` combined successor preserves live parser, security, response-lifecycle, and coverage heads; hosted acceptance pending** | Protected `main@37b10243…`의 common quality lock만 설치하는 전체 suite가 `defusedxml`을 찾지 못해 collection error 13건으로 중단됐고, 같은 suite의 신규 workflow 계약은 `yaml`을 import한다. Concurrent-head 재검증 뒤 live #2530 `2510618f…`와 live #2521 `61fb469a…`를 Force Push·rebase 없이 ordinary merge했다. 이 ancestry는 security prerequisite #2531 `d1aa3659…`, response-lifecycle prerequisite #2532 `9d3ec75d…`, parser lock, coverage 수리를 함께 보존한다. 잠금을 직접 설치하는 모든 workflow는 생성 lock, 두 source input, compiler 변경을 추적하며 선택형 runtime quality도 실제 consumer suite를 실행한다. Review-repair owner는 launcher runtime 두 suite를 직접 실행·계측한다. Combined local evidence는 Python 3.14 warnings-fatal 5,291 passed, 5 skipped, 40 subtests, production 18,729/18,729 statements·7,642/7,642 branches, Docstring 100%다. 이 행은 live Project #1 상태나 merge authorization을 주장하지 않는다. | Canonical owner는 중앙 `.github`의 source requirement, 생성 hash lock, compiler, 직접 소비 quality workflows다. 새 combined exact head의 hosted Checks, 미해결 thread 0, qualifying independent approval을 다시 수집해야 ordinary protected merge할 수 있다. #2531/#2532/#2521은 protected successor merge와 complete carryover를 확인하기 전 닫지 않는다. | +| CONTROL-REPOSITORY-BRANCH-COVERAGE-01 | **Proposed — `.github#2521`의 전체 유효 delta를 `.github#2530` combined successor가 ordinary merge로 승계; exact-head hosted acceptance pending** | `.github#2521`는 production exclusion을 제거해 전역 100% 주장을 정직하게 RED로 되돌렸다. 네 잔여 소유자인 `opencode_queue_priority`, `strix_unverified_dependency`, `prescreen_release_runtime_archives`, `release_dependency_gate`의 실제 분기를 test-first로 모두 실행했고, launcher production omission도 제거했다. `.github#2530`의 parser lock 없이는 전체 suite collection이 실패하고, parser-lock PR은 이 coverage gap 때문에 전역 gate가 실패하는 순환 선행조건이었다. predecessor의 current head `61fb469a…`까지 successor ancestry에 보존하며 predecessor는 successor의 보호 병합과 tree 동등성을 확인하기 전 닫지 않는다. | Canonical owner는 중앙 `.github`의 production CI modules와 complete repository gate다. coverage 제외·pragma·threshold 하향·샘플 축소는 허용하지 않는다. refreshed combined successor의 complete warnings-fatal suite, 100% statement/branch report, hosted Checks, independent approval, ordinary protected merge를 새 exact head에서 완료해야 한다. | + +### 2026-09-30 공유 보안 기준 exact-head delta + +이 delta는 아래 2026-08-26 인벤토리를 덮어쓰지 않는다. 2026-09-30 재수집한 +보호 `main`은 `37b10243cec3d160ecc9c1be75c71428b160a703`이고, live API의 첫 +페이지에는 열린 PR 50개가 있었다. 페이지 전체를 조직의 총 PR 수로 추론하지 않는다. + +| Gap ID | 상태 | exact-head evidence | causal owner / next gate | +|---|---|---|---| +| CONTROL-SHARED-SECURITY-LOCK-01 | **Source repair in progress — release HOLD** | `.github#1026@6f645a73502e159d5a229805afa34868ad9bb851`의 Security Scan run `36495499815`는 공통 Rust fixture의 PyO3 `0.22.6`에서 GHSA-36hh-v3qg-5jq4와 GHSA-chgr-c6px-7xpp를 검출했고, Python Security run `36495499871`은 공통 Strix hash lock의 PyJWT `2.13.0`에서 CVE-2026-102274를 검출했다. 두 파일은 #1026 변경 범위 밖이며 보호 `main`에도 동일하게 남아 있었다. RED commit `cd84d887`는 PyO3 `0.29.2`와 PyJWT `2.14.0` source/lock parity를 요구한다. | 중앙 `.github`가 공통 fixture와 Strix lock을 소유한다. [RCA와 검증 계약](doctoring/shared-security-baseline-pyjwt-pyo3-20260930.md)에 따라 owner PR의 exact-head Checks와 독립 승인, ordinary protected merge, immutable consumer source pin 갱신, 그리고 #1026의 비강제 main merge-forward가 순서대로 필요하다. 어떤 실패도 #1026 전용 패치나 bypass로 처리하지 않는다. | + +### 2026-09-30 GitHub API response lifecycle incident delta + +| Gap ID | 상태 | exact-head evidence | causal owner / next gate | +|---|---|---|---| +| CONTROL-GITHUB-API-HTTP-ERROR-CLOSE-01 | **Proposed — protected-main RED reproduced; source repair under hosted exact-head verification** | 보호된 `.github/main@37b10243cec3d160ecc9c1be75c71428b160a703`의 Python 3.14.7 `tests/test_github_api_url_boundary.py -W error`가 실제 CodeQL/Strix opener의 synthetic 302 여덟 경우에서 `ResourceWarning: Implicitly cleaning up `로 `8 failed, 26 passed`였다. 첫 repair의 warning-fatal full suite가 동일 defect를 Noema/Pingora/preflight/Pages/sandbox readiness에서 추가로 드러냈다. | Canonical owner는 중앙 `.github`이다. 각 caller가 기존 bounded status/telemetry와 fail-closed mapping을 보존한 뒤 file-like error response를 명시적으로 닫는다. `5161 passed, 10 skipped, 40 subtests passed`로 complete warning-fatal local tree가 GREEN이다. [RCA와 acceptance](doctoring/github-api-http-error-response-lifecycle.md)를 따라 exact-head hosted security, independent review, ordinary protected merge를 완료한 뒤 `.github#2040`과 review-transport stack이 새 protected head를 정상 병합해 downstream 증거를 재생성해야 한다. | + +### 2026-10-03 Dependency Review transport-completion delta + +| Gap ID / 상태 | PRD / TRD / Context Map / evidence / next gate | +|---|---| +| `CONTROL-DEPENDENCY-REVIEW-TRANSPORT-EXIT-01` — **Complete owner carryover integrated into `.github#2565`; Proposed/HOLD pending exact-head hosted evidence and independent approval** | **PRD:** 구매자는 불완전한 dependency comparison이 보안 gate를 통과하지 않음을 보장받아야 한다. **TRD/RCA:** reusable preflight가 curl의 nonzero 종료를 `|| true`로 숨기고 출력된 HTTP `200`만 검사해 partial transfer에도 `available=true`를 발행할 수 있었다. **Context Map:** 중앙 `.github` Dependency Review admission owner가 thin callers 앞에서 exact base/head와 authenticated transport completion을 소유하며, bundled Security Scan은 같은 identity/transport invariant를 적용한다. **실행 흐름:** `exact identity validation → authenticated curl → curl exit == 0 AND HTTP 200 → available=true → pinned Dependency Review`; DB/UI/ERD 변화는 N/A다. **RED→GREEN:** #1725 RED `fe9d1933…`의 fake curl `HTTP 200 + exit 18` 회귀는 defect head에서 실패했다. #2565 published ordinary two-parent merge `650bc6e35409d52d24fec890c2ab74063794e9b8` (tree `9522f0b9c96247b3412e147bc6f89fd2595c0776`)가 #1725 exact `229027280e8bce6cc4f13e722b2b0c280a1ac17d`의 source/tests/ADR/doctoring/CHANGELOG/Gap delta를 runner-group stack에 보존했고, 결합 focused suite는 일반 및 `GITHUB_ACTIONS=true`에서 각각 53/53, warning-fatal 전체 suite는 5,437 passed, 10 skipped, 40 subtests다. Review에서 발견된 ADR-0025/0032 충돌은 security decision을 ADR-0034로, owned CodeQL decision을 ADR-0033으로 이동하고 numeric identity uniqueness 계약을 추가해 제거했다. **다음 gate:** fresh exact-head hosted security/review Checks, unresolved thread 0, qualifying independent approval, runner-group/capacity/cleanup canary, ordinary protected merge가 필요하다. | + +### 2026-09-19 exact-head incident delta + +| Gap ID | 상태 | exact-head evidence | causal owner / next gate | +|---|---|---|---| +| CONTROL-LAW-CI-CLUSTER-SYMLINK-01 | **Proposed — `.github#2581` owner repair; local GREEN, hosted acceptance pending** | Exact parent `7558db6749e6f73a83cefd5f67dfaca380c8ef7d` allowed symlinks at `data`, `private`, `postgres`, `cluster`, and `postmaster.pid` to reach the synthetic `pg_ctl` boundary. Five RED cases now exercise the real helper; the minimal shared `cleanup_root` guard makes all five GREEN and preserves the root/marker/foreign PID evidence. | Canonical owner is `scripts/ci/law_ai_agent_ci.sh`. Keep `.github#2581` Draft until the final exact head passes the complete local suite, source-executing hosted Checks, and qualifying independent review; never substitute a skipped/zero-step run or bypass. | +| CONTROL-OPENCODE-COVERAGE-LOCK-CONTEXT-01 | **Proposed — PR-bound incident register; GitHub Project #1 roadmap item이 아님; `.github#2385@950ab885…` source convergence, hosted acceptance pending** | Required OpenCode run `35370902053`의 `coverage-evidence` job `105778600365`은 PR source 실행 전에 `COPY requirements-opencode-review-ci-hashes.txt requirements-noema-document-ci-hashes.txt /tmp/`에서 두 번째 파일을 찾지 못해 종료했다. RED `9b9f5edcd`는 Dockerfile의 모든 lock input이 trusted build context에 존재해야 한다는 계약을 고정했다. 이 행은 live Project 상태를 주장하지 않고 exact-head PR evidence만 추적하며, protected integration 뒤 제거 여부를 재평가한다. | Canonical owner는 중앙 `.github/.github/workflows/opencode-review-dispatch.yml`이고 complete successor는 `.github#2385`이다. 두 lockfile을 각각 regular non-symlink로 검증하고 build context로 복사한 뒤 exact-head focused/full suite와 새 hosted `coverage-evidence`를 통과해야 한다. PR 제품 source나 coverage 비율의 결함으로 오인하지 않으며 synthetic status·manual rerun·bypass를 사용하지 않는다. | + +### 2026-09-13 current-head incident delta + +| Gap ID | 상태 | exact-head evidence | causal owner / next gate | +|---|---|---|---| +| CONTROL-OPENCODE-VCS-PYROOT-01 | **Source repaired on `main` (#2123 `ebc69a401`); image-path helper extracted + offline-proven under #2157 follow-up; hosted consumer step-#17 link still required to close the issue** | `ContextualWisdomLab/contextual-orchestrator#1149@684cf28f`의 중앙 [OpenCode run 34701472466](https://github.com/ContextualWisdomLab/.github/actions/runs/34701472466) `coverage-evidence` job `103574547257`은 PR 코드를 실행하기 전에 immutable `ContextualWisdomLab/fast-mlsirm@09f762d`의 `python/fast_mlsirm` import root를 찾지 못해 종료했다. 같은 head의 제품 테스트는 `3602 passed, 2 skipped`, native CodeQL·fuzz·SBOM·SAST·Strix는 성공했다. | `.github`의 `opencode-review-dispatch.yml`이 root/`src/`만 허용한 계약 drift를 소유했다. #2123이 `python/` candidates를 추가해 `main`에 병합했고, #2157 follow-up은 동일 로직을 `scripts/ci/resolve_opencode_base_vcs_import_root.sh`로 추출해 `tests/test_opencode_vcs_python_source_root_contract.py` fixture로 증명한다. Issue #2157 종료는 post-`ebc69a401` consumer `coverage-evidence`가 docker step #17을 통과한 job id를 문서에 링크한 뒤에만 한다. | +| CONTROL-PINGORA-DECLARED-BINARY-RUNTIME-01 | **Source repaired on `.github#2386@dea7532e`; protected integration pending** | A base-owned artifact-prefix declaration admitted a no-patch file after any non-UTF-8 byte, even when readable bytes contained `nginx -c /etc/nginx/nginx.conf`. The production-bound regression covers `.sh`, `.dat`, and `.txt`; the focused suite is the exact-head acceptance target. | `.github` owns `scripts/ci/pingora_edge_policy.py`. Replacement-decoded content must contain no `CONTENT_RULES` match before an unrecognized binary suffix is admitted. Current-head hosted security Checks, qualifying independent approval, ordinary protected merge, and downstream `late-life-anxiety-reanalysis#269` revalidation remain required. | + +### 2026-09-27 CodeQL compatibility retirement delta + +| Gap ID | Status | Evidence and remaining gate | +|---|---|---| +| CONTROL-CODEQL-OBSOLETE-VERDICT-01 | Source repair under verification | ContextualWisdomLab/fast-mlsirm#2172 closed before compatibility job 108414341704 began. The live read returned no verdict and enforcement failed. Explicit obsolete output repairs closed/superseded target retirement without weakening exact-head security evidence. See [RCA and regression checks](doctoring/codeql-obsolete-pr-verdict.md); protected merge and hosted current-head gates remain required. | + ## 1. 근거와 범위 ### 1.1 우선순위가 높은 근거 @@ -94,6 +225,7 @@ flowchart LR | G-14 | release/changelog/version 증거가 각 PR에 분산되고 현재 central repo 보호 main의 release candidate가 명확하지 않다 | 운영자는 어떤 기능이 supportable release인지 확인할 수 없다 | merge 후 release readiness ledger, CHANGELOG, semantic version/tag, rollback/operability evidence를 함께 갱신한다 | | G-15 | 첨부파일 처리 경계가 제품별로 다르고, 1MB 상한은 업무 데이터와 맞지 않으며 미지원 MIME/컨테이너가 parser registry에서 명시적으로 pending/quarantine 되는지 확인되지 않았다. 현재 20MB 초과 파일 가능성과 PDF/HWP/HWPX·이미지·압축파일의 parse/sidecar 흐름을 하나의 exact contract로 묶지 못했다 | 큰 업무 첨부를 거부하거나 파싱 실패를 조용히 잃으면 고객의 메일·문서 업무가 중단된다 | naruon/newsdom-api 소유 PR에서 streaming upload, configurable bounded limit above 20MB, MIME sniffing, parser capability registry, quarantine/retry, source-position provenance, and ADR를 추가하고 size/unsupported-type/zip-bomb tests를 required evidence로 만든다 | | G-16 | Required Pingora policy treated a changed documentation PNG screenshot as UTF-8 runtime evidence | Valid UI evidence blocked otherwise valid product PRs before policy evaluation | This branch verifies bounded PNG magic before exemption while runtime paths and malformed assets continue to fail closed; protected-main delivery remains the release gate | +| G-17 | `.github#2279` blocked authenticated GitHub REST redirects in source, but redirect tests invoked `_RejectRedirects` directly and four Strix transport fixtures still patched the removed `urlopen` seam | A future opener-composition regression could forward a bearer token on a 3xx while redirect tests stayed green; Strix error mapping could fail before exercising production | Proposed `57477289ebec5631b0c48f0bc419f336dbe19deb` sends all four synthetic redirect classes through both real module-level openers; `663ffac390d27ab21daa58b91b624d3f00dce7de` moves every Strix fixture to the production opener; `9c19c6e00eafc028068719ab482282c1256f8893` adds malformed-authority coverage and records the owner evidence. Mutation RED proves the default opener contacts a second same-authority URL with the bearer header. The focused suite passes twice (`87 passed` normal and `GITHUB_ACTIONS=true`) with 100% statement/branch coverage on both affected modules. Exact-head hosted security and independent review remain required | ## 4. 열린 PR live inventory @@ -2778,6 +2910,41 @@ prose" convention already stated in `CLAUDE.md`. ## Item 41: CodeQL PR `startup_failure` blocking merges org-wide — dispatch-safe re-admission in progress +**2026-09-12 control-plane update — handler-first bootstrap Proposed.** +Protected `main@691fb78932eff5fbe52db69077848134b0b4e053` still runs the +legacy handler while complete successor #2040 is open at +`6476b919d3febf79cc53e71d6d60f15d7e83ced4` (Draft at the latest live +revalidation). Exact predecessor run `34684228601` +proved the current per-language wake cannot converge: Actions woke the shared +required run, then Python received HTTP 403; subsequent same-tuple handler +runs were cancelled and redispatched, including `34684575249`. This is a +canonical `.github` control-plane defect, not a consumer CodeQL finding. + +The minimum repair is one versioned handler, not a workflow copy. Temporary +`codeql-scan` v1 preserves the protected client title/payload/status contract; +`codeql-scan-v2` requires the source/base/head/SARIF evidence carried by +#2040. Both share one repository/PR concurrency identity and a single +post-matrix `actions:write` settlement. The scan matrix is read-only. v1 is +removed only after the protected v2 producer lands, all v1 attempts terminate, +and caller inventory reaches zero. Current status remains **Proposed**: +bootstrap PR ordinary merge, #2040 non-force restack, and a fresh successful +exact-head required CodeQL run are still required. ADR-0025 and +`docs/doctoring/codeql-versioned-handler-bootstrap-20260912.md` carry the +decision and exact evidence. Settlement credential fallback releases only the +successful `gh api` body; its RED fixture uses a rejected +`{"state":"closed"}` document because a generic error message does not exercise +the consumed-field contamination path. + +The first overlapping successors were each incomplete in a different way: +#2105 required v2-only producer provenance from the still-protected legacy +client, while #2106 initially omitted #2105's nested-rerun schema and +attempt-exhaustion guards. The canonical #2106 integration preserves its +legacy/v2 event bridge and carries forward both valid #2105 guards: only string +schema `"1"` grants nested rerun authority, and the settlement writer stops +before mutation at required-run attempt 48. Status remains **Proposed** until +the integrated exact head passes hosted checks and independent review, lands +on protected `main`, and a fresh #2040 producer canary converges. + **2026-09-04 correction.** The emergency ruleset removal below fixed the old entrypoint, but became stale after `.github#1778` moved `github/codeql-action` into the native `codeql-scan-dispatch.yml` handler. Seven current PR heads then @@ -3010,6 +3177,8 @@ or whether default-setup landed on it (and possibly others) through an unrelated - **`fast-mlsirm`** — false positive. `.github/workflows/codeql.yml` runs two real jobs (`analyze-actions` on every PR, `analyze-python` gated to `workflow_dispatch` only), and **both** `analyze` steps carry `with: upload: never`, with comments stating *"Default setup remains the repository's code-scanning upload owner"* and *"Default setup already owns ordinary Python code-scanning uploads."* Confirmed via a live job log (run `33754939454`, job `100646992008`, `2026-09-04T00:45Z`): `upload: never` present in the action's resolved input dump, `Exported results to SARIF` followed by no upload call, job concluded `success`. Deliberately engineered the opposite way from `contextual-orchestrator`'s fix (default-setup keeps ownership, the local workflow stays silent) rather than the way `contextual-orchestrator` was fixed (local workflow keeps ownership, default-setup disabled) — both are valid resolutions of the same conflict; this repository already had one in place. No fix needed. - **`scopeweave`** — no live conflict, but two dangling artifacts worth a light cleanup. The workflow with real `init`/`analyze` steps (`.github/workflows/codeql.yml`) is `disabled_manually`, so it never runs and cannot collide with default-setup today. A second, unrelated workflow entry — "CodeQL Required," id `335384625`, `.github/workflows/codeql-required.yml` — is registered `state: "active"` in the Actions API, but the file itself no longer exists on the `develop` default branch (`404` on direct content fetch); GitHub retains the workflow-run registration for a file that has since been deleted, so this entry can never actually trigger. Net effect: default-setup is the sole current CodeQL coverage source for this repository, matching item-41's own "zero coverage from any source" criterion at whatever point `codeql.yml` was disabled — not a misclassification, just a repository whose local workflow went inactive after (or independent of) the rollout. Not fixed in this pass: re-enabling the disabled `codeql.yml` would immediately recreate `contextual-orchestrator`'s exact conflict, so any future re-enable of that workflow must add `upload: never` (matching `fast-mlsirm`'s pattern) or disable default-setup first, whichever this repository's owner intends as the coverage source of record. +**2026-10-11 update (ContextualWisdomLab/.github#2607):** the inventory above is historical. Live default setup now reports `state: not-configured`. fast-mlsirm #2073 CodeQL job `114230230294` failed at `2026-10-10T15:36:55Z` because Actions artifact quota blocked diagnostic SARIF retention. The reusable workflow now uploads SARIF directly to code-scanning, verifies a nonempty local SARIF, and avoids Actions diagnostic artifacts. Its current-head live result and base/head configuration continuity remain unverified. + **The remaining 7 repositories** (`newsdom-api`, `keyverse`, `bandscope`, `mightyETL`, `litellm-patched-proxy`, `pg-erd-cloud`, `.github`) all returned `default-setup=not-configured` — no conflict is possible regardless of their local workflow's upload configuration. **Conclusion.** `contextual-orchestrator`'s conflict was an isolated incident, not a symptom of a broader misclassification in item-41's rollout (none of the 3 repositories found here with `default-setup=configured` alongside a local workflow were among that rollout's 23 targets) and not evidence of an org policy silently re-enabling default-setup on repositories that already had real coverage. Two of the three already carry a deliberate, working design for this exact conflict (`if: false` / `upload: never`) that predates or is independent of this audit — worth keeping as the reference pattern if this conflict resurfaces elsewhere, in preference to `contextual-orchestrator`'s "disable default-setup" fix when the local workflow does not yet have established real-coverage precedence. @@ -3237,8 +3406,8 @@ intended contract before rewriting the assertion — left for a dedicated follow ## Items 15/16/17 measurement: `Detect changed scope` gate jobs — 2 of 3 are pure runner overhead — 2026-09-05 -**Status:** Measured, not yet fixed. Recorded so the fix is grounded in real numbers rather than the intuition -this measurement partly refuted. +**Status:** Measured 2026-09-05; `sast-semgrep.yml` fixed 2026-09-13 (below); `strix.yml` deferred. Recorded so +the fix is grounded in real numbers rather than the intuition this measurement partly refuted. **Why measured.** Items 15/16/17 ask to remove needlessly-triggered workflows, consolidate workflow files ("bootup에도 시간이 듦"), and cut redundant steps; the standing complaint is the org's 60-concurrent-job @@ -3353,3 +3522,243 @@ queries the check-runs API at its own time, order-independently. The implementin their change was safe because they had scoped it narrowly, not because they had checked for the name collision — which is the more useful lesson: **a job name is unique only within one workflow file, and the same name in another file can carry the opposite safety property.** + +**Fixed for `sast-semgrep.yml`, 2026-09-13.** The standalone `changed-scope` job is gone; its +"Classify changed paths" step now runs inside the single consumer `semgrep` (after `harden-runner`, +which must audit the classifier's own `gh api` egress) and the four expensive steps plus the final +"Enforce Semgrep gate" step carry `steps.scope.outputs.code == 'true'`. The job keeps +`if: github.event.action != 'closed'` with no `needs.` term, so a doc-only PR's run still executes one +job that concludes `success` -- the load-bearing property from +[`required-workflow-path-filter-boundary.md`](doctoring/required-workflow-path-filter-boundary.md) is +preserved, and neither `Detect changed scope` nor `Semgrep (multi-language SAST)` is among `.github`'s +classic required contexts, so nothing goes Pending there. One trap the first draft would have shipped: +the enforce step's `always() && (... || steps.semgrep.outputs.rc != '0')` evaluates `rc` as the empty +string when `Run Semgrep` is step-skipped, which is `!= '0'` and would have failed every doc-only PR; +the guard on that step is what makes the fold safe. Net: one runner allocation per PR for this +workflow instead of two, org-wide. `strix.yml` (the other single-consumer gate) is deliberately left +alone -- it is a documented multi-PR hot-file collision zone. Contract: +`tests/test_docs_only_pr_runner_admission.py::test_sast_semgrep_folds_the_gate_into_its_single_consumer_at_step_level`, +`tests/test_required_security_runner_image_contract.py`. + +## 2026-09-19 GitHub API production-opener redirect proof + +**Status:** Proposed on `ContextualWisdomLab/.github#2279`; exact-head hosted checks and qualifying independent review remain mandatory. + +**Context Map / owner.** The central `.github` CI bounded context owns the bearer-authenticated CodeQL-analysis and Strix changed-file GitHub REST clients. GitHub remains the upstream REST authority. Product repositories consume only the released central workflow contract; they do not copy either client. + +**Gap.** Initial URL admission and direct `_RejectRedirects.redirect_request()` unit cases did not prove that each module-level production `OpenerDirector` actually retained the no-redirect handler chain. A future opener reconstruction could silently re-enable authenticated redirects while the prior tests stayed green. + +**Action.** Exact `57477289ebec5631b0c48f0bc419f336dbe19deb` adds a dependency-free synthetic-302 transport to `tests/test_github_api_url_boundary.py`. For both actual production openers, the case drives a canonical bearer request through the real HTTPS open/response chain, requires the typed HTTP-302 failure mapping, and proves transport receives exactly one original request; lookalike HTTPS, HTTP, `file:`, and same-authority redirect targets never receive a second request or bearer. Exact `e0b0b4d4fff5b6ea88236a1e91dcd7dbb3be09b5` repairs the doctoring claim so direct-handler coverage is not mislabeled as production-chain proof. + +**Evidence / remaining condition.** The standalone fixture mechanism was executed locally against Python stdlib and produced one canonical request followed by terminal HTTP 302 for every hostile target. This is mechanism evidence, not repository acceptance. Final authority requires focused/full exact-tree GREEN, fresh exact-head Security/SAST/Python Security/CodeQL/runtime-quality checks, no unresolved actionable review, ordinary protected-main integration, and downstream consumer validation. No scanner suppression, redirect allowlist widening, provider fallback, workflow gate weakening, or credential-boundary change is included. + +## 2026-09-27 exact release distribution/scope evidence coverage + +**Status:** Proposed on `ContextualWisdomLab/.github#2400`; the current +architecture-binding repair starts from reviewed parent +`51db1d0c00c2eab36d051b5307541558bbc735c2`. The PR body—not a +self-referential SHA in this file—is the authority for the current exact head. +The PR remains Draft. + +**Context Map / owner.** The central `.github` release-control bounded context +owns same-run distribution/scope artifact verification and the immutable +licence/Strix verdict contract. Product release workflows consume only the +pinned central workflow and helper commits; product repositories do not copy +the verifier source or read central transient state. + +**Gap.** The release prescreener was already complete, but the adjacent +distribution and scope evidence verifiers still had unexecuted fail-closed +paths. At predecessor `27cf2f339393aa08b9f8a26c3a9bd0da47de33c1`, +`verify_release_distribution_set.py` covered 148/200 statements with 20 +partial branches (71%). At predecessor +`eb8130c5573b4bfc59bdc725be5e1466f24c25db`, +`verify_release_scope_evidence_set.py` covered 197/242 statements with 33 +partial branches (77%). The repository-wide mandatory 100% coverage gate was +therefore RED even though the positive release path passed. + +**Action.** Two ordinary, non-force commits add test-only boundary evidence for +duplicate/non-finite/oversized controls, canonical time and digest identity, +unsafe and oversized ZIP members, download failure/termination, build snapshot +inventory and byte binding, runtime wheel identity, consumer native layout, +lock drift, scope envelope/row identity, aggregate size, and both CLI entry +paths. Production release code and workflow admission policy are unchanged. + +A same-PR continuation covers the adjacent release gate's real trust +boundaries: bounded and nonregular archive input, declared Python/Cargo licence +paths, archive links and member counts, raw-capture/destination symlinks, Cargo +workspace identity, Strix fanout identity/fixture/runtime-report binding, and +install-time licence rebinding. `parse_member_listing` and its isolated test +were removed after repository-wide caller search proved that immutable archive +bytes—not the unused shell listing—are the member authority. The redundant +post-read length branch was also removed because both stdlib ZIP and tar readers +already clamp reads to the entry size checked immediately beforehand. + +**Exact-tree evidence / remaining condition.** Distribution focused tests are +15 passed with 200/200 statements and 84/84 branches; scope focused tests are +48 passed with 242/242 statements and 120/120 branches. The warnings-as-errors +full suite is 3,953 passed, 28 skipped, and 40 subtests passed. Against the +pre-repair full-repository run, uncovered statements fell 386→289 and partial +branches 112→59, but the total remains 98%; the 100% gate is still RED. Fresh +exact-head CodeQL PR run `36280393614`, SAST run `36280393599`, and Security +Scan run `36280393621` were queued on that repair head. Adding this baseline +record creates a documentation-only successor with its own fresh runs; their +current IDs and conclusions are tracked in the PR body and must not inherit +the predecessor's status. Qualifying independent approval is absent. Do not +merge, tag, publish, or create an admission manifest until the remaining +production surfaces reach 100%, all required checks are terminal GREEN on one +exact head, and an independent current-head approval exists. + +The continuation's focused release-dependency suite is 442 passed with +`release_dependency_gate.py` at 1,126/1,126 statements and 472/472 branches. +The warnings-as-errors full suite is 3,976 passed and 28 skipped; uncovered +repository statements fell 289→249 and partial branches 59→26, raising the +rounded total to 99% but not satisfying the fail-under-100 gate. The remaining +misses belong to queue health, Noema document review, and the separately owned +Rust materializer work on `ContextualWisdomLab/.github#2360`; no duplicate Rust +repair is introduced here. Current exact-head hosted runs and conclusions remain +PR-body authority after the next ordinary-forward update. + +The queue-health continuation removes a responsibility contradiction rather +than preserving it with tests: `actions_queue_health_core.py` still contained +a second collector and CLI even though the Context Map assigns collection, +identity reconciliation, and process exit to `actions_queue_health.py`. The +duplicate was unreachable after the executable imported the core and replaced +those names. A source-shape RED contract now prevents either entrypoint from +returning to the core; the executable owns its `time.sleep` retry dependency +directly. Boundary cases cover both pre-evidence identity retry outcomes, +malformed active and terminal run IDs, irrelevant terminal conclusions, +obsolete target cancellations, and remediation-action deduplication. The +focused queue-health suite is 80 passed; both queue-health production modules +are 100% statement and branch covered. No workflow permission, API scope, +queue-age threshold, cancellation behavior, or merge policy changes. The +full exact-tree suite is 3,982 passed, 28 skipped, and 40 subtests passed; +uncovered statements fell from 249 to 163 and partial branches from 26 to 19. +The only remaining uncovered production owners are the Noema document reader +successor and Rust materializer `ContextualWisdomLab/.github#2360`. Hosted-run +identity and conclusions remain PR-body authority. + +The Noema document-reader continuation executes the existing fail-closed trust +boundaries without changing production policy: unsupported and oversized +input, bounded DOCX archive and XML structure, empty content, visible Word +controls, ragged and escaped tables, local HWP reader configuration and process +failure, bounded/UTF-8/non-empty adapter output, code-point-safe prompt +truncation, and the smoke-test CLI. The focused suite is 11 passed and 2 +optional real-fixture skips; `noema_review_document.py` is 144/144 statements +and 52/52 branches. The warnings-as-errors full exact-tree suite is 3,988 +passed, 28 skipped, and 40 subtests passed. Repository coverage stays rounded +to 99% because the separately owned Rust materializer on +`ContextualWisdomLab/.github#2360` retains 128 uncovered statements and one +partial branch. That owner boundary is preserved: this PR does not duplicate +the Rust repair. The 100% gate therefore remains RED, the PR remains Draft, +and current hosted-run identity and conclusions remain PR-body authority after +the next ordinary-forward update. + +The coverage successor integrates the canonical Rust materializer owner by an +ordinary two-parent merge rather than copying its source or tests. The owner +branch contributes the full foundation ancestry, deterministic multi-root +`cargo vendor --sync --locked` closure, confinement of synthesized Cargo target +paths to each manifest root, real-Cargo integration contracts, and +toolchain-independent Git/mock/error/CLI coverage. Focused evidence is 26 +passed and 3 real-Cargo skips with +`materialize_base_rust_dependencies.py` at 155/155 statements and 60/60 +branches. The full merged tree is 4,030 passed, 8 skipped, and 40 subtests +passed; all 17,144 production statements and 6,982 branches are covered. This +closes the repository coverage Gap but is not merge authorization: the release +stack remains Draft/Proposed until fresh exact-head hosted Checks reach terminal +success and a qualifying independent review approves the unchanged head. + +The subsequent native-inspection continuation exposed a new exact-tree +coverage Gap rather than inheriting predecessor evidence. Runtime wheels and +build-interpreter snapshots now pass every admitted native member through the +pinned `llvm-readobj-18` boundary, but the first full run on that source left +six prescreener statements/four partial branches and one release-gate +statement/one partial branch uncovered. The RED suite still passed 4,033 tests, +8 skips, and 40 subtests, while `coverage report --fail-under=100` correctly +failed at 99%. The repair adds fail-closed cases for directory members, +analyzer reuse/failure, oversized native files, receipt omissions, unknown +runtime dynamic links, and malformed static-link records. The exact repaired +tree is 4,037 passed, 8 skipped, and 40 subtests passed with all 17,186 +production statements and 7,000 branches covered. Context Map ownership stays +in the central release-control gate; consumer repositories receive only its +immutable released workflow contract. Status remains Proposed/Draft and release +admission remains HOLD until fresh exact-head hosted Checks and a qualifying +independent approval complete. + +The next ordinary integration closes a distinct native-link review Gap. The +pinned analyzer previously proved which dynamic libraries each wheel needed, +but the sealed report did not bind why those external names were admissible on +the declared Linux, macOS, or Windows target. The central release-control +bounded context remains the single owner: it now classifies only explicit +operating-system runtimes, the wheel-tag-matched CPython DLL, the inspected +extension's own macOS install name, and the named Visual C++ runtimes. Unknown +names fail before verdict sealing, while every accepted name and review basis +is carried in `cwl.release-native-links/2`; consumers receive only the released +workflow contract. The native-link continuation and the coverage repair were +combined by an ordinary two-parent merge, preserving both histories without a +force update. The concurrent Maturin asset verifier initially reproduced a 99% +coverage failure with 22 missing statements and 10 partial branches; its +bounded-download, archive-shape, executable-identity, reviewed-link, CLI, and +prescreen failure paths are now executable contracts. Fresh current-tree +evidence is 4,049 passed, 8 skipped, and 40 subtests passed, with all 17,302 +production statements and 7,058 branches covered. Ruff E9/F/I, compileall, and +diff checks pass after import-order repair. Status is Proposed/Draft and +release admission remains HOLD because hosted exact-head Checks and a +qualifying independent approval are not yet complete. + +The Intel macOS continuation closes one part of the universal2 runtime Gap. +Three additional same-run artifacts contain x86_64 install receipts and exact +dependency wheel archives. The central verifier authenticates each ZIP, +source SHA, selected distribution row, x86_64 interpreter, and archive member; +the licence prescreen includes distinct x86_64 archive bytes in the Strix +fixture matrix, and the final verdict seals their artifact IDs and digests. +The thirteen publishable distributions remain the only release outputs. +The changed verifier, prescreen, and verdict collector have 100% statement +and branch coverage in the focused suite; the full local suite is 4,063 passed, +4 skipped, and 40 subtests passed. The fast-mlsirm admission consumer has not +yet accepted this verdict shape, and hosted exact-head checks are still +required. Release remains HOLD. + +An architecture-binding review then found that the Intel receipt's +`machine=x86_64` claim did not reach the bytes of native dependency wheels. +The common universal2 inspector deliberately permits an architecture subset, +but the prescreener discarded that subset and deduplicated package/hash pairs +before applying any Intel-specific constraint. An aarch64-only Mach-O wheel +could therefore satisfy the Intel continuation. A RED integration contract at +parent `51db1d0c00c2eab36d051b5307541558bbc735c2` reproduces that acceptance. +The repair requires x86_64 in every native member of each Intel variant before +deduplication; universal2 binaries containing both architectures remain valid, +and pure-Python wheels are unchanged. Local exact-tree evidence and hosted +current-head run identities remain PR-body authority. The local exact tree is +4,061 passed, 8 skipped, and 40 subtests passed, with all 17,383 production +statements and 7,098 branches covered. Status stays Proposed/Draft and release +admission remains HOLD pending terminal GREEN hosted Checks, downstream +verdict-shape acceptance, and qualifying independent approval. + +## 2026-09-27 Strix AnyIO security-lock carryover + +**Status:** Proposed on `ContextualWisdomLab/.github#2386`; fresh exact-head hosted Checks and qualifying independent approval remain mandatory. + +**Context Map / owner.** The central `.github` security/review bounded context owns the hash-locked Strix CI runtime. PyPI packages and the vulnerability advisory service are upstream evidence; product repositories consume only the released central workflow contract. + +**Gap / RCA.** Exact-head Python Security run [36236245577](https://github.com/ContextualWisdomLab/.github/actions/runs/36236245577), job `108402877544`, found AnyIO `4.14.0` vulnerable to `CVE-2026-63374`, `CVE-2026-64847`, and `CVE-2026-63349`; all three list `4.14.2` as fixed. The generated lock had no explicit AnyIO source constraint, so unrelated PR #2386 inherited a known-vulnerable transitive selection. + +**RED → GREEN / carryover.** RED `761be5b0f63422505b37e28a367a4c5170f302ba` imports #2385's source↔lock contract and fails `1 failed, 1 passed` because the source input lacks `anyio==4.14.2`. GREEN `c59ef9aed32ab4c5138c2b7770ddcc10d7ee8393` adds that exact source constraint; `a895dc5aec775076c3819679eadf0b50a563aa2e` adopts #2385's generated lock blob `eb83beda177c9d2e4ca9b7e2888a1ccb55a123ac`, whose only predecessor differences are version line 143 and hash lines 144–145. Exact remote blobs pass the focused contract `2 passed`. This is complete three-file delta integration, not a claim that #2385 or #2386 is accepted. Completion still requires fresh exact-head pip-audit/other required Checks, no unresolved actionable review, qualifying independent approval, and ordinary protected-main integration. +## 2026-09-27 Git blob protocol-hash SAST authority + +**Status:** Proposed on `ContextualWisdomLab/.github#2396`; fresh exact-head hosted Checks and qualifying independent approval remain mandatory. + +**Context Map / owner.** The central `.github` Pingora policy owns exact-head changed-file evidence admission. GitHub's Git blob API remains the upstream object-identity authority; Semgrep remains the independent static-analysis gate. + +**Gap / RCA.** Exact-head SAST run [36243375994](https://github.com/ContextualWisdomLab/.github/actions/runs/36243375994), job `108407968534`, reported `python.lang.security.insecure-hash-algorithms.insecure-hash-algorithm-sha1` at `scripts/ci/pingora_edge_policy.py:602`. The call recomputes Git's protocol-defined `blob \\0` object ID with `usedforsecurity=False`; it is equality evidence for the exact GitHub blob, not a cryptographic signature. Replacing it with SHA-256 would contradict the upstream 40-hex blob identifier and remove tamper detection. + +**Action / evidence.** RED is the exact hosted failure above. Commit `53f447f73f0ef33eb708bf44202ec4d5954ade66`, formatted by `d00cdff974f5ac665a5f7481620d550735bd26c8`, adds one rule-scoped `nosemgrep` annotation plus the protocol rationale without changing the hash input, comparison, download bound, or failure behavior. Existing executable cases still require exact byte count and reject altered bytes by Git blob-ID mismatch. Completion requires fresh exact-head SAST GREEN, the remaining protected checks, no unresolved actionable review thread, qualifying independent approval, and ordinary merge. + +## 2026-09-27 CodeQL terminal-proof fallback run identity + +**Status:** Proposed on `ContextualWisdomLab/.github#2405`; direct repair parent `5a77a8c711bc93330c24a4821dff7439f600a264`, tree `5ce8ba7448cb878a5b130ed1acaba1578e4940fd`. This documentation-only successor preserves that executable tree; the PR body is the authority for the current exact head and hosted-run IDs. Merge and required-workflow admission remain HOLD. + +**Context Map / owner.** The central `.github` CodeQL required-workflow and dispatch bounded context owns dispatch identity, terminal evidence, and exact job recovery. Product repositories consume the protected workflow contract; they do not copy the producer or manufacture success receipts. + +**Gap / failure scene.** The v2 handler names a run with `head/base/required-run/producer-source`, but its required-workflow fallback looked up only `head/base/required-run`. When authenticated status publication is unavailable, a completed clean handler job could not be found and a rerun ended false RED. Omitting the producer source would also allow a regenerated live merge revision to reuse predecessor evidence. + +**Action / evidence.** Correct the fallback lookup to include the live merge source and retain fail-closed base, head, required-run, workflow-path, job-name, GHAS-identity, and SARIF checks. The test-first repair reproduced two failures, then passed 96 focused workflow-contract tests; the new edge case rejects a stale merge-source title. Ruff E9/F/I on the changed dispatch-contract file and `git diff --check` pass. Fresh hosted Checks and a qualifying independent approval are still required on the unchanged executable delta before merge. diff --git a/docs/repository-readme-quality-standard.md b/docs/repository-readme-quality-standard.md new file mode 100644 index 0000000000..e663ef8d94 --- /dev/null +++ b/docs/repository-readme-quality-standard.md @@ -0,0 +1,249 @@ +# ContextualWisdomLab repository README quality standard + +## Purpose + +Every ContextualWisdomLab repository owns its own product README because the README must be reviewed against that repository's actual code, PRD, ADRs, release state, security boundary, and license provenance. The organization control plane may define a reusable quality standard, but it must not manufacture product claims or overwrite repository-specific language. + +This document is the shared review pattern for repository landing pages. It combines a **quality contract with an adaptable authoring template**, not automatically generated product claims. A good README should feel consistent across the organization while still making the product's bounded context, terminology, operating reality, and obligations obvious. + +## Reusable authoring scaffold + +Start from [the repository README template](templates/repository-readme-template.md), +then replace its placeholders with evidence from the owning repository. The shared +structure is reusable; product claims, commands, output, license grants and support +routes are not interchangeable. Remove irrelevant sections and never publish raw +placeholders. The template grants no runtime, release or integration authority. + +## Reader jobs + +A root README should help four readers reach a safe next action quickly: + +1. **Prospective user or buyer** — understand what problem the product solves, what it does today, and what it deliberately does not claim. +2. **Integrator** — understand how to install or consume it, the public integration boundary, and which neighboring product owns adjacent authority. +3. **Maintainer** — find the verification, architecture, contribution, security, and release evidence without exposing internal automation procedure as customer copy. +4. **Diligence reviewer** — distinguish source metadata from released artifacts, first-party licensing from dependency licensing, and implemented capability from roadmap or active-PR evidence. + +The first screen should answer "what is this, why would I use it, and what can I do next?" before explaining implementation internals. + +## Recommended information architecture + +Use the sections that are relevant to the repository. Do not add empty headings merely for visual consistency. + +### 1. Product name and one-line promise + +Start with the exact repository/product casing and a one- or two-sentence value proposition written in domain language. Prefer the user outcome over an internal technology inventory. + +Good: + +> Generate browsable static directory indexes without running a dynamic listing service. + +Weak: + +> Kotlin 1.3 CLI using Clikt, Gradle, and JaCoCo. + +Technology belongs later unless the technology itself is the product. + +### 2. Product boundary and non-goals + +State what the repository owns and, where confusion is likely, what it does not own. Keep adjacent ContextualWisdomLab products behind explicit integration boundaries rather than making a leaf repository sound like the whole platform. + +Useful boundary language includes: + +- source system remains authoritative; +- this library computes X but does not decide Y; +- this adapter consumes a released/versioned contract but does not own the foreign product's database; +- this documentation/source version is not release or deployment evidence. + +Do not expose private table names, secret names, internal incident procedures, raw infrastructure topology, or maintainer-only automation unless a customer genuinely needs them to use the product safely. + +### 3. Install or quick start + +Provide the shortest **truthful, code-current** path to a useful result. Verify every command against current package metadata, lockfiles, Makefiles, build files, Compose files, CLI help, or tests. + +Rules: + +- do not advertise a package registry installation when only source checkout is supported; +- do not claim a hosted service exists because local Compose exists; +- do not use private sibling checkouts as a public installation contract; +- include real runtime prerequisites and version floors when they are enforced; +- keep irreversible or privileged actions out of the default quick start unless the product inherently requires them and the safety boundary is explicit. + +If the repository is architecture-only or pre-runtime, say so instead of inventing an installation section. Give the reader the correct next action, such as reading the contract or running repository validation. + +### Quick-start execution evidence + +A command found in a manifest is source evidence, not proof that the complete +onboarding journey ran. Record these fields in the PR or doctoring and keep only +the useful instructions in the README: + +| Evidence field | Required distinction | +| --- | --- | +| Working directory | Released installation, source worktree, or design-only checkout; name the actual command root. | +| Runtime and lock | Tested toolchain and exact dependency/lock identity, separate from merely declared support. | +| Observed result | Exit status, actual output or interface, and the next useful action; do not manufacture example success. | +| Side effects | Network listener, external calls, credentials, filesystem/database writes and costs. | +| Stop and recovery | How to stop the process, retain user data and undo or recover safely. | +| Not executed | State the unavailable capability and the missing execution evidence explicitly. | + +Prefer local-only evaluation defaults. A README-only workaround is insufficient +when the advertised default script violates its own privacy or safety contract; +repair the owning configuration and add a regression. Preserve existing tests. +Check generated README sources as well as generated output, and validate links +against the proposed tree and the real publishing root. Placeholder scaffold +checks do not prove an individual product's commands, claims or rights. + +### 4. Common usage or public API + +Show the stable user/integrator surface, not a tour of internal modules. Prefer one representative example plus links to complete reference material. + +For libraries, name exported/public symbols and their responsibility. For services, name supported public endpoints only when they are current code truth. For CLI products, show the primary task-oriented commands and keep exhaustive flags in generated help/reference docs. + +### 5. Architecture and integration context + +Explain enough architecture to prevent misuse: + +- core responsibility; +- main data/evidence flow; +- authority boundaries; +- optional versus required integrations; +- local versus external processing where relevant; +- security or privacy boundary that changes how a user should operate the product. + +Link to ADRs, PRD/TRD, architecture diagrams, API schemas, or operator runbooks for detail. The README should navigate to technical authority rather than duplicate it until the two inevitably drift. + +### 6. Status and quality signals + +Status claims must be exact and durable. + +Prefer: + +- "package metadata declares version 0.5-9"; +- "this is a pre-release architecture foundation"; +- "the repository contains CI/SAST/security workflows; inspect the exact revision's results". + +Avoid: + +- treating a package version as proof of a published release; +- copying mutable PR head SHAs or run IDs into the root README; +- badges for workflows that do not exist or no longer represent the protected branch; +- unsupported benchmark, customer, certification, adoption, availability, or production-readiness claims. + +Mutable exact-head integration evidence belongs in PR descriptions, gap ledgers, or generated evidence—not evergreen customer copy. + +### 7. Documentation map and support + +Give readers a compact map to the canonical sources that actually exist. Typical links include: + +- documentation home; +- architecture / ADR index; +- API or schema reference; +- security policy / private vulnerability reporting; +- contribution guidance; +- changelog and releases; +- advanced operator reference when detailed runbooks were intentionally moved out of the README. + +Do not link to planned files or Pages sites that are not published. A `docs/index.md` source is not proof that GitHub Pages is live. + +### 8. Contribution guidance + +State the smallest useful contributor contract: how to verify changes, what public boundary must remain stable, and where deeper maintainer instructions live. Avoid turning the customer README into an hourly-agent or PR-automation manual. + +### 9. License and commercial-use boundary + +A README license section is evidence-backed, not ceremonial. Before writing it, perform repository-level due diligence. + +Distinguish all of the following: + +1. **license of repository-authored source/documentation**; +2. **inherited/copied/derived source obligations**; +3. **third-party runtime/build/test dependency licenses**; +4. **vendored assets, models, datasets, fonts, standards-derived material, container bases, or binaries**; +5. **external service/provider terms**. + +Do not say "MIT" or "Apache-2.0" merely because the organization prefers those licenses. Preserve valid existing MIT/Apache lineage. If the repository is wholly ContextualWisdomLab-authored and provenance establishes the necessary rights, add the appropriate permissive root license and matching package metadata in the authoritative PR. + +If rights are inherited or uncertain, do not silently override them. Examples: + +- a package whose metadata declares GPL and names upstream/external copyright holders remains GPL unless sufficient rights for relicensing are established; +- a repository MIT license does not relicense an LGPL/GPL dependency; +- a dependency's permissive license does not grant a license to otherwise unlicensed repository source; +- absence of a root `LICENSE` is a diligence prompt, not a reason to omit the question. + +Under current ContextualWisdomLab commercial-intake policy, GPL/LGPL/AGPL-family source or dependencies are not an approved default inbound baseline. GPL can permit commercial use under its terms; the policy issue is copyleft/distribution compatibility, not a claim that GPL is a noncommercial license. Record the exact component and obligation, then remove/replace it when safe or preserve a precise provenance blocker and closure evidence. + +## README anti-patterns + +Treat these as review findings when they materially reduce clarity or accuracy: + +- implementation inventory before product value; +- internal PR/agent instructions in customer copy; +- mutable check/PR status persisted as evergreen product truth; +- stale personal/upstream installation URLs after repository ownership changes; +- giant runbooks that bury the supported public workflow; +- unverified badges, customer logos, certifications, benchmarks, or release claims; +- copy-pasted architecture that contradicts the current PRD/ADR/code; +- claiming one service owns data or authorization that actually belongs to another bounded context; +- saying "commercial friendly" while a known GPL-family inbound blocker remains; +- adding a new permissive LICENSE solely because none existed, without provenance review; +- hiding third-party license obligations behind the repository's first-party license; +- creating a duplicate README PR when an existing writable product/documentation branch already owns the file. + +When a README has valuable but overly detailed operator content, prefer moving that content intact to a durable advanced/reference document and linking to it from a concise landing page rather than deleting knowledge. + +## Evidence checklist before editing + +Read the smallest authoritative set necessary to verify claims. Depending on repository type, inspect: + +- current protected/default branch and open README/documentation/license PRs; +- root README and documentation index; +- PRD/TRD/product-planning documents; +- accepted ADRs and architecture docs; +- package/build metadata (`pyproject.toml`, `package.json`, `Cargo.toml`, `DESCRIPTION`, Gradle files, etc.); +- lockfiles and dependency manifests; +- CLI/API/source symbols used by quick-start examples; +- CI/workflow commands used for verification; +- root LICENSE, NOTICE, THIRD_PARTY_NOTICES, file headers and package license metadata; +- git/upstream/fork provenance when ownership or relicensing is not obvious; +- vendored/copied/generated assets and submodules; +- live GitHub Releases/Pages state only when the README makes a release/publication claim. + +Do not use dependency licenses as a shortcut for source-license analysis. + +## Integration loop + +README delivery is complete only after ordinary protected integration and +verification of the integrated content. A blocker leaves the work incomplete; +retain its exact evidence and ownership, repair another safe lane, and revisit it. +An unmerged successor, saved patch, or issue description is not delivery. + +1. Search for overlapping README/documentation/license PRs before creating a new lane. +2. Update the most authoritative writable existing lane when coherent with its scope. +3. Make the smallest safe concrete improvement in the same run that confirms the defect. +4. Re-read current reviews and inline threads. +5. Inspect exact-head workflow/check results; predecessor evidence never transfers after a push. +6. Root-cause repository-owned failures and fix them rather than documenting around them. +7. When the root cause is central, move to the owning control-plane/library repository rather than adding a leaf workaround. +8. Merge through the normal protected path as soon as the unchanged exact head satisfies all applicable checks, review/thread requirements, mergeability, licensing/provenance gates, and current governance. +9. If one PR is waiting, continue another safe README/documentation lane; waiting is not completion. +10. After merge, continue to the next highest-leverage repository. + +Before retiring an overlapping or stale PR, prove every valid delta is retained +in the canonical successor's source, requirements, tests and licensing notices. +A title match, successful test in isolation, or shared ancestry is not proof of +complete content/behavior carryover. Keep unresolved predecessors alive. + +Do not bypass substantive failing tests, unresolved security findings, meaningful review objections, conflicts, required governance, or genuine provenance blockers. + +## Quality bar + +A README is good enough when a new reader can answer, without reading source code first: + +- What problem does this product solve? +- What is the repository responsible for—and not responsible for? +- What can I safely run or integrate today? +- Where is the deeper technical authority? +- What evidence should I use to judge current quality/release state? +- How do I report a security problem or contribute? +- What license does the repository actually grant, and what important third-party/provenance limits remain? + +Consistency across ContextualWisdomLab comes from answering those questions with the same evidence discipline, not from making every README sound identical. diff --git a/docs/sbom/inventory.json b/docs/sbom/inventory.json index 4e6441ad46..2b522614ab 100644 --- a/docs/sbom/inventory.json +++ b/docs/sbom/inventory.json @@ -1,12 +1,46805 @@ { "schema": "cwl-sbom-inventory/v1", "summary": { - "repo_count": 0, - "component_count": 0, - "flagged_count": 0, + "repo_count": 76, + "component_count": 6967, + "flagged_count": 729, "policy": "commercial-license-only" }, - "license_totals": {}, - "flagged_licenses": [], - "repos": [] + "license_totals": { + "(Apache-2.0 OR MIT)": 2, + "(Apache-2.0 OR MIT) AND BSD-3-Clause": 1, + "(MIT OR Apache-2.0) AND NCSA": 1, + "(MIT OR Apache-2.0) AND Unicode-3.0": 9, + "(MIT OR CC0-1.0)": 1, + "(MIT OR GPL-3.0-or-later)": 1, + "0BSD": 8, + "0BSD AND BSD-2-Clause AND BSD-3-Clause AND BSD-4-Clause AND LicenseRef-scancode-python-cwi AND LicenseRef-scancode-secret-labs-2011 AND LicenseRef-scancode-unicode AND MIT AND Python-2.0": 1, + "0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0": 5, + "0BSD AND BSD-3-Clause AND MIT AND Python-2.0": 2, + "0BSD OR MIT OR Apache-2.0": 2, + "AGPL-3.0 AND AGPL-3.0-only AND AGPL-3.0-or-later": 1, + "Apache-2.0": 368, + "Apache-2.0 AND BSD-2-Clause": 21, + "Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND CC0-1.0 AND MIT AND OFL-1.1": 1, + "Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND LicenseRef-scancode-public-domain AND MIT": 1, + "Apache-2.0 AND BSD-3-Clause AND MIT AND Zlib": 2, + "Apache-2.0 AND BSD-3-Clause AND MPL-2.0": 5, + "Apache-2.0 AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later": 1, + "Apache-2.0 AND GPL-1.0-or-later AND MIT": 2, + "Apache-2.0 AND ISC": 6, + "Apache-2.0 AND LGPL-3.0-or-later": 3, + "Apache-2.0 AND LGPL-3.0-or-later AND MIT": 1, + "Apache-2.0 AND MIT": 24, + "Apache-2.0 AND MIT AND MPL-2.0": 1, + "Apache-2.0 AND Python-2.0": 6, + "Apache-2.0 OR BSD-2-Clause": 7, + "Apache-2.0 OR BSD-3-Clause": 14, + "Apache-2.0 OR BSD-3-Clause OR (Apache-2.0 AND BSD-3-Clause)": 1, + "Apache-2.0 OR BSL-1.0": 3, + "Apache-2.0 OR ISC OR MIT": 4, + "Apache-2.0 OR MIT": 144, + "Apache-2.0 WITH LLVM-exception": 6, + "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT": 20, + "BSD-2-Clause": 72, + "BSD-2-Clause AND BSD-2-Clause-Views": 2, + "BSD-2-Clause AND BSD-3-Clause": 63, + "BSD-2-Clause AND BSD-3-Clause AND CC-PDDC AND GPL-1.0-or-later AND GPL-3.0-only AND GPL-3.0-or-later AND LicenseRef-scancode-other-copyleft AND LicenseRef-scancode-public-domain": 1, + "BSD-2-Clause AND BSD-3-Clause AND ISC AND MIT": 1, + "BSD-2-Clause AND BSD-3-Clause AND MIT": 5, + "BSD-2-Clause AND BSD-3-Clause AND Python-2.0 AND Ruby": 1, + "BSD-2-Clause AND JSON": 1, + "BSD-2-Clause AND MIT AND Python-2.0 AND Python-2.0.1": 2, + "BSD-2-Clause OR Apache-2.0 OR MIT": 8, + "BSD-3-Clause": 224, + "BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0": 3, + "BSD-3-Clause AND GPL-1.0-or-later": 3, + "BSD-3-Clause AND LicenseRef-scancode-protobuf": 5, + "BSD-3-Clause AND MIT": 4, + "BSD-3-Clause AND Python-2.0": 1, + "BSD-3-Clause OR Apache-2.0": 5, + "BSD-3-Clause OR GPL-2.0-only": 1, + "BSD-3-Clause OR MIT OR Apache-2.0": 4, + "BlueOak-1.0.0": 42, + "CC-BY-3.0": 1, + "CC-BY-4.0": 5, + "CC0-1.0": 6, + "CC0-1.0 AND MIT": 6, + "CC0-1.0 AND Unlicense": 1, + "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception": 1, + "CC0-1.0 OR MIT-0 OR Apache-2.0": 3, + "CDLA-Permissive-2.0": 2, + "CNRI-Python AND Apache-2.0": 6, + "EPL-2.0": 5, + "EPL-2.0 OR (Apache-2.0 AND EPL-2.0)": 2, + "GPL-2.0-only AND GPL-2.0-or-later": 1, + "GPL-3.0 AND GPL-3.0-only AND GPL-3.0-or-later": 1, + "GPL-3.0-or-later": 6, + "ISC": 175, + "ISC AND MIT": 10, + "ISC AND MPL-2.0": 1, + "JSON AND MIT": 1, + "LGPL-2.1-or-later": 2, + "LGPL-3.0 AND LGPL-3.0-only": 5, + "LGPL-3.0 AND LGPL-3.0-only AND LGPL-3.0-or-later": 2, + "LGPL-3.0-only": 2, + "LGPL-3.0-or-later": 10, + "LicenseRef-NVIDIA-Proprietary": 7, + "LicenseRef-bad-apache-2.0mit": 3, + "LicenseRef-bad-bsd-2-clause-and-apache-2.0-and-llvm-exception-and-bsd-2-clause": 1, + "LicenseRef-bad-bsd-3-clausemit": 1, + "LicenseRef-bad-non-standard": 2, + "LicenseRef-github-OTHER": 1, + "LicenseRef-scancode-public-domain AND Unlicense": 1, + "LicenseRef-scancode-unicode AND MIT": 3, + "LicenseRef-scancode-unknown": 4, + "MIT": 3722, + "MIT AND Apache-2.0": 1, + "MIT AND BSD-3-Clause": 1, + "MIT AND MIT-0": 1, + "MIT AND MPL-2.0": 9, + "MIT AND PSF-2.0": 5, + "MIT AND Python-2.0": 13, + "MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause AND MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause": 2, + "MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause": 1, + "MIT AND ZPL-2.1": 1, + "MIT AND Zlib": 1, + "MIT OR (Apache-2.0 AND MIT)": 1, + "MIT OR (CC0-1.0 AND MIT)": 1, + "MIT OR Apache-2.0": 963, + "MIT OR Apache-2.0 OR LGPL-2.1-or-later": 8, + "MIT OR Apache-2.0 OR Zlib": 10, + "MIT OR Zlib OR Apache-2.0": 2, + "MIT-0": 19, + "MIT-CMU": 6, + "MPL-2.0": 133, + "MPL-2.0 AND MPL-1.1": 2, + "NOASSERTION": 495, + "OFL-1.1": 2, + "PSF-2.0": 11, + "Python-2.0": 3, + "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD": 18, + "Python-2.0 AND LGPL-2.1-or-later": 1, + "Python-2.0 AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD": 1, + "Unicode-3.0": 54, + "Unlicense": 2, + "Unlicense AND Unlicense AND GPL-3.0-or-later AND MPL-2.0 AND MIT AND BSD-3-Clause AND Apache-2.0 AND MIT AND GPL-2.0-or-later AND BSD-3-Clause AND MIT AND LGPL-2.1-only AND BSD-2-Clause AND GPL-2.0-or-later": 1, + "Unlicense OR MIT": 26, + "Zlib": 5, + "Zlib OR Apache-2.0 OR MIT": 46 + }, + "flagged_licenses": [ + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/attest", + "version": "v4.1.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/checkout", + "version": "11d5960a326750d5838078e36cf38b85af677262", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/checkout", + "version": "631c942040754b6e095e929c1677c07e10ed4f87", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/checkout", + "version": "de0fac2e4500dabe0009e67214ff5f5447ce83dd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/checkout", + "version": "fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/checkout", + "version": "v7.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/checkout", + "version": "v7.0.1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/create-github-app-token", + "version": "v3.2.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/dependency-review-action", + "version": "v5.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/download-artifact", + "version": "v8.0.1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/setup-node", + "version": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/setup-node", + "version": "49933ea5288caeca8642d1e84afbd3f7d6820020", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/setup-python", + "version": "ece7cb06caefa5fff74198d8649806c4678c61a1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/setup-python", + "version": "v7.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/upload-artifact", + "version": "b7c566a772e6b6bfb58ed0dc250532a479d7789f", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/upload-artifact", + "version": "v6.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/upload-artifact", + "version": "v7.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "actions/upload-artifact", + "version": "v7.0.1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "aiohappyeyeballs", + "version": "2.7.1", + "license": "0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "anchore/sbom-action", + "version": "v0.24.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "aquasecurity/trivy-action", + "version": "v0.36.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "astral-sh/setup-uv", + "version": "37802adc94f370d6bfd71619e3f0bf239e1f3b78", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "astral-sh/setup-uv", + "version": "bec219d24cd3e171d82865faccec33120bb574f4", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "astral-sh/setup-uv", + "version": "d0cc045d04ccac9d8b7881df0226f9e82c39688e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "caido-server-auth", + "version": "0.1.2", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "cloudflare/wrangler-action", + "version": "v4.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "contextualwisdomlab-opencode-codegraph-tooling", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "ContextualWisdomLab/.github/.github/workflows/release-dependency-license-strix-gate.yml", + "version": "e249236e3f420c4e9c470f26925640dae59f0d93", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "coverage-cargo-fixtures", + "version": "0.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "cvss", + "version": "3.6", + "license": "LGPL-3.0 AND LGPL-3.0-only AND LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "dtolnay/rust-toolchain", + "version": "4be7066ada62dd38de10e7b70166bc74ed198c30", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "github/codeql-action", + "version": "v4.37.9", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "github/codeql-action/analyze", + "version": "ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "github/codeql-action/init", + "version": "ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "google/clusterfuzzlite/actions/build_fuzzers", + "version": "884713a6c30a92e5e8544c39945cd7cb630abcd1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "google/clusterfuzzlite/actions/run_fuzzers", + "version": "884713a6c30a92e5e8544c39945cd7cb630abcd1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "google/osv-scanner-action", + "version": "v2.3.8", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "google/osv-scanner-action", + "version": "v2.5.1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "grpcio", + "version": "1.81.1", + "license": "Apache-2.0 AND BSD-3-Clause AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "grpcio-status", + "version": "1.81.1", + "license": "Apache-2.0 AND BSD-3-Clause AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "hypothesis", + "version": "6.168.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "interrogate", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "jszip", + "version": "3.10.2", + "license": "(MIT OR GPL-3.0-or-later)" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "noema-document-reader-runtime", + "version": "1.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "ossf/scorecard-action", + "version": "v2.4.3", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "PyO3/maturin-action", + "version": "e83996d129638aa358a18fbd1dfb82f0b0fb5d3b", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "pypa/gh-action-pypi-publish", + "version": "dc37677b2e1c63e2034f94d8a5b11f265b73ba33", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "pytest", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "pytest-cov", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "r-lib/actions", + "version": "6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "step-security/harden-runner", + "version": "bf7454d06d71f1098171f2acdf0cd4708d7b5920", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "step-security/harden-runner", + "version": "v2.13.2", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "step-security/harden-runner", + "version": "v2.20.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "step-security/harden-runner", + "version": "v2.20.1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "tqdm", + "version": "4.68.3", + "license": "MIT AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/.github", + "name": "typing-extensions", + "version": "4.15.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/accounting-information-platform", + "name": "accounting-information-platform", + "version": "0.1.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/accounting-information-platform", + "name": "actions/attest", + "version": "508db95dd578ae2727ebd6217d5ba78e4fbda05d", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/accounting-information-platform", + "name": "actions/checkout", + "version": "631c942040754b6e095e929c1677c07e10ed4f87", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/accounting-information-platform", + "name": "actions/download-artifact", + "version": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/accounting-information-platform", + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/accounting-information-platform", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/accounting-information-platform", + "name": "aquasecurity/trivy-action", + "version": "a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/accounting-information-platform", + "name": "psycopg", + "version": "3.3.4", + "license": "LGPL-3.0 AND LGPL-3.0-only" + }, + { + "repo": "ContextualWisdomLab/accounting-information-platform", + "name": "psycopg-binary", + "version": "3.3.4", + "license": "GPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/aFIPC", + "name": "actions/checkout", + "version": "de0fac2e4500dabe0009e67214ff5f5447ce83dd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/aFIPC", + "name": "r-lib/actions/check-r-package", + "version": "6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/aFIPC", + "name": "r-lib/actions/setup-r", + "version": "d3c5be51b12e724e68f33216ca3c148b66d5f0b6", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/aFIPC", + "name": "r-lib/actions/setup-r-dependencies", + "version": "d3c5be51b12e724e68f33216ca3c148b66d5f0b6", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/aFIPC", + "name": "step-security/harden-runner", + "version": "bf7454d06d71f1098171f2acdf0cd4708d7b5920", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "actions/create-github-app-token", + "version": "bcd2ba49218906704ab6c1aa796996da409d3eb1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "actions/download-artifact", + "version": "37930b1c2abaa49bbe596cd826c3c89aef350131", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "anomalyco/opencode/github", + "version": "77fc88c8ade8e5a620ebbe1197f3a572d29ae91a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "aquasecurity/trivy-action", + "version": "ed142fd0673e97e23eac54620cfb913e5ce36c25", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "backports-tarfile", + "version": "1.2.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "chardet", + "version": "5.2.0", + "license": "LGPL-2.1-or-later" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "docutils", + "version": "0.23", + "license": "BSD-2-Clause AND BSD-3-Clause AND CC-PDDC AND GPL-1.0-or-later AND GPL-3.0-only AND GPL-3.0-or-later AND LicenseRef-scancode-other-copyleft AND LicenseRef-scancode-public-domain" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "fqdn", + "version": "1.5.1", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "github/codeql-action/upload-sarif", + "version": "5595ccaf912efad79be6eef63a5619ff05969be3", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "lark", + "version": "1.3.1", + "license": "MIT AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "lxml", + "version": "6.1.1", + "license": "BSD-3-Clause AND GPL-1.0-or-later" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "pypa/gh-action-pypi-publish", + "version": "dc37677b2e1c63e2034f94d8a5b11f265b73ba33", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "rfc3987-syntax", + "version": "1.1.0", + "license": "Apache-2.0 AND GPL-1.0-or-later AND MIT" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "setuptools", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "@bandscope/desktop", + "version": "0.1.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "@bandscope/shared-types", + "version": "0.1.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "@tauri-apps/api", + "version": "^2.11.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "@tauri-apps/cli", + "version": "^2.11.4", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "actions/download-artifact", + "version": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "actions/setup-node", + "version": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "actions/setup-python", + "version": "a309ff8b426b58ec0e2a45f0f869d46889d02405", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "anchore/sbom-action", + "version": "e22c389904149dbc22b58101806040fa8d37a610", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "aquasecurity/trivy-action", + "version": "ed142fd0673e97e23eac54620cfb913e5ce36c25", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "astral-sh/setup-uv", + "version": "11f9893b081a58869d3b5fccaea48c9e9e46f990", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "bandscope-analysis", + "version": "0.1.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "bandscope-workspace", + "version": "0.1.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "certifi", + "version": "2026.2.25", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "cssparser", + "version": "0.36.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "cssparser-macros", + "version": "0.6.1", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "dtoa-short", + "version": "0.3.5", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "eslint", + "version": "^10.7.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "github/codeql-action/upload-sarif", + "version": "99df26d4f13ea111d4ec1a7dddef6063f76b97e9", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lameenc", + "version": "1.8.4", + "license": "GPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-android-arm64", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-darwin-arm64", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-darwin-x64", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-freebsd-x64", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-linux-arm64-gnu", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-linux-arm64-musl", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-linux-x64-gnu", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-linux-x64-musl", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-win32-arm64-msvc", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-win32-x64-msvc", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "lucide-react", + "version": "^1.24.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "maturin", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "option-ext", + "version": "0.2.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "ossf/scorecard-action", + "version": "4eaacf0543bb3f2c246792bd56e8cdeffafb205a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "pathspec", + "version": "1.0.4", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "r-efi", + "version": "5.3.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "r-efi", + "version": "6.0.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "react", + "version": "^19.2.4", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "react-dom", + "version": "^19.2.7", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "selectors", + "version": "0.36.1", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "serde", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "serde_json", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "soxr", + "version": "1.0.0", + "license": "Python-2.0 AND LGPL-2.1-or-later" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "storybook", + "version": "^10.4.6", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "time", + "version": ">= 0.3.0,< 0.4.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "tqdm", + "version": "4.68.3", + "license": "MIT AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "typing-extensions", + "version": "4.15.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "url", + "version": ">= 2.5.8,< 3.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "uuid", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "vite", + "version": "^8.1.4", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "vitest", + "version": "^4.1.10", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/bandscope", + "name": "yt-dlp", + "version": "2026.7.4", + "license": "Unlicense AND Unlicense AND GPL-3.0-or-later AND MPL-2.0 AND MIT AND BSD-3-Clause AND Apache-2.0 AND MIT AND GPL-2.0-or-later AND BSD-3-Clause AND MIT AND LGPL-2.1-only AND BSD-2-Clause AND GPL-2.0-or-later" + }, + { + "repo": "ContextualWisdomLab/clearfolio", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/clearfolio", + "name": "actions/setup-java", + "version": "b6effb05e454b25005698d916606bdc6ffcbf961", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/clearfolio", + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/clearfolio", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/clearfolio", + "name": "org.apache.maven.plugins:maven-compiler-plugin", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/clearfolio", + "name": "org.apache.maven.plugins:maven-surefire-plugin", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/clearfolio", + "name": "org.jacoco:jacoco-maven-plugin", + "version": "0.8.15", + "license": "EPL-2.0 OR (Apache-2.0 AND EPL-2.0)" + }, + { + "repo": "ContextualWisdomLab/clearfolio", + "name": "org.springframework.boot:spring-boot-maven-plugin", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/clearfolio", + "name": "org.springframework.boot:spring-boot-starter-log4j2", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/clearfolio", + "name": "org.springframework.boot:spring-boot-starter-test", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/clearfolio", + "name": "org.springframework.boot:spring-boot-starter-validation", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/clearfolio", + "name": "org.springframework.boot:spring-boot-starter-webflux", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "aiofiles", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "aiohappyeyeballs", + "version": "2.7.1", + "license": "0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "coverage", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "fastapi", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "httpx", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "hypothesis", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "hypothesis", + "version": "6.165.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "mcp", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "python-multipart", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "tqdm", + "version": "4.68.4", + "license": "MIT AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "name": "uvicorn", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "actions/download-artifact", + "version": "634f93cb2916e3fdff6788551b99b062d0335ce0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "actions/setup-node", + "version": "820762786026740c76f36085b0efc47a31fe5020", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "actions/setup-python", + "version": "ece7cb06caefa5fff74198d8649806c4678c61a1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "actions/upload-artifact", + "version": "330a01c490aca151604b8cf639adc76d48f6c5d4", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "astral-sh/setup-uv", + "version": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "axe-core", + "version": "4.13.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "certifi", + "version": "2026.7.22", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "chardet", + "version": "5.2.0", + "license": "LGPL-2.1-or-later" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "contextual-orchestrator", + "version": "0.2.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "docker/setup-compose-action", + "version": "54042514f505b273907334ae2b9cdbb9a0213c1a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "dtolnay/rust-toolchain", + "version": "6bed0761d98439e5a578e2877258200ad565ba87", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "egressweave", + "version": "0.1.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "fqdn", + "version": "1.5.1", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "github/codeql-action/analyze", + "version": "b96794f015dfd88f77b49b1c93e0fa7110f94c63", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "github/codeql-action/init", + "version": "b96794f015dfd88f77b49b1c93e0fa7110f94c63", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "hypothesis", + "version": "6.165.10", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lark", + "version": "1.3.1", + "license": "MIT AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lightningcss", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "lxml", + "version": "6.1.1", + "license": "BSD-3-Clause AND GPL-1.0-or-later" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "maturin", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "opencode-darwin-arm64", + "version": "1.18.22", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "opencode-darwin-x64", + "version": "1.18.22", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "opencode-darwin-x64-baseline", + "version": "1.18.22", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "opencode-linux-arm64", + "version": "1.18.22", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "opencode-linux-arm64-musl", + "version": "1.18.22", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "opencode-linux-x64", + "version": "1.18.22", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "opencode-linux-x64-baseline", + "version": "1.18.22", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "opencode-linux-x64-baseline-musl", + "version": "1.18.22", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "opencode-linux-x64-musl", + "version": "1.18.22", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "opencode-windows-x64", + "version": "1.18.22", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "opencode-windows-x64-baseline", + "version": "1.18.22", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "playwright", + "version": "latest", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "psycopg", + "version": "3.3.4", + "license": "LGPL-3.0 AND LGPL-3.0-only" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "psycopg-binary", + "version": "3.3.4", + "license": "GPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "pyo3", + "version": ">= 0.29.0,< 0.30.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "rayon", + "version": ">= 1.10.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "react", + "version": "^19.2.8", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "react-dom", + "version": "^19.2.8", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "rfc3987-syntax", + "version": "1.1.0", + "license": "Apache-2.0 AND GPL-1.0-or-later AND MIT" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "serde", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "serde_json", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "storybook", + "version": "^10.5.10", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "tiktoken-rs", + "version": ">= 0.7.0,< 0.8.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "tqdm", + "version": "4.70.0", + "license": "MIT AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "vite", + "version": "^8.2.2", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "name": "vitest", + "version": "latest", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/ContextualWisdomLab.github.io", + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/ContextualWisdomLab.github.io", + "name": "github/codeql-action/analyze", + "version": "cdf488f595d80d6e07e03d4674febd5ab45fa938", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/ContextualWisdomLab.github.io", + "name": "github/codeql-action/init", + "version": "cdf488f595d80d6e07e03d4674febd5ab45fa938", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "actions/attest", + "version": "1e69f48acb82d1966a394da916b4c1698aa569d6", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "actions/download-artifact", + "version": "37930b1c2abaa49bbe596cd826c3c89aef350131", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "actions/download-artifact", + "version": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "actions/setup-node", + "version": "820762786026740c76f36085b0efc47a31fe5020", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "cssparser", + "version": "0.36.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "cssparser-macros", + "version": "0.6.1", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "dtoa-short", + "version": "0.3.5", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "dtolnay/rust-toolchain", + "version": "4be7066ada62dd38de10e7b70166bc74ed198c30", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "ilammy/msvc-dev-cmd", + "version": "0b201ec74fa43914dc39ae48a89fd1d8cb592756", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "jakoch/install-vulkan-sdk-action", + "version": "37effcfa045411f8bfbbda26df2fd1b3bf3436fa", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "Jimver/cuda-toolkit", + "version": "b8bf9c6c28f8a92fbb04dcfcaee872e60c57462d", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "mlsirm-core", + "version": "0.7.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "option-ext", + "version": "0.2.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "r-efi", + "version": "5.3.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "r-efi", + "version": "6.0.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "selectors", + "version": "0.36.1", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "softprops/action-gh-release", + "version": "3d0d9888cb7fd7b750713d6e236d1fcb99157228", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/disksage", + "name": "Swatinem/rust-cache", + "version": "6323deb102c322ba6fcbdcafc7e3dddab59af2b6", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "actions/download-artifact", + "version": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "actions/download-artifact", + "version": "d3f86a106a0bac45b974a628896c90dbdf5c8093", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "backports-asyncio-runner", + "version": "1.2.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "certifi", + "version": "2026.7.22", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "coverage", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "hatchling", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "idna", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "pathspec", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "pypa/gh-action-pypi-publish", + "version": "dc37677b2e1c63e2034f94d8a5b11f265b73ba33", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "pytest", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "pytest-asyncio", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "step-security/harden-runner", + "version": "bf7454d06d71f1098171f2acdf0cd4708d7b5920", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "trove-classifiers", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "bytemuck", + "version": ">= 1.25.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-cflite_pr.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-ci.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-codeql.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-hourly-pr-governance.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-publish-pypi.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-pypi-gap-guard.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-release-tag.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-statistical-studies.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "hypothesis", + "version": "6.156.6", + "license": "MPL-2.0 AND MPL-1.1" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "hypothesis", + "version": "6.168.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "maturin", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "numpy", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "pollster", + "version": ">= 1.0.1,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "proptest", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "pytest", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "r-efi", + "version": "5.3.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "r-efi", + "version": "6.0.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "serde", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "serde_json", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "sha2", + "version": ">= 0.10.9,< 0.11.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "time", + "version": ">= 0.3.0,< 0.4.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "uuid", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "name": "wgpu", + "version": ">= 30.0.0,< 31.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "axum", + "version": ">= 0.7.0,< 0.8.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "base64", + "version": ">= 0.22.0,< 0.23.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "cryptography", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "ext-curl", + "version": ">= 0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "ext-dom", + "version": ">= 0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "ext-mcrypt", + "version": ">= 0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "ext-openssl", + "version": ">= 0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "fastapi", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "flate2", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "form_urlencoded", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "httpx", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "itsdangerous", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "jinja2", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "jsonwebtoken", + "version": ">= 10.3.0,< 11.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "lxml", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "Microsoft.AspNet.Mvc", + "version": "5.2.9", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "Microsoft.AspNet.Mvc.ko", + "version": "5.2.9", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "Microsoft.AspNet.WebPages", + "version": "3.2.9", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "pem", + "version": ">= 3.0.0,< 4.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "php", + "version": ">= 5.3.2", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "phploc/phploc", + "version": ">= 0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "pyjwt", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "pytest", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "python-multipart", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "rand", + "version": ">= 0.8.0,< 0.9.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "reqwest", + "version": ">= 0.12.0,< 0.13.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "roxmltree", + "version": ">= 0.20.0,< 0.21.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "rsa", + "version": ">= 0.9.0,< 0.10.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "rust-xmlsec", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "sebastian/phpcpd", + "version": ">= 0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "serde", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "serde_json", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "sha2", + "version": ">= 0.10.0,< 0.11.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "signxml", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "System.IdentityModel.Tokens.Jwt", + "version": "5.4.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "thiserror", + "version": ">= 2.0.0,< 3.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "time", + "version": ">= 0.3.0,< 0.4.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "tokio", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "tower-sessions", + "version": ">= 0.13.0,< 0.14.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "uvicorn", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "name": "x509-parser", + "version": ">= 0.16.0,< 0.17.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "actions/checkout", + "version": "11d5960a326750d5838078e36cf38b85af677262", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "actions/create-github-app-token", + "version": "bcd2ba49218906704ab6c1aa796996da409d3eb1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "actions/download-artifact", + "version": "d3f86a106a0bac45b974a628896c90dbdf5c8093", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "actions/setup-python", + "version": "a26af69be951a213d495a4c3e4e4022e16d87065", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "certifi", + "version": "2026.7.22", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "fastapi", + "version": ">= 0.115,< 1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "httpx", + "version": ">= 0.27,< 1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "jinja2", + "version": ">= 3.1,< 4", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "korean-lunar-calendar", + "version": ">= 0.3.1,< 1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "pathspec", + "version": "1.1.1", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "pydantic", + "version": ">= 2.10,< 3", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "pydantic-settings", + "version": ">= 2.7,< 3", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "python-multipart", + "version": ">= 0.0.20,< 1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "reportlab", + "version": ">= 4.2,< 5", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "typer", + "version": ">= 0.15,< 1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "name": "uvicorn", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "actions/setup-node", + "version": "249970729cb0ef3589644e2896645e5dc5ba9c38", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "lightningcss", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/gyeot", + "name": "node-forge", + "version": "1.4.0", + "license": "BSD-3-Clause OR GPL-2.0-only" + }, + { + "repo": "ContextualWisdomLab/hyosung-itx-slogan-brief", + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/hyosung-itx-slogan-brief", + "name": "actions/setup-node", + "version": "49933ea5288caeca8642d1e84afbd3f7d6820020", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "actions/attest", + "version": "59d89421af93a897026c735860bf21b6eb4f7b26", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "actions/download-artifact", + "version": "37930b1c2abaa49bbe596cd826c3c89aef350131", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "actions/setup-node", + "version": "820762786026740c76f36085b0efc47a31fe5020", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "actions/setup-python", + "version": "a26af69be951a213d495a4c3e4e4022e16d87065", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "lxml", + "version": "6.1.0", + "license": "BSD-3-Clause AND GPL-1.0-or-later" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "openpyxl", + "version": ">= 3.1.5,< 4", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "pnpm/action-setup", + "version": "0977fd99725f1db4007ccb2928dbb4e90d06cc86", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "pypa/gh-action-pypi-publish", + "version": "dc37677b2e1c63e2034f94d8a5b11f265b73ba33", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "python-docx", + "version": ">= 1.2.0,< 2", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "python-pptx", + "version": ">= 1.0.2,< 2", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "sigstore/cosign-installer", + "version": "6f9f17788090df1f26f669e9d70d6ae9567deba6", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/inkspan", + "name": "typing-extensions", + "version": "4.15.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/k-csap-skills", + "name": "actions/checkout", + "version": "4.*.*", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/k-csap-skills", + "name": "astral-sh/setup-uv", + "version": "6.*.*", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/kaefa", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/kaefa", + "name": "r-lib/actions/check-r-package", + "version": "6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/kaefa", + "name": "r-lib/actions/setup-pandoc", + "version": "d3c5be51b12e724e68f33216ca3c148b66d5f0b6", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/kaefa", + "name": "r-lib/actions/setup-r", + "version": "6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/kaefa", + "name": "r-lib/actions/setup-r-dependencies", + "version": "6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/keyverse", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/keyverse", + "name": "actions/download-artifact", + "version": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/keyverse", + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/keyverse", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/keyverse", + "name": "astral-sh/setup-uv", + "version": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/keyverse", + "name": "certifi", + "version": "2026.7.22", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/keyverse", + "name": "cwl-idp-account-unification", + "version": "0.1.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/keyverse", + "name": "step-security/harden-runner", + "version": "05e31511f85b41b11d1cf0ef85d0992719546e2c", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/keyverse", + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/late-life-anxiety-reanalysis", + "name": "fast-mlsirm", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/law-ai-agent", + "name": "ContextualWisdomLab/.github/.github/workflows/law-ai-agent-ci.yml", + "version": "8dff0538c55b99df3f6f19b25a06633fc72b38ef", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/law-ai-agent", + "name": "pathspec", + "version": "1.1.1", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/law-ai-agent", + "name": "psycopg", + "version": "3.3.6", + "license": "LGPL-3.0-only" + }, + { + "repo": "ContextualWisdomLab/law-ai-agent", + "name": "psycopg-binary", + "version": "3.3.6", + "license": "LGPL-3.0-only" + }, + { + "repo": "ContextualWisdomLab/law-ai-agent", + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/linux-cluster-ops", + "name": "actions/attest-build-provenance", + "version": "a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/linux-cluster-ops", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/linux-cluster-ops", + "name": "actions/setup-python", + "version": "ece7cb06caefa5fff74198d8649806c4678c61a1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/linux-cluster-ops", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mcp-shared-gateway", + "name": "actions/checkout", + "version": "11bd71901bbe5b1630ceea73d27597364c9af683", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mcp-shared-gateway", + "name": "actions/setup-node", + "version": "49933ea5288caeca8642d1e84afbd3f7d6820020", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mcp-shared-gateway", + "name": "actions/setup-python", + "version": "a26af69be951a213d495a4c3e4e4022e16d87065", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "actions/dependency-review-action", + "version": "a1d282b36b6f3519aa1f3fc636f609c47dddb294", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "actions/setup-java", + "version": "1bcf9fb12cf4aa7d266a90ae39939e61372fe520", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "com.fasterxml.jackson.core:jackson-databind", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "com.h2database:h2", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "github/codeql-action/analyze", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "github/codeql-action/autobuild", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "github/codeql-action/init", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "github/codeql-action/upload-sarif", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "io.micrometer:micrometer-tracing-bridge-brave", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "io.zipkin.reporter2:zipkin-reporter-brave", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.apache.maven.plugins:maven-dependency-plugin", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.flywaydb:flyway-core", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.flywaydb:flyway-database-postgresql", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.jacoco:jacoco-maven-plugin", + "version": "0.8.15", + "license": "EPL-2.0 OR (Apache-2.0 AND EPL-2.0)" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.junit.jupiter:junit-jupiter-api", + "version": "5.12.2", + "license": "EPL-2.0" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.junit.jupiter:junit-jupiter-engine", + "version": "5.12.2", + "license": "EPL-2.0" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.junit.platform:junit-platform-commons", + "version": "1.12.2", + "license": "EPL-2.0" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.junit.platform:junit-platform-engine", + "version": "1.12.2", + "license": "EPL-2.0" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.junit.platform:junit-platform-launcher", + "version": "1.12.2", + "license": "EPL-2.0" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.postgresql:postgresql", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.boot:spring-boot-configuration-processor", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.boot:spring-boot-maven-plugin", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.boot:spring-boot-starter-actuator", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.boot:spring-boot-starter-aop", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.boot:spring-boot-starter-data-jpa", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.boot:spring-boot-starter-security", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.boot:spring-boot-starter-test", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.boot:spring-boot-starter-web", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.cloud:spring-cloud-config-server", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.cloud:spring-cloud-starter-gateway", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.cloud:spring-cloud-starter-netflix-eureka-server", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.kafka:spring-kafka", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.retry:spring-retry", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "org.springframework.security:spring-security-test", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "name": "ossf/scorecard-action", + "version": "4eaacf0543bb3f2c246792bd56e8cdeffafb205a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-libvips-darwin-arm64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-libvips-darwin-x64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-libvips-linux-arm", + "version": "1.3.0", + "license": "LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-libvips-linux-arm64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-libvips-linux-ppc64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-libvips-linux-riscv64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-libvips-linux-s390x", + "version": "1.3.0", + "license": "LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-libvips-linux-x64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-libvips-linuxmusl-arm64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-libvips-linuxmusl-x64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-wasm32", + "version": "0.35.0", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-win32-arm64", + "version": "0.35.0", + "license": "Apache-2.0 AND LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-win32-ia32", + "version": "0.35.0", + "license": "Apache-2.0 AND LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "@img/sharp-win32-x64", + "version": "0.35.0", + "license": "Apache-2.0 AND LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "actions/cache", + "version": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "actions/dependency-review-action", + "version": "a1d282b36b6f3519aa1f3fc636f609c47dddb294", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "actions/setup-node", + "version": "820762786026740c76f36085b0efc47a31fe5020", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "aiohappyeyeballs", + "version": "2.7.1", + "license": "0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "aioimaplib", + "version": "2.0.1", + "license": "GPL-3.0 AND GPL-3.0-only AND GPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "axe-core", + "version": "4.12.1", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "caido-server-auth", + "version": "0.1.2", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "cvss", + "version": "3.6", + "license": "LGPL-3.0 AND LGPL-3.0-only AND LGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "dnspython", + "version": "2.8.0", + "license": "ISC AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "docker/build-push-action", + "version": "53b7df96c91f9c12dcc8a07bcb9ccacbed38856a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "docker/login-action", + "version": "dbcb813823bdd20940b903addbd779551569679f", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "docker/metadata-action", + "version": "dc802804100637a589fabce1cb79ff13a1411302", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "docker/setup-buildx-action", + "version": "bb05f3f5519dd87d3ba754cc423b652a5edd6d2c", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "docker/setup-qemu-action", + "version": "96fe6ef7f33517b61c61be40b68a1882f3264fb8", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "github/codeql-action/upload-sarif", + "version": "f205ea1c3313d32999d8d6a48b4f6530d4437b38", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "grpcio", + "version": "1.81.1", + "license": "Apache-2.0 AND BSD-3-Clause AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "grpcio", + "version": "1.82.1", + "license": "Apache-2.0 AND BSD-3-Clause AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "grpcio-status", + "version": "1.81.1", + "license": "Apache-2.0 AND BSD-3-Clause AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-android-arm64", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-darwin-arm64", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-darwin-x64", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-freebsd-x64", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-linux-arm64-gnu", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-linux-arm64-musl", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-linux-x64-gnu", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-linux-x64-musl", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-win32-arm64-msvc", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-win32-x64-msvc", + "version": "1.32.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "naruon-backend", + "version": "0.14.4", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "orjson", + "version": "3.11.9", + "license": "Apache-2.0 AND MIT AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "rankweave", + "version": "0.1.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "step-security/harden-runner", + "version": "bf7454d06d71f1098171f2acdf0cd4708d7b5920", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "tqdm", + "version": "4.68.3", + "license": "MIT AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "tqdm", + "version": "4.68.4", + "license": "MIT AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "typing-extensions", + "version": "4.15.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/naruon", + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "actions/attest-build-provenance", + "version": "0f67c3f4856b2e3261c31976d6725780e5e4c373", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "actions/dependency-review-action", + "version": "a1d282b36b6f3519aa1f3fc636f609c47dddb294", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "actions/deploy-pages", + "version": "cd2ce8fcbc39b97be8ca5fce6e763baed58fa128", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "astral-sh/setup-uv", + "version": "c771a70e6277c0a99b617c7a806ffedaca235ff9", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "backports-asyncio-runner", + "version": "1.2.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "certifi", + "version": "2026.2.25", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "docker/build-push-action", + "version": "53b7df96c91f9c12dcc8a07bcb9ccacbed38856a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "docker/login-action", + "version": "dbcb813823bdd20940b903addbd779551569679f", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "docker/metadata-action", + "version": "dc802804100637a589fabce1cb79ff13a1411302", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "docker/setup-buildx-action", + "version": "bb05f3f5519dd87d3ba754cc423b652a5edd6d2c", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "docker/setup-qemu-action", + "version": "96fe6ef7f33517b61c61be40b68a1882f3264fb8", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "github/codeql-action/analyze", + "version": "f205ea1c3313d32999d8d6a48b4f6530d4437b38", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "github/codeql-action/autobuild", + "version": "f205ea1c3313d32999d8d6a48b4f6530d4437b38", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "github/codeql-action/init", + "version": "f205ea1c3313d32999d8d6a48b4f6530d4437b38", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "github/codeql-action/upload-sarif", + "version": "f205ea1c3313d32999d8d6a48b4f6530d4437b38", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "google/clusterfuzzlite/actions/build_fuzzers", + "version": "52ecc61cb587ee99c26825a112a21abf19c7448c", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "google/clusterfuzzlite/actions/run_fuzzers", + "version": "52ecc61cb587ee99c26825a112a21abf19c7448c", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "newsdom-api", + "version": "0.2.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "ossf/scorecard-action", + "version": "2d1146689b8cda280b9bc96326124645441f03bc", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "pathspec", + "version": "1.0.4", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "pyinstaller", + "version": "6.21.0", + "license": "GPL-2.0-only AND GPL-2.0-or-later" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "pyinstaller-hooks-contrib", + "version": "2026.6", + "license": "Apache-2.0 AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later" + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "name": "typing-extensions", + "version": "4.15.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "actions/attest", + "version": "59d89421af93a897026c735860bf21b6eb4f7b26", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "actions/checkout", + "version": "11bd71901bbe5b1630ceea73d27597364c9af683", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "actions/checkout", + "version": "de0fac2e4500dabe0009e67214ff5f5447ce83dd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "actions/create-github-app-token", + "version": "bcd2ba49218906704ab6c1aa796996da409d3eb1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "actions/download-artifact", + "version": "d3f86a106a0bac45b974a628896c90dbdf5c8093", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "actions/setup-node", + "version": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "actions/setup-python", + "version": "a26af69be951a213d495a4c3e4e4022e16d87065", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "aquasecurity/setup-trivy", + "version": "81e514348e19b6112ce2a7e3ecbafe19c1e1f567", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "docker/build-push-action", + "version": "d08e5c354a6adb9ed34480a06d141179aa583294", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "docker/setup-buildx-action", + "version": "37fe631027851001ddb9b187196cc803df7f5f0e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "interrogate", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "lightningcss", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "pydantic", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "pydantic-ai-slim", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "pytest", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "pytest-cov", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "setuptools", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "sigstore/cosign-installer", + "version": "6f9f17788090df1f26f669e9d70d6ae9567deba6", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "tqdm", + "version": "4.68.4", + "license": "MIT AND MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/noema", + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/OriginWeave", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/OriginWeave", + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/OriginWeave", + "name": "dtolnay/rust-toolchain", + "version": "4be7066ada62dd38de10e7b70166bc74ed198c30", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/OriginWeave", + "name": "step-security/harden-runner", + "version": "bf7454d06d71f1098171f2acdf0cd4708d7b5920", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "actions/setup-node", + "version": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "actions/setup-python", + "version": "ece7cb06caefa5fff74198d8649806c4678c61a1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "aiohttp", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "alembic", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "asyncpg", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "asyncpg-stubs", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "cryptography", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "fastapi", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "github/codeql-action/analyze", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "github/codeql-action/autobuild", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "github/codeql-action/init", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "httpx", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "hypercorn", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "lightningcss", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "mypy", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "prometheus-client", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "psycopg", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "pydantic", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "pydantic-settings", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "pyjwt", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "pytest", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "pytest-asyncio", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "pytest-cov", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "python-jose", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "python-multipart", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "redis", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "requests", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "setuptools", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "snowflake-connector-python", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "sqlalchemy", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "starlette", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "step-security/harden-runner", + "version": "b09bb98e06d4d774595224525879c09bc6e98c40", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "types-python-jose", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "name": "urllib3", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "aiohappyeyeballs", + "version": "2.7.1", + "license": "0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "aiohttp", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "astral-sh/setup-uv", + "version": "c771a70e6277c0a99b617c7a806ffedaca235ff9", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "backports-asyncio-runner", + "version": "1.2.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "cryptography", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "pg-llm-batch", + "version": "0.1.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "psycopg", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "psycopg", + "version": "3.3.4", + "license": "LGPL-3.0 AND LGPL-3.0-only" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "psycopg-binary", + "version": "3.3.4", + "license": "GPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "pytest", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "pytest-asyncio", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "setuptools", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "step-security/harden-runner", + "version": "b09bb98e06d4d774595224525879c09bc6e98c40", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/procurement-workbench", + "name": "certifi", + "version": "2026.7.22", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/procurement-workbench", + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/scopeweave", + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/scopeweave", + "name": "actions/configure-pages", + "version": "45bfe0192ca1faeb007ade9deae92b16b8254a0d", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/scopeweave", + "name": "actions/dependency-review-action", + "version": "a1d282b36b6f3519aa1f3fc636f609c47dddb294", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/scopeweave", + "name": "actions/deploy-pages", + "version": "cd2ce8fcbc39b97be8ca5fce6e763baed58fa128", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/scopeweave", + "name": "actions/setup-node", + "version": "2028fbc5c25fe9cf00d9f06a71cc4710d4507903", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/scopeweave", + "name": "actions/setup-node", + "version": "39370e3970a6d050c480ffad4ff0ed4d3fdee5af", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/scopeweave", + "name": "actions/upload-pages-artifact", + "version": "fc324d3547104276b827a68afc52ff2a11cc49c9", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/scopeweave", + "name": "github/codeql-action/analyze", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/scopeweave", + "name": "github/codeql-action/init", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/scopeweave", + "name": "google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml", + "version": "3a7550f43ba5b58905a821ce3a0ed24c4858b3f4", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/semantic-data-portal", + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/semantic-data-portal", + "name": "actions/setup-python", + "version": "ece7cb06caefa5fff74198d8649806c4678c61a1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/semantic-data-portal", + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/semantic-data-portal", + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/semantic-data-portal", + "name": "github/codeql-action/upload-sarif", + "version": "54f647b7e1bb85c95cddabcd46b0c578ec92bc1a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/semantic-data-portal", + "name": "hypothesis", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/semantic-data-portal", + "name": "hypothesis", + "version": "6.156.6", + "license": "MPL-2.0 AND MPL-1.1" + }, + { + "repo": "ContextualWisdomLab/semantic-data-portal", + "name": "ossf/scorecard-action", + "version": "4eaacf0543bb3f2c246792bd56e8cdeffafb205a", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/semantic-data-portal", + "name": "psycopg", + "version": "3.3.4", + "license": "LGPL-3.0 AND LGPL-3.0-only" + }, + { + "repo": "ContextualWisdomLab/semantic-data-portal", + "name": "psycopg-binary", + "version": "3.3.4", + "license": "GPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/semantic-data-portal", + "name": "sqlalchemy", + "version": "", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/semantic-data-portal", + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/tokyo-travel-2026", + "name": "pymupdf", + "version": "1.28.2", + "license": "AGPL-3.0 AND AGPL-3.0-only AND AGPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/traveling-agent", + "name": "ContextualWisdomLab/.github/.github/workflows/traveling-agent-poc-quality.yml", + "version": "f52a0d71b3ce2b60d738f4326f0f9906af9ea1b9", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "actions/cache", + "version": "0057852bfaa89a56745cba8c7296529d2fc39830", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "arbitrary", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "dtolnay/rust-toolchain", + "version": "4be7066ada62dd38de10e7b70166bc74ed198c30", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "dtolnay/rust-toolchain", + "version": "efcb852328a9f50117170cc43094fb6f09eaf1ae", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "github/codeql-action/upload-sarif", + "version": "ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "libfuzzer-sys", + "version": ">= 0.4.0,< 0.5.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "ossf/scorecard-action", + "version": "2d1146689b8cda280b9bc96326124645441f03bc", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "percent-encoding", + "version": ">= 2.0.0,< 3.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "proptest", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "r-efi", + "version": "5.3.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "r-efi", + "version": "6.0.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "serde", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/wardnet", + "name": "serde_json", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "actions/setup-python", + "version": "a26af69be951a213d495a4c3e4e4022e16d87065", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "aiohappyeyeballs", + "version": "2.7.1", + "license": "0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "astral-sh/setup-uv", + "version": "c771a70e6277c0a99b617c7a806ffedaca235ff9", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "backports-asyncio-runner", + "version": "1.2.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "commander", + "version": "^14.0.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "eslint", + "version": "^9.27.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "express", + "version": "^4.18.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "extract-zip", + "version": "^2.0.1", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "pino", + "version": "^9.7.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "psycopg", + "version": "3.3.4", + "license": "LGPL-3.0 AND LGPL-3.0-only" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "psycopg-binary", + "version": "3.3.4", + "license": "GPL-3.0-or-later" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "pypa/gh-action-pip-audit", + "version": "1220774d901786e6f652ae159f7b6bc8fea6d266", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "vitest", + "version": "^3.2.6", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "xtrmllmbatch", + "version": "0.1.0", + "license": "NOASSERTION" + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "name": "zod", + "version": "^3.25.67", + "license": "NOASSERTION" + } + ], + "repos": [ + { + "repo": "ContextualWisdomLab/.github", + "error": null, + "component_count": 330, + "components": [ + { + "name": "@colbymchenry/codegraph", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@colbymchenry/codegraph-darwin-arm64", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@colbymchenry/codegraph-darwin-x64", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@colbymchenry/codegraph-linux-arm64", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@colbymchenry/codegraph-linux-x64", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@colbymchenry/codegraph-win32-arm64", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@colbymchenry/codegraph-win32-x64", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@hono/node-server", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@modelcontextprotocol/sdk", + "version": "1.30.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@rhwp/core", + "version": "0.7.7", + "license": "MIT", + "flagged": false + }, + { + "name": "accepts", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "actions/attest", + "version": "v4.1.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "11d5960a326750d5838078e36cf38b85af677262", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "631c942040754b6e095e929c1677c07e10ed4f87", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "de0fac2e4500dabe0009e67214ff5f5447ce83dd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "v7.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "v7.0.1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/create-github-app-token", + "version": "v3.2.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/dependency-review-action", + "version": "v5.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/download-artifact", + "version": "v8.0.1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "49933ea5288caeca8642d1e84afbd3f7d6820020", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "ece7cb06caefa5fff74198d8649806c4678c61a1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "v7.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "b7c566a772e6b6bfb58ed0dc250532a479d7789f", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "v6.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "v7.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "v7.0.1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "aiohappyeyeballs", + "version": "2.7.1", + "license": "0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0", + "flagged": true + }, + { + "name": "aiohttp", + "version": "3.14.3", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "aiosignal", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "ajv", + "version": "8.20.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ajv-formats", + "version": "3.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "anchore/sbom-action", + "version": "v0.24.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "annotated-doc", + "version": "0.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "annotated-types", + "version": "0.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "anyio", + "version": "4.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "aquasecurity/trivy-action", + "version": "v0.36.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "astral-sh/setup-uv", + "version": "37802adc94f370d6bfd71619e3f0bf239e1f3b78", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "astral-sh/setup-uv", + "version": "bec219d24cd3e171d82865faccec33120bb574f4", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "astral-sh/setup-uv", + "version": "d0cc045d04ccac9d8b7881df0226f9e82c39688e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "attrs", + "version": "26.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "autocfg", + "version": "1.5.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "backoff", + "version": "2.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "bandit", + "version": "1.9.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "body-parser", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "boolean-py", + "version": "5.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "bytes", + "version": "3.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "cachecontrol", + "version": "0.14.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "caido-sdk-client", + "version": "0.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "caido-server-auth", + "version": "0.1.2", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "call-bind-apply-helpers", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "call-bound", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "cffi", + "version": "2.0.0", + "license": "MIT-0", + "flagged": false + }, + { + "name": "cfg-if", + "version": "1.0.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "charset-normalizer", + "version": "3.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "click", + "version": "8.4.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "click", + "version": "8.5.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "cloudflare/wrangler-action", + "version": "v4.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "content-disposition", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "content-type", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "content-type", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "contextualwisdomlab-opencode-codegraph-tooling", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ContextualWisdomLab/.github/.github/workflows/release-dependency-license-strix-gate.yml", + "version": "e249236e3f420c4e9c470f26925640dae59f0d93", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "cookie", + "version": "0.7.2", + "license": "MIT", + "flagged": false + }, + { + "name": "cookie-signature", + "version": "1.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "core-util-is", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "cors", + "version": "2.8.6", + "license": "MIT", + "flagged": false + }, + { + "name": "coverage", + "version": "7.15.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "coverage-cargo-fixtures", + "version": "0.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "cross-spawn", + "version": "7.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "cryptography", + "version": "50.0.0", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "cvss", + "version": "3.6", + "license": "LGPL-3.0 AND LGPL-3.0-only AND LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "cyclonedx-python-lib", + "version": "9.1.0", + "license": "Apache-2.0 AND Python-2.0", + "flagged": false + }, + { + "name": "debug", + "version": "4.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "defusedxml", + "version": "0.7.1", + "license": "PSF-2.0", + "flagged": false + }, + { + "name": "depd", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "distro", + "version": "1.9.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "docker", + "version": "7.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "docstring-parser", + "version": "0.18.0", + "license": "MIT", + "flagged": false + }, + { + "name": "dtolnay/rust-toolchain", + "version": "4be7066ada62dd38de10e7b70166bc74ed198c30", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "dunder-proto", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ee-first", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "encodeurl", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-define-property", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "es-errors", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-object-atoms", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "escape-html", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "etag", + "version": "1.8.1", + "license": "MIT", + "flagged": false + }, + { + "name": "eventsource", + "version": "3.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "eventsource-parser", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "express", + "version": "5.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "express-rate-limit", + "version": "8.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-deep-equal", + "version": "3.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-uri", + "version": "3.1.7", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "fastuuid", + "version": "0.14.0", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "filelock", + "version": "3.29.4", + "license": "MIT", + "flagged": false + }, + { + "name": "filelock", + "version": "3.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "finalhandler", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "forwarded", + "version": "0.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fresh", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "frozenlist", + "version": "1.8.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "fsspec", + "version": "2026.6.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "function-bind", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "get-intrinsic", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "get-proto", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "github/codeql-action", + "version": "v4.37.9", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/analyze", + "version": "ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/init", + "version": "ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "google-api-core", + "version": "2.33.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-auth", + "version": "2.55.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-cloud-aiplatform", + "version": "1.133.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-cloud-bigquery", + "version": "3.42.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-cloud-core", + "version": "2.6.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-cloud-resource-manager", + "version": "1.18.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-cloud-storage", + "version": "3.13.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-crc32c", + "version": "1.8.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-genai", + "version": "1.75.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-resumable-media", + "version": "2.10.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google/clusterfuzzlite/actions/build_fuzzers", + "version": "884713a6c30a92e5e8544c39945cd7cb630abcd1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "google/clusterfuzzlite/actions/run_fuzzers", + "version": "884713a6c30a92e5e8544c39945cd7cb630abcd1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "google/osv-scanner-action", + "version": "v2.3.8", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "google/osv-scanner-action", + "version": "v2.5.1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "googleapis-common-protos", + "version": "1.75.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "gopd", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "gql", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "graphql-core", + "version": "3.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "griffelib", + "version": "2.1.0", + "license": "ISC", + "flagged": false + }, + { + "name": "grpc-google-iam-v1", + "version": "0.14.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "grpcio", + "version": "1.81.1", + "license": "Apache-2.0 AND BSD-3-Clause AND MPL-2.0", + "flagged": true + }, + { + "name": "grpcio-status", + "version": "1.81.1", + "license": "Apache-2.0 AND BSD-3-Clause AND MPL-2.0", + "flagged": true + }, + { + "name": "h11", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "has-symbols", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hasown", + "version": "2.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "heck", + "version": "0.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hf-xet", + "version": "1.5.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "hono", + "version": "4.13.7", + "license": "MIT", + "flagged": false + }, + { + "name": "http-errors", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "httpcore", + "version": "1.0.9", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpcore2", + "version": "2.12.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx", + "version": "0.28.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx-sse", + "version": "0.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "httpx2", + "version": "2.12.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "huggingface-hub", + "version": "1.20.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "hwp-mcp", + "version": "0.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hypothesis", + "version": "6.168.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "iconv-lite", + "version": "0.7.3", + "license": "MIT", + "flagged": false + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "immediate", + "version": "3.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "importlib-metadata", + "version": "8.9.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "indoc", + "version": "2.0.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "inherits", + "version": "2.0.4", + "license": "ISC", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "interrogate", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "interrogate", + "version": "1.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ip-address", + "version": "10.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ipaddr.js", + "version": "1.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-promise", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "isarray", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "isexe", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "itoa", + "version": "1.0.15", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jinja2", + "version": "3.1.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "jiter", + "version": "0.15.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jose", + "version": "6.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "json-schema-traverse", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "json-schema-typed", + "version": "8.0.2", + "license": "BSD-2-Clause AND JSON", + "flagged": false + }, + { + "name": "jsonschema", + "version": "4.26.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jszip", + "version": "3.10.2", + "license": "(MIT OR GPL-3.0-or-later)", + "flagged": true + }, + { + "name": "libc", + "version": "0.2.189", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "license-expression", + "version": "30.4.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "lie", + "version": "3.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "litellm", + "version": "1.94.1", + "license": "MIT", + "flagged": false + }, + { + "name": "markdown-it-py", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "markupsafe", + "version": "3.0.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "math-intrinsics", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "maturin", + "version": "1.15.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "mcp", + "version": "1.28.1", + "license": "MIT AND Python-2.0", + "flagged": false + }, + { + "name": "mdurl", + "version": "0.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "media-typer", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "memoffset", + "version": "0.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "merge-descriptors", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mime-db", + "version": "1.54.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mime-types", + "version": "3.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ms", + "version": "2.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "msgpack", + "version": "1.2.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "multidict", + "version": "6.7.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "negotiator", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "noema-document-reader-runtime", + "version": "1.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "object-assign", + "version": "4.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "object-inspect", + "version": "1.13.4", + "license": "MIT", + "flagged": false + }, + { + "name": "on-finished", + "version": "2.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "once", + "version": "1.4.0", + "license": "ISC", + "flagged": false + }, + { + "name": "once_cell", + "version": "1.21.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "openai", + "version": "2.54.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "openai-agents", + "version": "0.19.4", + "license": "MIT", + "flagged": false + }, + { + "name": "ossf/scorecard-action", + "version": "v2.4.3", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "packageurl-python", + "version": "0.17.6", + "license": "MIT", + "flagged": false + }, + { + "name": "packaging", + "version": "26.2", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "packaging", + "version": "26.3", + "license": "Apache-2.0 OR BSD-2-Clause", + "flagged": false + }, + { + "name": "pako", + "version": "1.0.11", + "license": "MIT AND Zlib", + "flagged": false + }, + { + "name": "parseurl", + "version": "1.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "path-key", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "path-to-regexp", + "version": "8.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "picomatch", + "version": "4.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pillow", + "version": "12.3.0", + "license": "MIT-CMU", + "flagged": false + }, + { + "name": "pip", + "version": "26.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pip-api", + "version": "0.0.34", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pip-audit", + "version": "2.10.1", + "license": "Apache-2.0 AND ISC", + "flagged": false + }, + { + "name": "pip-requirements-parser", + "version": "32.0.1", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "pkce-challenge", + "version": "5.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "platformdirs", + "version": "4.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "portable-atomic", + "version": "1.15.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "proc-macro2", + "version": "1.0.107", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "process-nextick-args", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "propcache", + "version": "0.5.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "proto-plus", + "version": "1.28.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "protobuf", + "version": "6.33.6", + "license": "BSD-3-Clause AND LicenseRef-scancode-protobuf", + "flagged": false + }, + { + "name": "proxy-addr", + "version": "2.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "py", + "version": "1.11.0", + "license": "MIT", + "flagged": false + }, + { + "name": "py-serializable", + "version": "2.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pyasn1", + "version": "0.6.4", + "license": "BSD-2-Clause AND BSD-3-Clause AND MIT", + "flagged": false + }, + { + "name": "pyasn1-modules", + "version": "0.4.2", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "pycparser", + "version": "3.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pydantic", + "version": "2.13.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-core", + "version": "2.46.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-settings", + "version": "2.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pygments", + "version": "2.21.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pyjwt", + "version": "2.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pyo3", + "version": "0.22.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-build-config", + "version": "0.22.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-ffi", + "version": "0.22.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-macros", + "version": "0.22.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-macros-backend", + "version": "0.22.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "PyO3/maturin-action", + "version": "e83996d129638aa358a18fbd1dfb82f0b0fb5d3b", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pyopenssl", + "version": "26.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pypa/gh-action-pypi-publish", + "version": "dc37677b2e1c63e2034f94d8a5b11f265b73ba33", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pyparsing", + "version": "3.3.2", + "license": "MIT AND Python-2.0", + "flagged": false + }, + { + "name": "pypdf", + "version": "6.16.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pytest", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest-cov", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest-cov", + "version": "7.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "python-dotenv", + "version": "1.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "python-multipart", + "version": "0.0.32", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pyyaml", + "version": "6.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "qs", + "version": "6.16.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "quote", + "version": "1.0.47", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "r-lib/actions", + "version": "6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "range-parser", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "raw-body", + "version": "3.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "readable-stream", + "version": "2.3.8", + "license": "MIT", + "flagged": false + }, + { + "name": "referencing", + "version": "0.37.0", + "license": "MIT", + "flagged": false + }, + { + "name": "regex", + "version": "2026.7.19", + "license": "CNRI-Python AND Apache-2.0", + "flagged": false + }, + { + "name": "reportlab", + "version": "5.0.0", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "requests", + "version": "2.34.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "require-from-string", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "rich", + "version": "15.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "router", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "rpds-py", + "version": "2026.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "rustversion", + "version": "1.0.23", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ryu", + "version": "1.0.20", + "license": "Apache-2.0 OR BSL-1.0", + "flagged": false + }, + { + "name": "safe-buffer", + "version": "5.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "safer-buffer", + "version": "2.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "send", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "serve-static", + "version": "2.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "setimmediate", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "setprototypeof", + "version": "1.2.0", + "license": "ISC", + "flagged": false + }, + { + "name": "shebang-command", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "shebang-regex", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "shellingham", + "version": "1.5.4", + "license": "ISC", + "flagged": false + }, + { + "name": "side-channel", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "side-channel-list", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "side-channel-map", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "side-channel-weakmap", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "six", + "version": "1.17.0", + "license": "MIT", + "flagged": false + }, + { + "name": "sniffio", + "version": "1.3.1", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "sortedcontainers", + "version": "2.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "sse-starlette", + "version": "3.4.4", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "starlette", + "version": "1.3.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "statuses", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "step-security/harden-runner", + "version": "bf7454d06d71f1098171f2acdf0cd4708d7b5920", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "step-security/harden-runner", + "version": "v2.13.2", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "step-security/harden-runner", + "version": "v2.20.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "step-security/harden-runner", + "version": "v2.20.1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "stevedore", + "version": "5.9.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "string_decoder", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "strix-agent", + "version": "1.5.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "syn", + "version": "2.0.119", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "tabulate", + "version": "0.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "target-lexicon", + "version": "0.12.16", + "license": "Apache-2.0 WITH LLVM-exception", + "flagged": false + }, + { + "name": "tenacity", + "version": "9.1.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "tiktoken", + "version": "0.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "toidentifier", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tokenizers", + "version": "0.23.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "tomli", + "version": "2.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tomli-w", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tqdm", + "version": "4.68.3", + "license": "MIT AND MPL-2.0", + "flagged": true + }, + { + "name": "truststore", + "version": "0.10.4", + "license": "MIT", + "flagged": false + }, + { + "name": "type-is", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "typer", + "version": "0.25.1", + "license": "MIT", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.15.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "typing-inspection", + "version": "0.4.4", + "license": "MIT", + "flagged": false + }, + { + "name": "unicode-ident", + "version": "1.0.26", + "license": "(MIT OR Apache-2.0) AND Unicode-3.0", + "flagged": false + }, + { + "name": "unindent", + "version": "0.2.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unpipe", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "urllib3", + "version": "2.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "util-deprecate", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "uv", + "version": "0.12.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "uvicorn", + "version": "0.49.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "vary", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "websockets", + "version": "15.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "which", + "version": "2.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "wrappy", + "version": "1.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "yarl", + "version": "1.24.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "zipp", + "version": "4.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "zod", + "version": "4.6.4", + "license": "MIT", + "flagged": false + }, + { + "name": "zod-to-json-schema", + "version": "3.25.2", + "license": "ISC", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/accounting-information-platform", + "error": null, + "component_count": 13, + "components": [ + { + "name": "accounting-information-platform", + "version": "0.1.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/attest", + "version": "508db95dd578ae2727ebd6217d5ba78e4fbda05d", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "631c942040754b6e095e929c1677c07e10ed4f87", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/download-artifact", + "version": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "aquasecurity/trivy-action", + "version": "a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "coverage", + "version": "7.15.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "packaging", + "version": "26.3", + "license": "Apache-2.0 OR BSD-2-Clause", + "flagged": false + }, + { + "name": "psycopg", + "version": "3.3.4", + "license": "LGPL-3.0 AND LGPL-3.0-only", + "flagged": true + }, + { + "name": "psycopg-binary", + "version": "3.3.4", + "license": "GPL-3.0-or-later", + "flagged": true + }, + { + "name": "setuptools", + "version": "84.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "wheel", + "version": "0.48.0", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/aFIPC", + "error": null, + "component_count": 5, + "components": [ + { + "name": "actions/checkout", + "version": "de0fac2e4500dabe0009e67214ff5f5447ce83dd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "r-lib/actions/check-r-package", + "version": "6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "r-lib/actions/setup-r", + "version": "d3c5be51b12e724e68f33216ca3c148b66d5f0b6", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "r-lib/actions/setup-r-dependencies", + "version": "d3c5be51b12e724e68f33216ca3c148b66d5f0b6", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "step-security/harden-runner", + "version": "bf7454d06d71f1098171f2acdf0cd4708d7b5920", + "license": "NOASSERTION", + "flagged": true + } + ] + }, + { + "repo": "ContextualWisdomLab/appguardrail", + "error": null, + "component_count": 85, + "components": [ + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/create-github-app-token", + "version": "bcd2ba49218906704ab6c1aa796996da409d3eb1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/download-artifact", + "version": "37930b1c2abaa49bbe596cd826c3c89aef350131", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "anomalyco/opencode/github", + "version": "77fc88c8ade8e5a620ebbe1197f3a572d29ae91a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "aquasecurity/trivy-action", + "version": "ed142fd0673e97e23eac54620cfb913e5ce36c25", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "arrow", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "attrs", + "version": "26.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "backports-tarfile", + "version": "1.2.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "boolean-py", + "version": "5.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "build", + "version": "1.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "cachecontrol", + "version": "0.14.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "cffi", + "version": "2.0.0", + "license": "MIT-0", + "flagged": false + }, + { + "name": "chardet", + "version": "5.2.0", + "license": "LGPL-2.1-or-later", + "flagged": true + }, + { + "name": "charset-normalizer", + "version": "3.4.7", + "license": "MIT", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "cryptography", + "version": "49.0.0", + "license": "BSD-3-Clause OR Apache-2.0", + "flagged": false + }, + { + "name": "cyclonedx-bom", + "version": "7.3.0", + "license": "Apache-2.0 AND Python-2.0", + "flagged": false + }, + { + "name": "cyclonedx-python-lib", + "version": "11.11.0", + "license": "Apache-2.0 AND Python-2.0", + "flagged": false + }, + { + "name": "defusedxml", + "version": "0.7.1", + "license": "PSF-2.0", + "flagged": false + }, + { + "name": "docutils", + "version": "0.23", + "license": "BSD-2-Clause AND BSD-3-Clause AND CC-PDDC AND GPL-1.0-or-later AND GPL-3.0-only AND GPL-3.0-or-later AND LicenseRef-scancode-other-copyleft AND LicenseRef-scancode-public-domain", + "flagged": true + }, + { + "name": "filelock", + "version": "3.29.4", + "license": "MIT", + "flagged": false + }, + { + "name": "fqdn", + "version": "1.5.1", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "github/codeql-action/upload-sarif", + "version": "5595ccaf912efad79be6eef63a5619ff05969be3", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "id", + "version": "1.6.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "importlib-metadata", + "version": "9.0.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "isoduration", + "version": "20.11.0", + "license": "ISC", + "flagged": false + }, + { + "name": "jaraco-classes", + "version": "3.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jaraco-context", + "version": "6.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "jaraco-functools", + "version": "4.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jeepney", + "version": "0.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonpointer", + "version": "3.1.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "jsonschema", + "version": "4.26.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "keyring", + "version": "25.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "lark", + "version": "1.3.1", + "license": "MIT AND MPL-2.0", + "flagged": true + }, + { + "name": "license-expression", + "version": "30.4.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "lxml", + "version": "6.1.1", + "license": "BSD-3-Clause AND GPL-1.0-or-later", + "flagged": true + }, + { + "name": "markdown-it-py", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mdurl", + "version": "0.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "more-itertools", + "version": "11.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "msgpack", + "version": "1.2.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "nh3", + "version": "0.3.6", + "license": "MIT", + "flagged": false + }, + { + "name": "packageurl-python", + "version": "0.17.6", + "license": "MIT", + "flagged": false + }, + { + "name": "packaging", + "version": "26.2", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "pip-api", + "version": "0.0.34", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pip-audit", + "version": "2.10.1", + "license": "Apache-2.0 AND ISC", + "flagged": false + }, + { + "name": "pip-requirements-parser", + "version": "32.0.1", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "platformdirs", + "version": "4.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "py-serializable", + "version": "2.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pycparser", + "version": "3.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pypa/gh-action-pypi-publish", + "version": "dc37677b2e1c63e2034f94d8a5b11f265b73ba33", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pyparsing", + "version": "3.3.2", + "license": "MIT AND Python-2.0", + "flagged": false + }, + { + "name": "pyproject-hooks", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "readme-renderer", + "version": "45.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "referencing", + "version": "0.37.0", + "license": "MIT", + "flagged": false + }, + { + "name": "requests", + "version": "2.34.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "requests-toolbelt", + "version": "1.0.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "rfc3339-validator", + "version": "0.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "rfc3986", + "version": "2.0.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "rfc3986-validator", + "version": "0.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "rfc3987-syntax", + "version": "1.1.0", + "license": "Apache-2.0 AND GPL-1.0-or-later AND MIT", + "flagged": true + }, + { + "name": "rich", + "version": "15.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "rpds-py", + "version": "0.30.0", + "license": "MIT", + "flagged": false + }, + { + "name": "secretstorage", + "version": "3.5.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "setuptools", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "six", + "version": "1.17.0", + "license": "MIT", + "flagged": false + }, + { + "name": "sortedcontainers", + "version": "2.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "tomli", + "version": "2.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tomli-w", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "twine", + "version": "6.2.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "tzdata", + "version": "2026.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "uri-template", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "urllib3", + "version": "2.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "webcolors", + "version": "25.10.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "zipp", + "version": "4.1.0", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/bandscope", + "error": null, + "component_count": 1074, + "components": [ + { + "name": "@adobe/css-tools", + "version": "4.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@asamuzakjp/css-color", + "version": "5.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@asamuzakjp/dom-selector", + "version": "7.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@asamuzakjp/generational-cache", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@asamuzakjp/nwsapi", + "version": "2.3.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/code-frame", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/compat-data", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/core", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/generator", + "version": "7.29.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-compilation-targets", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-globals", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-module-imports", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-module-transforms", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-string-parser", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-validator-identifier", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-validator-option", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helpers", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/parser", + "version": "7.29.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/runtime", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/template", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/traverse", + "version": "7.29.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/types", + "version": "7.29.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@bandscope/desktop", + "version": "0.1.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "@bandscope/shared-types", + "version": "0.1.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "@base-ui/react", + "version": "1.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@base-ui/react", + "version": "^1.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@base-ui/utils", + "version": "0.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@bcoe/v8-coverage", + "version": "1.0.2", + "license": "ISC AND MIT", + "flagged": false + }, + { + "name": "@bramus/specificity", + "version": "2.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/color-helpers", + "version": "6.1.0", + "license": "MIT-0", + "flagged": false + }, + { + "name": "@csstools/css-calc", + "version": "3.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/css-color-parser", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/css-parser-algorithms", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/css-syntax-patches-for-csstree", + "version": "1.1.7", + "license": "MIT-0", + "flagged": false + }, + { + "name": "@csstools/css-tokenizer", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/core", + "version": "1.11.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/core", + "version": "1.9.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/runtime", + "version": "1.11.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/runtime", + "version": "1.9.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/wasi-threads", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/wasi-threads", + "version": "1.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@es-joy/jsdoccomment", + "version": "0.91.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@es-joy/resolve.exports", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/aix-ppc64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-arm", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/darwin-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/darwin-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/freebsd-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/freebsd-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-arm", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-ia32", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-loong64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-mips64el", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-ppc64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-riscv64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-s390x", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/netbsd-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/netbsd-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openbsd-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openbsd-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openharmony-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/sunos-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-ia32", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@eslint-community/eslint-utils", + "version": "4.10.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@eslint-community/regexpp", + "version": "4.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@eslint/config-array", + "version": "0.23.5", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@eslint/config-helpers", + "version": "0.7.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@eslint/core", + "version": "1.2.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@eslint/js", + "version": "10.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@eslint/object-schema", + "version": "3.0.5", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@eslint/plugin-kit", + "version": "0.7.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@exodus/bytes", + "version": "1.15.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@floating-ui/core", + "version": "1.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@floating-ui/dom", + "version": "1.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@floating-ui/react-dom", + "version": "2.1.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@floating-ui/utils", + "version": "0.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@fontsource-variable/geist", + "version": "5.3.0", + "license": "OFL-1.1", + "flagged": false + }, + { + "name": "@fontsource-variable/geist", + "version": "^5.2.9", + "license": "OFL-1.1", + "flagged": false + }, + { + "name": "@humanfs/core", + "version": "0.19.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@humanfs/node", + "version": "0.16.8", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@humanfs/types", + "version": "0.15.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@humanwhocodes/module-importer", + "version": "1.0.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@humanwhocodes/retry", + "version": "0.4.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@joshwooding/vite-plugin-react-docgen-typescript", + "version": "0.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/gen-mapping", + "version": "0.3.13", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/remapping", + "version": "2.3.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/resolve-uri", + "version": "3.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/sourcemap-codec", + "version": "1.5.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/trace-mapping", + "version": "0.3.31", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/canvas", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/canvas-android-arm64", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/canvas-darwin-arm64", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/canvas-darwin-x64", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/canvas-linux-arm-gnueabihf", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/canvas-linux-arm64-gnu", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/canvas-linux-arm64-musl", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/canvas-linux-riscv64-gnu", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/canvas-linux-x64-gnu", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/canvas-linux-x64-musl", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/canvas-win32-arm64-msvc", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/canvas-win32-x64-msvc", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/wasm-runtime", + "version": "1.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-android-arm-eabi", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-android-arm64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-darwin-arm64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-darwin-x64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-freebsd-x64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-arm-gnueabihf", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-arm-musleabihf", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-arm64-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-arm64-musl", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-ppc64-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-riscv64-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-riscv64-musl", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-s390x-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-x64-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-x64-musl", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-openharmony-arm64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-wasm32-wasi", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-win32-arm64-msvc", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-win32-ia32-msvc", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-win32-x64-msvc", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-project/types", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-project/types", + "version": "0.143.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-android-arm-eabi", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-android-arm64", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-darwin-arm64", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-darwin-x64", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-freebsd-x64", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-arm-gnueabihf", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-arm-musleabihf", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-arm64-gnu", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-arm64-musl", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-ppc64-gnu", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-riscv64-gnu", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-riscv64-musl", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-s390x-gnu", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-x64-gnu", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-x64-musl", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-openharmony-arm64", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-wasm32-wasi", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-win32-arm64-msvc", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-win32-x64-msvc", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-android-arm64", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-darwin-arm64", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-darwin-x64", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-freebsd-x64", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm-gnueabihf", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm64-gnu", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm64-musl", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-ppc64-gnu", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-s390x-gnu", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-x64-gnu", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-x64-musl", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-openharmony-arm64", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-win32-arm64-msvc", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-win32-x64-msvc", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/pluginutils", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/pluginutils", + "version": "5.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@sindresorhus/base62", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@standard-schema/spec", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/builder-vite", + "version": "10.5.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/csf-plugin", + "version": "10.5.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/global", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/icons", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/react", + "version": "10.5.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/react-dom-shim", + "version": "10.5.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/react-vite", + "version": "10.5.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/react-vite", + "version": "^10.4.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/node", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-android-arm64", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-darwin-arm64", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-darwin-x64", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-freebsd-x64", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-linux-arm-gnueabihf", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-linux-arm64-gnu", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-linux-arm64-musl", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-linux-x64-gnu", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-linux-x64-musl", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-wasm32-wasi", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-win32-arm64-msvc", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-win32-x64-msvc", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/vite", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/vite", + "version": "^4.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tauri-apps/api", + "version": "2.11.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "@tauri-apps/api", + "version": "^2.11.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "@tauri-apps/cli", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "@tauri-apps/cli", + "version": "^2.11.4", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "@tauri-apps/cli-darwin-arm64", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "@tauri-apps/cli-darwin-x64", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "@tauri-apps/cli-linux-arm-gnueabihf", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "@tauri-apps/cli-linux-arm64-gnu", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "@tauri-apps/cli-linux-arm64-musl", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "@tauri-apps/cli-linux-riscv64-gnu", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "@tauri-apps/cli-linux-x64-gnu", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "@tauri-apps/cli-linux-x64-musl", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "@tauri-apps/cli-win32-arm64-msvc", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "@tauri-apps/cli-win32-ia32-msvc", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "@tauri-apps/cli-win32-x64-msvc", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "@testing-library/dom", + "version": "10.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/jest-dom", + "version": "6.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/jest-dom", + "version": "^6.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/react", + "version": "16.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/react", + "version": "^16.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/user-event", + "version": "14.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tybys/wasm-util", + "version": "0.10.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/aria-query", + "version": "5.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__core", + "version": "7.20.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__generator", + "version": "7.27.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__template", + "version": "7.4.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__traverse", + "version": "7.28.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/chai", + "version": "5.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/deep-eql", + "version": "4.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/doctrine", + "version": "0.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/esrecurse", + "version": "4.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/estree", + "version": "1.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/json-schema", + "version": "7.0.15", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/node", + "version": "26.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/node", + "version": "^26.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react", + "version": "19.2.18", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react", + "version": "^19.2.17", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react-dom", + "version": "19.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react-dom", + "version": "^19.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/resolve", + "version": "1.20.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/eslint-plugin", + "version": "8.66.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/parser", + "version": "8.66.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/project-service", + "version": "8.66.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/scope-manager", + "version": "8.66.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/tsconfig-utils", + "version": "8.66.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/type-utils", + "version": "8.66.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/types", + "version": "8.66.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/typescript-estree", + "version": "8.66.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/utils", + "version": "8.66.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/visitor-keys", + "version": "8.66.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitejs/plugin-react", + "version": "6.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitejs/plugin-react", + "version": "^6.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/coverage-v8", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/coverage-v8", + "version": "^4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/expect", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/expect", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/mocker", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/pretty-format", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/pretty-format", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/runner", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/snapshot", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/spy", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/spy", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/utils", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/utils", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@webcontainer/env", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "acorn", + "version": "8.18.0", + "license": "MIT", + "flagged": false + }, + { + "name": "acorn-jsx", + "version": "5.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/download-artifact", + "version": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "a309ff8b426b58ec0e2a45f0f869d46889d02405", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "adler2", + "version": "2.0.1", + "license": "0BSD OR MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "aho-corasick", + "version": "1.1.4", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "ajv", + "version": "6.15.0", + "license": "MIT", + "flagged": false + }, + { + "name": "anchore/sbom-action", + "version": "e22c389904149dbc22b58101806040fa8d37a610", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "android_system_properties", + "version": "0.1.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ansi-regex", + "version": "5.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "5.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "antlr4-python3-runtime", + "version": "4.9.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "anyhow", + "version": "1.0.103", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "aquasecurity/trivy-action", + "version": "ed142fd0673e97e23eac54620cfb913e5ce36c25", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "are-docs-informative", + "version": "0.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "aria-query", + "version": "5.3.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "assertion-error", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ast-types", + "version": "0.16.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ast-v8-to-istanbul", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "astral-sh/setup-uv", + "version": "11f9893b081a58869d3b5fccaea48c9e9e46f990", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "atk", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "atk-sys", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "atomic-waker", + "version": "1.1.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "audioop-lts", + "version": "0.2.2", + "license": "Python-2.0 AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": false + }, + { + "name": "audioread", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "autocfg", + "version": "1.5.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "balanced-match", + "version": "4.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "bandit", + "version": "1.9.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "bandscope-analysis", + "version": "0.1.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "bandscope-workspace", + "version": "0.1.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "base64", + "version": "0.21.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "base64", + "version": "0.22.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "baseline-browser-mapping", + "version": "2.11.13", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "bidi-js", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "bit-set", + "version": "0.8.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bit-vec", + "version": "0.8.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bitflags", + "version": "1.3.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bitflags", + "version": "2.13.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "block-buffer", + "version": "0.10.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "block2", + "version": "0.6.2", + "license": "MIT", + "flagged": false + }, + { + "name": "brace-expansion", + "version": "5.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "browserslist", + "version": "4.28.8", + "license": "MIT", + "flagged": false + }, + { + "name": "bs58", + "version": "0.5.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bumpalo", + "version": "3.20.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bundle-name", + "version": "4.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "bytemuck", + "version": "1.25.1", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "byteorder", + "version": "1.5.0", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "bytes", + "version": "1.12.1", + "license": "MIT", + "flagged": false + }, + { + "name": "cairo-rs", + "version": "0.18.5", + "license": "MIT", + "flagged": false + }, + { + "name": "cairo-sys-rs", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "camino", + "version": "1.2.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "caniuse-lite", + "version": "1.0.30001809", + "license": "CC-BY-4.0", + "flagged": false + }, + { + "name": "cargo-platform", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cargo_metadata", + "version": "0.19.2", + "license": "MIT", + "flagged": false + }, + { + "name": "cargo_toml", + "version": "0.22.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "cc", + "version": "1.2.67", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "certifi", + "version": "2026.2.25", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "cesu8", + "version": "1.1.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "cfb", + "version": "0.7.3", + "license": "MIT", + "flagged": false + }, + { + "name": "cffi", + "version": "2.0.0", + "license": "MIT-0", + "flagged": false + }, + { + "name": "cfg-expr", + "version": "0.15.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cfg-if", + "version": "1.0.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "chai", + "version": "5.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "chai", + "version": "6.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "charset-normalizer", + "version": "3.4.6", + "license": "MIT", + "flagged": false + }, + { + "name": "check-error", + "version": "2.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "chrono", + "version": "0.4.45", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "class-variance-authority", + "version": "0.7.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "class-variance-authority", + "version": "^0.7.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "cloudpickle", + "version": "3.1.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "clsx", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "clsx", + "version": "^2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "combine", + "version": "4.6.7", + "license": "MIT", + "flagged": false + }, + { + "name": "comment-parser", + "version": "1.4.7", + "license": "MIT", + "flagged": false + }, + { + "name": "convert-source-map", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "cookie", + "version": "0.18.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "core-foundation", + "version": "0.10.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "core-foundation-sys", + "version": "0.8.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "core-graphics", + "version": "0.25.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "core-graphics-types", + "version": "0.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "coverage", + "version": "7.13.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "cpufeatures", + "version": "0.2.17", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crc32fast", + "version": "1.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cross-spawn", + "version": "7.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "crossbeam-channel", + "version": "0.5.16", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crossbeam-utils", + "version": "0.8.22", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crypto-common", + "version": "0.1.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "css-tree", + "version": "3.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "css.escape", + "version": "1.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "cssparser", + "version": "0.36.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "cssparser-macros", + "version": "0.6.1", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "csstype", + "version": "3.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "ctor", + "version": "0.8.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "ctor-proc-macro", + "version": "0.0.7", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "cuda-bindings", + "version": "13.3.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "cuda-pathfinder", + "version": "1.5.6", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "cuda-toolkit", + "version": "13.0.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "darling", + "version": "0.23.0", + "license": "MIT", + "flagged": false + }, + { + "name": "darling_core", + "version": "0.23.0", + "license": "MIT", + "flagged": false + }, + { + "name": "darling_macro", + "version": "0.23.0", + "license": "MIT", + "flagged": false + }, + { + "name": "data-urls", + "version": "7.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "debug", + "version": "4.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "decimal.js", + "version": "10.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "decorator", + "version": "5.2.1", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "deep-eql", + "version": "5.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "deep-is", + "version": "0.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "default-browser", + "version": "5.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "default-browser-id", + "version": "5.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "define-lazy-prop", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "demucs", + "version": "4.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "dequal", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "deranged", + "version": "0.5.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "derive_more", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "derive_more-impl", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "detect-libc", + "version": "2.1.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "digest", + "version": "0.10.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dirs", + "version": "6.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dirs-sys", + "version": "0.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dispatch2", + "version": "0.3.1", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "displaydoc", + "version": "0.2.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dlib", + "version": "0.5.3", + "license": "MIT", + "flagged": false + }, + { + "name": "dlopen2", + "version": "0.8.2", + "license": "MIT", + "flagged": false + }, + { + "name": "dlopen2_derive", + "version": "0.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "doctrine", + "version": "3.0.0", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "dom-accessibility-api", + "version": "0.5.16", + "license": "MIT", + "flagged": false + }, + { + "name": "dom-accessibility-api", + "version": "0.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "dom_query", + "version": "0.27.0", + "license": "MIT", + "flagged": false + }, + { + "name": "dora-search", + "version": "0.1.12", + "license": "MIT", + "flagged": false + }, + { + "name": "downcast-rs", + "version": "1.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dpi", + "version": "0.1.2", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "dtoa", + "version": "1.0.11", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dtoa-short", + "version": "0.3.5", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "dtor", + "version": "0.3.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "dtor-proc-macro", + "version": "0.0.6", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "dunce", + "version": "1.0.5", + "license": "CC0-1.0 OR MIT-0 OR Apache-2.0", + "flagged": false + }, + { + "name": "dyn-clone", + "version": "1.0.20", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "einops", + "version": "0.8.2", + "license": "MIT", + "flagged": false + }, + { + "name": "electron-to-chromium", + "version": "1.5.403", + "license": "ISC", + "flagged": false + }, + { + "name": "embed-resource", + "version": "3.0.11", + "license": "MIT", + "flagged": false + }, + { + "name": "embed_plist", + "version": "1.2.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "empathic", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "enhanced-resolve", + "version": "5.24.5", + "license": "MIT", + "flagged": false + }, + { + "name": "entities", + "version": "8.0.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "equivalent", + "version": "1.0.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "erased-serde", + "version": "0.4.10", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "errno", + "version": "0.3.14", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "es-errors", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-module-lexer", + "version": "2.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "esbuild", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "escalade", + "version": "3.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "escape-string-regexp", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "eslint", + "version": "10.8.1", + "license": "MIT", + "flagged": false + }, + { + "name": "eslint", + "version": "^10.7.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "eslint-plugin-jsdoc", + "version": "63.3.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "eslint-scope", + "version": "9.1.2", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "eslint-visitor-keys", + "version": "3.4.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "eslint-visitor-keys", + "version": "5.0.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "espree", + "version": "11.2.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "esprima", + "version": "4.0.1", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "esquery", + "version": "1.7.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "esrecurse", + "version": "4.3.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "estraverse", + "version": "5.3.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "estree-walker", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "estree-walker", + "version": "3.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "esutils", + "version": "2.0.3", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "expect-type", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "fast-check", + "version": "4.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-check", + "version": "^4.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-deep-equal", + "version": "3.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-json-stable-stringify", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-levenshtein", + "version": "2.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "fastrand", + "version": "2.4.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "fdeflate", + "version": "0.3.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "fdir", + "version": "6.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "field-offset", + "version": "0.3.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "file-entry-cache", + "version": "8.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "filelock", + "version": "3.29.5", + "license": "MIT", + "flagged": false + }, + { + "name": "find-msvc-tools", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "find-up", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "flat-cache", + "version": "4.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "flate2", + "version": "1.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "flatted", + "version": "3.4.4", + "license": "ISC", + "flagged": false + }, + { + "name": "fnv", + "version": "1.0.7", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "foldhash", + "version": "0.2.0", + "license": "Zlib", + "flagged": false + }, + { + "name": "foreign-types", + "version": "0.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "foreign-types-macros", + "version": "0.2.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "foreign-types-shared", + "version": "0.3.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "form_urlencoded", + "version": "1.2.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "fsevents", + "version": "2.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "fsspec", + "version": "2026.6.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "function-bind", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "futures-channel", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-core", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-executor", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-io", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-macro", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-sink", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-task", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-util", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "gdk", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "gdk-pixbuf", + "version": "0.18.5", + "license": "MIT", + "flagged": false + }, + { + "name": "gdk-pixbuf-sys", + "version": "0.18.0", + "license": "MIT", + "flagged": false + }, + { + "name": "gdk-sys", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "gdkwayland-sys", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "generic-array", + "version": "0.14.7", + "license": "MIT", + "flagged": false + }, + { + "name": "gensync", + "version": "1.0.0-beta.2", + "license": "MIT", + "flagged": false + }, + { + "name": "getrandom", + "version": "0.2.17", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "getrandom", + "version": "0.3.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "getrandom", + "version": "0.4.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "gio", + "version": "0.18.4", + "license": "MIT", + "flagged": false + }, + { + "name": "gio-sys", + "version": "0.18.1", + "license": "MIT", + "flagged": false + }, + { + "name": "github/codeql-action/upload-sarif", + "version": "99df26d4f13ea111d4ec1a7dddef6063f76b97e9", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "glib", + "version": "0.18.5", + "license": "MIT", + "flagged": false + }, + { + "name": "glib-macros", + "version": "0.18.5", + "license": "MIT", + "flagged": false + }, + { + "name": "glib-sys", + "version": "0.18.1", + "license": "MIT", + "flagged": false + }, + { + "name": "glob", + "version": "0.3.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "glob", + "version": "13.0.6", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "glob-parent", + "version": "6.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "gobject-sys", + "version": "0.18.0", + "license": "MIT", + "flagged": false + }, + { + "name": "graceful-fs", + "version": "4.2.11", + "license": "ISC", + "flagged": false + }, + { + "name": "gtk", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "gtk-sys", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "gtk3-macros", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "has-flag", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hashbrown", + "version": "0.12.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hashbrown", + "version": "0.17.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hasown", + "version": "2.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "heck", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "heck", + "version": "0.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hex", + "version": "0.4.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "html-encoding-sniffer", + "version": "6.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "html-entities", + "version": "2.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "html-escaper", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "html5ever", + "version": "0.38.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "http", + "version": "1.4.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "http-body", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "http-body-util", + "version": "0.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "httparse", + "version": "1.10.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hyper", + "version": "1.10.1", + "license": "MIT", + "flagged": false + }, + { + "name": "hyper-util", + "version": "0.1.20", + "license": "MIT", + "flagged": false + }, + { + "name": "iana-time-zone", + "version": "0.1.65", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "iana-time-zone-haiku", + "version": "0.1.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ico", + "version": "0.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "icu_collections", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_locale_core", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_normalizer", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_normalizer_data", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_properties", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_properties_data", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_provider", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "ident_case", + "version": "1.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "idna", + "version": "1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "idna_adapter", + "version": "1.2.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "ignore", + "version": "5.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ignore", + "version": "7.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "imurmurhash", + "version": "0.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "indent-string", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "indexmap", + "version": "1.9.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "indexmap", + "version": "2.14.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "infer", + "version": "0.19.0", + "license": "MIT", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ipnet", + "version": "2.12.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "is-core-module", + "version": "2.16.2", + "license": "MIT", + "flagged": false + }, + { + "name": "is-docker", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-extglob", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-glob", + "version": "4.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "is-inside-container", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-potential-custom-element-name", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-wsl", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "isexe", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "istanbul-lib-coverage", + "version": "3.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-lib-report", + "version": "3.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-reports", + "version": "3.2.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "itoa", + "version": "1.0.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "javascriptcore-rs", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "javascriptcore-rs-sys", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jinja2", + "version": "3.1.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "jiti", + "version": "2.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jni", + "version": "0.21.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jni-sys", + "version": "0.3.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jni-sys", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jni-sys-macros", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "joblib", + "version": "1.5.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "js-sys", + "version": "0.3.103", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "js-tokens", + "version": "10.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "js-tokens", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jsdoc-type-pratt-parser", + "version": "8.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jsdom", + "version": "29.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jsdom", + "version": "^29.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jsesc", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "json-buffer", + "version": "3.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "json-patch", + "version": "3.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "json-schema-traverse", + "version": "0.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "json-stable-stringify-without-jsonify", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "json5", + "version": "2.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonc-parser", + "version": "3.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonptr", + "version": "0.6.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "julius", + "version": "0.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "keyboard-types", + "version": "0.7.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "keyv", + "version": "4.5.4", + "license": "MIT", + "flagged": false + }, + { + "name": "lameenc", + "version": "1.8.4", + "license": "GPL-3.0-or-later", + "flagged": true + }, + { + "name": "lazy-loader", + "version": "0.5", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "levn", + "version": "0.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "libc", + "version": "0.2.186", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "libloading", + "version": "0.8.9", + "license": "ISC", + "flagged": false + }, + { + "name": "libredox", + "version": "0.1.18", + "license": "MIT", + "flagged": false + }, + { + "name": "librosa", + "version": "0.11.0", + "license": "ISC", + "flagged": false + }, + { + "name": "librt", + "version": "0.8.1", + "license": "BSD-2-Clause AND MIT AND Python-2.0 AND Python-2.0.1", + "flagged": false + }, + { + "name": "lightningcss", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-android-arm64", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-arm64", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-x64", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-freebsd-x64", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-gnu", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-musl", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-gnu", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-musl", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-arm64-msvc", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-x64-msvc", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "linux-raw-sys", + "version": "0.12.1", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "litemap", + "version": "0.8.2", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "llvmlite", + "version": "0.45.1", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "locate-path", + "version": "6.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "lock_api", + "version": "0.4.14", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "log", + "version": "0.4.33", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "loupe", + "version": "3.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "lru-cache", + "version": "11.5.2", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "lru-cache", + "version": "5.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "lucide-react", + "version": "1.30.0", + "license": "ISC", + "flagged": false + }, + { + "name": "lucide-react", + "version": "^1.24.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "lz-string", + "version": "1.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "magic-string", + "version": "0.30.21", + "license": "MIT", + "flagged": false + }, + { + "name": "magicast", + "version": "0.5.4", + "license": "MIT", + "flagged": false + }, + { + "name": "make-dir", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "markdown-it-py", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "markup5ever", + "version": "0.38.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "markupsafe", + "version": "3.0.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "matrixmultiply", + "version": "0.3.10", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "maturin", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "mdn-data", + "version": "2.27.1", + "license": "CC0-1.0", + "flagged": false + }, + { + "name": "mdurl", + "version": "0.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "memchr", + "version": "2.8.3", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "memoffset", + "version": "0.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "mime", + "version": "0.3.17", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "min-indent", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "minimatch", + "version": "10.2.6", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "minimist", + "version": "1.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "minipass", + "version": "7.1.3", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "miniz_oxide", + "version": "0.8.9", + "license": "MIT OR Zlib OR Apache-2.0", + "flagged": false + }, + { + "name": "mio", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "mpmath", + "version": "1.3.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "ms", + "version": "2.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "msgpack", + "version": "1.2.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "muda", + "version": "0.19.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "mypy", + "version": "1.19.1", + "license": "BSD-2-Clause AND MIT AND Python-2.0 AND Python-2.0.1", + "flagged": false + }, + { + "name": "mypy-extensions", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "nanoid", + "version": "3.3.18", + "license": "MIT", + "flagged": false + }, + { + "name": "natural-compare", + "version": "1.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ndarray", + "version": "0.16.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ndk", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ndk-sys", + "version": "0.6.0+11769913", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "networkx", + "version": "3.6.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "new_debug_unreachable", + "version": "1.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "node-releases", + "version": "2.0.53", + "license": "MIT", + "flagged": false + }, + { + "name": "num-complex", + "version": "0.4.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-conv", + "version": "0.2.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-integer", + "version": "0.1.46", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-traits", + "version": "0.2.19", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num_enum", + "version": "0.7.6", + "license": "BSD-3-Clause OR MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num_enum_derive", + "version": "0.7.6", + "license": "BSD-3-Clause OR MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "numba", + "version": "0.62.1", + "license": "0BSD AND BSD-2-Clause AND BSD-3-Clause AND BSD-4-Clause AND LicenseRef-scancode-python-cwi AND LicenseRef-scancode-secret-labs-2011 AND LicenseRef-scancode-unicode AND MIT AND Python-2.0", + "flagged": false + }, + { + "name": "numpy", + "version": "0.29.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "numpy", + "version": "2.3.5", + "license": "Apache-2.0 AND BSD-3-Clause AND MIT AND Zlib", + "flagged": false + }, + { + "name": "nvidia-cublas", + "version": "13.1.1.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "nvidia-cuda-cupti", + "version": "13.0.85", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "nvidia-cuda-nvrtc", + "version": "13.0.88", + "license": "LicenseRef-NVIDIA-Proprietary", + "flagged": false + }, + { + "name": "nvidia-cuda-runtime", + "version": "13.0.96", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "nvidia-cudnn-cu13", + "version": "9.20.0.48", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "nvidia-cufft", + "version": "12.0.0.61", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "nvidia-cufile", + "version": "1.15.1.6", + "license": "LicenseRef-NVIDIA-Proprietary", + "flagged": false + }, + { + "name": "nvidia-curand", + "version": "10.4.0.35", + "license": "LicenseRef-NVIDIA-Proprietary", + "flagged": false + }, + { + "name": "nvidia-cusolver", + "version": "12.0.4.66", + "license": "LicenseRef-NVIDIA-Proprietary", + "flagged": false + }, + { + "name": "nvidia-cusparse", + "version": "12.6.3.3", + "license": "LicenseRef-NVIDIA-Proprietary", + "flagged": false + }, + { + "name": "nvidia-cusparselt-cu13", + "version": "0.8.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "nvidia-nccl-cu13", + "version": "2.29.7", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "nvidia-nvjitlink", + "version": "13.0.88", + "license": "LicenseRef-NVIDIA-Proprietary", + "flagged": false + }, + { + "name": "nvidia-nvshmem-cu13", + "version": "3.4.5", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "nvidia-nvtx", + "version": "13.0.85", + "license": "LicenseRef-NVIDIA-Proprietary", + "flagged": false + }, + { + "name": "objc2", + "version": "0.6.4", + "license": "MIT", + "flagged": false + }, + { + "name": "objc2-app-kit", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-cloud-kit", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-core-data", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-core-foundation", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-core-graphics", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-core-image", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-core-location", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-core-text", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-encode", + "version": "4.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "objc2-exception-helper", + "version": "0.1.1", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-foundation", + "version": "0.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "objc2-io-surface", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-quartz-core", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-ui-kit", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-user-notifications", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-web-kit", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "object-deep-merge", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "obug", + "version": "2.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "omegaconf", + "version": "2.3.1", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "once_cell", + "version": "1.21.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "open", + "version": "10.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "openunmix", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "option-ext", + "version": "0.2.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "optionator", + "version": "0.9.4", + "license": "MIT", + "flagged": false + }, + { + "name": "ossf/scorecard-action", + "version": "4eaacf0543bb3f2c246792bd56e8cdeffafb205a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "oxc-parser", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "oxc-resolver", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "p-limit", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "p-locate", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "packaging", + "version": "26.0", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "pango", + "version": "0.18.3", + "license": "MIT", + "flagged": false + }, + { + "name": "pango-sys", + "version": "0.18.0", + "license": "MIT", + "flagged": false + }, + { + "name": "parking_lot", + "version": "0.12.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "parking_lot_core", + "version": "0.9.12", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "parse-imports-exports", + "version": "0.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "parse-statements", + "version": "1.0.11", + "license": "JSON AND MIT", + "flagged": false + }, + { + "name": "parse5", + "version": "8.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "path-exists", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "path-key", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "path-parse", + "version": "1.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "path-scurry", + "version": "2.0.2", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "pathe", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "pathspec", + "version": "1.0.4", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "pathval", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pdfjs-dist", + "version": "6.2.108", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "percent-encoding", + "version": "2.3.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "phf", + "version": "0.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "phf_codegen", + "version": "0.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "phf_generator", + "version": "0.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "phf_macros", + "version": "0.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "phf_shared", + "version": "0.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "picocolors", + "version": "1.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "picomatch", + "version": "4.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "pin-project-lite", + "version": "0.2.17", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "pkg-config", + "version": "0.3.33", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "platformdirs", + "version": "4.9.4", + "license": "MIT", + "flagged": false + }, + { + "name": "plist", + "version": "1.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "png", + "version": "0.17.16", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "png", + "version": "0.18.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pollster", + "version": "0.4.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "pooch", + "version": "1.9.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "portable-atomic", + "version": "1.13.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "portable-atomic-util", + "version": "0.2.7", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "postcss", + "version": "8.5.25", + "license": "MIT", + "flagged": false + }, + { + "name": "potential_utf", + "version": "0.1.5", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "powerfmt", + "version": "0.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "precomputed-hash", + "version": "0.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "prelude-ls", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pretty-format", + "version": "27.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "proc-macro-crate", + "version": "1.3.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro-crate", + "version": "2.0.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro-crate", + "version": "3.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro-error", + "version": "1.0.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro-error-attr", + "version": "1.0.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro2", + "version": "1.0.106", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "punycode", + "version": "2.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pure-rand", + "version": "8.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "pycparser", + "version": "3.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pyo3", + "version": "0.29.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-build-config", + "version": "0.29.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-ffi", + "version": "0.29.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-macros", + "version": "0.29.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-macros-backend", + "version": "0.29.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pytest", + "version": "9.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest-cov", + "version": "7.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pyyaml", + "version": "6.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "quick-xml", + "version": "0.39.4", + "license": "MIT", + "flagged": false + }, + { + "name": "quick-xml", + "version": "0.41.0", + "license": "MIT", + "flagged": false + }, + { + "name": "quote", + "version": "1.0.46", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "r-efi", + "version": "5.3.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later", + "flagged": true + }, + { + "name": "r-efi", + "version": "6.0.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later", + "flagged": true + }, + { + "name": "raw-window-handle", + "version": "0.6.2", + "license": "MIT OR Apache-2.0 OR Zlib", + "flagged": false + }, + { + "name": "rawpointer", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "react", + "version": "19.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "react", + "version": "^19.2.4", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "react-docgen", + "version": "8.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "react-docgen-typescript", + "version": "2.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "react-dom", + "version": "19.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "react-dom", + "version": "^19.2.7", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "react-is", + "version": "17.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "recast", + "version": "0.23.19", + "license": "MIT", + "flagged": false + }, + { + "name": "redent", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "redox_syscall", + "version": "0.5.18", + "license": "MIT", + "flagged": false + }, + { + "name": "redox_users", + "version": "0.5.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ref-cast", + "version": "1.0.25", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ref-cast-impl", + "version": "1.0.25", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "regex", + "version": "1.13.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "regex-automata", + "version": "0.4.15", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "regex-syntax", + "version": "0.8.11", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "requests", + "version": "2.33.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "require-from-string", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "reqwest", + "version": "0.13.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "reselect", + "version": "5.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "reserved-identifiers", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "resolve", + "version": "1.22.12", + "license": "MIT", + "flagged": false + }, + { + "name": "retrying", + "version": "1.4.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "rfd", + "version": "0.17.2", + "license": "MIT", + "flagged": false + }, + { + "name": "rich", + "version": "15.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "rolldown", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "ruff", + "version": "0.15.5", + "license": "MIT", + "flagged": false + }, + { + "name": "run-applescript", + "version": "7.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "rustc-hash", + "version": "2.1.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "rustc_version", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rustix", + "version": "1.1.4", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "rustversion", + "version": "1.0.23", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "same-file", + "version": "1.0.6", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "saxes", + "version": "6.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "scheduler", + "version": "0.27.0", + "license": "MIT", + "flagged": false + }, + { + "name": "schemars", + "version": "0.8.22", + "license": "MIT", + "flagged": false + }, + { + "name": "schemars", + "version": "0.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "schemars", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "schemars_derive", + "version": "0.8.22", + "license": "MIT", + "flagged": false + }, + { + "name": "scikit-learn", + "version": "1.8.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "scipy", + "version": "1.17.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "scoped-tls", + "version": "1.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "scopeguard", + "version": "1.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "selectors", + "version": "0.36.1", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "semver", + "version": "1.0.28", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "semver", + "version": "6.3.1", + "license": "ISC", + "flagged": false + }, + { + "name": "semver", + "version": "7.8.5", + "license": "ISC", + "flagged": false + }, + { + "name": "serde", + "version": "1.0.228", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "serde-untagged", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_core", + "version": "1.0.228", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_derive", + "version": "1.0.228", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_derive_internals", + "version": "0.29.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_json", + "version": "1.0.150", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_json", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "serde_repr", + "version": "0.1.20", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_spanned", + "version": "0.6.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_spanned", + "version": "1.1.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_with", + "version": "3.21.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_with_macros", + "version": "3.21.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serialize-to-javascript", + "version": "0.1.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serialize-to-javascript-impl", + "version": "0.1.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "servo_arc", + "version": "0.4.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "setuptools", + "version": "81.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "sha2", + "version": "0.10.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "shebang-command", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "shebang-regex", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "shlex", + "version": "2.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "siginfo", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "simd-adler32", + "version": "0.3.9", + "license": "MIT", + "flagged": false + }, + { + "name": "siphasher", + "version": "1.0.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "slab", + "version": "0.4.12", + "license": "MIT", + "flagged": false + }, + { + "name": "smallvec", + "version": "1.15.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "socket2", + "version": "0.6.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "softbuffer", + "version": "0.4.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "sonner", + "version": "2.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "sonner", + "version": "^2.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "soundfile", + "version": "0.13.1", + "license": "BSD-3-Clause AND Python-2.0", + "flagged": false + }, + { + "name": "soup3", + "version": "0.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "soup3-sys", + "version": "0.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "source-map", + "version": "0.6.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "source-map-js", + "version": "1.2.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "soxr", + "version": "1.0.0", + "license": "Python-2.0 AND LGPL-2.1-or-later", + "flagged": true + }, + { + "name": "spdx-exceptions", + "version": "2.5.0", + "license": "CC-BY-3.0", + "flagged": false + }, + { + "name": "spdx-expression-parse", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "spdx-license-ids", + "version": "3.0.23", + "license": "CC0-1.0", + "flagged": false + }, + { + "name": "stable_deref_trait", + "version": "1.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "stackback", + "version": "0.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "standard-aifc", + "version": "3.13.0", + "license": "PSF-2.0", + "flagged": false + }, + { + "name": "standard-chunk", + "version": "3.13.0", + "license": "PSF-2.0", + "flagged": false + }, + { + "name": "standard-sunau", + "version": "3.13.0", + "license": "PSF-2.0", + "flagged": false + }, + { + "name": "std-env", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "stevedore", + "version": "5.7.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "storybook", + "version": "10.5.7", + "license": "MIT", + "flagged": false + }, + { + "name": "storybook", + "version": "^10.4.6", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "string_cache", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "string_cache_codegen", + "version": "0.6.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "strip-bom", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-indent", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-indent", + "version": "4.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "strsim", + "version": "0.11.1", + "license": "MIT", + "flagged": false + }, + { + "name": "submitit", + "version": "1.5.4", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-color", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-preserve-symlinks-flag", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "swift-rs", + "version": "1.0.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "symbol-tree", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "sympy", + "version": "1.14.0", + "license": "BSD-2-Clause AND BSD-3-Clause AND MIT", + "flagged": false + }, + { + "name": "syn", + "version": "1.0.109", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "syn", + "version": "2.0.118", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "sync_wrapper", + "version": "1.0.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "synstructure", + "version": "0.13.2", + "license": "MIT", + "flagged": false + }, + { + "name": "system-deps", + "version": "6.2.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "tailwind-merge", + "version": "3.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tailwind-merge", + "version": "^3.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tailwindcss", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tailwindcss", + "version": "^4.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "tao", + "version": "0.35.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "tao-macros", + "version": "0.1.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "tapable", + "version": "2.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "target-lexicon", + "version": "0.12.16", + "license": "Apache-2.0 WITH LLVM-exception", + "flagged": false + }, + { + "name": "target-lexicon", + "version": "0.13.5", + "license": "Apache-2.0 WITH LLVM-exception", + "flagged": false + }, + { + "name": "tauri", + "version": "2.11.5", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-build", + "version": "2.6.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-codegen", + "version": "2.6.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-macros", + "version": "2.6.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-runtime", + "version": "2.11.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-runtime-wry", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-utils", + "version": "2.9.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-winres", + "version": "0.3.6", + "license": "MIT", + "flagged": false + }, + { + "name": "tendril", + "version": "0.5.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror", + "version": "1.0.69", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror", + "version": "2.0.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror-impl", + "version": "1.0.69", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror-impl", + "version": "2.0.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "threadpoolctl", + "version": "3.6.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "time", + "version": "0.3.53", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "time", + "version": ">= 0.3.0,< 0.4.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "time-core", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "time-macros", + "version": "0.2.31", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "tiny-invariant", + "version": "1.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tinybench", + "version": "2.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyexec", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyglobby", + "version": "0.2.17", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyrainbow", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyrainbow", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyspy", + "version": "4.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "tinystr", + "version": "0.8.3", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "tinyvec", + "version": "1.12.0", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tinyvec_macros", + "version": "0.1.1", + "license": "MIT OR Apache-2.0 OR Zlib", + "flagged": false + }, + { + "name": "tldts", + "version": "7.4.10", + "license": "MIT", + "flagged": false + }, + { + "name": "tldts-core", + "version": "7.4.10", + "license": "MIT", + "flagged": false + }, + { + "name": "to-valid-identifier", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tokio", + "version": "1.52.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tokio-util", + "version": "0.7.18", + "license": "MIT", + "flagged": false + }, + { + "name": "toml", + "version": "0.8.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml", + "version": "0.9.12+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml", + "version": "1.1.2+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_datetime", + "version": "0.6.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_datetime", + "version": "0.7.5+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_datetime", + "version": "1.1.1+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_edit", + "version": "0.19.15", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_edit", + "version": "0.20.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_edit", + "version": "0.25.12+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_parser", + "version": "1.1.2+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_writer", + "version": "1.1.1+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "torch", + "version": "2.12.1", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "torchaudio", + "version": "2.11.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "tough-cookie", + "version": "6.0.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "tower", + "version": "0.5.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tower-http", + "version": "0.6.11", + "license": "MIT", + "flagged": false + }, + { + "name": "tower-layer", + "version": "0.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tower-service", + "version": "0.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tqdm", + "version": "4.68.3", + "license": "MIT AND MPL-2.0", + "flagged": true + }, + { + "name": "tr46", + "version": "6.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tracing", + "version": "0.1.44", + "license": "MIT", + "flagged": false + }, + { + "name": "tracing-core", + "version": "0.1.36", + "license": "MIT", + "flagged": false + }, + { + "name": "treetable", + "version": "0.2.6", + "license": "Unlicense", + "flagged": false + }, + { + "name": "triton", + "version": "3.7.1", + "license": "MIT", + "flagged": false + }, + { + "name": "try-lock", + "version": "0.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "ts-api-utils", + "version": "2.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ts-dedent", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tsconfig-paths", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tslib", + "version": "2.8.1", + "license": "0BSD", + "flagged": false + }, + { + "name": "tw-animate-css", + "version": "1.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tw-animate-css", + "version": "^1.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "type-check", + "version": "0.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "typeid", + "version": "1.0.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "typenum", + "version": "1.20.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "typescript", + "version": "6.0.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "typescript", + "version": "^6.0.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "typescript-eslint", + "version": "8.66.0", + "license": "MIT", + "flagged": false + }, + { + "name": "typescript-eslint", + "version": "^8.63.0", + "license": "MIT", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.15.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "undici", + "version": "7.29.0", + "license": "MIT", + "flagged": false + }, + { + "name": "undici-types", + "version": "8.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "unic-char-property", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unic-char-range", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unic-common", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unic-ucd-ident", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unic-ucd-version", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unicode-ident", + "version": "1.0.24", + "license": "(MIT OR Apache-2.0) AND Unicode-3.0", + "flagged": false + }, + { + "name": "unicode-segmentation", + "version": "1.13.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unplugin", + "version": "2.3.11", + "license": "MIT", + "flagged": false + }, + { + "name": "update-browserslist-db", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "uri-js", + "version": "4.4.1", + "license": "BSD-2-Clause AND BSD-2-Clause-Views", + "flagged": false + }, + { + "name": "url", + "version": "2.5.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "url", + "version": ">= 2.5.8,< 3.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "urllib3", + "version": "2.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "urlpattern", + "version": "0.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "use-sync-external-store", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "utf8_iter", + "version": "1.0.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "uuid", + "version": "1.23.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "uuid", + "version": "1.25.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "uuid", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "version-compare", + "version": "0.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "version_check", + "version": "0.9.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "vite", + "version": "8.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "vite", + "version": "^8.1.4", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "vitest", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "vitest", + "version": "^4.1.10", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "vswhom", + "version": "0.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "vswhom-sys", + "version": "0.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "w3c-xmlserializer", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "walkdir", + "version": "2.5.0", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "want", + "version": "0.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "wasi", + "version": "0.11.1+wasi-snapshot-preview1", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "wasip2", + "version": "1.0.4+wasi-0.2.12", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "wasm-bindgen", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-futures", + "version": "0.4.76", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-macro", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-macro-support", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-shared", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-streams", + "version": "0.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wayland-backend", + "version": "0.3.15", + "license": "MIT", + "flagged": false + }, + { + "name": "wayland-client", + "version": "0.31.14", + "license": "MIT", + "flagged": false + }, + { + "name": "wayland-protocols", + "version": "0.32.13", + "license": "MIT", + "flagged": false + }, + { + "name": "wayland-scanner", + "version": "0.31.10", + "license": "MIT", + "flagged": false + }, + { + "name": "wayland-sys", + "version": "0.31.11", + "license": "MIT", + "flagged": false + }, + { + "name": "web-sys", + "version": "0.3.103", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "web_atoms", + "version": "0.2.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "webidl-conversions", + "version": "8.0.1", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "webkit2gtk", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "webkit2gtk-sys", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "webpack-virtual-modules", + "version": "0.6.2", + "license": "MIT", + "flagged": false + }, + { + "name": "webview2-com", + "version": "0.38.2", + "license": "MIT", + "flagged": false + }, + { + "name": "webview2-com-macros", + "version": "0.8.1", + "license": "MIT", + "flagged": false + }, + { + "name": "webview2-com-sys", + "version": "0.38.2", + "license": "MIT", + "flagged": false + }, + { + "name": "whatwg-mimetype", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "whatwg-url", + "version": "16.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "which", + "version": "2.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "why-is-node-running", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "winapi", + "version": "0.3.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "winapi-i686-pc-windows-gnu", + "version": "0.4.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "winapi-util", + "version": "0.1.11", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "version": "0.4.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "window-vibrancy", + "version": "0.6.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "windows", + "version": "0.61.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-collections", + "version": "0.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-core", + "version": "0.61.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-core", + "version": "0.62.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-future", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-implement", + "version": "0.60.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-interface", + "version": "0.59.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-link", + "version": "0.1.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-link", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-numerics", + "version": "0.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-result", + "version": "0.3.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-result", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-strings", + "version": "0.4.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-strings", + "version": "0.5.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-sys", + "version": "0.45.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-sys", + "version": "0.59.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-sys", + "version": "0.61.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-targets", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-targets", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-threading", + "version": "0.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-version", + "version": "0.1.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_gnullvm", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_gnullvm", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_msvc", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_msvc", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_gnu", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_gnu", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_gnullvm", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_msvc", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_msvc", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnu", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnu", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnullvm", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnullvm", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_msvc", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_msvc", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "winnow", + "version": "0.5.40", + "license": "MIT", + "flagged": false + }, + { + "name": "winnow", + "version": "0.7.15", + "license": "MIT", + "flagged": false + }, + { + "name": "winnow", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "winreg", + "version": "0.55.0", + "license": "MIT", + "flagged": false + }, + { + "name": "wit-bindgen", + "version": "0.57.1", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "word-wrap", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "writeable", + "version": "0.6.3", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "wry", + "version": "0.55.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "ws", + "version": "8.21.3", + "license": "MIT", + "flagged": false + }, + { + "name": "wsl-utils", + "version": "0.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "xml-name-validator", + "version": "5.0.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "xmlchars", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "yallist", + "version": "3.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "yocto-queue", + "version": "0.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "yoke", + "version": "0.8.3", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "yoke-derive", + "version": "0.8.2", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "yt-dlp", + "version": "2026.7.4", + "license": "Unlicense AND Unlicense AND GPL-3.0-or-later AND MPL-2.0 AND MIT AND BSD-3-Clause AND Apache-2.0 AND MIT AND GPL-2.0-or-later AND BSD-3-Clause AND MIT AND LGPL-2.1-only AND BSD-2-Clause AND GPL-2.0-or-later", + "flagged": true + }, + { + "name": "zerofrom", + "version": "0.1.8", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zerofrom-derive", + "version": "0.1.7", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zerotrie", + "version": "0.2.4", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zerovec", + "version": "0.11.6", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zerovec-derive", + "version": "0.11.3", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zmij", + "version": "1.0.21", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/CalendarWeave", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/ccube-jco-potential-customer", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/clearfolio", + "error": null, + "component_count": 30, + "components": [ + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-java", + "version": "b6effb05e454b25005698d916606bdc6ffcbf961", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "com.code-intelligence:jazzer-api", + "version": "0.30.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "com.code-intelligence:jazzer-junit", + "version": "0.30.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "com.fasterxml.jackson.core:jackson-databind", + "version": "2.22.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "com.fasterxml.jackson:jackson-bom", + "version": "2.22.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "com.github.junrar:junrar", + "version": "8.1.0", + "license": "LicenseRef-bad-non-standard", + "flagged": false + }, + { + "name": "commons-io:commons-io", + "version": "2.22.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "org.apache.commons:commons-lang3", + "version": "3.20.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "org.apache.maven.plugins:maven-compiler-plugin", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.apache.maven.plugins:maven-javadoc-plugin", + "version": "3.12.0", + "license": "Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND LicenseRef-scancode-public-domain AND MIT", + "flagged": false + }, + { + "name": "org.apache.maven.plugins:maven-surefire-plugin", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.apache.pdfbox:pdfbox", + "version": "3.0.8", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "org.bouncycastle:bcpkix-jdk18on", + "version": "1.85", + "license": "MIT", + "flagged": false + }, + { + "name": "org.bouncycastle:bcprov-jdk18on", + "version": "1.85.2", + "license": "LicenseRef-bad-non-standard", + "flagged": false + }, + { + "name": "org.jacoco:jacoco-maven-plugin", + "version": "0.8.15", + "license": "EPL-2.0 OR (Apache-2.0 AND EPL-2.0)", + "flagged": true + }, + { + "name": "org.springframework.boot:spring-boot-maven-plugin", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.boot:spring-boot-starter-log4j2", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.boot:spring-boot-starter-test", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.boot:spring-boot-starter-validation", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.boot:spring-boot-starter-webflux", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.webjars.npm:pdfjs-dist", + "version": "6.1.200", + "license": "Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND CC0-1.0 AND MIT AND OFL-1.1", + "flagged": false + }, + { + "name": "packaging", + "version": "26.2", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/codec-carver", + "error": null, + "component_count": 208, + "components": [ + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "aho-corasick", + "version": "1.1.4", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "aiofiles", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "aiofiles", + "version": "25.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "aiohappyeyeballs", + "version": "2.7.1", + "license": "0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0", + "flagged": true + }, + { + "name": "aiohttp", + "version": "3.14.3", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "aiosignal", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "android_system_properties", + "version": "0.1.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "annotated-doc", + "version": "0.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "annotated-types", + "version": "0.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "anstream", + "version": "1.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "anstyle", + "version": "1.0.14", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "anstyle-parse", + "version": "1.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "anstyle-query", + "version": "1.1.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "anstyle-wincon", + "version": "3.0.11", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "anyhow", + "version": "1.0.103", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "anyio", + "version": "4.14.1", + "license": "MIT", + "flagged": false + }, + { + "name": "anyio", + "version": "4.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "atheris", + "version": "3.0.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "attrs", + "version": "26.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "autocfg", + "version": "1.5.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bitflags", + "version": "2.13.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "block-buffer", + "version": "0.10.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "block2", + "version": "0.6.2", + "license": "MIT", + "flagged": false + }, + { + "name": "bumpalo", + "version": "3.20.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cc", + "version": "1.2.67", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "cffi", + "version": "2.1.0", + "license": "MIT-0", + "flagged": false + }, + { + "name": "cfg-if", + "version": "1.0.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "charset-normalizer", + "version": "3.4.9", + "license": "MIT", + "flagged": false + }, + { + "name": "chrono", + "version": "0.4.45", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "clap", + "version": "4.6.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "clap_builder", + "version": "4.6.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "clap_derive", + "version": "4.6.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "clap_lex", + "version": "1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "click", + "version": "8.4.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "colorchoice", + "version": "1.0.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "core-foundation-sys", + "version": "0.8.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "coverage", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "coverage", + "version": "7.15.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "cpufeatures", + "version": "0.2.17", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crossbeam-deque", + "version": "0.8.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crossbeam-epoch", + "version": "0.9.20", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crossbeam-utils", + "version": "0.8.22", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crypto-common", + "version": "0.1.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cryptography", + "version": "49.0.0", + "license": "BSD-3-Clause OR Apache-2.0", + "flagged": false + }, + { + "name": "cryptography", + "version": "50.0.0", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "datasets", + "version": "5.0.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "digest", + "version": "0.10.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dill", + "version": "0.4.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "either", + "version": "1.16.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "exceptiongroup", + "version": "1.3.1", + "license": "MIT AND Python-2.0", + "flagged": false + }, + { + "name": "fastapi", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "fastapi", + "version": "0.139.0", + "license": "MIT", + "flagged": false + }, + { + "name": "faster-whisper", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "filelock", + "version": "3.30.2", + "license": "MIT", + "flagged": false + }, + { + "name": "find-msvc-tools", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "frozenlist", + "version": "1.8.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "fsspec", + "version": "2026.4.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "futures-core", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-task", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-util", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "generic-array", + "version": "0.14.7", + "license": "MIT", + "flagged": false + }, + { + "name": "h11", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "heck", + "version": "0.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hf-xet", + "version": "1.5.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "httpcore", + "version": "1.0.9", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpcore2", + "version": "2.5.0", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "httpx", + "version": "0.28.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx-sse", + "version": "0.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "httpx2", + "version": "2.5.0", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "huggingface-hub", + "version": "1.23.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "hypothesis", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "hypothesis", + "version": "6.165.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "iana-time-zone", + "version": "0.1.65", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "iana-time-zone-haiku", + "version": "0.1.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "interrogate", + "version": "1.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is_terminal_polyfill", + "version": "1.70.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "itoa", + "version": "1.0.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jinja2", + "version": "3.1.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "js-sys", + "version": "0.3.103", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jsonschema", + "version": "4.26.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "libc", + "version": "0.2.186", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "llguidance", + "version": "1.7.6", + "license": "MIT", + "flagged": false + }, + { + "name": "llvmlite", + "version": "0.48.0", + "license": "LicenseRef-bad-bsd-2-clause-and-apache-2.0-and-llvm-exception-and-bsd-2-clause", + "flagged": false + }, + { + "name": "log", + "version": "0.4.33", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "markdown-it-py", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "markupsafe", + "version": "3.0.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "mcp", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "mcp", + "version": "1.28.1", + "license": "MIT AND Python-2.0", + "flagged": false + }, + { + "name": "mdurl", + "version": "0.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "memchr", + "version": "2.8.3", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "miniaudio", + "version": "1.71", + "license": "MIT AND Python-2.0", + "flagged": false + }, + { + "name": "mlx", + "version": "0.32.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mlx-audio", + "version": "0.4.5", + "license": "MIT", + "flagged": false + }, + { + "name": "mlx-lm", + "version": "0.31.3", + "license": "MIT", + "flagged": false + }, + { + "name": "mlx-metal", + "version": "0.32.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mlx-vlm", + "version": "0.6.4", + "license": "MIT", + "flagged": false + }, + { + "name": "mlx-whisper", + "version": "0.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "more-itertools", + "version": "11.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mpmath", + "version": "1.3.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "multidict", + "version": "6.7.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "multiprocess", + "version": "0.70.19", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "networkx", + "version": "3.6.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "num-traits", + "version": "0.2.19", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "numba", + "version": "0.66.0", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "numpy", + "version": "2.4.6", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "objc2", + "version": "0.6.4", + "license": "MIT", + "flagged": false + }, + { + "name": "objc2-encode", + "version": "4.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "objc2-foundation", + "version": "0.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "once_cell", + "version": "1.21.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "once_cell_polyfill", + "version": "1.70.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "opencv-python", + "version": "5.0.0.93", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "packaging", + "version": "26.2", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "pandas", + "version": "3.0.3", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "pillow", + "version": "12.3.0", + "license": "MIT-CMU", + "flagged": false + }, + { + "name": "pin-project-lite", + "version": "0.2.17", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "proc-macro2", + "version": "1.0.106", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "propcache", + "version": "0.5.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "protobuf", + "version": "7.35.1", + "license": "BSD-3-Clause AND LicenseRef-scancode-protobuf", + "flagged": false + }, + { + "name": "pyarrow", + "version": "25.0.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pycparser", + "version": "3.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pydantic", + "version": "2.13.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-core", + "version": "2.46.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-settings", + "version": "2.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pyjwt", + "version": "2.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "python-dotenv", + "version": "1.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "python-multipart", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "python-multipart", + "version": "0.0.32", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pywin32", + "version": "312", + "license": "PSF-2.0", + "flagged": false + }, + { + "name": "pyyaml", + "version": "6.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "quote", + "version": "1.0.46", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rayon", + "version": "1.12.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rayon-core", + "version": "1.13.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "referencing", + "version": "0.37.0", + "license": "MIT", + "flagged": false + }, + { + "name": "regex", + "version": "1.13.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "regex", + "version": "2026.7.10", + "license": "CNRI-Python AND Apache-2.0", + "flagged": false + }, + { + "name": "regex-automata", + "version": "0.4.15", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "regex-syntax", + "version": "0.8.11", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "requests", + "version": "2.34.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "rich", + "version": "15.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "rpds-py", + "version": "0.30.0", + "license": "MIT", + "flagged": false + }, + { + "name": "rpds-py", + "version": "2026.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "rustversion", + "version": "1.0.23", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "safetensors", + "version": "0.8.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "same-file", + "version": "1.0.6", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "scipy", + "version": "1.18.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "sentencepiece", + "version": "0.2.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "serde", + "version": "1.0.228", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_core", + "version": "1.0.228", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_derive", + "version": "1.0.228", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_json", + "version": "1.0.150", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "setuptools", + "version": "83.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "sha2", + "version": "0.10.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "shellingham", + "version": "1.5.4", + "license": "ISC", + "flagged": false + }, + { + "name": "shlex", + "version": "2.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "six", + "version": "1.17.0", + "license": "MIT", + "flagged": false + }, + { + "name": "slab", + "version": "0.4.12", + "license": "MIT", + "flagged": false + }, + { + "name": "sortedcontainers", + "version": "2.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "sounddevice", + "version": "0.5.5", + "license": "MIT", + "flagged": false + }, + { + "name": "sse-starlette", + "version": "3.4.5", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "starlette", + "version": "1.3.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "strsim", + "version": "0.11.1", + "license": "MIT", + "flagged": false + }, + { + "name": "sympy", + "version": "1.14.0", + "license": "BSD-2-Clause AND BSD-3-Clause AND MIT", + "flagged": false + }, + { + "name": "syn", + "version": "2.0.119", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "tiktoken", + "version": "0.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyvec", + "version": "1.12.0", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tinyvec_macros", + "version": "0.1.1", + "license": "MIT OR Apache-2.0 OR Zlib", + "flagged": false + }, + { + "name": "tokenizers", + "version": "0.22.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "torch", + "version": "2.13.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "tqdm", + "version": "4.68.4", + "license": "MIT AND MPL-2.0", + "flagged": true + }, + { + "name": "transformers", + "version": "5.12.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "truststore", + "version": "0.10.4", + "license": "MIT", + "flagged": false + }, + { + "name": "typenum", + "version": "1.20.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "typer", + "version": "0.27.0", + "license": "MIT", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "typing-inspection", + "version": "0.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "unicode-ident", + "version": "1.0.24", + "license": "(MIT OR Apache-2.0) AND Unicode-3.0", + "flagged": false + }, + { + "name": "unicode-normalization", + "version": "0.1.25", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "urllib3", + "version": "2.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "utf8parse", + "version": "0.2.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "uvicorn", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "uvicorn", + "version": "0.51.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "version_check", + "version": "0.9.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "walkdir", + "version": "2.5.0", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "wasm-bindgen", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-macro", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-macro-support", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-shared", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "winapi-util", + "version": "0.1.11", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "windows-core", + "version": "0.62.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-implement", + "version": "0.60.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-interface", + "version": "0.59.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-link", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-result", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-strings", + "version": "0.5.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-sys", + "version": "0.61.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "xxhash", + "version": "3.8.1", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "yarl", + "version": "1.24.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "zmij", + "version": "1.0.23", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/ConceptWeave", + "error": null, + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/context-graph-contracts", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/contextual-orchestrator", + "error": null, + "component_count": 559, + "components": [ + { + "name": "@adobe/css-tools", + "version": "4.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/code-frame", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/compat-data", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/core", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/generator", + "version": "7.29.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-compilation-targets", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-globals", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-module-imports", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-module-transforms", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-string-parser", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-validator-identifier", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-validator-option", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helpers", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/parser", + "version": "7.29.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/runtime", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/template", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/traverse", + "version": "7.29.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/types", + "version": "7.29.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@bcoe/v8-coverage", + "version": "1.0.2", + "license": "ISC AND MIT", + "flagged": false + }, + { + "name": "@blazediff/core", + "version": "1.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@chromatic-com/storybook", + "version": "5.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@chromatic-com/storybook", + "version": "latest", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/core", + "version": "1.11.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/core", + "version": "1.9.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/runtime", + "version": "1.11.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/runtime", + "version": "1.9.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/wasi-threads", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/wasi-threads", + "version": "1.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/aix-ppc64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-arm", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/darwin-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/darwin-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/freebsd-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/freebsd-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-arm", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-ia32", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-loong64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-mips64el", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-ppc64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-riscv64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-s390x", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/netbsd-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/netbsd-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openbsd-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openbsd-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openharmony-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/sunos-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-arm64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-ia32", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-x64", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@joshwooding/vite-plugin-react-docgen-typescript", + "version": "0.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/gen-mapping", + "version": "0.3.13", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/remapping", + "version": "2.3.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/resolve-uri", + "version": "3.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/sourcemap-codec", + "version": "1.5.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/trace-mapping", + "version": "0.3.31", + "license": "MIT", + "flagged": false + }, + { + "name": "@mdx-js/react", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/wasm-runtime", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@neoconfetti/react", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-android-arm-eabi", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-android-arm64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-darwin-arm64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-darwin-x64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-freebsd-x64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-arm-gnueabihf", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-arm-musleabihf", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-arm64-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-arm64-musl", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-ppc64-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-riscv64-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-riscv64-musl", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-s390x-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-x64-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-x64-musl", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-openharmony-arm64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-wasm32-wasi", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-win32-arm64-msvc", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-win32-ia32-msvc", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-win32-x64-msvc", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-project/types", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-project/types", + "version": "0.146.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-android-arm-eabi", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-android-arm64", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-darwin-arm64", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-darwin-x64", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-freebsd-x64", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-arm-gnueabihf", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-arm-musleabihf", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-arm64-gnu", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-arm64-musl", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-ppc64-gnu", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-riscv64-gnu", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-riscv64-musl", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-s390x-gnu", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-x64-gnu", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-x64-musl", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-openharmony-arm64", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-wasm32-wasi", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-win32-arm64-msvc", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-win32-x64-msvc", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-android-arm-eabi", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-android-arm64", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-darwin-arm64", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-darwin-x64", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-freebsd-x64", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-linux-arm-gnueabihf", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-linux-arm-musleabihf", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-linux-arm64-gnu", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-linux-arm64-musl", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-linux-ppc64-gnu", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-linux-riscv64-gnu", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-linux-riscv64-musl", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-linux-s390x-gnu", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-linux-x64-gnu", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-linux-x64-musl", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-openharmony-arm64", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-win32-arm64-msvc", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-win32-ia32-msvc", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxlint/binding-win32-x64-msvc", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@polka/url", + "version": "1.0.0-next.29", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-android-arm-eabi", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-android-arm64", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-darwin-arm64", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-darwin-x64", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-freebsd-x64", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm-gnueabihf", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm64-gnu", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm64-musl", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-ppc64-gnu", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-s390x-gnu", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-x64-gnu", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-x64-musl", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-openharmony-arm64", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-win32-arm64-msvc", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-win32-x64-msvc", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/pluginutils", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/pluginutils", + "version": "5.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@standard-schema/spec", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/addon-a11y", + "version": "10.5.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/addon-a11y", + "version": "^10.5.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/addon-docs", + "version": "10.5.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/addon-docs", + "version": "^10.5.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/addon-vitest", + "version": "10.5.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/addon-vitest", + "version": "^10.5.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/builder-vite", + "version": "10.5.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/csf-plugin", + "version": "10.5.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/global", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/icons", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/react", + "version": "10.5.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/react-dom-shim", + "version": "10.5.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/react-vite", + "version": "10.5.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/react-vite", + "version": "^10.5.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/dom", + "version": "10.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/jest-dom", + "version": "6.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/user-event", + "version": "14.6.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@tybys/wasm-util", + "version": "0.10.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/aria-query", + "version": "5.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__core", + "version": "7.20.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__generator", + "version": "7.27.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__template", + "version": "7.4.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__traverse", + "version": "7.28.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/chai", + "version": "5.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/deep-eql", + "version": "4.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/doctrine", + "version": "0.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/estree", + "version": "1.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/mdx", + "version": "2.0.14", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/node", + "version": "24.13.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/node", + "version": "^24.13.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react", + "version": "19.2.18", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react", + "version": "^19.2.18", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react-dom", + "version": "19.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react-dom", + "version": "^19.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/resolve", + "version": "1.20.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitejs/plugin-react", + "version": "6.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitejs/plugin-react", + "version": "^6.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/browser", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/browser-playwright", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/browser-playwright", + "version": "latest", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/coverage-v8", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/coverage-v8", + "version": "latest", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/expect", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/expect", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/mocker", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/pretty-format", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/pretty-format", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/runner", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/snapshot", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/spy", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/spy", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/utils", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/utils", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@webcontainer/env", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "acorn", + "version": "8.18.0", + "license": "MIT", + "flagged": false + }, + { + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/download-artifact", + "version": "634f93cb2916e3fdff6788551b99b062d0335ce0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "820762786026740c76f36085b0efc47a31fe5020", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "ece7cb06caefa5fff74198d8649806c4678c61a1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "330a01c490aca151604b8cf639adc76d48f6c5d4", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "aho-corasick", + "version": "1.1.5", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "alembic", + "version": "1.19.1", + "license": "MIT", + "flagged": false + }, + { + "name": "annotated-doc", + "version": "0.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "annotated-types", + "version": "0.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-regex", + "version": "5.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-regex", + "version": "6.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "5.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "anyhow", + "version": "1.0.104", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "anyio", + "version": "4.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "aria-query", + "version": "5.3.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "aria-query", + "version": "5.3.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "arrow", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "assertion-error", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ast-types", + "version": "0.16.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ast-v8-to-istanbul", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "astral-sh/setup-uv", + "version": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "atheris", + "version": "3.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "attrs", + "version": "26.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "axe-core", + "version": "4.13.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "balanced-match", + "version": "4.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "base64", + "version": "0.22.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "baseline-browser-mapping", + "version": "2.11.19", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "bit-set", + "version": "0.5.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bit-vec", + "version": "0.6.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "boolean-py", + "version": "5.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "brace-expansion", + "version": "5.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "browserslist", + "version": "4.28.8", + "license": "MIT", + "flagged": false + }, + { + "name": "bstr", + "version": "1.13.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bundle-name", + "version": "4.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "cachecontrol", + "version": "0.14.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "caniuse-lite", + "version": "1.0.30001810", + "license": "CC-BY-4.0", + "flagged": false + }, + { + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "certifi", + "version": "2026.7.22", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "cffi", + "version": "2.1.1", + "license": "MIT-0", + "flagged": false + }, + { + "name": "chai", + "version": "5.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "chai", + "version": "6.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "chardet", + "version": "5.2.0", + "license": "LGPL-2.1-or-later", + "flagged": true + }, + { + "name": "charset-normalizer", + "version": "3.4.9", + "license": "MIT", + "flagged": false + }, + { + "name": "charset-normalizer", + "version": "3.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "check-error", + "version": "2.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "chromatic", + "version": "18.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "click", + "version": "8.4.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "contextual-orchestrator", + "version": "0.2.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "convert-source-map", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "coverage", + "version": "7.15.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "crossbeam-deque", + "version": "0.8.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crossbeam-epoch", + "version": "0.9.20", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crossbeam-utils", + "version": "0.8.22", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cryptography", + "version": "50.0.1", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "css.escape", + "version": "1.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "csstype", + "version": "3.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "cyclonedx-bom", + "version": "7.3.0", + "license": "Apache-2.0 AND Python-2.0", + "flagged": false + }, + { + "name": "cyclonedx-python-lib", + "version": "11.11.0", + "license": "Apache-2.0 AND Python-2.0", + "flagged": false + }, + { + "name": "debug", + "version": "4.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "deep-eql", + "version": "5.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "default-browser", + "version": "5.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "default-browser-id", + "version": "5.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "define-lazy-prop", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "defusedxml", + "version": "0.7.1", + "license": "PSF-2.0", + "flagged": false + }, + { + "name": "dequal", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "detect-libc", + "version": "2.1.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "distro", + "version": "1.9.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "docker/setup-compose-action", + "version": "54042514f505b273907334ae2b9cdbb9a0213c1a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "doctrine", + "version": "3.0.0", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "dom-accessibility-api", + "version": "0.5.16", + "license": "MIT", + "flagged": false + }, + { + "name": "dom-accessibility-api", + "version": "0.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "dtolnay/rust-toolchain", + "version": "6bed0761d98439e5a578e2877258200ad565ba87", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "egressweave", + "version": "0.1.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "either", + "version": "1.18.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "electron-to-chromium", + "version": "1.5.415", + "license": "ISC", + "flagged": false + }, + { + "name": "empathic", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "es-errors", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-module-lexer", + "version": "2.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "esbuild", + "version": "0.28.2", + "license": "MIT", + "flagged": false + }, + { + "name": "escalade", + "version": "3.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "esprima", + "version": "4.0.1", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "estree-walker", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "estree-walker", + "version": "3.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "esutils", + "version": "2.0.3", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "expect-type", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "fancy-regex", + "version": "0.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-mlsirm", + "version": "0.11.4", + "license": "MIT", + "flagged": false + }, + { + "name": "fastapi", + "version": "0.141.1", + "license": "MIT", + "flagged": false + }, + { + "name": "fdir", + "version": "6.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "filelock", + "version": "3.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "fqdn", + "version": "1.5.1", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "fsevents", + "version": "2.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "fsevents", + "version": "2.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "function-bind", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "gensync", + "version": "1.0.0-beta.2", + "license": "MIT", + "flagged": false + }, + { + "name": "github/codeql-action/analyze", + "version": "b96794f015dfd88f77b49b1c93e0fa7110f94c63", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/init", + "version": "b96794f015dfd88f77b49b1c93e0fa7110f94c63", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "glob", + "version": "13.0.6", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "googleapis-common-protos", + "version": "1.75.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "graceful-fs", + "version": "4.2.11", + "license": "ISC", + "flagged": false + }, + { + "name": "greenlet", + "version": "3.5.5", + "license": "MIT AND PSF-2.0", + "flagged": false + }, + { + "name": "h11", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "has-flag", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hasown", + "version": "2.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "heck", + "version": "0.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "html-escaper", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "httpcore", + "version": "1.0.9", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpcore2", + "version": "2.13.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx", + "version": "0.28.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx2", + "version": "2.13.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx2-jsfetch", + "version": "1.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "hypothesis", + "version": "6.165.10", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "idna", + "version": "3.19", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "indent-string", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "interrogate", + "version": "1.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-core-module", + "version": "2.16.2", + "license": "MIT", + "flagged": false + }, + { + "name": "is-docker", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-inside-container", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-wsl", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "isoduration", + "version": "20.11.0", + "license": "ISC", + "flagged": false + }, + { + "name": "istanbul-lib-coverage", + "version": "3.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-lib-report", + "version": "3.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-reports", + "version": "3.2.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "itoa", + "version": "1.0.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jiter", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "js-tokens", + "version": "10.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "js-tokens", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jsesc", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "json5", + "version": "2.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonc-parser", + "version": "3.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonfile", + "version": "6.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonpointer", + "version": "3.1.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "jsonschema", + "version": "4.26.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "lark", + "version": "1.3.1", + "license": "MIT AND MPL-2.0", + "flagged": true + }, + { + "name": "lazy_static", + "version": "1.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "libc", + "version": "0.2.189", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "license-expression", + "version": "30.4.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "lightningcss", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "loupe", + "version": "3.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "lru-cache", + "version": "11.5.2", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "lru-cache", + "version": "5.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "lxml", + "version": "6.1.1", + "license": "BSD-3-Clause AND GPL-1.0-or-later", + "flagged": true + }, + { + "name": "lz-string", + "version": "1.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "magic-string", + "version": "0.30.21", + "license": "MIT", + "flagged": false + }, + { + "name": "magicast", + "version": "0.5.4", + "license": "MIT", + "flagged": false + }, + { + "name": "make-dir", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mako", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "markdown-it-py", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "markupsafe", + "version": "3.0.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "maturin", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "maturin", + "version": "1.15.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "mcp", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mcp-types", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mdurl", + "version": "0.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "memchr", + "version": "2.8.3", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "min-indent", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "minimatch", + "version": "10.2.6", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "minimist", + "version": "1.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "minipass", + "version": "7.1.3", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "mrmime", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ms", + "version": "2.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "msgpack", + "version": "1.2.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "nanoid", + "version": "3.3.18", + "license": "MIT", + "flagged": false + }, + { + "name": "node-releases", + "version": "2.0.53", + "license": "MIT", + "flagged": false + }, + { + "name": "numpy", + "version": "2.5.2", + "license": "BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0", + "flagged": false + }, + { + "name": "obug", + "version": "2.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "once_cell", + "version": "1.21.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "open", + "version": "10.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "openai", + "version": "2.54.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opencode-ai", + "version": "1.18.22", + "license": "MIT", + "flagged": false + }, + { + "name": "opencode-darwin-arm64", + "version": "1.18.22", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "opencode-darwin-x64", + "version": "1.18.22", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "opencode-darwin-x64-baseline", + "version": "1.18.22", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "opencode-linux-arm64", + "version": "1.18.22", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "opencode-linux-arm64-musl", + "version": "1.18.22", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "opencode-linux-x64", + "version": "1.18.22", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "opencode-linux-x64-baseline", + "version": "1.18.22", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "opencode-linux-x64-baseline-musl", + "version": "1.18.22", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "opencode-linux-x64-musl", + "version": "1.18.22", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "opencode-windows-arm64", + "version": "1.18.22", + "license": "MIT", + "flagged": false + }, + { + "name": "opencode-windows-x64", + "version": "1.18.22", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "opencode-windows-x64-baseline", + "version": "1.18.22", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "opentelemetry-api", + "version": "1.44.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-exporter-otlp-proto-common", + "version": "1.44.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-exporter-otlp-proto-http", + "version": "1.44.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-proto", + "version": "1.44.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-sdk", + "version": "1.44.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-semantic-conventions", + "version": "0.65b0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "oxc-parser", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "oxc-resolver", + "version": "11.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "oxlint", + "version": "1.80.0", + "license": "MIT", + "flagged": false + }, + { + "name": "oxlint", + "version": "^1.79.0", + "license": "MIT", + "flagged": false + }, + { + "name": "packageurl-python", + "version": "0.17.6", + "license": "MIT", + "flagged": false + }, + { + "name": "packaging", + "version": "26.2", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "packaging", + "version": "26.3", + "license": "Apache-2.0 OR BSD-2-Clause", + "flagged": false + }, + { + "name": "path-parse", + "version": "1.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "path-scurry", + "version": "2.0.2", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "pathe", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "pathval", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "picocolors", + "version": "1.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "picomatch", + "version": "4.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "pip", + "version": "26.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pip-api", + "version": "0.0.34", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pip-audit", + "version": "2.10.1", + "license": "Apache-2.0 AND ISC", + "flagged": false + }, + { + "name": "pip-requirements-parser", + "version": "32.0.1", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "platformdirs", + "version": "4.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "playwright", + "version": "1.62.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "playwright", + "version": "latest", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "playwright-core", + "version": "1.62.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pngjs", + "version": "7.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "portable-atomic", + "version": "1.15.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "postcss", + "version": "8.5.26", + "license": "MIT", + "flagged": false + }, + { + "name": "pretty-format", + "version": "27.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "proc-macro2", + "version": "1.0.107", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "protobuf", + "version": "7.36.0", + "license": "BSD-3-Clause AND LicenseRef-scancode-protobuf", + "flagged": false + }, + { + "name": "psycopg", + "version": "3.3.4", + "license": "LGPL-3.0 AND LGPL-3.0-only", + "flagged": true + }, + { + "name": "psycopg-binary", + "version": "3.3.4", + "license": "GPL-3.0-or-later", + "flagged": true + }, + { + "name": "py", + "version": "1.11.0", + "license": "MIT", + "flagged": false + }, + { + "name": "py-serializable", + "version": "2.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pycparser", + "version": "3.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pydantic", + "version": "2.13.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-core", + "version": "2.46.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pygments", + "version": "2.21.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pyjwt", + "version": "2.15.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pyo3", + "version": "0.29.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3", + "version": ">= 0.29.0,< 0.30.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pyo3-build-config", + "version": "0.29.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-ffi", + "version": "0.29.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-macros", + "version": "0.29.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-macros-backend", + "version": "0.29.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyparsing", + "version": "3.3.2", + "license": "MIT AND Python-2.0", + "flagged": false + }, + { + "name": "pytest", + "version": "9.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest-cov", + "version": "7.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "python-multipart", + "version": "0.0.32", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pywin32", + "version": "312", + "license": "PSF-2.0", + "flagged": false + }, + { + "name": "quote", + "version": "1.0.47", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rayon", + "version": "1.12.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rayon", + "version": ">= 1.10.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "rayon-core", + "version": "1.13.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "react", + "version": "19.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "react", + "version": "^19.2.8", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "react-docgen", + "version": "8.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "react-docgen-typescript", + "version": "2.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "react-dom", + "version": "19.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "react-dom", + "version": "^19.2.8", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "react-is", + "version": "17.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "recast", + "version": "0.23.21", + "license": "MIT", + "flagged": false + }, + { + "name": "redent", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "redis", + "version": "8.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "referencing", + "version": "0.37.0", + "license": "MIT", + "flagged": false + }, + { + "name": "regex", + "version": "1.13.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "regex-automata", + "version": "0.4.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "regex-syntax", + "version": "0.8.11", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "requests", + "version": "2.34.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "resolve", + "version": "1.22.12", + "license": "MIT", + "flagged": false + }, + { + "name": "rfc3339-validator", + "version": "0.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "rfc3986-validator", + "version": "0.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "rfc3987-syntax", + "version": "1.1.0", + "license": "Apache-2.0 AND GPL-1.0-or-later AND MIT", + "flagged": true + }, + { + "name": "rich", + "version": "15.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "rolldown", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "rpds-py", + "version": "0.30.0", + "license": "MIT", + "flagged": false + }, + { + "name": "rpds-py", + "version": "2026.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "run-applescript", + "version": "7.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "rustc-hash", + "version": "1.1.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "scheduler", + "version": "0.27.0", + "license": "MIT", + "flagged": false + }, + { + "name": "semver", + "version": "6.3.1", + "license": "ISC", + "flagged": false + }, + { + "name": "semver", + "version": "7.8.5", + "license": "ISC", + "flagged": false + }, + { + "name": "serde", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "serde_core", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_derive", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_json", + "version": "1.0.151", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_json", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "setuptools", + "version": "84.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "siginfo", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "sirv", + "version": "3.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "six", + "version": "1.17.0", + "license": "MIT", + "flagged": false + }, + { + "name": "sniffio", + "version": "1.3.1", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "sortedcontainers", + "version": "2.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "source-map", + "version": "0.6.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "source-map-js", + "version": "1.2.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "sqlalchemy", + "version": "2.0.52", + "license": "MIT", + "flagged": false + }, + { + "name": "sse-starlette", + "version": "3.5.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "stackback", + "version": "0.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "starlette", + "version": "1.6.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "std-env", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "storybook", + "version": "10.5.10", + "license": "MIT", + "flagged": false + }, + { + "name": "storybook", + "version": "^10.5.10", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "strip-ansi", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-bom", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-indent", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-indent", + "version": "4.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-color", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-preserve-symlinks-flag", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "syn", + "version": "2.0.119", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "syn", + "version": "3.0.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "tabulate", + "version": "0.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "target-lexicon", + "version": "0.13.5", + "license": "Apache-2.0 WITH LLVM-exception", + "flagged": false + }, + { + "name": "tiktoken-rs", + "version": "0.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tiktoken-rs", + "version": ">= 0.7.0,< 0.8.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "tiny-invariant", + "version": "1.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tinybench", + "version": "2.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyexec", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyglobby", + "version": "0.2.17", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyrainbow", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyrainbow", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyspy", + "version": "4.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "tomli", + "version": "2.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tomli-w", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "totalist", + "version": "3.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tqdm", + "version": "4.70.0", + "license": "MIT AND MPL-2.0", + "flagged": true + }, + { + "name": "truststore", + "version": "0.10.4", + "license": "MIT", + "flagged": false + }, + { + "name": "ts-dedent", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tsconfig-paths", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tslib", + "version": "2.8.1", + "license": "0BSD", + "flagged": false + }, + { + "name": "typescript", + "version": "6.0.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "typescript", + "version": "~6.0.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "typing-inspection", + "version": "0.4.4", + "license": "MIT", + "flagged": false + }, + { + "name": "tzdata", + "version": "2026.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "tzdata", + "version": "2026.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "undici-types", + "version": "7.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "unicode-ident", + "version": "1.0.24", + "license": "(MIT OR Apache-2.0) AND Unicode-3.0", + "flagged": false + }, + { + "name": "universalify", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "unplugin", + "version": "2.3.11", + "license": "MIT", + "flagged": false + }, + { + "name": "update-browserslist-db", + "version": "1.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "uri-template", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "urllib3", + "version": "2.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "use-sync-external-store", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "uv", + "version": "0.12.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "uvicorn", + "version": "0.52.4", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "vite", + "version": "8.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "vite", + "version": "^8.2.2", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "vitest", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "vitest", + "version": "latest", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "webcolors", + "version": "25.10.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "webpack-virtual-modules", + "version": "0.6.2", + "license": "MIT", + "flagged": false + }, + { + "name": "why-is-node-running", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ws", + "version": "8.21.3", + "license": "MIT", + "flagged": false + }, + { + "name": "wsl-utils", + "version": "0.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "yallist", + "version": "3.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "zmij", + "version": "1.0.23", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/ContextualWisdomLab.github.io", + "error": null, + "component_count": 3, + "components": [ + { + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/analyze", + "version": "cdf488f595d80d6e07e03d4674febd5ab45fa938", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/init", + "version": "cdf488f595d80d6e07e03d4674febd5ab45fa938", + "license": "NOASSERTION", + "flagged": true + } + ] + }, + { + "repo": "ContextualWisdomLab/cwl-telemetry", + "error": null, + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/DiagramWeave", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/disksage", + "error": null, + "component_count": 586, + "components": [ + { + "name": "actions/attest", + "version": "1e69f48acb82d1966a394da916b4c1698aa569d6", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/download-artifact", + "version": "37930b1c2abaa49bbe596cd826c3c89aef350131", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/download-artifact", + "version": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "820762786026740c76f36085b0efc47a31fe5020", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "adler2", + "version": "2.0.1", + "license": "0BSD OR MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "aes", + "version": "0.8.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "aho-corasick", + "version": "1.1.4", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "alloc-no-stdlib", + "version": "2.0.4", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "alloc-stdlib", + "version": "0.2.4", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "android_system_properties", + "version": "0.1.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "anyhow", + "version": "1.0.103", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "apple-native-keyring-store", + "version": "1.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "arrayvec", + "version": "0.7.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "async-broadcast", + "version": "0.7.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "async-channel", + "version": "2.5.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "async-executor", + "version": "1.14.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "async-io", + "version": "2.6.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "async-lock", + "version": "3.4.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "async-process", + "version": "2.5.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "async-recursion", + "version": "1.1.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "async-signal", + "version": "0.2.14", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "async-task", + "version": "4.7.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "async-trait", + "version": "0.1.89", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "atk", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "atk-sys", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "atoi_simd", + "version": "0.18.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "atomic-waker", + "version": "1.1.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "autocfg", + "version": "1.5.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "base64", + "version": "0.21.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "base64", + "version": "0.22.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "base64", + "version": "0.23.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bindgen", + "version": "0.72.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "bit-set", + "version": "0.8.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bit-vec", + "version": "0.8.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bitflags", + "version": "1.3.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bitflags", + "version": "2.13.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "blake3", + "version": "1.8.7", + "license": "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception", + "flagged": false + }, + { + "name": "block-buffer", + "version": "0.10.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "block-buffer", + "version": "0.12.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "block-padding", + "version": "0.3.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "block2", + "version": "0.6.2", + "license": "MIT", + "flagged": false + }, + { + "name": "blocking", + "version": "1.6.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "brotli", + "version": "8.0.4", + "license": "BSD-3-Clause AND MIT", + "flagged": false + }, + { + "name": "brotli-decompressor", + "version": "5.0.3", + "license": "LicenseRef-bad-bsd-3-clausemit", + "flagged": false + }, + { + "name": "bs58", + "version": "0.5.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bumpalo", + "version": "3.20.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bytemuck", + "version": "1.25.0", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "byteorder", + "version": "1.5.0", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "bytes", + "version": "1.12.1", + "license": "MIT", + "flagged": false + }, + { + "name": "cairo-rs", + "version": "0.18.5", + "license": "MIT", + "flagged": false + }, + { + "name": "cairo-sys-rs", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "calamine", + "version": "0.36.1", + "license": "MIT", + "flagged": false + }, + { + "name": "camino", + "version": "1.2.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cargo-platform", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cargo_metadata", + "version": "0.19.2", + "license": "MIT", + "flagged": false + }, + { + "name": "cargo_toml", + "version": "0.22.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "cbc", + "version": "0.1.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cc", + "version": "1.2.66", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cesu8", + "version": "1.1.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "cexpr", + "version": "0.6.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "cfb", + "version": "0.14.0", + "license": "MIT", + "flagged": false + }, + { + "name": "cfb", + "version": "0.7.3", + "license": "MIT", + "flagged": false + }, + { + "name": "cfg-expr", + "version": "0.15.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cfg-if", + "version": "1.0.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "chrono", + "version": "0.4.45", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cipher", + "version": "0.4.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "clang-sys", + "version": "1.8.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "cmake", + "version": "0.1.58", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "codepage", + "version": "0.1.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "combine", + "version": "4.6.7", + "license": "MIT", + "flagged": false + }, + { + "name": "concurrent-queue", + "version": "2.5.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "const-oid", + "version": "0.10.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "constant_time_eq", + "version": "0.4.2", + "license": "CC0-1.0 OR MIT-0 OR Apache-2.0", + "flagged": false + }, + { + "name": "cookie", + "version": "0.18.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "core-foundation", + "version": "0.10.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "core-foundation-sys", + "version": "0.8.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "core-graphics", + "version": "0.25.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "core-graphics-types", + "version": "0.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cpufeatures", + "version": "0.2.17", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cpufeatures", + "version": "0.3.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crc32fast", + "version": "1.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crossbeam-channel", + "version": "0.5.16", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crossbeam-utils", + "version": "0.8.22", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crypto-common", + "version": "0.1.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crypto-common", + "version": "0.2.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cssparser", + "version": "0.36.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "cssparser-macros", + "version": "0.6.1", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "csv", + "version": "1.4.0", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "csv-core", + "version": "0.1.13", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "ctor", + "version": "0.8.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "ctor-proc-macro", + "version": "0.0.7", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "darling", + "version": "0.23.0", + "license": "MIT", + "flagged": false + }, + { + "name": "darling_core", + "version": "0.23.0", + "license": "MIT", + "flagged": false + }, + { + "name": "darling_macro", + "version": "0.23.0", + "license": "MIT", + "flagged": false + }, + { + "name": "dbus", + "version": "0.9.12", + "license": "LicenseRef-bad-apache-2.0mit", + "flagged": false + }, + { + "name": "debug_unsafe", + "version": "0.1.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "deranged", + "version": "0.5.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "derive_more", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "derive_more-impl", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "digest", + "version": "0.10.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "digest", + "version": "0.11.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dirs", + "version": "6.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dirs-sys", + "version": "0.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dispatch2", + "version": "0.3.1", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "displaydoc", + "version": "0.2.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dlopen2", + "version": "0.8.2", + "license": "MIT", + "flagged": false + }, + { + "name": "dlopen2_derive", + "version": "0.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "dom_query", + "version": "0.27.0", + "license": "MIT", + "flagged": false + }, + { + "name": "dpi", + "version": "0.1.2", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "dtoa", + "version": "1.0.11", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dtoa-short", + "version": "0.3.5", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "dtolnay/rust-toolchain", + "version": "4be7066ada62dd38de10e7b70166bc74ed198c30", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "dtor", + "version": "0.3.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "dtor-proc-macro", + "version": "0.0.6", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "dunce", + "version": "1.0.5", + "license": "CC0-1.0 OR MIT-0 OR Apache-2.0", + "flagged": false + }, + { + "name": "dyn-clone", + "version": "1.0.20", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "either", + "version": "1.16.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "embed-resource", + "version": "3.0.11", + "license": "MIT", + "flagged": false + }, + { + "name": "embed_plist", + "version": "1.2.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "encoding_rs", + "version": "0.8.35", + "license": "(Apache-2.0 OR MIT) AND BSD-3-Clause", + "flagged": false + }, + { + "name": "endi", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "enumflags2", + "version": "0.7.12", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "enumflags2_derive", + "version": "0.7.12", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "equivalent", + "version": "1.0.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "erased-serde", + "version": "0.4.10", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "errno", + "version": "0.3.14", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "event-listener", + "version": "5.4.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "event-listener-strategy", + "version": "0.5.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "fast-float2", + "version": "0.2.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "fastrand", + "version": "2.4.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "fdeflate", + "version": "0.3.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "field-offset", + "version": "0.3.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "find-msvc-tools", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "find_cuda_helper", + "version": "0.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "flate2", + "version": "1.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "fnv", + "version": "1.0.7", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "foldhash", + "version": "0.2.0", + "license": "Zlib", + "flagged": false + }, + { + "name": "foreign-types", + "version": "0.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "foreign-types-macros", + "version": "0.2.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "foreign-types-shared", + "version": "0.3.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "form_urlencoded", + "version": "1.2.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "fs4", + "version": "1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-channel", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-core", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-executor", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-io", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-lite", + "version": "2.6.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "futures-macro", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-sink", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-task", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-util", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "gdk", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "gdk-pixbuf", + "version": "0.18.5", + "license": "MIT", + "flagged": false + }, + { + "name": "gdk-pixbuf-sys", + "version": "0.18.0", + "license": "MIT", + "flagged": false + }, + { + "name": "gdk-sys", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "gdkwayland-sys", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "gdkx11", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "gdkx11-sys", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "generic-array", + "version": "0.14.7", + "license": "MIT", + "flagged": false + }, + { + "name": "getrandom", + "version": "0.2.17", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "getrandom", + "version": "0.3.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "getrandom", + "version": "0.4.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "gio", + "version": "0.18.4", + "license": "MIT", + "flagged": false + }, + { + "name": "gio-sys", + "version": "0.18.1", + "license": "MIT", + "flagged": false + }, + { + "name": "glib", + "version": "0.18.5", + "license": "MIT", + "flagged": false + }, + { + "name": "glib-macros", + "version": "0.18.5", + "license": "MIT", + "flagged": false + }, + { + "name": "glib-sys", + "version": "0.18.1", + "license": "MIT", + "flagged": false + }, + { + "name": "glob", + "version": "0.3.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "gobject-sys", + "version": "0.18.0", + "license": "MIT", + "flagged": false + }, + { + "name": "gtk", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "gtk-sys", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "gtk3-macros", + "version": "0.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "hashbrown", + "version": "0.12.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hashbrown", + "version": "0.17.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hashify", + "version": "0.2.9", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "heck", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "heck", + "version": "0.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hermit-abi", + "version": "0.5.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hex", + "version": "0.4.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hkdf", + "version": "0.12.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hmac", + "version": "0.12.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "html5ever", + "version": "0.38.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "http", + "version": "1.4.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "http-body", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "http-body-util", + "version": "0.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "httparse", + "version": "1.10.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hybrid-array", + "version": "0.4.13", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hyper", + "version": "1.10.1", + "license": "MIT", + "flagged": false + }, + { + "name": "hyper-util", + "version": "0.1.20", + "license": "MIT", + "flagged": false + }, + { + "name": "iana-time-zone", + "version": "0.1.65", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "iana-time-zone-haiku", + "version": "0.1.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ico", + "version": "0.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "icu_collections", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_locale_core", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_normalizer", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_normalizer_data", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_properties", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_properties_data", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_provider", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "ident_case", + "version": "1.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "idna", + "version": "1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "idna_adapter", + "version": "1.2.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "ilammy/msvc-dev-cmd", + "version": "0b201ec74fa43914dc39ae48a89fd1d8cb592756", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "indexmap", + "version": "1.9.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "indexmap", + "version": "2.14.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "infer", + "version": "0.19.0", + "license": "MIT", + "flagged": false + }, + { + "name": "infer", + "version": "0.22.0", + "license": "MIT", + "flagged": false + }, + { + "name": "inout", + "version": "0.1.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ipnet", + "version": "2.12.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "is-docker", + "version": "0.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-wsl", + "version": "0.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "itertools", + "version": "0.13.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "itoa", + "version": "1.0.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jakoch/install-vulkan-sdk-action", + "version": "37effcfa045411f8bfbbda26df2fd1b3bf3436fa", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "javascriptcore-rs", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "javascriptcore-rs-sys", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "Jimver/cuda-toolkit", + "version": "b8bf9c6c28f8a92fbb04dcfcaee872e60c57462d", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "jni", + "version": "0.21.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jni-sys", + "version": "0.3.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jni-sys", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jni-sys-macros", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jobserver", + "version": "0.1.35", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "js-sys", + "version": "0.3.103", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "json-patch", + "version": "3.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jsonptr", + "version": "0.6.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "keyboard-types", + "version": "0.7.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "keyring", + "version": "4.1.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "keyring-core", + "version": "1.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "libappindicator", + "version": "0.9.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "libappindicator-sys", + "version": "0.9.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "libc", + "version": "0.2.189", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "libdbus-sys", + "version": "0.2.7", + "license": "LicenseRef-bad-apache-2.0mit", + "flagged": false + }, + { + "name": "libloading", + "version": "0.7.4", + "license": "ISC", + "flagged": false + }, + { + "name": "libloading", + "version": "0.8.9", + "license": "ISC", + "flagged": false + }, + { + "name": "libredox", + "version": "0.1.18", + "license": "MIT", + "flagged": false + }, + { + "name": "linux-raw-sys", + "version": "0.12.1", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "litemap", + "version": "0.8.2", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "llama-cpp-2", + "version": "0.1.154", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "llama-cpp-sys-2", + "version": "0.1.154", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "lock_api", + "version": "0.4.14", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "log", + "version": "0.4.33", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "mail-parser", + "version": "0.11.7", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "markup5ever", + "version": "0.38.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "md-5", + "version": "0.10.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "memchr", + "version": "2.8.3", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "memoffset", + "version": "0.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "mime", + "version": "0.3.17", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "minimal-lexical", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "miniz_oxide", + "version": "0.8.9", + "license": "MIT OR Zlib OR Apache-2.0", + "flagged": false + }, + { + "name": "mio", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "mlsirm-core", + "version": "0.7.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "muda", + "version": "0.19.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "ndk", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ndk-sys", + "version": "0.6.0+11769913", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "new_debug_unreachable", + "version": "1.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "nom", + "version": "7.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "num", + "version": "0.4.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-bigint", + "version": "0.4.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-complex", + "version": "0.4.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-conv", + "version": "0.2.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-integer", + "version": "0.1.46", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-iter", + "version": "0.1.46", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-rational", + "version": "0.4.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-traits", + "version": "0.2.19", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num_enum", + "version": "0.7.6", + "license": "BSD-3-Clause OR MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num_enum_derive", + "version": "0.7.6", + "license": "BSD-3-Clause OR MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "objc2", + "version": "0.6.4", + "license": "MIT", + "flagged": false + }, + { + "name": "objc2-app-kit", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-cloud-kit", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-core-data", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-core-foundation", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-core-graphics", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-core-image", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-core-location", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-core-text", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-encode", + "version": "4.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "objc2-exception-helper", + "version": "0.1.1", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-foundation", + "version": "0.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "objc2-io-surface", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-quartz-core", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-ui-kit", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-user-notifications", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-web-kit", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "once_cell", + "version": "1.21.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "open", + "version": "5.3.6", + "license": "MIT", + "flagged": false + }, + { + "name": "option-ext", + "version": "0.2.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "ordered-stream", + "version": "0.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "oxilangtag", + "version": "0.1.6", + "license": "MIT", + "flagged": false + }, + { + "name": "oxiri", + "version": "0.2.11", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "oxrdf", + "version": "0.3.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "oxttl", + "version": "0.2.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pango", + "version": "0.18.3", + "license": "MIT", + "flagged": false + }, + { + "name": "pango-sys", + "version": "0.18.0", + "license": "MIT", + "flagged": false + }, + { + "name": "parking", + "version": "2.2.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "parking_lot", + "version": "0.12.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "parking_lot_core", + "version": "0.9.12", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "percent-encoding", + "version": "2.3.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "phf", + "version": "0.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "phf_codegen", + "version": "0.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "phf_generator", + "version": "0.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "phf_macros", + "version": "0.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "phf_shared", + "version": "0.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pin-project-lite", + "version": "0.2.17", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "piper", + "version": "0.2.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pkg-config", + "version": "0.3.33", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "plist", + "version": "1.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "png", + "version": "0.17.16", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "png", + "version": "0.18.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "polling", + "version": "3.11.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "potential_utf", + "version": "0.1.5", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "powerfmt", + "version": "0.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ppv-lite86", + "version": "0.2.21", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "precomputed-hash", + "version": "0.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "prettyplease", + "version": "0.2.37", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro-crate", + "version": "1.3.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro-crate", + "version": "2.0.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro-crate", + "version": "3.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro-error", + "version": "1.0.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro-error-attr", + "version": "1.0.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro2", + "version": "1.0.106", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "quick-xml", + "version": "0.41.0", + "license": "MIT", + "flagged": false + }, + { + "name": "quote", + "version": "1.0.46", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "r-efi", + "version": "5.3.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later", + "flagged": true + }, + { + "name": "r-efi", + "version": "6.0.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later", + "flagged": true + }, + { + "name": "rand", + "version": "0.9.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rand_chacha", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rand_core", + "version": "0.9.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "raw-window-handle", + "version": "0.6.2", + "license": "MIT OR Apache-2.0 OR Zlib", + "flagged": false + }, + { + "name": "redox_syscall", + "version": "0.5.18", + "license": "MIT", + "flagged": false + }, + { + "name": "redox_users", + "version": "0.5.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ref-cast", + "version": "1.0.25", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ref-cast-impl", + "version": "1.0.25", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "regex", + "version": "1.13.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "regex-automata", + "version": "0.4.15", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "regex-syntax", + "version": "0.8.11", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "reqwest", + "version": "0.13.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rfd", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ring", + "version": "0.17.14", + "license": "Apache-2.0 AND ISC", + "flagged": false + }, + { + "name": "rustc-hash", + "version": "2.1.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "rustc_version", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rustix", + "version": "1.1.4", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "rustls", + "version": "0.23.41", + "license": "Apache-2.0 OR ISC OR MIT", + "flagged": false + }, + { + "name": "rustls-pki-types", + "version": "1.15.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rustls-webpki", + "version": "0.103.13", + "license": "ISC", + "flagged": false + }, + { + "name": "rustversion", + "version": "1.0.23", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ryu", + "version": "1.0.23", + "license": "Apache-2.0 OR BSL-1.0", + "flagged": false + }, + { + "name": "same-file", + "version": "1.0.6", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "schemars", + "version": "0.8.22", + "license": "MIT", + "flagged": false + }, + { + "name": "schemars", + "version": "0.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "schemars", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "schemars_derive", + "version": "0.8.22", + "license": "MIT", + "flagged": false + }, + { + "name": "scopeguard", + "version": "1.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "secret-service", + "version": "5.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "security-framework", + "version": "3.7.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "security-framework-sys", + "version": "2.17.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "selectors", + "version": "0.36.1", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "semver", + "version": "1.0.28", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde-untagged", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_core", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_derive", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_derive_internals", + "version": "0.29.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_json", + "version": "1.0.151", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_repr", + "version": "0.1.20", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_spanned", + "version": "0.6.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_spanned", + "version": "1.1.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_with", + "version": "3.21.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_with_macros", + "version": "3.21.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serialize-to-javascript", + "version": "0.1.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serialize-to-javascript-impl", + "version": "0.1.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "servo_arc", + "version": "0.4.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "sha1", + "version": "0.11.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "sha2", + "version": "0.10.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "sha2", + "version": "0.11.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "shlex", + "version": "1.3.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "shlex", + "version": "2.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "signal-hook-registry", + "version": "1.4.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "simd-adler32", + "version": "0.3.9", + "license": "MIT", + "flagged": false + }, + { + "name": "siphasher", + "version": "1.0.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "slab", + "version": "0.4.12", + "license": "MIT", + "flagged": false + }, + { + "name": "smallvec", + "version": "1.15.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "socket2", + "version": "0.6.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "softbuffer", + "version": "0.4.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "softprops/action-gh-release", + "version": "3d0d9888cb7fd7b750713d6e236d1fcb99157228", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "soup3", + "version": "0.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "soup3-sys", + "version": "0.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "stable_deref_trait", + "version": "1.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "string_cache", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "string_cache_codegen", + "version": "0.6.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "strsim", + "version": "0.11.1", + "license": "MIT", + "flagged": false + }, + { + "name": "subtle", + "version": "2.6.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "Swatinem/rust-cache", + "version": "6323deb102c322ba6fcbdcafc7e3dddab59af2b6", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "swift-rs", + "version": "1.0.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "syn", + "version": "1.0.109", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "syn", + "version": "2.0.118", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "syn", + "version": "3.0.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "sync_wrapper", + "version": "1.0.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "synstructure", + "version": "0.13.2", + "license": "MIT", + "flagged": false + }, + { + "name": "system-deps", + "version": "6.2.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "tao", + "version": "0.35.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "tao-macros", + "version": "0.1.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "target-lexicon", + "version": "0.12.16", + "license": "Apache-2.0 WITH LLVM-exception", + "flagged": false + }, + { + "name": "tauri", + "version": "2.11.5", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-build", + "version": "2.6.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-codegen", + "version": "2.6.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-macros", + "version": "2.6.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-plugin", + "version": "2.6.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-plugin-dialog", + "version": "2.7.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-plugin-fs", + "version": "2.5.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-plugin-opener", + "version": "2.5.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-runtime", + "version": "2.11.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-runtime-wry", + "version": "2.11.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-utils", + "version": "2.9.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tauri-winres", + "version": "0.3.6", + "license": "MIT", + "flagged": false + }, + { + "name": "tempfile", + "version": "3.27.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "tendril", + "version": "0.5.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror", + "version": "1.0.69", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror", + "version": "2.0.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror-impl", + "version": "1.0.69", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror-impl", + "version": "2.0.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "time", + "version": "0.3.53", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "time-core", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "time-macros", + "version": "0.2.31", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "tinystr", + "version": "0.8.3", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "tinyvec", + "version": "1.11.0", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tinyvec_macros", + "version": "0.1.1", + "license": "MIT OR Apache-2.0 OR Zlib", + "flagged": false + }, + { + "name": "tokio", + "version": "1.52.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tokio-util", + "version": "0.7.18", + "license": "MIT", + "flagged": false + }, + { + "name": "toml", + "version": "0.8.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml", + "version": "0.9.12+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml", + "version": "1.1.2+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_datetime", + "version": "0.6.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_datetime", + "version": "0.7.5+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_datetime", + "version": "1.1.1+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_edit", + "version": "0.19.15", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_edit", + "version": "0.20.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_edit", + "version": "0.25.12+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_parser", + "version": "1.1.2+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "toml_writer", + "version": "1.1.1+spec-1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "tower", + "version": "0.5.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tower-http", + "version": "0.6.11", + "license": "MIT", + "flagged": false + }, + { + "name": "tower-layer", + "version": "0.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tower-service", + "version": "0.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tracing", + "version": "0.1.44", + "license": "MIT", + "flagged": false + }, + { + "name": "tracing-attributes", + "version": "0.1.31", + "license": "MIT", + "flagged": false + }, + { + "name": "tracing-core", + "version": "0.1.36", + "license": "MIT", + "flagged": false + }, + { + "name": "trash", + "version": "5.2.6", + "license": "MIT", + "flagged": false + }, + { + "name": "tray-icon", + "version": "0.24.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "try-lock", + "version": "0.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "typed-path", + "version": "0.12.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "typeid", + "version": "1.0.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "typenum", + "version": "1.20.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "uds_windows", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "unic-char-property", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unic-char-range", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unic-common", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unic-ucd-ident", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unic-ucd-version", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unicode-general-category", + "version": "1.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "unicode-ident", + "version": "1.0.24", + "license": "(MIT OR Apache-2.0) AND Unicode-3.0", + "flagged": false + }, + { + "name": "unicode-normalization", + "version": "0.1.25", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unicode-segmentation", + "version": "1.13.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "untrusted", + "version": "0.9.0", + "license": "ISC", + "flagged": false + }, + { + "name": "ureq", + "version": "3.4.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ureq-proto", + "version": "0.6.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "url", + "version": "2.5.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "urlencoding", + "version": "2.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "urlpattern", + "version": "0.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "utf8-zero", + "version": "0.8.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "utf8_iter", + "version": "1.0.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "uuid", + "version": "1.23.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "valuable", + "version": "0.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "version-compare", + "version": "0.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "version_check", + "version": "0.9.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "vswhom", + "version": "0.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "vswhom-sys", + "version": "0.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "walkdir", + "version": "2.5.0", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "want", + "version": "0.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "wasi", + "version": "0.11.1+wasi-snapshot-preview1", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "wasip2", + "version": "1.0.4+wasi-0.2.12", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "wasm-bindgen", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-futures", + "version": "0.4.76", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-macro", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-macro-support", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-shared", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-streams", + "version": "0.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "web-sys", + "version": "0.3.103", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "web-time", + "version": "1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "web_atoms", + "version": "0.2.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "webkit2gtk", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "webkit2gtk-sys", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "webpki-roots", + "version": "1.0.8", + "license": "CDLA-Permissive-2.0", + "flagged": false + }, + { + "name": "webview2-com", + "version": "0.38.2", + "license": "MIT", + "flagged": false + }, + { + "name": "webview2-com-macros", + "version": "0.8.1", + "license": "MIT", + "flagged": false + }, + { + "name": "webview2-com-sys", + "version": "0.38.2", + "license": "MIT", + "flagged": false + }, + { + "name": "winapi", + "version": "0.3.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "winapi-i686-pc-windows-gnu", + "version": "0.4.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "winapi-util", + "version": "0.1.11", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "winapi-x86_64-pc-windows-gnu", + "version": "0.4.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "window-vibrancy", + "version": "0.6.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "windows", + "version": "0.56.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows", + "version": "0.61.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-collections", + "version": "0.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-core", + "version": "0.56.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-core", + "version": "0.61.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-core", + "version": "0.62.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-future", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-implement", + "version": "0.56.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-implement", + "version": "0.60.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-interface", + "version": "0.56.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-interface", + "version": "0.59.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-link", + "version": "0.1.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-link", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-native-keyring-store", + "version": "1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-numerics", + "version": "0.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-result", + "version": "0.1.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-result", + "version": "0.3.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-result", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-strings", + "version": "0.4.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-strings", + "version": "0.5.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-sys", + "version": "0.45.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-sys", + "version": "0.52.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-sys", + "version": "0.59.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-sys", + "version": "0.60.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-sys", + "version": "0.61.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-targets", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-targets", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-targets", + "version": "0.53.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-threading", + "version": "0.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-version", + "version": "0.1.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_gnullvm", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_gnullvm", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_gnullvm", + "version": "0.53.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_msvc", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_msvc", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_msvc", + "version": "0.53.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_gnu", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_gnu", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_gnu", + "version": "0.53.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_gnullvm", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_gnullvm", + "version": "0.53.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_msvc", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_msvc", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_msvc", + "version": "0.53.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnu", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnu", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnu", + "version": "0.53.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnullvm", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnullvm", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnullvm", + "version": "0.53.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_msvc", + "version": "0.42.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_msvc", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_msvc", + "version": "0.53.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "winnow", + "version": "0.5.40", + "license": "MIT", + "flagged": false + }, + { + "name": "winnow", + "version": "0.7.15", + "license": "MIT", + "flagged": false + }, + { + "name": "winnow", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "winreg", + "version": "0.55.0", + "license": "MIT", + "flagged": false + }, + { + "name": "wit-bindgen", + "version": "0.57.1", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "writeable", + "version": "0.6.3", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "wry", + "version": "0.55.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "x11", + "version": "2.21.0", + "license": "MIT", + "flagged": false + }, + { + "name": "x11-dl", + "version": "2.21.0", + "license": "MIT", + "flagged": false + }, + { + "name": "yoke", + "version": "0.8.3", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "yoke-derive", + "version": "0.8.2", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zbus", + "version": "5.17.0", + "license": "MIT", + "flagged": false + }, + { + "name": "zbus-secret-service-keyring-store", + "version": "1.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "zbus_macros", + "version": "5.17.0", + "license": "MIT", + "flagged": false + }, + { + "name": "zbus_names", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "zerocopy", + "version": "0.8.54", + "license": "BSD-2-Clause OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "zerocopy-derive", + "version": "0.8.54", + "license": "BSD-2-Clause OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "zerofrom", + "version": "0.1.8", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zerofrom-derive", + "version": "0.1.7", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zeroize", + "version": "1.9.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "zeroize_derive", + "version": "1.5.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "zerotrie", + "version": "0.2.4", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zerovec", + "version": "0.11.6", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zerovec-derive", + "version": "0.11.3", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zip", + "version": "8.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "zlib-rs", + "version": "0.6.6", + "license": "Zlib", + "flagged": false + }, + { + "name": "zmij", + "version": "1.0.21", + "license": "MIT", + "flagged": false + }, + { + "name": "zopfli", + "version": "0.8.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "zvariant", + "version": "5.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "zvariant_derive", + "version": "5.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "zvariant_utils", + "version": "3.5.0", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/EgressWeave", + "error": null, + "component_count": 32, + "components": [ + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/download-artifact", + "version": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/download-artifact", + "version": "d3f86a106a0bac45b974a628896c90dbdf5c8093", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "anyio", + "version": "4.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "backports-asyncio-runner", + "version": "1.2.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "certifi", + "version": "2026.7.22", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "coverage", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "exceptiongroup", + "version": "1.3.1", + "license": "MIT AND Python-2.0", + "flagged": false + }, + { + "name": "h11", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hatchling", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "httpcore", + "version": "1.0.9", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx", + "version": "0.28.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "idna", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "packaging", + "version": "26.3", + "license": "Apache-2.0 OR BSD-2-Clause", + "flagged": false + }, + { + "name": "pathspec", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pypa/gh-action-pypi-publish", + "version": "dc37677b2e1c63e2034f94d8a5b11f265b73ba33", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest-asyncio", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest-asyncio", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "ruff", + "version": "0.16.1", + "license": "MIT", + "flagged": false + }, + { + "name": "step-security/harden-runner", + "version": "bf7454d06d71f1098171f2acdf0cd4708d7b5920", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "tomli", + "version": "2.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "trove-classifiers", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + } + ] + }, + { + "repo": "ContextualWisdomLab/ELUNVERA", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/EmbedRelay", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/enterprise-architecture-core", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/fast-mlsirm", + "error": null, + "component_count": 269, + "components": [ + { + "name": "allocator-api2", + "version": "0.2.21", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "android_system_properties", + "version": "0.1.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "android_system_properties", + "version": "0.1.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "arbitrary", + "version": "1.4.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "arrayvec", + "version": "0.7.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ash", + "version": "0.38.0+1.3.281", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "atheris", + "version": "3.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "autocfg", + "version": "1.5.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bit-set", + "version": "0.10.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bit-set", + "version": "0.8.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bit-vec", + "version": "0.8.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bit-vec", + "version": "0.9.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bitflags", + "version": "2.13.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bitflags", + "version": "2.13.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "block-buffer", + "version": "0.10.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "block2", + "version": "0.6.2", + "license": "MIT", + "flagged": false + }, + { + "name": "build", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "bumpalo", + "version": "3.20.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bytemuck", + "version": "1.25.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bytemuck", + "version": ">= 1.25.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "bytemuck_derive", + "version": "1.10.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bytemuck_derive", + "version": "1.12.1", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "cc", + "version": "1.2.66", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cfg-if", + "version": "1.0.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cfg-if", + "version": "1.0.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cfg_aliases", + "version": "0.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "cfg_aliases", + "version": "0.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "codespan-reporting", + "version": "0.13.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-cflite_pr.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-ci.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-codeql.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-hourly-pr-governance.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-publish-pypi.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-pypi-gap-guard.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-release-tag.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-statistical-studies.yml", + "version": "4e32b99ec79b6565800d1081f60acfea368adecd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "cpufeatures", + "version": "0.2.17", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crunchy", + "version": "0.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "crypto-common", + "version": "0.1.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "deranged", + "version": "0.5.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "digest", + "version": "0.10.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dispatch2", + "version": "0.3.1", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "dlib", + "version": "0.5.3", + "license": "MIT", + "flagged": false + }, + { + "name": "document-features", + "version": "0.2.12", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "equivalent", + "version": "1.0.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "errno", + "version": "0.3.14", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "fast-mlsirm", + "version": "0.11.4", + "license": "MIT", + "flagged": false + }, + { + "name": "fastrand", + "version": "2.4.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "find-msvc-tools", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "fnv", + "version": "1.0.7", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "foldhash", + "version": "0.2.0", + "license": "Zlib", + "flagged": false + }, + { + "name": "futures-core", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-core", + "version": "0.3.34", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-task", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-task", + "version": "0.3.34", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-util", + "version": "0.3.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-util", + "version": "0.3.34", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "generic-array", + "version": "0.14.7", + "license": "MIT", + "flagged": false + }, + { + "name": "getrandom", + "version": "0.3.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "getrandom", + "version": "0.4.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "gl_generator", + "version": "0.14.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "glow", + "version": "0.17.0", + "license": "MIT OR Apache-2.0 OR Zlib", + "flagged": false + }, + { + "name": "glutin_wgl_sys", + "version": "0.6.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "gpu-allocator", + "version": "0.28.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "half", + "version": "2.7.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hashbrown", + "version": "0.16.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hashbrown", + "version": "0.17.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "heck", + "version": "0.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hypothesis", + "version": "6.156.6", + "license": "MPL-2.0 AND MPL-1.1", + "flagged": true + }, + { + "name": "hypothesis", + "version": "6.168.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "indexmap", + "version": "2.14.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "indexmap", + "version": "2.14.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "itoa", + "version": "1.0.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jni-sys", + "version": "0.3.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jni-sys", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jni-sys-macros", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "jobserver", + "version": "0.1.35", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "js-sys", + "version": "0.3.103", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "js-sys", + "version": "0.3.105", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "khronos-egl", + "version": "6.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "khronos_api", + "version": "3.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "libc", + "version": "0.2.186", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "libc", + "version": "0.2.189", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "libfuzzer-sys", + "version": "0.4.13", + "license": "(MIT OR Apache-2.0) AND NCSA", + "flagged": false + }, + { + "name": "libloading", + "version": "0.8.9", + "license": "ISC", + "flagged": false + }, + { + "name": "libm", + "version": "0.2.16", + "license": "MIT", + "flagged": false + }, + { + "name": "linux-raw-sys", + "version": "0.12.1", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "litrs", + "version": "1.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "lock_api", + "version": "0.4.14", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "log", + "version": "0.4.33", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "log", + "version": "0.4.34", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "matrixmultiply", + "version": "0.3.11", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "maturin", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "maturin", + "version": "1.15.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "memchr", + "version": "2.8.3", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "naga", + "version": "30.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "naga", + "version": "30.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "naga-types", + "version": "30.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "naga-types", + "version": "30.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ndarray", + "version": "0.17.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ndk-sys", + "version": "0.6.0+11769913", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-complex", + "version": "0.4.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-conv", + "version": "0.2.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-integer", + "version": "0.1.47", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-traits", + "version": "0.2.19", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "numpy", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "numpy", + "version": "0.29.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "numpy", + "version": "2.5.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "numpy", + "version": "2.5.2", + "license": "BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0", + "flagged": false + }, + { + "name": "objc2", + "version": "0.6.4", + "license": "MIT", + "flagged": false + }, + { + "name": "objc2-core-foundation", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-core-graphics", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-encode", + "version": "4.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "objc2-foundation", + "version": "0.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "objc2-io-surface", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-metal", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "objc2-quartz-core", + "version": "0.3.2", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "once_cell", + "version": "1.21.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ordered-float", + "version": "5.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ordered-float", + "version": "5.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "packaging", + "version": "26.2", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "parking_lot", + "version": "0.12.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "parking_lot_core", + "version": "0.9.12", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pin-project-lite", + "version": "0.2.17", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "pkg-config", + "version": "0.3.33", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pkg-config", + "version": "0.3.34", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pollster", + "version": "1.0.1", + "license": "LicenseRef-bad-apache-2.0mit", + "flagged": false + }, + { + "name": "pollster", + "version": ">= 1.0.1,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "portable-atomic", + "version": "1.13.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "portable-atomic", + "version": "1.15.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "portable-atomic-util", + "version": "0.2.7", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "portable-atomic-util", + "version": "0.2.8", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "powerfmt", + "version": "0.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ppv-lite86", + "version": "0.2.21", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "presser", + "version": "0.3.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro2", + "version": "1.0.106", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro2", + "version": "1.0.107", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "profiling", + "version": "1.0.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proptest", + "version": "1.11.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proptest", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pyo3", + "version": "0.29.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-build-config", + "version": "0.29.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-ffi", + "version": "0.29.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-macros", + "version": "0.29.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyo3-macros-backend", + "version": "0.29.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pyproject-hooks", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "quick-error", + "version": "1.2.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "quote", + "version": "1.0.46", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "quote", + "version": "1.0.47", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "r-efi", + "version": "5.3.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later", + "flagged": true + }, + { + "name": "r-efi", + "version": "6.0.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later", + "flagged": true + }, + { + "name": "rand", + "version": "0.9.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rand_chacha", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rand_core", + "version": "0.9.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rand_xorshift", + "version": "0.4.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "range-alloc", + "version": "0.1.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "raw-window-handle", + "version": "0.6.2", + "license": "MIT OR Apache-2.0 OR Zlib", + "flagged": false + }, + { + "name": "raw-window-metal", + "version": "1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rawpointer", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "redox_syscall", + "version": "0.5.18", + "license": "MIT", + "flagged": false + }, + { + "name": "regex-syntax", + "version": "0.8.11", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "renderdoc-sys", + "version": "1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rustc-hash", + "version": "1.1.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "rustc-hash", + "version": "2.1.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "rustix", + "version": "1.1.4", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "rustversion", + "version": "1.0.23", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rusty-fork", + "version": "0.3.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "scopeguard", + "version": "1.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde", + "version": "1.0.228", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "serde_core", + "version": "1.0.228", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_core", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_derive", + "version": "1.0.228", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_derive", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_json", + "version": "1.0.151", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_json", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "sha2", + "version": "0.10.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "sha2", + "version": ">= 0.10.9,< 0.11.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "shlex", + "version": "2.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "slab", + "version": "0.4.12", + "license": "MIT", + "flagged": false + }, + { + "name": "slotmap", + "version": "1.1.1", + "license": "Zlib", + "flagged": false + }, + { + "name": "smallvec", + "version": "1.15.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "smallvec", + "version": "1.16.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "sortedcontainers", + "version": "2.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "spirv", + "version": "0.4.0+sdk-1.4.341.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "static_assertions", + "version": "1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "syn", + "version": "2.0.118", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "syn", + "version": "2.0.119", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "syn", + "version": "3.0.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "target-lexicon", + "version": "0.13.5", + "license": "Apache-2.0 WITH LLVM-exception", + "flagged": false + }, + { + "name": "tempfile", + "version": "3.27.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "termcolor", + "version": "1.4.1", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "thiserror", + "version": "2.0.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror", + "version": "2.0.20", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror-impl", + "version": "2.0.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror-impl", + "version": "2.0.20", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "time", + "version": "0.3.55", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "time", + "version": ">= 0.3.0,< 0.4.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "time-core", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "time-macros", + "version": "0.2.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "typenum", + "version": "1.20.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unarray", + "version": "0.1.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unicode-ident", + "version": "1.0.24", + "license": "(MIT OR Apache-2.0) AND Unicode-3.0", + "flagged": false + }, + { + "name": "unicode-ident", + "version": "1.0.26", + "license": "(MIT OR Apache-2.0) AND Unicode-3.0", + "flagged": false + }, + { + "name": "unicode-width", + "version": "0.2.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "uuid", + "version": "1.25.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "uuid", + "version": "1.26.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "uuid", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "version_check", + "version": "0.9.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wait-timeout", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasip2", + "version": "1.0.4+wasi-0.2.12", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "wasm-bindgen", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen", + "version": "0.2.128", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-futures", + "version": "0.4.76", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-futures", + "version": "0.4.78", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-macro", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-macro", + "version": "0.2.128", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-macro-support", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-macro-support", + "version": "0.2.128", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-shared", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-shared", + "version": "0.2.128", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wayland-sys", + "version": "0.31.11", + "license": "MIT", + "flagged": false + }, + { + "name": "web-sys", + "version": "0.3.103", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "web-sys", + "version": "0.3.105", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu", + "version": "30.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu", + "version": "30.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu", + "version": ">= 30.0.0,< 31.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "wgpu-core", + "version": "30.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu-core", + "version": "30.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu-core-deps-apple", + "version": "30.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu-core-deps-apple", + "version": "30.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu-core-deps-emscripten", + "version": "30.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu-core-deps-emscripten", + "version": "30.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu-core-deps-windows-linux-android", + "version": "30.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu-core-deps-windows-linux-android", + "version": "30.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu-hal", + "version": "30.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu-hal", + "version": "30.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu-naga-bridge", + "version": "30.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu-naga-bridge", + "version": "30.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu-types", + "version": "30.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wgpu-types", + "version": "30.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "winapi-util", + "version": "0.1.11", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "windows", + "version": "0.62.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-collections", + "version": "0.3.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-core", + "version": "0.62.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-future", + "version": "0.3.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-implement", + "version": "0.60.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-interface", + "version": "0.59.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-link", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-numerics", + "version": "0.3.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-result", + "version": "0.4.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-strings", + "version": "0.5.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-sys", + "version": "0.61.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-threading", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wit-bindgen", + "version": "0.57.1", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "xml-rs", + "version": "0.8.28", + "license": "MIT", + "flagged": false + }, + { + "name": "xml-rs", + "version": "0.8.29", + "license": "MIT", + "flagged": false + }, + { + "name": "zerocopy", + "version": "0.8.54", + "license": "BSD-2-Clause OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "zerocopy", + "version": "0.8.57", + "license": "BSD-2-Clause OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "zerocopy-derive", + "version": "0.8.54", + "license": "BSD-2-Clause OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "zerocopy-derive", + "version": "0.8.57", + "license": "BSD-2-Clause OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "zmij", + "version": "1.0.23", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/feelanet-adfs", + "error": null, + "component_count": 303, + "components": [ + { + "name": "@bcoe/v8-coverage", + "version": "1.0.2", + "license": "ISC AND MIT", + "flagged": false + }, + { + "name": "@isaacs/cliui", + "version": "8.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "@istanbuljs/schema", + "version": "0.1.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/resolve-uri", + "version": "3.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/sourcemap-codec", + "version": "1.5.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/trace-mapping", + "version": "0.3.31", + "license": "MIT", + "flagged": false + }, + { + "name": "@node-saml/node-saml", + "version": "5.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@node-saml/passport-saml", + "version": "5.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@pkgjs/parseargs", + "version": "0.11.0", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "@types/babel-types", + "version": "7.0.16", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babylon", + "version": "6.16.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/body-parser", + "version": "1.19.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/connect", + "version": "3.4.38", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/debug", + "version": "4.1.13", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/express", + "version": "4.17.25", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/express-serve-static-core", + "version": "4.19.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/http-errors", + "version": "2.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/istanbul-lib-coverage", + "version": "2.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/mime", + "version": "1.3.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/ms", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/node", + "version": "26.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/passport", + "version": "1.0.17", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/passport-strategy", + "version": "0.2.38", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/qs", + "version": "6.15.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/range-parser", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/send", + "version": "0.17.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/send", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/serve-static", + "version": "1.15.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/xml-encryption", + "version": "1.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/xml2js", + "version": "0.4.14", + "license": "MIT", + "flagged": false + }, + { + "name": "@xmldom/is-dom-node", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@xmldom/xmldom", + "version": "0.8.13", + "license": "MIT", + "flagged": false + }, + { + "name": "accepts", + "version": "1.3.8", + "license": "MIT", + "flagged": false + }, + { + "name": "acorn", + "version": "1.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "acorn", + "version": "2.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "acorn-globals", + "version": "1.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "align-text", + "version": "0.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-regex", + "version": "5.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-regex", + "version": "6.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "4.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "6.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "array-flatten", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "asap", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "axum", + "version": ">= 0.7.0,< 0.8.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "babel-runtime", + "version": "6.26.0", + "license": "MIT", + "flagged": false + }, + { + "name": "babel-types", + "version": "6.26.0", + "license": "MIT", + "flagged": false + }, + { + "name": "babylon", + "version": "6.18.0", + "license": "MIT", + "flagged": false + }, + { + "name": "balanced-match", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "balanced-match", + "version": "4.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "base64", + "version": ">= 0.22.0,< 0.23.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "basic-auth", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "body-parser", + "version": "1.20.5", + "license": "MIT", + "flagged": false + }, + { + "name": "body-parser", + "version": "1.20.6", + "license": "MIT", + "flagged": false + }, + { + "name": "brace-expansion", + "version": "2.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "brace-expansion", + "version": "5.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "buffer-equal-constant-time", + "version": "1.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "bytes", + "version": "3.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "c8", + "version": "10.1.3", + "license": "ISC", + "flagged": false + }, + { + "name": "call-bind-apply-helpers", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "call-bound", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "camelcase", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "center-align", + "version": "0.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "character-parser", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "clean-css", + "version": "4.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "cliui", + "version": "2.1.0", + "license": "ISC", + "flagged": false + }, + { + "name": "cliui", + "version": "8.0.1", + "license": "ISC", + "flagged": false + }, + { + "name": "color-convert", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "color-name", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "commander", + "version": "2.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "constantinople", + "version": "3.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "content-disposition", + "version": "0.5.4", + "license": "MIT", + "flagged": false + }, + { + "name": "content-type", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "convert-source-map", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "cookie", + "version": "0.7.2", + "license": "MIT", + "flagged": false + }, + { + "name": "cookie-parser", + "version": "1.4.7", + "license": "MIT", + "flagged": false + }, + { + "name": "cookie-signature", + "version": "1.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "core-js", + "version": "2.6.12", + "license": "MIT", + "flagged": false + }, + { + "name": "cross-spawn", + "version": "7.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "cryptography", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "css", + "version": "1.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "css-parse", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "css-stringify", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "debug", + "version": "2.6.9", + "license": "MIT", + "flagged": false + }, + { + "name": "debug", + "version": "4.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "decamelize", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "depd", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "destroy", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "dunder-proto", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "eastasianwidth", + "version": "0.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ecdsa-sig-formatter", + "version": "1.0.11", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "ee-first", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "emoji-regex", + "version": "8.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "emoji-regex", + "version": "9.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "encodeurl", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-define-property", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "es-errors", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-object-atoms", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "escalade", + "version": "3.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "escape-html", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "esutils", + "version": "2.0.3", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "etag", + "version": "1.8.1", + "license": "MIT", + "flagged": false + }, + { + "name": "express", + "version": "4.22.2", + "license": "MIT", + "flagged": false + }, + { + "name": "express-rate-limit", + "version": "8.5.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ext-curl", + "version": ">= 0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ext-dom", + "version": ">= 0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ext-mcrypt", + "version": ">= 0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ext-openssl", + "version": ">= 0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "fastapi", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "finalhandler", + "version": "1.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "find-up", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "firebase/php-jwt", + "version": "5.0.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "flate2", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "foreground-child", + "version": "3.3.1", + "license": "ISC", + "flagged": false + }, + { + "name": "form_urlencoded", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "forwarded", + "version": "0.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fresh", + "version": "0.5.2", + "license": "MIT", + "flagged": false + }, + { + "name": "function-bind", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "get-caller-file", + "version": "2.0.5", + "license": "ISC", + "flagged": false + }, + { + "name": "get-intrinsic", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "get-proto", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "glob", + "version": "10.5.0", + "license": "ISC", + "flagged": false + }, + { + "name": "gopd", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "has-flag", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "has-symbols", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hasown", + "version": "2.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "html-escaper", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "http-errors", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "httpx", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "iconv-lite", + "version": "0.4.24", + "license": "MIT", + "flagged": false + }, + { + "name": "inherits", + "version": "2.0.4", + "license": "ISC", + "flagged": false + }, + { + "name": "io.jsonwebtoken:jjwt-api", + "version": "0.10.7", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "io.jsonwebtoken:jjwt-impl", + "version": "0.10.7", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "io.jsonwebtoken:jjwt-jackson", + "version": "0.10.7", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "ip-address", + "version": "10.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ipaddr.js", + "version": "1.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-buffer", + "version": "1.1.6", + "license": "MIT", + "flagged": false + }, + { + "name": "is-fullwidth-code-point", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-promise", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-promise", + "version": "2.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "isexe", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "istanbul-lib-coverage", + "version": "3.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-lib-report", + "version": "3.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-reports", + "version": "3.2.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "itsdangerous", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "jackspeak", + "version": "3.4.3", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "jade", + "version": "1.11.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jinja2", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "jsonwebtoken", + "version": "9.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonwebtoken", + "version": ">= 10.3.0,< 11.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "jstransformer", + "version": "0.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "jwa", + "version": "1.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "jws", + "version": "3.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "kind-of", + "version": "3.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "lazy-cache", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "locate-path", + "version": "6.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "lodash", + "version": "4.18.1", + "license": "MIT", + "flagged": false + }, + { + "name": "lodash.includes", + "version": "4.3.0", + "license": "CC0-1.0 AND MIT", + "flagged": false + }, + { + "name": "lodash.isboolean", + "version": "3.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "lodash.isinteger", + "version": "4.0.4", + "license": "CC0-1.0 AND MIT", + "flagged": false + }, + { + "name": "lodash.isnumber", + "version": "3.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "lodash.isplainobject", + "version": "4.0.6", + "license": "CC0-1.0 AND MIT", + "flagged": false + }, + { + "name": "lodash.isstring", + "version": "4.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "lodash.once", + "version": "4.1.1", + "license": "CC0-1.0 AND MIT", + "flagged": false + }, + { + "name": "longest", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "lru-cache", + "version": "10.4.3", + "license": "ISC", + "flagged": false + }, + { + "name": "lxml", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "make-dir", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "math-intrinsics", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "media-typer", + "version": "0.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "merge-descriptors", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "methods", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "Microsoft.AspNet.Mvc", + "version": "5.2.9", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "Microsoft.AspNet.Mvc.ko", + "version": "5.2.9", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "Microsoft.AspNet.Razor", + "version": "3.2.9", + "license": "LicenseRef-scancode-unknown", + "flagged": false + }, + { + "name": "Microsoft.AspNet.Razor.ko", + "version": "3.2.9", + "license": "LicenseRef-scancode-unknown", + "flagged": false + }, + { + "name": "Microsoft.AspNet.WebPages", + "version": "3.2.9", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "Microsoft.AspNet.WebPages.ko", + "version": "3.2.9", + "license": "LicenseRef-scancode-unknown", + "flagged": false + }, + { + "name": "Microsoft.CodeDom.Providers.DotNetCompilerPlatform", + "version": "2.0.1", + "license": "LicenseRef-github-OTHER", + "flagged": false + }, + { + "name": "Microsoft.IdentityModel.Clients.ActiveDirectory", + "version": "4.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "Microsoft.IdentityModel.JsonWebTokens", + "version": "5.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "Microsoft.IdentityModel.Logging", + "version": "5.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "Microsoft.IdentityModel.Tokens", + "version": "5.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "Microsoft.Web.Infrastructure", + "version": "2.0.1", + "license": "LicenseRef-scancode-unknown", + "flagged": false + }, + { + "name": "mime", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mime-db", + "version": "1.52.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mime-types", + "version": "2.1.35", + "license": "MIT", + "flagged": false + }, + { + "name": "minimatch", + "version": "10.2.5", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "minimatch", + "version": "9.0.9", + "license": "ISC", + "flagged": false + }, + { + "name": "minimist", + "version": "1.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "minipass", + "version": "7.1.3", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "mkdirp", + "version": "0.5.6", + "license": "MIT", + "flagged": false + }, + { + "name": "morgan", + "version": "1.11.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ms", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ms", + "version": "2.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "negotiator", + "version": "0.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "Newtonsoft.Json", + "version": "12.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "node-uuid", + "version": "1.4.8", + "license": "MIT", + "flagged": false + }, + { + "name": "object-inspect", + "version": "1.13.4", + "license": "MIT", + "flagged": false + }, + { + "name": "on-finished", + "version": "2.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "on-headers", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "p-limit", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "p-locate", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "package-json-from-dist", + "version": "1.0.1", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "parseurl", + "version": "1.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "passport", + "version": "0.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "passport-strategy", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "path-exists", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "path-key", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "path-scurry", + "version": "1.11.1", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "path-to-regexp", + "version": "0.1.13", + "license": "MIT", + "flagged": false + }, + { + "name": "pause", + "version": "0.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pdepend/pdepend", + "version": "1.1.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pem", + "version": ">= 3.0.0,< 4.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "php", + "version": ">= 5.3.2", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "phploc/phploc", + "version": ">= 0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "phpunit/phpunit", + "version": "4.8", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "promise", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "promise", + "version": "6.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "proxy-addr", + "version": "2.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "pyjwt", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "python-multipart", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "qs", + "version": "6.15.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "rand", + "version": ">= 0.8.0,< 0.9.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "range-parser", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "raw-body", + "version": "2.5.3", + "license": "MIT", + "flagged": false + }, + { + "name": "regenerator-runtime", + "version": "0.11.1", + "license": "MIT", + "flagged": false + }, + { + "name": "repeat-string", + "version": "1.6.1", + "license": "MIT", + "flagged": false + }, + { + "name": "require-directory", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "reqwest", + "version": ">= 0.12.0,< 0.13.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "right-align", + "version": "0.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "roxmltree", + "version": ">= 0.20.0,< 0.21.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "rsa", + "version": ">= 0.9.0,< 0.10.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "rust-xmlsec", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "safe-buffer", + "version": "5.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "safe-buffer", + "version": "5.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "safer-buffer", + "version": "2.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "satooshi/php-coveralls", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "sax", + "version": "1.6.0", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "sebastian/phpcpd", + "version": ">= 0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "semver", + "version": "7.8.5", + "license": "ISC", + "flagged": false + }, + { + "name": "send", + "version": "0.19.2", + "license": "MIT", + "flagged": false + }, + { + "name": "serde", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "serde_json", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "serve-static", + "version": "1.16.3", + "license": "MIT", + "flagged": false + }, + { + "name": "setprototypeof", + "version": "1.2.0", + "license": "ISC", + "flagged": false + }, + { + "name": "sha2", + "version": ">= 0.10.0,< 0.11.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "shebang-command", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "shebang-regex", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "side-channel", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "side-channel-list", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "side-channel-map", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "side-channel-weakmap", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "signal-exit", + "version": "4.1.0", + "license": "ISC", + "flagged": false + }, + { + "name": "signxml", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "source-map", + "version": "0.5.7", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "source-map", + "version": "0.6.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "squizlabs/php_codesniffer", + "version": "2.9.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "statuses", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "string-width", + "version": "4.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "string-width", + "version": "5.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-ansi", + "version": "6.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-ansi", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-color", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "System.IdentityModel.Tokens.Jwt", + "version": "5.4.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "test-exclude", + "version": "7.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "thiserror", + "version": ">= 2.0.0,< 3.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "time", + "version": ">= 0.3.0,< 0.4.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "to-fast-properties", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "toidentifier", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tokio", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "tower-sessions", + "version": ">= 0.13.0,< 0.14.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "transformers", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "type-is", + "version": "1.6.18", + "license": "MIT", + "flagged": false + }, + { + "name": "uglify-js", + "version": "2.8.29", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "uglify-to-browserify", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "undici-types", + "version": "8.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "unpipe", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "utils-merge", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "uvicorn", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "v8-to-istanbul", + "version": "9.3.0", + "license": "ISC", + "flagged": false + }, + { + "name": "vary", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "void-elements", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "which", + "version": "2.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "window-size", + "version": "0.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "with", + "version": "4.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "wordwrap", + "version": "0.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "wrap-ansi", + "version": "7.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "wrap-ansi", + "version": "8.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "x509-parser", + "version": ">= 0.16.0,< 0.17.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "xml-crypto", + "version": "6.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "xml-encryption", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "xml2js", + "version": "0.6.2", + "license": "MIT", + "flagged": false + }, + { + "name": "xmlbuilder", + "version": "11.0.1", + "license": "LicenseRef-scancode-unicode AND MIT", + "flagged": false + }, + { + "name": "xmlbuilder", + "version": "15.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "xpath", + "version": "0.0.32", + "license": "MIT", + "flagged": false + }, + { + "name": "xpath", + "version": "0.0.33", + "license": "MIT", + "flagged": false + }, + { + "name": "xpath", + "version": "0.0.34", + "license": "MIT", + "flagged": false + }, + { + "name": "y18n", + "version": "5.0.8", + "license": "ISC", + "flagged": false + }, + { + "name": "yargs", + "version": "17.7.3", + "license": "MIT", + "flagged": false + }, + { + "name": "yargs", + "version": "3.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "yargs-parser", + "version": "21.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "yocto-queue", + "version": "0.1.0", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/four-pillars", + "error": null, + "component_count": 70, + "components": [ + { + "name": "actions/checkout", + "version": "11d5960a326750d5838078e36cf38b85af677262", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/create-github-app-token", + "version": "bcd2ba49218906704ab6c1aa796996da409d3eb1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/download-artifact", + "version": "d3f86a106a0bac45b974a628896c90dbdf5c8093", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "a26af69be951a213d495a4c3e4e4022e16d87065", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "annotated-doc", + "version": "0.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "annotated-types", + "version": "0.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "anyio", + "version": "4.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "build", + "version": "1.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "certifi", + "version": "2026.7.22", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "charset-normalizer", + "version": "3.4.9", + "license": "MIT", + "flagged": false + }, + { + "name": "click", + "version": "8.4.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "coverage", + "version": "7.15.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "fastapi", + "version": "0.141.1", + "license": "MIT", + "flagged": false + }, + { + "name": "fastapi", + "version": ">= 0.115,< 1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "h11", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hatchling", + "version": "1.31.0", + "license": "MIT", + "flagged": false + }, + { + "name": "httpcore", + "version": "1.0.9", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpcore2", + "version": "2.9.1", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httptools", + "version": "0.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "httpx", + "version": "0.28.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx", + "version": ">= 0.27,< 1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "httpx2", + "version": "2.9.1", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jinja2", + "version": "3.1.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "jinja2", + "version": ">= 3.1,< 4", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "korean-lunar-calendar", + "version": "0.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "korean-lunar-calendar", + "version": ">= 0.3.1,< 1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "librt", + "version": "0.13.0", + "license": "MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "markdown-it-py", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "markupsafe", + "version": "3.0.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "mdurl", + "version": "0.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "mypy", + "version": "1.20.2", + "license": "MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause AND MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "mypy-extensions", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "packaging", + "version": "26.2", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "pathspec", + "version": "1.1.1", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "pillow", + "version": "12.3.0", + "license": "MIT-CMU", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic", + "version": "2.13.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic", + "version": ">= 2.10,< 3", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pydantic-core", + "version": "2.46.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-settings", + "version": "2.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-settings", + "version": ">= 2.7,< 3", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pyproject-hooks", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest", + "version": "8.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest-asyncio", + "version": "0.26.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pytest-cov", + "version": "6.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "python-dotenv", + "version": "1.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "python-multipart", + "version": "0.0.32", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "python-multipart", + "version": ">= 0.0.20,< 1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pyyaml", + "version": "6.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "reportlab", + "version": "4.5.1", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "reportlab", + "version": ">= 4.2,< 5", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "rich", + "version": "15.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ruff", + "version": "0.16.1", + "license": "MIT", + "flagged": false + }, + { + "name": "shellingham", + "version": "1.5.4", + "license": "ISC", + "flagged": false + }, + { + "name": "starlette", + "version": "1.3.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "trove-classifiers", + "version": "2026.6.1.19", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "truststore", + "version": "0.10.4", + "license": "MIT", + "flagged": false + }, + { + "name": "typer", + "version": "0.27.1", + "license": "MIT", + "flagged": false + }, + { + "name": "typer", + "version": ">= 0.15,< 1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "typing-inspection", + "version": "0.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "uvicorn", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "uvicorn", + "version": "0.52.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "uvloop", + "version": "0.22.1", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "watchfiles", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "websockets", + "version": "17.0.1", + "license": "BSD-3-Clause", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/global-hs-trade", + "error": null, + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/governance-risk-compliance", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/gyeot", + "error": null, + "component_count": 831, + "components": [ + { + "name": "@babel/code-frame", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/compat-data", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/core", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/generator", + "version": "7.29.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-annotate-as-pure", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-compilation-targets", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-create-class-features-plugin", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-create-regexp-features-plugin", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-define-polyfill-provider", + "version": "0.6.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-globals", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-member-expression-to-functions", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-module-imports", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-module-transforms", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-optimise-call-expression", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-plugin-utils", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-remap-async-to-generator", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-replace-supers", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-skip-transparent-expression-wrappers", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-string-parser", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-validator-identifier", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-validator-option", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-wrap-function", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helpers", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/parser", + "version": "7.29.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-proposal-decorators", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-proposal-export-default-from", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-async-generators", + "version": "7.8.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-bigint", + "version": "7.8.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-class-properties", + "version": "7.12.13", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-class-static-block", + "version": "7.14.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-decorators", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-dynamic-import", + "version": "7.8.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-export-default-from", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-flow", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-import-attributes", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-import-meta", + "version": "7.10.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-json-strings", + "version": "7.8.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-jsx", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-logical-assignment-operators", + "version": "7.10.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-nullish-coalescing-operator", + "version": "7.8.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-numeric-separator", + "version": "7.10.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-object-rest-spread", + "version": "7.8.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-optional-catch-binding", + "version": "7.8.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-optional-chaining", + "version": "7.8.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-private-property-in-object", + "version": "7.14.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-top-level-await", + "version": "7.14.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-syntax-typescript", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-async-generator-functions", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-async-to-generator", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-block-scoping", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-class-properties", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-class-static-block", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-classes", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-destructuring", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-export-namespace-from", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-flow-strip-types", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-for-of", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-logical-assignment-operators", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-modules-commonjs", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-named-capturing-groups-regex", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-nullish-coalescing-operator", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-object-rest-spread", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-optional-catch-binding", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-optional-chaining", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-parameters", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-private-methods", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-private-property-in-object", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-react-display-name", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-react-jsx", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-react-jsx-development", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-react-pure-annotations", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-runtime", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-typescript", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-unicode-regex", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/preset-typescript", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/runtime", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/template", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/traverse", + "version": "7.29.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/types", + "version": "7.29.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@bcoe/v8-coverage", + "version": "0.2.3", + "license": "ISC AND MIT", + "flagged": false + }, + { + "name": "@egjs/hammerjs", + "version": "2.0.17", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/core", + "version": "1.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/runtime", + "version": "1.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/wasi-threads", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/aix-ppc64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-arm", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/darwin-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/darwin-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/freebsd-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/freebsd-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-arm", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-ia32", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-loong64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-mips64el", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-ppc64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-riscv64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-s390x", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/netbsd-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/netbsd-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openbsd-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openbsd-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openharmony-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/sunos-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-ia32", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo-google-fonts/material-symbols", + "version": "0.4.44", + "license": "MIT AND Apache-2.0", + "flagged": false + }, + { + "name": "@expo/cli", + "version": "57.0.17", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/code-signing-certificates", + "version": "0.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/config", + "version": "57.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/config-plugins", + "version": "57.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/config-types", + "version": "57.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/devcert", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/devtools", + "version": "57.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/dom-webview", + "version": "57.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/env", + "version": "2.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/expo-modules-macros-plugin", + "version": "0.6.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/fingerprint", + "version": "0.20.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/image-utils", + "version": "0.11.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/inline-modules", + "version": "0.1.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/json-file", + "version": "11.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/local-build-cache-provider", + "version": "57.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/log-box", + "version": "57.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/metro", + "version": "56.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/metro-config", + "version": "57.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/metro-file-map", + "version": "57.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/metro-runtime", + "version": "57.0.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/osascript", + "version": "2.7.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/package-manager", + "version": "1.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/plist", + "version": "0.8.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/prebuild-config", + "version": "57.0.13", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/require-utils", + "version": "57.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/router-server", + "version": "57.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/schema-utils", + "version": "57.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/sdk-runtime-versions", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/spawn-async", + "version": "1.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/sudo-prompt", + "version": "9.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/ui", + "version": "57.0.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/ws-tunnel", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@expo/xcpretty", + "version": "4.4.4", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "@isaacs/cliui", + "version": "8.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "@isaacs/ttlcache", + "version": "1.4.1", + "license": "ISC", + "flagged": false + }, + { + "name": "@istanbuljs/load-nyc-config", + "version": "1.1.0", + "license": "ISC", + "flagged": false + }, + { + "name": "@istanbuljs/schema", + "version": "0.1.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/console", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/core", + "version": "30.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/diff-sequences", + "version": "30.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/environment", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/expect", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/expect-utils", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/fake-timers", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/get-type", + "version": "30.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/globals", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/pattern", + "version": "30.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/reporters", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/schemas", + "version": "29.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/schemas", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/snapshot-utils", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/source-map", + "version": "30.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/test-result", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/test-sequencer", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/transform", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/types", + "version": "29.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@jest/types", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/gen-mapping", + "version": "0.3.13", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/remapping", + "version": "2.3.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/resolve-uri", + "version": "3.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/source-map", + "version": "0.3.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/sourcemap-codec", + "version": "1.5.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/trace-mapping", + "version": "0.3.31", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/wasm-runtime", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@pkgjs/parseargs", + "version": "0.11.0", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "@pkgr/core", + "version": "0.3.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/primitive", + "version": "1.1.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-collection", + "version": "1.1.15", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-compose-refs", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-context", + "version": "1.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-dialog", + "version": "1.1.23", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-direction", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-dismissable-layer", + "version": "1.1.19", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-focus-guards", + "version": "1.1.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-focus-scope", + "version": "1.1.16", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-id", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-portal", + "version": "1.1.17", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-presence", + "version": "1.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-primitive", + "version": "2.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-roving-focus", + "version": "1.1.19", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-slot", + "version": "1.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-tabs", + "version": "1.1.21", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-use-callback-ref", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-use-controllable-state", + "version": "1.2.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-use-effect-event", + "version": "0.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-use-is-hydrated", + "version": "0.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-use-layout-effect", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native-async-storage/async-storage", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native-masked-view/masked-view", + "version": "0.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native/asset-utils", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native/babel-plugin-codegen", + "version": "0.86.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native/codegen", + "version": "0.86.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native/codegen", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native/community-cli-plugin", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native/debugger-frontend", + "version": "0.86.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "@react-native/debugger-frontend", + "version": "0.87.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "@react-native/debugger-shell", + "version": "0.86.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native/debugger-shell", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native/dev-middleware", + "version": "0.86.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native/dev-middleware", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native/gradle-plugin", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native/normalize-colors", + "version": "0.86.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native/normalize-colors", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@react-native/virtualized-lists", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@sinclair/typebox", + "version": "0.27.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@sinclair/typebox", + "version": "0.34.52", + "license": "MIT", + "flagged": false + }, + { + "name": "@sinonjs/commons", + "version": "3.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "@sinonjs/fake-timers", + "version": "15.4.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "@tybys/wasm-util", + "version": "0.10.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__core", + "version": "7.20.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__generator", + "version": "7.27.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__template", + "version": "7.4.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__traverse", + "version": "7.28.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/emscripten", + "version": "1.41.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/hammerjs", + "version": "2.0.46", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/istanbul-lib-coverage", + "version": "2.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/istanbul-lib-report", + "version": "3.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/istanbul-reports", + "version": "3.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/jest", + "version": "30.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/node", + "version": "24.13.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/node", + "version": "26.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/pg", + "version": "8.20.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react", + "version": "19.2.18", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react-test-renderer", + "version": "19.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/stack-utils", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/yargs", + "version": "17.0.35", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/yargs-parser", + "version": "21.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@ungap/structured-clone", + "version": "1.3.3", + "license": "ISC", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-android-arm-eabi", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-android-arm64", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-darwin-arm64", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-darwin-x64", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-freebsd-x64", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-arm-gnueabihf", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-arm-musleabihf", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-arm64-gnu", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-arm64-musl", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-loong64-gnu", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-loong64-musl", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-ppc64-gnu", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-riscv64-gnu", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-riscv64-musl", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-s390x-gnu", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-x64-gnu", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-x64-musl", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-openharmony-arm64", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-wasm32-wasi", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-win32-arm64-msvc", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-win32-ia32-msvc", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-win32-x64-msvc", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@xmldom/xmldom", + "version": "0.8.15", + "license": "MIT", + "flagged": false + }, + { + "name": "@xmldom/xmldom", + "version": "0.9.12", + "license": "MIT", + "flagged": false + }, + { + "name": "abort-controller", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "accepts", + "version": "1.3.8", + "license": "MIT", + "flagged": false + }, + { + "name": "accepts", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "acorn", + "version": "8.18.0", + "license": "MIT", + "flagged": false + }, + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "249970729cb0ef3589644e2896645e5dc5ba9c38", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "agent-base", + "version": "7.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "agent-cli-detector", + "version": "0.1.6", + "license": "MIT", + "flagged": false + }, + { + "name": "anser", + "version": "1.4.10", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-escapes", + "version": "4.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-regex", + "version": "4.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-regex", + "version": "5.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-regex", + "version": "6.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "3.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "4.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "5.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "6.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "anymatch", + "version": "3.1.3", + "license": "ISC", + "flagged": false + }, + { + "name": "arg", + "version": "5.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "argparse", + "version": "1.0.10", + "license": "MIT", + "flagged": false + }, + { + "name": "argparse", + "version": "2.0.1", + "license": "Python-2.0", + "flagged": false + }, + { + "name": "aria-hidden", + "version": "1.2.6", + "license": "MIT", + "flagged": false + }, + { + "name": "asap", + "version": "2.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "await-lock", + "version": "2.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "babel-jest", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "babel-plugin-istanbul", + "version": "7.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "babel-plugin-jest-hoist", + "version": "30.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "babel-plugin-polyfill-corejs2", + "version": "0.4.17", + "license": "MIT", + "flagged": false + }, + { + "name": "babel-plugin-polyfill-corejs3", + "version": "0.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "babel-plugin-polyfill-regenerator", + "version": "0.6.8", + "license": "MIT", + "flagged": false + }, + { + "name": "babel-plugin-react-compiler", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "babel-plugin-react-native-web", + "version": "0.21.2", + "license": "MIT", + "flagged": false + }, + { + "name": "babel-plugin-syntax-hermes-parser", + "version": "0.36.1", + "license": "MIT", + "flagged": false + }, + { + "name": "babel-plugin-transform-flow-enums", + "version": "0.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "babel-preset-current-node-syntax", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "babel-preset-expo", + "version": "57.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "babel-preset-jest", + "version": "30.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "badgin", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "balanced-match", + "version": "4.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "barcode-detector", + "version": "3.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "base64-js", + "version": "1.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "baseline-browser-mapping", + "version": "2.11.18", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "big-integer", + "version": "1.6.52", + "license": "LicenseRef-scancode-public-domain AND Unlicense", + "flagged": false + }, + { + "name": "boolbase", + "version": "1.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "bplist-creator", + "version": "0.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "bplist-parser", + "version": "0.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "bplist-parser", + "version": "0.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "brace-expansion", + "version": "5.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "braces", + "version": "3.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "browserslist", + "version": "4.28.8", + "license": "MIT", + "flagged": false + }, + { + "name": "bs-logger", + "version": "0.2.6", + "license": "MIT", + "flagged": false + }, + { + "name": "bser", + "version": "2.1.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "buffer-from", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "bytes", + "version": "3.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "callsites", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "camelcase", + "version": "5.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "camelcase", + "version": "6.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "caniuse-lite", + "version": "1.0.30001809", + "license": "CC-BY-4.0", + "flagged": false + }, + { + "name": "chalk", + "version": "2.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "chalk", + "version": "4.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "char-regex", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "chrome-launcher", + "version": "0.15.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "chromium-edge-launcher", + "version": "0.3.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "ci-info", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ci-info", + "version": "3.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ci-info", + "version": "4.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "cjs-module-lexer", + "version": "2.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "cli-cursor", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "cli-spinners", + "version": "2.9.2", + "license": "MIT", + "flagged": false + }, + { + "name": "client-only", + "version": "0.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "cliui", + "version": "8.0.1", + "license": "ISC", + "flagged": false + }, + { + "name": "clone", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "co", + "version": "4.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "collect-v8-coverage", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "color", + "version": "4.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "color-convert", + "version": "1.9.3", + "license": "MIT", + "flagged": false + }, + { + "name": "color-convert", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "color-name", + "version": "1.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "color-name", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "color-string", + "version": "1.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "commander", + "version": "12.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "commander", + "version": "2.20.3", + "license": "MIT", + "flagged": false + }, + { + "name": "commander", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "compressible", + "version": "2.0.18", + "license": "MIT", + "flagged": false + }, + { + "name": "compression", + "version": "1.8.1", + "license": "MIT", + "flagged": false + }, + { + "name": "connect", + "version": "3.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "convert-source-map", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "core-js-compat", + "version": "3.50.0", + "license": "MIT", + "flagged": false + }, + { + "name": "cross-spawn", + "version": "7.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "css-select", + "version": "5.2.2", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "css-tree", + "version": "1.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "css-what", + "version": "6.2.2", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "csstype", + "version": "3.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "debug", + "version": "2.6.9", + "license": "MIT", + "flagged": false + }, + { + "name": "debug", + "version": "3.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "debug", + "version": "4.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "decode-uri-component", + "version": "0.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "dedent", + "version": "1.7.2", + "license": "MIT", + "flagged": false + }, + { + "name": "deepmerge", + "version": "4.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "defaults", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "depd", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "destroy", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "detect-libc", + "version": "2.1.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "detect-newline", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "detect-node-es", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "dnssd-advertise", + "version": "1.1.6", + "license": "MIT", + "flagged": false + }, + { + "name": "dom-serializer", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "domelementtype", + "version": "2.3.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "domhandler", + "version": "5.0.3", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "domutils", + "version": "3.2.2", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "eastasianwidth", + "version": "0.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ee-first", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "electron-to-chromium", + "version": "1.5.412", + "license": "ISC", + "flagged": false + }, + { + "name": "emittery", + "version": "0.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "emoji-regex", + "version": "8.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "emoji-regex", + "version": "9.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "encodeurl", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "encodeurl", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "entities", + "version": "4.5.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "error-ex", + "version": "1.3.4", + "license": "MIT", + "flagged": false + }, + { + "name": "error-stack-parser", + "version": "2.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "es-errors", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "esbuild", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "escalade", + "version": "3.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "escape-html", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "escape-string-regexp", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "escape-string-regexp", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "escape-string-regexp", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "esprima", + "version": "4.0.1", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "etag", + "version": "1.8.1", + "license": "MIT", + "flagged": false + }, + { + "name": "event-target-shim", + "version": "5.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "execa", + "version": "5.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "exit-x", + "version": "0.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "expect", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "expo", + "version": "57.0.15", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-application", + "version": "57.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-asset", + "version": "57.0.13", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-audio", + "version": "57.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-camera", + "version": "57.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-constants", + "version": "57.0.13", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-crypto", + "version": "57.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-file-system", + "version": "57.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-font", + "version": "57.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-glass-effect", + "version": "57.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-haptics", + "version": "57.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-image-loader", + "version": "57.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-image-manipulator", + "version": "57.0.12", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-image-picker", + "version": "57.0.12", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-keep-awake", + "version": "57.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-linking", + "version": "57.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-media-library", + "version": "57.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-modules-autolinking", + "version": "57.0.10", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-modules-core", + "version": "57.0.12", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-modules-jsi", + "version": "57.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-notifications", + "version": "57.0.13", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-router", + "version": "57.0.15", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-secure-store", + "version": "57.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-server", + "version": "57.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-sqlite", + "version": "57.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-status-bar", + "version": "57.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-symbols", + "version": "57.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "expo-video", + "version": "57.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "exponential-backoff", + "version": "3.1.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "fast-deep-equal", + "version": "3.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-json-stable-stringify", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fb-dotslash", + "version": "0.5.8", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "fb-watchman", + "version": "2.0.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "fdir", + "version": "6.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fetch-nodeshim", + "version": "0.4.10", + "license": "MIT", + "flagged": false + }, + { + "name": "fill-range", + "version": "7.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "filter-obj", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "finalhandler", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "find-up", + "version": "4.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "flow-enums-runtime", + "version": "0.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "fontfaceobserver", + "version": "2.3.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "foreground-child", + "version": "3.3.1", + "license": "ISC", + "flagged": false + }, + { + "name": "fresh", + "version": "0.5.2", + "license": "MIT", + "flagged": false + }, + { + "name": "fs.realpath", + "version": "1.0.0", + "license": "ISC AND MIT", + "flagged": false + }, + { + "name": "fsevents", + "version": "2.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "function-bind", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "gensync", + "version": "1.0.0-beta.2", + "license": "MIT", + "flagged": false + }, + { + "name": "get-caller-file", + "version": "2.0.5", + "license": "ISC", + "flagged": false + }, + { + "name": "get-nonce", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "get-package-type", + "version": "0.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "get-stream", + "version": "6.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "getenv", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "glob", + "version": "10.5.0", + "license": "ISC", + "flagged": false + }, + { + "name": "glob", + "version": "13.0.6", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "glob", + "version": "7.2.3", + "license": "ISC", + "flagged": false + }, + { + "name": "graceful-fs", + "version": "4.2.11", + "license": "ISC", + "flagged": false + }, + { + "name": "handlebars", + "version": "4.7.9", + "license": "MIT", + "flagged": false + }, + { + "name": "has-flag", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "has-flag", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hasown", + "version": "2.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "hermes-compiler", + "version": "250829098.0.16", + "license": "MIT", + "flagged": false + }, + { + "name": "hermes-estree", + "version": "0.35.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hermes-estree", + "version": "0.36.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hermes-estree", + "version": "0.36.1", + "license": "MIT", + "flagged": false + }, + { + "name": "hermes-parser", + "version": "0.35.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hermes-parser", + "version": "0.36.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hermes-parser", + "version": "0.36.1", + "license": "MIT", + "flagged": false + }, + { + "name": "hoist-non-react-statics", + "version": "3.3.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "hosted-git-info", + "version": "7.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "html-escaper", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "http-errors", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "https-proxy-agent", + "version": "7.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "human-signals", + "version": "2.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "ignore", + "version": "5.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "image-size", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "import-local", + "version": "3.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "imurmurhash", + "version": "0.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "inflight", + "version": "1.0.6", + "license": "ISC", + "flagged": false + }, + { + "name": "inherits", + "version": "2.0.4", + "license": "ISC", + "flagged": false + }, + { + "name": "invariant", + "version": "2.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "is-arrayish", + "version": "0.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-arrayish", + "version": "0.3.4", + "license": "MIT", + "flagged": false + }, + { + "name": "is-core-module", + "version": "2.16.2", + "license": "MIT", + "flagged": false + }, + { + "name": "is-docker", + "version": "2.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-fullwidth-code-point", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-generator-fn", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-number", + "version": "7.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-plain-obj", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-stream", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-wsl", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "isexe", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "istanbul-lib-coverage", + "version": "3.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-lib-instrument", + "version": "6.0.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-lib-report", + "version": "3.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-lib-source-maps", + "version": "5.0.6", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-reports", + "version": "3.2.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "jackspeak", + "version": "3.4.3", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "jest", + "version": "30.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-changed-files", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-circus", + "version": "30.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-cli", + "version": "30.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-config", + "version": "30.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-diff", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-docblock", + "version": "30.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-each", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-environment-node", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-get-type", + "version": "29.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-haste-map", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-leak-detector", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-matcher-utils", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-message-util", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-mock", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-pnp-resolver", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-regex-util", + "version": "30.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-resolve", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-resolve-dependencies", + "version": "30.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-runner", + "version": "30.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-runtime", + "version": "30.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-snapshot", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-util", + "version": "29.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-util", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-validate", + "version": "29.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-validate", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-watcher", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-worker", + "version": "29.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jest-worker", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jimp-compact", + "version": "0.16.1", + "license": "MIT", + "flagged": false + }, + { + "name": "js-tokens", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "js-yaml", + "version": "3.15.1", + "license": "MIT", + "flagged": false + }, + { + "name": "js-yaml", + "version": "4.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jsc-safe-url", + "version": "0.2.4", + "license": "0BSD", + "flagged": false + }, + { + "name": "jsesc", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "json-parse-even-better-errors", + "version": "2.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "json5", + "version": "2.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "kleur", + "version": "3.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "lan-network", + "version": "0.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "leven", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "lighthouse-logger", + "version": "1.4.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "lightningcss", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lines-and-columns", + "version": "1.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "locate-path", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "lodash.debounce", + "version": "4.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "lodash.memoize", + "version": "4.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "lodash.throttle", + "version": "4.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "log-symbols", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "loose-envify", + "version": "1.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "lru-cache", + "version": "10.4.3", + "license": "ISC", + "flagged": false + }, + { + "name": "lru-cache", + "version": "11.5.2", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "lru-cache", + "version": "5.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "make-dir", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "make-error", + "version": "1.3.6", + "license": "ISC", + "flagged": false + }, + { + "name": "makeerror", + "version": "1.0.12", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "marky", + "version": "1.3.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "mdn-data", + "version": "2.0.14", + "license": "CC0-1.0", + "flagged": false + }, + { + "name": "memoize-one", + "version": "5.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "merge-options", + "version": "3.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "merge-stream", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-babel-transformer", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-babel-transformer", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-cache", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-cache", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-cache-key", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-cache-key", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-config", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-config", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-core", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-core", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-file-map", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-file-map", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-minify-terser", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-minify-terser", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-resolver", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-resolver", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-runtime", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-runtime", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-source-map", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-source-map", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-symbolicate", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-symbolicate", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-transform-plugins", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-transform-plugins", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-transform-worker", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "metro-transform-worker", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "micromatch", + "version": "4.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "mime", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mime-db", + "version": "1.52.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mime-db", + "version": "1.54.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mime-types", + "version": "2.1.35", + "license": "MIT", + "flagged": false + }, + { + "name": "mime-types", + "version": "3.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "mimic-fn", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mimic-fn", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "minimatch", + "version": "10.2.6", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "minimatch", + "version": "3.1.5", + "license": "ISC", + "flagged": false + }, + { + "name": "minimatch", + "version": "9.0.9", + "license": "ISC", + "flagged": false + }, + { + "name": "minimist", + "version": "1.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "minipass", + "version": "7.1.3", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "mkdirp", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "ms", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ms", + "version": "2.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "multitars", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "nanoid", + "version": "3.3.18", + "license": "MIT", + "flagged": false + }, + { + "name": "napi-postinstall", + "version": "0.3.4", + "license": "MIT", + "flagged": false + }, + { + "name": "natural-compare", + "version": "1.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "negotiator", + "version": "0.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "negotiator", + "version": "0.6.4", + "license": "MIT", + "flagged": false + }, + { + "name": "negotiator", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "neo-async", + "version": "2.6.2", + "license": "MIT", + "flagged": false + }, + { + "name": "node-forge", + "version": "1.4.0", + "license": "BSD-3-Clause OR GPL-2.0-only", + "flagged": true + }, + { + "name": "node-int64", + "version": "0.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "node-releases", + "version": "2.0.53", + "license": "MIT", + "flagged": false + }, + { + "name": "normalize-path", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "npm-package-arg", + "version": "11.0.3", + "license": "ISC", + "flagged": false + }, + { + "name": "npm-run-path", + "version": "4.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "nth-check", + "version": "2.1.1", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "nullthrows", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ob1", + "version": "0.84.5", + "license": "MIT", + "flagged": false + }, + { + "name": "ob1", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "on-finished", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "on-finished", + "version": "2.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "on-headers", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "once", + "version": "1.4.0", + "license": "ISC", + "flagged": false + }, + { + "name": "onetime", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "onetime", + "version": "5.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "open", + "version": "7.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ora", + "version": "3.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "p-limit", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "p-limit", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "p-locate", + "version": "4.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "p-try", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "package-json-from-dist", + "version": "1.0.1", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "parse-json", + "version": "5.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "parse-png", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "parseurl", + "version": "1.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "path-exists", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "path-is-absolute", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "path-key", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "path-parse", + "version": "1.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "path-scurry", + "version": "1.11.1", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "path-scurry", + "version": "2.0.2", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "pg", + "version": "8.22.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pg-cloudflare", + "version": "1.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pg-connection-string", + "version": "2.14.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pg-int8", + "version": "1.0.1", + "license": "ISC", + "flagged": false + }, + { + "name": "pg-pool", + "version": "3.14.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pg-protocol", + "version": "1.15.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pg-types", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pgpass", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "picocolors", + "version": "1.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "picomatch", + "version": "2.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "picomatch", + "version": "4.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "pirates", + "version": "4.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "pkg-dir", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "plist", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pngjs", + "version": "3.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "postcss", + "version": "8.5.26", + "license": "MIT", + "flagged": false + }, + { + "name": "postgres-array", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "postgres-bytea", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "postgres-date", + "version": "1.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "postgres-interval", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pretty-format", + "version": "29.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pretty-format", + "version": "30.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "proc-log", + "version": "4.2.0", + "license": "ISC", + "flagged": false + }, + { + "name": "progress", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "promise", + "version": "8.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "prompts", + "version": "2.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "pure-rand", + "version": "7.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "query-string", + "version": "7.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "queue", + "version": "6.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "range-parser", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "react", + "version": "19.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "react-devtools-core", + "version": "6.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "react-dom", + "version": "19.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "react-fast-compare", + "version": "3.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "react-freeze", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "react-is", + "version": "16.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "react-is", + "version": "18.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "react-is", + "version": "19.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "react-native", + "version": "0.87.0", + "license": "MIT", + "flagged": false + }, + { + "name": "react-native-drawer-layout", + "version": "4.2.10", + "license": "MIT", + "flagged": false + }, + { + "name": "react-native-gesture-handler", + "version": "2.32.0", + "license": "MIT", + "flagged": false + }, + { + "name": "react-native-safe-area-context", + "version": "5.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "react-native-screens", + "version": "4.27.0", + "license": "MIT", + "flagged": false + }, + { + "name": "react-native-svg", + "version": "15.15.5", + "license": "MIT", + "flagged": false + }, + { + "name": "react-refresh", + "version": "0.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "react-remove-scroll", + "version": "2.7.2", + "license": "MIT", + "flagged": false + }, + { + "name": "react-remove-scroll-bar", + "version": "2.3.8", + "license": "MIT", + "flagged": false + }, + { + "name": "react-style-singleton", + "version": "2.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "regenerate", + "version": "1.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "regenerate-unicode-properties", + "version": "10.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "regenerator-runtime", + "version": "0.13.11", + "license": "MIT", + "flagged": false + }, + { + "name": "regexpu-core", + "version": "6.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "regjsgen", + "version": "0.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "regjsparser", + "version": "0.13.2", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "require-directory", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "resolve", + "version": "1.22.12", + "license": "MIT", + "flagged": false + }, + { + "name": "resolve-cwd", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "resolve-from", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "resolve-workspace-root", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "restore-cursor", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "safe-buffer", + "version": "5.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "sandbox-cli-detector", + "version": "0.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "sax", + "version": "1.6.1", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "scheduler", + "version": "0.27.0", + "license": "MIT", + "flagged": false + }, + { + "name": "semver", + "version": "6.3.1", + "license": "ISC", + "flagged": false + }, + { + "name": "semver", + "version": "7.8.5", + "license": "ISC", + "flagged": false + }, + { + "name": "send", + "version": "0.19.2", + "license": "MIT", + "flagged": false + }, + { + "name": "serialize-error", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "serve-static", + "version": "1.16.3", + "license": "MIT", + "flagged": false + }, + { + "name": "server-only", + "version": "0.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "setprototypeof", + "version": "1.2.0", + "license": "ISC", + "flagged": false + }, + { + "name": "sf-symbols-typescript", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "shallowequal", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "shebang-command", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "shebang-regex", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "shell-quote", + "version": "1.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "signal-exit", + "version": "3.0.7", + "license": "ISC", + "flagged": false + }, + { + "name": "signal-exit", + "version": "4.1.0", + "license": "ISC", + "flagged": false + }, + { + "name": "simple-plist", + "version": "1.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "simple-swizzle", + "version": "0.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "sisteransi", + "version": "1.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "slash", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "slugify", + "version": "1.6.9", + "license": "MIT", + "flagged": false + }, + { + "name": "source-map", + "version": "0.5.7", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "source-map", + "version": "0.6.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "source-map-js", + "version": "1.2.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "source-map-support", + "version": "0.5.13", + "license": "MIT", + "flagged": false + }, + { + "name": "source-map-support", + "version": "0.5.21", + "license": "MIT", + "flagged": false + }, + { + "name": "split-on-first", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "split2", + "version": "4.2.0", + "license": "ISC", + "flagged": false + }, + { + "name": "sprintf-js", + "version": "1.0.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "stack-utils", + "version": "2.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "stackframe", + "version": "1.3.4", + "license": "MIT", + "flagged": false + }, + { + "name": "stacktrace-parser", + "version": "0.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "standard-navigation", + "version": "0.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "statuses", + "version": "1.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "statuses", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "stream-buffers", + "version": "2.2.0", + "license": "Unlicense", + "flagged": false + }, + { + "name": "strict-uri-encode", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "string-length", + "version": "4.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "string-width", + "version": "4.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "string-width", + "version": "5.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-ansi", + "version": "5.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-ansi", + "version": "6.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-ansi", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-bom", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-final-newline", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-json-comments", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "structured-headers", + "version": "0.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-color", + "version": "5.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-color", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-color", + "version": "8.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-hyperlinks", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-preserve-symlinks-flag", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "synckit", + "version": "0.11.13", + "license": "MIT", + "flagged": false + }, + { + "name": "tagged-tag", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "terminal-link", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "terser", + "version": "5.50.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "test-exclude", + "version": "6.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "throat", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyglobby", + "version": "0.2.17", + "license": "MIT", + "flagged": false + }, + { + "name": "tmpl", + "version": "1.0.5", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "to-regex-range", + "version": "5.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "toidentifier", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "toqr", + "version": "0.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ts-jest", + "version": "29.4.12", + "license": "MIT", + "flagged": false + }, + { + "name": "tslib", + "version": "2.8.1", + "license": "0BSD", + "flagged": false + }, + { + "name": "tsx", + "version": "4.23.1", + "license": "MIT", + "flagged": false + }, + { + "name": "type-detect", + "version": "4.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "type-fest", + "version": "0.21.3", + "license": "CC0-1.0 AND MIT", + "flagged": false + }, + { + "name": "type-fest", + "version": "0.7.1", + "license": "MIT OR (CC0-1.0 AND MIT)", + "flagged": false + }, + { + "name": "type-fest", + "version": "4.41.0", + "license": "CC0-1.0 AND MIT", + "flagged": false + }, + { + "name": "type-fest", + "version": "5.8.0", + "license": "(MIT OR CC0-1.0)", + "flagged": false + }, + { + "name": "typescript", + "version": "5.9.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "typescript", + "version": "6.0.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "uglify-js", + "version": "3.19.3", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "undici-types", + "version": "7.18.2", + "license": "MIT", + "flagged": false + }, + { + "name": "undici-types", + "version": "8.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "unicode-canonical-property-names-ecmascript", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "unicode-match-property-ecmascript", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "unicode-match-property-value-ecmascript", + "version": "2.2.1", + "license": "LicenseRef-scancode-unicode AND MIT", + "flagged": false + }, + { + "name": "unicode-property-aliases-ecmascript", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "unpipe", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "unrs-resolver", + "version": "1.12.2", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "update-browserslist-db", + "version": "1.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "use-callback-ref", + "version": "1.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "use-latest-callback", + "version": "0.2.6", + "license": "MIT", + "flagged": false + }, + { + "name": "use-sidecar", + "version": "1.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "utils-merge", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "uuid", + "version": "11.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "v8-to-istanbul", + "version": "9.3.0", + "license": "ISC", + "flagged": false + }, + { + "name": "validate-npm-package-name", + "version": "5.0.1", + "license": "ISC", + "flagged": false + }, + { + "name": "vary", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "vaul", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "vlq", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "walker", + "version": "1.0.8", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "warn-once", + "version": "0.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "wcwidth", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "whatwg-fetch", + "version": "3.6.20", + "license": "MIT", + "flagged": false + }, + { + "name": "whatwg-url-minimum", + "version": "0.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "which", + "version": "2.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "wordwrap", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "wrap-ansi", + "version": "7.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "wrap-ansi", + "version": "8.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "wrappy", + "version": "1.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "write-file-atomic", + "version": "5.0.1", + "license": "ISC", + "flagged": false + }, + { + "name": "ws", + "version": "7.5.13", + "license": "MIT", + "flagged": false + }, + { + "name": "ws", + "version": "8.21.3", + "license": "MIT", + "flagged": false + }, + { + "name": "xcode", + "version": "3.0.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "xml2js", + "version": "0.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "xmlbuilder", + "version": "11.0.1", + "license": "LicenseRef-scancode-unicode AND MIT", + "flagged": false + }, + { + "name": "xmlbuilder", + "version": "15.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "xtend", + "version": "4.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "y18n", + "version": "5.0.8", + "license": "ISC", + "flagged": false + }, + { + "name": "yallist", + "version": "3.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "yaml", + "version": "2.9.0", + "license": "ISC", + "flagged": false + }, + { + "name": "yargs", + "version": "17.7.3", + "license": "MIT", + "flagged": false + }, + { + "name": "yargs-parser", + "version": "21.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "yocto-queue", + "version": "0.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "zod", + "version": "3.25.76", + "license": "MIT", + "flagged": false + }, + { + "name": "zxing-wasm", + "version": "3.1.3", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/health-evidence", + "error": null, + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/hyosung-itx-slogan-brief", + "error": null, + "component_count": 2, + "components": [ + { + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "49933ea5288caeca8642d1e84afbd3f7d6820020", + "license": "NOASSERTION", + "flagged": true + } + ] + }, + { + "repo": "ContextualWisdomLab/inkspan", + "error": null, + "component_count": 567, + "components": [ + { + "name": "@adobe/css-tools", + "version": "4.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@ampproject/remapping", + "version": "2.3.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@asamuzakjp/css-color", + "version": "3.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/code-frame", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/compat-data", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/core", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/generator", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-compilation-targets", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-globals", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-module-imports", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-module-transforms", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-plugin-utils", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-string-parser", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-validator-identifier", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-validator-option", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helpers", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/parser", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-react-jsx-self", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/plugin-transform-react-jsx-source", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/runtime", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/template", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/traverse", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/types", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@bcoe/v8-coverage", + "version": "1.0.2", + "license": "ISC AND MIT", + "flagged": false + }, + { + "name": "@csstools/color-helpers", + "version": "5.1.0", + "license": "MIT-0", + "flagged": false + }, + { + "name": "@csstools/css-calc", + "version": "2.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/css-color-parser", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/css-parser-algorithms", + "version": "3.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/css-tokenizer", + "version": "3.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/core", + "version": "1.11.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/core", + "version": "1.9.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/runtime", + "version": "1.11.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/runtime", + "version": "1.9.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/wasi-threads", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/wasi-threads", + "version": "1.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/aix-ppc64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-arm", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-arm64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-x64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/darwin-arm64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/darwin-x64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/freebsd-arm64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/freebsd-x64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-arm", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-arm64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-ia32", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-loong64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-mips64el", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-ppc64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-riscv64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-s390x", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-x64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/netbsd-arm64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/netbsd-x64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openbsd-arm64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openbsd-x64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openharmony-arm64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/sunos-x64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-arm64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-ia32", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-x64", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@isaacs/cliui", + "version": "8.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "@istanbuljs/schema", + "version": "0.1.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@joshwooding/vite-plugin-react-docgen-typescript", + "version": "0.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/gen-mapping", + "version": "0.3.13", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/remapping", + "version": "2.3.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/resolve-uri", + "version": "3.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/sourcemap-codec", + "version": "1.5.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/trace-mapping", + "version": "0.3.31", + "license": "MIT", + "flagged": false + }, + { + "name": "@mdx-js/react", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@microsoft/api-extractor", + "version": "7.58.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@microsoft/api-extractor-model", + "version": "7.33.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@microsoft/tsdoc", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@microsoft/tsdoc-config", + "version": "0.18.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@mixmark-io/domino", + "version": "2.2.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "@napi-rs/wasm-runtime", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-android-arm-eabi", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-android-arm64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-darwin-arm64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-darwin-x64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-freebsd-x64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-arm-gnueabihf", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-arm-musleabihf", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-arm64-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-arm64-musl", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-ppc64-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-riscv64-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-riscv64-musl", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-s390x-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-x64-gnu", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-linux-x64-musl", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-openharmony-arm64", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-wasm32-wasi", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-win32-arm64-msvc", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-win32-ia32-msvc", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-parser/binding-win32-x64-msvc", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-project/types", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-android-arm-eabi", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-android-arm64", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-darwin-arm64", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-darwin-x64", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-freebsd-x64", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-arm-gnueabihf", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-arm-musleabihf", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-arm64-gnu", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-arm64-musl", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-ppc64-gnu", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-riscv64-gnu", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-riscv64-musl", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-s390x-gnu", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-x64-gnu", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-linux-x64-musl", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-openharmony-arm64", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-wasm32-wasi", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-win32-arm64-msvc", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-resolver/binding-win32-x64-msvc", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@pkgjs/parseargs", + "version": "0.11.0", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "@playwright/test", + "version": "1.62.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@popperjs/core", + "version": "2.11.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@remirror/core-constants", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/pluginutils", + "version": "1.0.0-beta.27", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/pluginutils", + "version": "5.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-android-arm-eabi", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-android-arm64", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-darwin-arm64", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-darwin-x64", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-freebsd-arm64", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-freebsd-x64", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-linux-arm-gnueabihf", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-linux-arm-musleabihf", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-linux-arm64-gnu", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-linux-arm64-musl", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-linux-loong64-gnu", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-linux-loong64-musl", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-linux-ppc64-gnu", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-linux-ppc64-musl", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-linux-riscv64-gnu", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-linux-riscv64-musl", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-linux-s390x-gnu", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-linux-x64-gnu", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-linux-x64-musl", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-openbsd-x64", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-openharmony-arm64", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-win32-arm64-msvc", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-win32-ia32-msvc", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-win32-x64-gnu", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rollup/rollup-win32-x64-msvc", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@rushstack/node-core-library", + "version": "5.23.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rushstack/problem-matcher", + "version": "0.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rushstack/rig-package", + "version": "0.7.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@rushstack/terminal", + "version": "0.24.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@rushstack/ts-command-line", + "version": "5.3.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/addon-docs", + "version": "10.5.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/builder-vite", + "version": "10.5.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/csf-plugin", + "version": "10.5.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/global", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/icons", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/react", + "version": "10.5.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/react-dom-shim", + "version": "10.5.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@storybook/react-vite", + "version": "10.5.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/dom", + "version": "10.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/jest-dom", + "version": "6.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/react", + "version": "16.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/user-event", + "version": "14.6.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/core", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-blockquote", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-bold", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-bubble-menu", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-bullet-list", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-code", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-code-block", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-collaboration", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-collaboration-cursor", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-document", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-dropcursor", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-floating-menu", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-gapcursor", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-hard-break", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-heading", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-history", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-horizontal-rule", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-image", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-italic", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-link", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-list-item", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-ordered-list", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-paragraph", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-placeholder", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-strike", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-table", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-table-cell", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-table-header", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-table-row", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-text", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/extension-text-style", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/pm", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/react", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tiptap/starter-kit", + "version": "2.27.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@tybys/wasm-util", + "version": "0.10.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/argparse", + "version": "1.0.38", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/aria-query", + "version": "5.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__core", + "version": "7.20.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__generator", + "version": "7.27.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__template", + "version": "7.4.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/babel__traverse", + "version": "7.28.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/chai", + "version": "5.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/deep-eql", + "version": "4.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/doctrine", + "version": "0.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/estree", + "version": "1.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/linkify-it", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/markdown-it", + "version": "14.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/mdurl", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/mdx", + "version": "2.0.14", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/node", + "version": "22.20.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/prop-types", + "version": "15.7.15", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react", + "version": "18.3.31", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react-dom", + "version": "18.3.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/resolve", + "version": "1.20.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/turndown", + "version": "5.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/use-sync-external-store", + "version": "0.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitejs/plugin-react", + "version": "4.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/coverage-v8", + "version": "3.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/expect", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/expect", + "version": "3.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/mocker", + "version": "3.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/pretty-format", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/pretty-format", + "version": "3.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/runner", + "version": "3.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/snapshot", + "version": "3.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/spy", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/spy", + "version": "3.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/utils", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/utils", + "version": "3.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@volar/language-core", + "version": "2.4.28", + "license": "MIT", + "flagged": false + }, + { + "name": "@volar/source-map", + "version": "2.4.28", + "license": "MIT", + "flagged": false + }, + { + "name": "@volar/typescript", + "version": "2.4.28", + "license": "MIT", + "flagged": false + }, + { + "name": "@vue/compiler-core", + "version": "3.5.39", + "license": "MIT", + "flagged": false + }, + { + "name": "@vue/compiler-dom", + "version": "3.5.39", + "license": "MIT", + "flagged": false + }, + { + "name": "@vue/compiler-vue2", + "version": "2.7.16", + "license": "MIT", + "flagged": false + }, + { + "name": "@vue/language-core", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@vue/shared", + "version": "3.5.39", + "license": "MIT", + "flagged": false + }, + { + "name": "@webcontainer/env", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "acorn", + "version": "8.17.0", + "license": "MIT", + "flagged": false + }, + { + "name": "actions/attest", + "version": "59d89421af93a897026c735860bf21b6eb4f7b26", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/download-artifact", + "version": "37930b1c2abaa49bbe596cd826c3c89aef350131", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "820762786026740c76f36085b0efc47a31fe5020", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "a26af69be951a213d495a4c3e4e4022e16d87065", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "agent-base", + "version": "7.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "ajv", + "version": "8.18.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ajv-draft-04", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ajv-formats", + "version": "3.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "alien-signals", + "version": "0.4.14", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-regex", + "version": "5.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-regex", + "version": "6.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "4.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "5.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "6.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "argparse", + "version": "1.0.10", + "license": "MIT", + "flagged": false + }, + { + "name": "argparse", + "version": "2.0.1", + "license": "Python-2.0", + "flagged": false + }, + { + "name": "aria-query", + "version": "5.3.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "aria-query", + "version": "5.3.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "assertion-error", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ast-types", + "version": "0.16.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ast-v8-to-istanbul", + "version": "0.3.12", + "license": "MIT", + "flagged": false + }, + { + "name": "asynckit", + "version": "0.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "balanced-match", + "version": "4.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "baseline-browser-mapping", + "version": "2.10.43", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "brace-expansion", + "version": "5.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "browserslist", + "version": "4.28.5", + "license": "MIT", + "flagged": false + }, + { + "name": "bundle-name", + "version": "4.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "cac", + "version": "6.7.14", + "license": "MIT", + "flagged": false + }, + { + "name": "call-bind-apply-helpers", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "caniuse-lite", + "version": "1.0.30001803", + "license": "CC-BY-4.0", + "flagged": false + }, + { + "name": "chai", + "version": "5.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "check-error", + "version": "2.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "color-convert", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "color-name", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "combined-stream", + "version": "1.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "compare-versions", + "version": "6.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "confbox", + "version": "0.1.8", + "license": "BSD-3-Clause AND MIT", + "flagged": false + }, + { + "name": "confbox", + "version": "0.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "convert-source-map", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "coverage", + "version": "7.15.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "crelt", + "version": "1.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "cross-spawn", + "version": "7.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "css.escape", + "version": "1.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "cssstyle", + "version": "4.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "csstype", + "version": "3.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "data-urls", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "de-indent", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "debug", + "version": "4.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "decimal.js", + "version": "10.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "deep-eql", + "version": "5.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "default-browser", + "version": "5.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "default-browser-id", + "version": "5.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "define-lazy-prop", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "delayed-stream", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "dequal", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "diff", + "version": "8.0.4", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "doctrine", + "version": "3.0.0", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "dom-accessibility-api", + "version": "0.5.16", + "license": "MIT", + "flagged": false + }, + { + "name": "dom-accessibility-api", + "version": "0.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "dunder-proto", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "eastasianwidth", + "version": "0.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "electron-to-chromium", + "version": "1.5.389", + "license": "ISC", + "flagged": false + }, + { + "name": "emoji-regex", + "version": "8.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "emoji-regex", + "version": "9.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "empathic", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "entities", + "version": "4.5.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "entities", + "version": "6.0.1", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "entities", + "version": "7.0.1", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "es-define-property", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "es-errors", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-module-lexer", + "version": "1.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-object-atoms", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "es-set-tostringtag", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "esbuild", + "version": "0.25.12", + "license": "MIT", + "flagged": false + }, + { + "name": "escalade", + "version": "3.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "escape-string-regexp", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "esprima", + "version": "4.0.1", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "estree-walker", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "estree-walker", + "version": "3.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "esutils", + "version": "2.0.3", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "et-xmlfile", + "version": "2.0.0", + "license": "0BSD AND BSD-3-Clause AND MIT AND Python-2.0", + "flagged": false + }, + { + "name": "expect-type", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "exsolve", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-deep-equal", + "version": "3.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-uri", + "version": "3.1.5", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "fdir", + "version": "6.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "foreground-child", + "version": "3.3.1", + "license": "ISC", + "flagged": false + }, + { + "name": "form-data", + "version": "4.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "fs-extra", + "version": "11.3.6", + "license": "MIT", + "flagged": false + }, + { + "name": "fsevents", + "version": "2.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "fsevents", + "version": "2.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "function-bind", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "gensync", + "version": "1.0.0-beta.2", + "license": "MIT", + "flagged": false + }, + { + "name": "get-intrinsic", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "get-proto", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "glob", + "version": "10.5.0", + "license": "ISC", + "flagged": false + }, + { + "name": "glob", + "version": "13.0.6", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "gopd", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "graceful-fs", + "version": "4.2.11", + "license": "ISC", + "flagged": false + }, + { + "name": "has-flag", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "has-symbols", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "has-tostringtag", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "hasown", + "version": "2.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "he", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "html-encoding-sniffer", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "html-escaper", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "http-proxy-agent", + "version": "7.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "https-proxy-agent", + "version": "7.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "iconv-lite", + "version": "0.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "import-lazy", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "indent-string", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-core-module", + "version": "2.16.2", + "license": "MIT", + "flagged": false + }, + { + "name": "is-docker", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-fullwidth-code-point", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-inside-container", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-potential-custom-element-name", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-wsl", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "isexe", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "isomorphic.js", + "version": "0.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "istanbul-lib-coverage", + "version": "3.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-lib-report", + "version": "3.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-lib-source-maps", + "version": "5.0.6", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-reports", + "version": "3.2.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "jackspeak", + "version": "3.4.3", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "jju", + "version": "1.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "js-tokens", + "version": "10.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "js-tokens", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "js-tokens", + "version": "9.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jsdom", + "version": "25.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jsesc", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "json-schema-traverse", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "json5", + "version": "2.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonc-parser", + "version": "3.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonfile", + "version": "6.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "kolorist", + "version": "1.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "lib0", + "version": "0.2.117", + "license": "MIT", + "flagged": false + }, + { + "name": "linkify-it", + "version": "5.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "linkifyjs", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "local-pkg", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "loose-envify", + "version": "1.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "loupe", + "version": "3.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "lru-cache", + "version": "10.4.3", + "license": "ISC", + "flagged": false + }, + { + "name": "lru-cache", + "version": "11.5.2", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "lru-cache", + "version": "5.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "lxml", + "version": "6.1.0", + "license": "BSD-3-Clause AND GPL-1.0-or-later", + "flagged": true + }, + { + "name": "lz-string", + "version": "1.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "magic-string", + "version": "0.30.21", + "license": "MIT", + "flagged": false + }, + { + "name": "magicast", + "version": "0.3.5", + "license": "MIT", + "flagged": false + }, + { + "name": "make-dir", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "markdown-it", + "version": "14.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "marked", + "version": "15.0.12", + "license": "BSD-3-Clause AND MIT", + "flagged": false + }, + { + "name": "math-intrinsics", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mdurl", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mime-db", + "version": "1.52.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mime-types", + "version": "2.1.35", + "license": "MIT", + "flagged": false + }, + { + "name": "min-indent", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "minimatch", + "version": "10.2.3", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "minimatch", + "version": "10.2.5", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "minimist", + "version": "1.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "minipass", + "version": "7.1.3", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "mlly", + "version": "1.8.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ms", + "version": "2.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "muggle-string", + "version": "0.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "nanoid", + "version": "3.3.18", + "license": "MIT", + "flagged": false + }, + { + "name": "node-releases", + "version": "2.0.51", + "license": "MIT", + "flagged": false + }, + { + "name": "nwsapi", + "version": "2.2.24", + "license": "MIT", + "flagged": false + }, + { + "name": "open", + "version": "10.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "openpyxl", + "version": "3.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "openpyxl", + "version": ">= 3.1.5,< 4", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "orderedmap", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "oxc-parser", + "version": "0.127.0", + "license": "MIT", + "flagged": false + }, + { + "name": "oxc-resolver", + "version": "11.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "package-json-from-dist", + "version": "1.0.1", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "packaging", + "version": "25.0", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "parse5", + "version": "7.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "path-browserify", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "path-key", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "path-parse", + "version": "1.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "path-scurry", + "version": "1.11.1", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "path-scurry", + "version": "2.0.2", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "pathe", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "pathval", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "picocolors", + "version": "1.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "picomatch", + "version": "4.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "pillow", + "version": "12.3.0", + "license": "MIT-CMU", + "flagged": false + }, + { + "name": "pkg-types", + "version": "1.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pkg-types", + "version": "2.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "playwright", + "version": "1.62.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "playwright-core", + "version": "1.62.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pnpm/action-setup", + "version": "0977fd99725f1db4007ccb2928dbb4e90d06cc86", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "postcss", + "version": "8.5.25", + "license": "MIT", + "flagged": false + }, + { + "name": "pretty-format", + "version": "27.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-changeset", + "version": "2.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-collab", + "version": "1.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-commands", + "version": "1.7.1", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-dropcursor", + "version": "1.8.3", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-gapcursor", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-history", + "version": "1.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-inputrules", + "version": "1.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-keymap", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-markdown", + "version": "1.13.5", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-menu", + "version": "1.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-model", + "version": "1.25.11", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-schema-basic", + "version": "1.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-schema-list", + "version": "1.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-state", + "version": "1.4.4", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-tables", + "version": "1.8.5", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-trailing-node", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-transform", + "version": "1.12.0", + "license": "MIT", + "flagged": false + }, + { + "name": "prosemirror-view", + "version": "1.42.1", + "license": "MIT", + "flagged": false + }, + { + "name": "punycode", + "version": "2.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "punycode.js", + "version": "2.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pypa/gh-action-pypi-publish", + "version": "dc37677b2e1c63e2034f94d8a5b11f265b73ba33", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest", + "version": "9.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "python-docx", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "python-docx", + "version": ">= 1.2.0,< 2", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "python-pptx", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "python-pptx", + "version": ">= 1.0.2,< 2", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "quansync", + "version": "0.2.11", + "license": "MIT", + "flagged": false + }, + { + "name": "react", + "version": "18.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "react-docgen", + "version": "8.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "react-docgen-typescript", + "version": "2.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "react-dom", + "version": "18.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "react-is", + "version": "17.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "react-refresh", + "version": "0.17.0", + "license": "MIT", + "flagged": false + }, + { + "name": "recast", + "version": "0.23.21", + "license": "MIT", + "flagged": false + }, + { + "name": "redent", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "require-from-string", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "resolve", + "version": "1.22.12", + "license": "MIT", + "flagged": false + }, + { + "name": "rollup", + "version": "4.62.2", + "license": "MIT", + "flagged": false + }, + { + "name": "rope-sequence", + "version": "1.3.4", + "license": "MIT", + "flagged": false + }, + { + "name": "rrweb-cssom", + "version": "0.7.1", + "license": "MIT", + "flagged": false + }, + { + "name": "rrweb-cssom", + "version": "0.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "run-applescript", + "version": "7.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "safer-buffer", + "version": "2.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "saxes", + "version": "6.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "scheduler", + "version": "0.23.2", + "license": "MIT", + "flagged": false + }, + { + "name": "semver", + "version": "6.3.1", + "license": "ISC", + "flagged": false + }, + { + "name": "semver", + "version": "7.7.4", + "license": "ISC", + "flagged": false + }, + { + "name": "semver", + "version": "7.8.5", + "license": "ISC", + "flagged": false + }, + { + "name": "setuptools", + "version": "83.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "shebang-command", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "shebang-regex", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "siginfo", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "signal-exit", + "version": "4.1.0", + "license": "ISC", + "flagged": false + }, + { + "name": "sigstore/cosign-installer", + "version": "6f9f17788090df1f26f669e9d70d6ae9567deba6", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "source-map", + "version": "0.6.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "source-map-js", + "version": "1.2.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "sprintf-js", + "version": "1.0.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "stackback", + "version": "0.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "std-env", + "version": "3.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "storybook", + "version": "10.5.8", + "license": "MIT", + "flagged": false + }, + { + "name": "string-argv", + "version": "0.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "string-width", + "version": "4.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "string-width", + "version": "5.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-ansi", + "version": "6.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-ansi", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-bom", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-indent", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-indent", + "version": "4.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-literal", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-color", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-color", + "version": "8.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-preserve-symlinks-flag", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "symbol-tree", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "test-exclude", + "version": "7.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "tiny-invariant", + "version": "1.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tinybench", + "version": "2.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyexec", + "version": "0.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyglobby", + "version": "0.2.17", + "license": "MIT", + "flagged": false + }, + { + "name": "tinypool", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyrainbow", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyspy", + "version": "4.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "tippy.js", + "version": "6.3.7", + "license": "MIT", + "flagged": false + }, + { + "name": "tldts", + "version": "6.1.86", + "license": "MIT", + "flagged": false + }, + { + "name": "tldts-core", + "version": "6.1.86", + "license": "MIT", + "flagged": false + }, + { + "name": "tough-cookie", + "version": "5.1.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "tr46", + "version": "5.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ts-dedent", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tsconfig-paths", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tslib", + "version": "2.8.1", + "license": "0BSD", + "flagged": false + }, + { + "name": "turndown", + "version": "7.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "turndown-plugin-gfm", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "typescript", + "version": "5.9.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.15.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "uc.micro", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ufo", + "version": "1.6.4", + "license": "MIT", + "flagged": false + }, + { + "name": "undici-types", + "version": "6.21.0", + "license": "MIT", + "flagged": false + }, + { + "name": "universalify", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "unplugin", + "version": "2.3.11", + "license": "MIT", + "flagged": false + }, + { + "name": "update-browserslist-db", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "use-sync-external-store", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "vite", + "version": "6.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "vite-node", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "vite-plugin-dts", + "version": "4.5.4", + "license": "MIT", + "flagged": false + }, + { + "name": "vitest", + "version": "3.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "vscode-uri", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "w3c-keyname", + "version": "2.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "w3c-xmlserializer", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "webidl-conversions", + "version": "7.0.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "webpack-virtual-modules", + "version": "0.6.2", + "license": "MIT", + "flagged": false + }, + { + "name": "whatwg-encoding", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "whatwg-mimetype", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "whatwg-url", + "version": "14.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "wheel", + "version": "0.47.0", + "license": "MIT", + "flagged": false + }, + { + "name": "which", + "version": "2.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "why-is-node-running", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "wrap-ansi", + "version": "7.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "wrap-ansi", + "version": "8.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ws", + "version": "8.21.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ws", + "version": "8.21.3", + "license": "MIT", + "flagged": false + }, + { + "name": "wsl-utils", + "version": "0.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "xlsxwriter", + "version": "3.2.9", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "xml-name-validator", + "version": "5.0.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "xmlchars", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "y-prosemirror", + "version": "1.3.7", + "license": "MIT", + "flagged": false + }, + { + "name": "y-protocols", + "version": "1.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "yallist", + "version": "3.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "yjs", + "version": "13.6.31", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/IRT-bibliography-set", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/j-planner", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/k-csap-skills", + "error": null, + "component_count": 10, + "components": [ + { + "name": "actions/checkout", + "version": "4.*.*", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "astral-sh/setup-uv", + "version": "6.*.*", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "packaging", + "version": "26.3", + "license": "Apache-2.0 OR BSD-2-Clause", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.21.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pyyaml", + "version": "6.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "ruff", + "version": "0.16.10", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/kaefa", + "error": null, + "component_count": 5, + "components": [ + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "r-lib/actions/check-r-package", + "version": "6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "r-lib/actions/setup-pandoc", + "version": "d3c5be51b12e724e68f33216ca3c148b66d5f0b6", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "r-lib/actions/setup-r", + "version": "6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "r-lib/actions/setup-r-dependencies", + "version": "6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590", + "license": "NOASSERTION", + "flagged": true + } + ] + }, + { + "repo": "ContextualWisdomLab/keyverse", + "error": null, + "component_count": 45, + "components": [ + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/download-artifact", + "version": "3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "annotated-doc", + "version": "0.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "annotated-types", + "version": "0.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "anyio", + "version": "4.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "astral-sh/setup-uv", + "version": "20cfd1bf945f4377ade1205e4dbc17946fc9a30d", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "atheris", + "version": "3.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "attrs", + "version": "26.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "certifi", + "version": "2026.7.22", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "cffi", + "version": "2.1.1", + "license": "MIT-0", + "flagged": false + }, + { + "name": "click", + "version": "8.4.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "coverage", + "version": "7.15.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "cryptography", + "version": "50.0.0", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "cwl-idp-account-unification", + "version": "0.1.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "fastapi", + "version": "0.141.1", + "license": "MIT", + "flagged": false + }, + { + "name": "h11", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "httpcore", + "version": "1.0.9", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpcore2", + "version": "2.12.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx", + "version": "0.28.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx2", + "version": "2.12.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx2-jsfetch", + "version": "1.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "interrogate", + "version": "1.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "packaging", + "version": "26.2", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "py", + "version": "1.11.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pycparser", + "version": "3.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pydantic", + "version": "2.13.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-core", + "version": "2.46.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pyyaml", + "version": "6.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "ruff", + "version": "0.16.3", + "license": "MIT", + "flagged": false + }, + { + "name": "setuptools", + "version": "84.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "starlette", + "version": "1.3.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "step-security/harden-runner", + "version": "05e31511f85b41b11d1cf0ef85d0992719546e2c", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "tabulate", + "version": "0.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "truststore", + "version": "0.10.4", + "license": "MIT", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "typing-inspection", + "version": "0.4.4", + "license": "MIT", + "flagged": false + }, + { + "name": "uvicorn", + "version": "0.52.3", + "license": "BSD-3-Clause", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/korean-writing-skills", + "error": null, + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/late-life-anxiety-reanalysis", + "error": null, + "component_count": 2, + "components": [ + { + "name": "fast-mlsirm", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "numpy", + "version": "2.5.3", + "license": "BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/law-ai-agent", + "error": null, + "component_count": 22, + "components": [ + { + "name": "annotated-types", + "version": "0.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "ContextualWisdomLab/.github/.github/workflows/law-ai-agent-ci.yml", + "version": "8dff0538c55b99df3f6f19b25a06633fc72b38ef", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "coverage", + "version": "7.16.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "librt", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mypy", + "version": "1.20.2", + "license": "MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause AND MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "mypy-extensions", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "packaging", + "version": "26.3", + "license": "Apache-2.0 OR BSD-2-Clause", + "flagged": false + }, + { + "name": "pathspec", + "version": "1.1.1", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "psycopg", + "version": "3.3.6", + "license": "LGPL-3.0-only", + "flagged": true + }, + { + "name": "psycopg-binary", + "version": "3.3.6", + "license": "LGPL-3.0-only", + "flagged": true + }, + { + "name": "pydantic", + "version": "2.13.5", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-core", + "version": "2.46.5", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.21.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest-cov", + "version": "7.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ruff", + "version": "0.16.10", + "license": "MIT", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "typing-inspection", + "version": "0.4.4", + "license": "MIT", + "flagged": false + }, + { + "name": "tzdata", + "version": "2026.5", + "license": "Apache-2.0", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/learning-content-studio", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/learning-interoperability-contracts", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/learning-management-platform", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/learning-record-store", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/life-os", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/LineageWeave", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/linux-cluster-ops", + "error": null, + "component_count": 5, + "components": [ + { + "name": "actions/attest-build-provenance", + "version": "a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "ece7cb06caefa5fff74198d8649806c4678c61a1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "atheris", + "version": "3.0.0", + "license": "Apache-2.0", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/litellm-patched-proxy", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/litellm-poinnetworks-ops", + "error": null, + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/macos_utility_packs", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/mcp-shared-gateway", + "error": null, + "component_count": 3, + "components": [ + { + "name": "actions/checkout", + "version": "11bd71901bbe5b1630ceea73d27597364c9af683", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "49933ea5288caeca8642d1e84afbd3f7d6820020", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "a26af69be951a213d495a4c3e4e4022e16d87065", + "license": "NOASSERTION", + "flagged": true + } + ] + }, + { + "repo": "ContextualWisdomLab/metering-billing-platform", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/mhtml-etl-gateway", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/mightyETL", + "error": null, + "component_count": 54, + "components": [ + { + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/dependency-review-action", + "version": "a1d282b36b6f3519aa1f3fc636f609c47dddb294", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-java", + "version": "1bcf9fb12cf4aa7d266a90ae39939e61372fe520", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "com.fasterxml.jackson.core:jackson-databind", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "com.fasterxml.jackson:jackson-bom", + "version": "2.21.5", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "com.h2database:h2", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/analyze", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/autobuild", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/init", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/upload-sarif", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "io.debezium:debezium-api", + "version": "3.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "io.debezium:debezium-connector-postgres", + "version": "3.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "io.debezium:debezium-embedded", + "version": "3.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "io.jsonwebtoken:jjwt", + "version": "0.13.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "io.micrometer:micrometer-tracing-bridge-brave", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "io.zipkin.reporter2:zipkin-reporter-brave", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.apache.maven.plugins:maven-compiler-plugin", + "version": "3.13.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "org.apache.maven.plugins:maven-dependency-plugin", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.apache.maven.plugins:maven-dependency-plugin", + "version": "3.9.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "org.apache.maven.plugins:maven-surefire-plugin", + "version": "3.5.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "org.flywaydb:flyway-core", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.flywaydb:flyway-database-postgresql", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.jacoco:jacoco-maven-plugin", + "version": "0.8.15", + "license": "EPL-2.0 OR (Apache-2.0 AND EPL-2.0)", + "flagged": true + }, + { + "name": "org.junit.jupiter:junit-jupiter-api", + "version": "5.12.2", + "license": "EPL-2.0", + "flagged": true + }, + { + "name": "org.junit.jupiter:junit-jupiter-engine", + "version": "5.12.2", + "license": "EPL-2.0", + "flagged": true + }, + { + "name": "org.junit.platform:junit-platform-commons", + "version": "1.12.2", + "license": "EPL-2.0", + "flagged": true + }, + { + "name": "org.junit.platform:junit-platform-engine", + "version": "1.12.2", + "license": "EPL-2.0", + "flagged": true + }, + { + "name": "org.junit.platform:junit-platform-launcher", + "version": "1.12.2", + "license": "EPL-2.0", + "flagged": true + }, + { + "name": "org.mockito:mockito-core", + "version": "5.21.0", + "license": "MIT", + "flagged": false + }, + { + "name": "org.mockito:mockito-junit-jupiter", + "version": "5.21.0", + "license": "MIT", + "flagged": false + }, + { + "name": "org.postgresql:postgresql", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.postgresql:postgresql", + "version": "42.7.12", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "org.springframework.boot:spring-boot-configuration-processor", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.boot:spring-boot-dependencies", + "version": "3.5.16", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "org.springframework.boot:spring-boot-maven-plugin", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.boot:spring-boot-maven-plugin", + "version": "3.5.16", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "org.springframework.boot:spring-boot-starter-actuator", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.boot:spring-boot-starter-aop", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.boot:spring-boot-starter-data-jpa", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.boot:spring-boot-starter-security", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.boot:spring-boot-starter-test", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.boot:spring-boot-starter-web", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.cloud:spring-cloud-config-server", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.cloud:spring-cloud-dependencies", + "version": "2025.0.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "org.springframework.cloud:spring-cloud-starter-gateway", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.cloud:spring-cloud-starter-netflix-eureka-client", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.cloud:spring-cloud-starter-netflix-eureka-server", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.kafka:spring-kafka", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.kafka:spring-kafka", + "version": "3.3.16", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "org.springframework.retry:spring-retry", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "org.springframework.retry:spring-retry", + "version": "2.0.13", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "org.springframework.security:spring-security-test", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ossf/scorecard-action", + "version": "4eaacf0543bb3f2c246792bd56e8cdeffafb205a", + "license": "NOASSERTION", + "flagged": true + } + ] + }, + { + "repo": "ContextualWisdomLab/naruon", + "error": null, + "component_count": 794, + "components": [ + { + "name": "@alloc/quick-lru", + "version": "5.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@asamuzakjp/css-color", + "version": "6.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@asamuzakjp/dom-selector", + "version": "8.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/code-frame", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/compat-data", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/core", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/generator", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-compilation-targets", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-globals", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-module-imports", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-module-transforms", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-string-parser", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-validator-identifier", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-validator-option", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helpers", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/parser", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/runtime", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/template", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/traverse", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/types", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@base-ui/react", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@base-ui/utils", + "version": "0.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@bcoe/v8-coverage", + "version": "1.0.2", + "license": "ISC AND MIT", + "flagged": false + }, + { + "name": "@bramus/specificity", + "version": "2.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/color-helpers", + "version": "6.1.0", + "license": "MIT-0", + "flagged": false + }, + { + "name": "@csstools/css-calc", + "version": "3.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/css-color-parser", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/css-parser-algorithms", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/css-syntax-patches-for-csstree", + "version": "1.1.7", + "license": "MIT-0", + "flagged": false + }, + { + "name": "@csstools/css-tokenizer", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@egjs/hammerjs", + "version": "2.0.17", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/core", + "version": "1.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/core", + "version": "1.11.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/runtime", + "version": "1.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/runtime", + "version": "1.11.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/runtime", + "version": "1.11.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/wasi-threads", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/wasi-threads", + "version": "1.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@eslint-community/eslint-utils", + "version": "4.10.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@eslint-community/regexpp", + "version": "4.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@eslint/config-array", + "version": "0.21.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@eslint/config-helpers", + "version": "0.4.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@eslint/core", + "version": "0.17.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@eslint/eslintrc", + "version": "3.3.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@eslint/js", + "version": "9.39.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@eslint/object-schema", + "version": "2.1.7", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@eslint/plugin-kit", + "version": "0.4.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@exodus/bytes", + "version": "1.15.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@floating-ui/core", + "version": "1.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@floating-ui/dom", + "version": "1.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@floating-ui/react-dom", + "version": "2.1.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@floating-ui/utils", + "version": "0.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@humanfs/core", + "version": "0.19.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@humanfs/node", + "version": "0.16.8", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@humanfs/types", + "version": "0.15.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@humanwhocodes/module-importer", + "version": "1.0.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@humanwhocodes/retry", + "version": "0.4.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@img/colour", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@img/sharp-darwin-arm64", + "version": "0.35.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@img/sharp-darwin-x64", + "version": "0.35.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@img/sharp-freebsd-wasm32", + "version": "0.35.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@img/sharp-libvips-darwin-arm64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "@img/sharp-libvips-darwin-x64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "@img/sharp-libvips-linux-arm", + "version": "1.3.0", + "license": "LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "@img/sharp-libvips-linux-arm64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "@img/sharp-libvips-linux-ppc64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "@img/sharp-libvips-linux-riscv64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "@img/sharp-libvips-linux-s390x", + "version": "1.3.0", + "license": "LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "@img/sharp-libvips-linux-x64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "@img/sharp-libvips-linuxmusl-arm64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "@img/sharp-libvips-linuxmusl-x64", + "version": "1.3.0", + "license": "LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "@img/sharp-linux-arm", + "version": "0.35.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@img/sharp-linux-arm64", + "version": "0.35.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@img/sharp-linux-ppc64", + "version": "0.35.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@img/sharp-linux-riscv64", + "version": "0.35.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@img/sharp-linux-s390x", + "version": "0.35.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@img/sharp-linux-x64", + "version": "0.35.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@img/sharp-linuxmusl-arm64", + "version": "0.35.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@img/sharp-linuxmusl-x64", + "version": "0.35.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@img/sharp-wasm32", + "version": "0.35.0", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "flagged": true + }, + { + "name": "@img/sharp-webcontainers-wasm32", + "version": "0.35.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@img/sharp-win32-arm64", + "version": "0.35.0", + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "@img/sharp-win32-ia32", + "version": "0.35.0", + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "@img/sharp-win32-x64", + "version": "0.35.0", + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "@jridgewell/gen-mapping", + "version": "0.3.13", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/remapping", + "version": "2.3.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/resolve-uri", + "version": "3.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/sourcemap-codec", + "version": "1.5.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/trace-mapping", + "version": "0.3.31", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/wasm-runtime", + "version": "1.1.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@next/env", + "version": "16.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@next/eslint-plugin-next", + "version": "16.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@next/swc-darwin-arm64", + "version": "16.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@next/swc-darwin-x64", + "version": "16.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@next/swc-linux-arm64-gnu", + "version": "16.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@next/swc-linux-arm64-musl", + "version": "16.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@next/swc-linux-x64-gnu", + "version": "16.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@next/swc-linux-x64-musl", + "version": "16.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@next/swc-win32-arm64-msvc", + "version": "16.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@next/swc-win32-x64-msvc", + "version": "16.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@nodelib/fs.scandir", + "version": "2.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@nodelib/fs.stat", + "version": "2.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@nodelib/fs.walk", + "version": "1.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@nolyfill/is-core-module", + "version": "1.0.39", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-project/types", + "version": "0.139.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@playwright/test", + "version": "1.62.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@radix-ui/primitive", + "version": "1.1.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-collection", + "version": "1.1.15", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-compose-refs", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-context", + "version": "1.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-direction", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-id", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-presence", + "version": "1.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-primitive", + "version": "2.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-roving-focus", + "version": "1.1.19", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-slot", + "version": "1.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-tabs", + "version": "1.1.21", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-use-callback-ref", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-use-controllable-state", + "version": "1.2.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-use-effect-event", + "version": "0.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-use-is-hydrated", + "version": "0.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@radix-ui/react-use-layout-effect", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-android-arm64", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-darwin-arm64", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-darwin-x64", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-freebsd-x64", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm-gnueabihf", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm64-gnu", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm64-musl", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-ppc64-gnu", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-s390x-gnu", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-x64-gnu", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-x64-musl", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-openharmony-arm64", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-wasm32-wasi", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-win32-arm64-msvc", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-win32-x64-msvc", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/pluginutils", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rtsao/scc", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@standard-schema/spec", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@swc/helpers", + "version": "0.5.15", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@tailwindcss/node", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-android-arm64", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-darwin-arm64", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-darwin-x64", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-freebsd-x64", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-linux-arm-gnueabihf", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-linux-arm64-gnu", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-linux-arm64-musl", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-linux-x64-gnu", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-linux-x64-musl", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-wasm32-wasi", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-win32-arm64-msvc", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/oxide-win32-x64-msvc", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tailwindcss/postcss", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@tybys/wasm-util", + "version": "0.10.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/chai", + "version": "5.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/deep-eql", + "version": "4.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/estree", + "version": "1.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/hammerjs", + "version": "2.0.46", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/json-schema", + "version": "7.0.15", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/json5", + "version": "0.0.29", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/node", + "version": "26.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react", + "version": "19.2.17", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react-dom", + "version": "19.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/eslint-plugin", + "version": "8.65.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/parser", + "version": "8.65.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/project-service", + "version": "8.65.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/scope-manager", + "version": "8.65.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/tsconfig-utils", + "version": "8.65.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/type-utils", + "version": "8.65.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/types", + "version": "8.65.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/typescript-estree", + "version": "8.65.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/utils", + "version": "8.65.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@typescript-eslint/visitor-keys", + "version": "8.65.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-android-arm-eabi", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-android-arm64", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-darwin-arm64", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-darwin-x64", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-freebsd-x64", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-arm-gnueabihf", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-arm-musleabihf", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-arm64-gnu", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-arm64-musl", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-loong64-gnu", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-loong64-musl", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-ppc64-gnu", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-riscv64-gnu", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-riscv64-musl", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-s390x-gnu", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-x64-gnu", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-linux-x64-musl", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-openharmony-arm64", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-wasm32-wasi", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-win32-arm64-msvc", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-win32-ia32-msvc", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@unrs/resolver-binding-win32-x64-msvc", + "version": "1.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/coverage-v8", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/expect", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/mocker", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/pretty-format", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/runner", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/snapshot", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/spy", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/utils", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "acorn", + "version": "8.17.0", + "license": "MIT", + "flagged": false + }, + { + "name": "acorn-jsx", + "version": "5.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "actions/cache", + "version": "55cc8345863c7cc4c66a329aec7e433d2d1c52a9", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/dependency-review-action", + "version": "a1d282b36b6f3519aa1f3fc636f609c47dddb294", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "820762786026740c76f36085b0efc47a31fe5020", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "aiohappyeyeballs", + "version": "2.7.1", + "license": "0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0", + "flagged": true + }, + { + "name": "aiohttp", + "version": "3.14.1", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "aioimaplib", + "version": "2.0.1", + "license": "GPL-3.0 AND GPL-3.0-only AND GPL-3.0-or-later", + "flagged": true + }, + { + "name": "aiosignal", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "aiosmtplib", + "version": "5.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ajv", + "version": "6.15.0", + "license": "MIT", + "flagged": false + }, + { + "name": "alembic", + "version": "1.18.5", + "license": "MIT", + "flagged": false + }, + { + "name": "annotated-doc", + "version": "0.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "annotated-types", + "version": "0.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "4.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "anyio", + "version": "4.14.1", + "license": "MIT", + "flagged": false + }, + { + "name": "anyio", + "version": "4.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "argparse", + "version": "2.0.1", + "license": "Python-2.0", + "flagged": false + }, + { + "name": "aria-query", + "version": "5.3.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "array-buffer-byte-length", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "array-includes", + "version": "3.1.9", + "license": "MIT", + "flagged": false + }, + { + "name": "array.prototype.findlast", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "array.prototype.findlastindex", + "version": "1.2.6", + "license": "MIT", + "flagged": false + }, + { + "name": "array.prototype.flat", + "version": "1.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "array.prototype.flatmap", + "version": "1.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "array.prototype.tosorted", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "arraybuffer.prototype.slice", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "asgiref", + "version": "3.11.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "assertion-error", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ast-types-flow", + "version": "0.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "ast-v8-to-istanbul", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "async-function", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "asyncpg", + "version": "0.31.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "attrs", + "version": "26.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "available-typed-arrays", + "version": "1.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "axe-core", + "version": "4.12.1", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "axobject-query", + "version": "4.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "backoff", + "version": "2.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "balanced-match", + "version": "4.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "bandit", + "version": "1.9.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "baseline-browser-mapping", + "version": "2.11.5", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "bidi-js", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "brace-expansion", + "version": "5.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "braces", + "version": "3.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "browserslist", + "version": "4.28.7", + "license": "MIT", + "flagged": false + }, + { + "name": "caido-sdk-client", + "version": "0.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "caido-server-auth", + "version": "0.1.2", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "call-bind", + "version": "1.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "call-bind-apply-helpers", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "call-bound", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "callsites", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "caniuse-lite", + "version": "1.0.30001806", + "license": "CC-BY-4.0", + "flagged": false + }, + { + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "cffi", + "version": "2.0.0", + "license": "MIT-0", + "flagged": false + }, + { + "name": "cffi", + "version": "2.1.0", + "license": "MIT-0", + "flagged": false + }, + { + "name": "chai", + "version": "6.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "chalk", + "version": "4.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "charset-normalizer", + "version": "3.4.7", + "license": "MIT", + "flagged": false + }, + { + "name": "charset-normalizer", + "version": "3.4.9", + "license": "MIT", + "flagged": false + }, + { + "name": "class-variance-authority", + "version": "0.7.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "click", + "version": "8.4.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "client-only", + "version": "0.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "clsx", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "color-convert", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "color-name", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "component-emitter", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "convert-source-map", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "coverage", + "version": "7.15.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "cross-spawn", + "version": "7.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "cryptography", + "version": "49.0.0", + "license": "BSD-3-Clause OR Apache-2.0", + "flagged": false + }, + { + "name": "cryptography", + "version": "50.0.0", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "css-tree", + "version": "3.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "csstype", + "version": "3.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "cvss", + "version": "3.6", + "license": "LGPL-3.0 AND LGPL-3.0-only AND LGPL-3.0-or-later", + "flagged": true + }, + { + "name": "damerau-levenshtein", + "version": "1.0.8", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "data-urls", + "version": "7.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "data-view-buffer", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "data-view-byte-length", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "data-view-byte-offset", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "debug", + "version": "3.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "debug", + "version": "4.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "decimal.js", + "version": "10.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "deep-is", + "version": "0.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "define-data-property", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "define-properties", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "defusedxml", + "version": "0.7.1", + "license": "PSF-2.0", + "flagged": false + }, + { + "name": "detect-libc", + "version": "2.1.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "distro", + "version": "1.9.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "dnspython", + "version": "2.8.0", + "license": "ISC AND MPL-2.0", + "flagged": true + }, + { + "name": "docker", + "version": "7.2.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "docker/build-push-action", + "version": "53b7df96c91f9c12dcc8a07bcb9ccacbed38856a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "docker/login-action", + "version": "dbcb813823bdd20940b903addbd779551569679f", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "docker/metadata-action", + "version": "dc802804100637a589fabce1cb79ff13a1411302", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "docker/setup-buildx-action", + "version": "bb05f3f5519dd87d3ba754cc423b652a5edd6d2c", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "docker/setup-qemu-action", + "version": "96fe6ef7f33517b61c61be40b68a1882f3264fb8", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "docstring-parser", + "version": "0.18.0", + "license": "MIT", + "flagged": false + }, + { + "name": "doctrine", + "version": "2.1.0", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "dunder-proto", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "electron-to-chromium", + "version": "1.5.396", + "license": "ISC", + "flagged": false + }, + { + "name": "email-validator", + "version": "2.3.0", + "license": "CC0-1.0 AND Unlicense", + "flagged": false + }, + { + "name": "emoji-regex", + "version": "9.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "enhanced-resolve", + "version": "5.24.3", + "license": "MIT", + "flagged": false + }, + { + "name": "entities", + "version": "8.0.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "es-abstract", + "version": "1.24.2", + "license": "MIT", + "flagged": false + }, + { + "name": "es-abstract-get", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-define-property", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "es-errors", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-iterator-helpers", + "version": "1.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-module-lexer", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-object-atoms", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "es-set-tostringtag", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-shim-unscopables", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "es-to-primitive", + "version": "1.3.4", + "license": "MIT", + "flagged": false + }, + { + "name": "escalade", + "version": "3.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "escape-string-regexp", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "eslint", + "version": "9.39.5", + "license": "MIT", + "flagged": false + }, + { + "name": "eslint-config-next", + "version": "16.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "eslint-import-resolver-node", + "version": "0.3.10", + "license": "MIT", + "flagged": false + }, + { + "name": "eslint-import-resolver-typescript", + "version": "3.10.1", + "license": "ISC", + "flagged": false + }, + { + "name": "eslint-module-utils", + "version": "2.14.0", + "license": "MIT", + "flagged": false + }, + { + "name": "eslint-plugin-import", + "version": "2.32.0", + "license": "MIT", + "flagged": false + }, + { + "name": "eslint-plugin-jsx-a11y", + "version": "6.10.2", + "license": "MIT", + "flagged": false + }, + { + "name": "eslint-plugin-react", + "version": "7.37.5", + "license": "MIT", + "flagged": false + }, + { + "name": "eslint-plugin-react-hooks", + "version": "7.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "eslint-scope", + "version": "8.4.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "eslint-visitor-keys", + "version": "3.4.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "eslint-visitor-keys", + "version": "4.2.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "eslint-visitor-keys", + "version": "5.0.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "espree", + "version": "10.4.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "esquery", + "version": "1.7.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "esrecurse", + "version": "4.3.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "estraverse", + "version": "5.3.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "estree-walker", + "version": "3.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "esutils", + "version": "2.0.3", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "expect-type", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "fast-check", + "version": "4.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-deep-equal", + "version": "3.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-glob", + "version": "3.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-json-stable-stringify", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-levenshtein", + "version": "2.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "fastapi", + "version": "0.138.2", + "license": "MIT", + "flagged": false + }, + { + "name": "fastapi", + "version": "0.139.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fastq", + "version": "1.20.1", + "license": "ISC", + "flagged": false + }, + { + "name": "fastuuid", + "version": "0.14.0", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "fdir", + "version": "6.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "file-entry-cache", + "version": "8.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "filelock", + "version": "3.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "fill-range", + "version": "7.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "find-up", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "flat-cache", + "version": "4.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "flatted", + "version": "3.4.3", + "license": "ISC", + "flagged": false + }, + { + "name": "for-each", + "version": "0.3.5", + "license": "MIT", + "flagged": false + }, + { + "name": "frozenlist", + "version": "1.8.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "fsevents", + "version": "2.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "fsevents", + "version": "2.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "fsspec", + "version": "2026.6.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "function-bind", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "function.prototype.name", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "functions-have-names", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "genai-prices", + "version": "0.0.71", + "license": "MIT", + "flagged": false + }, + { + "name": "generator-function", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "gensync", + "version": "1.0.0-beta.2", + "license": "MIT", + "flagged": false + }, + { + "name": "get-intrinsic", + "version": "1.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "get-proto", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "get-symbol-description", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "get-tsconfig", + "version": "4.14.0", + "license": "MIT", + "flagged": false + }, + { + "name": "github/codeql-action/upload-sarif", + "version": "f205ea1c3313d32999d8d6a48b4f6530d4437b38", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "glob-parent", + "version": "5.1.2", + "license": "ISC", + "flagged": false + }, + { + "name": "glob-parent", + "version": "6.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "globals", + "version": "14.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "globals", + "version": "16.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "globalthis", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "google-api-core", + "version": "2.31.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-api-python-client", + "version": "2.198.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-auth", + "version": "2.55.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-auth", + "version": "2.55.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-auth-httplib2", + "version": "0.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-auth-oauthlib", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-cloud-aiplatform", + "version": "1.160.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-cloud-bigquery", + "version": "3.42.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-cloud-core", + "version": "2.6.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-cloud-resource-manager", + "version": "1.18.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-cloud-storage", + "version": "3.12.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-crc32c", + "version": "1.8.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-genai", + "version": "2.11.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "google-resumable-media", + "version": "2.10.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "googleapis-common-protos", + "version": "1.75.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "gopd", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "gql", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "graceful-fs", + "version": "4.2.11", + "license": "ISC", + "flagged": false + }, + { + "name": "graphql-core", + "version": "3.2.11", + "license": "MIT", + "flagged": false + }, + { + "name": "greenlet", + "version": "3.5.3", + "license": "MIT AND PSF-2.0", + "flagged": false + }, + { + "name": "griffelib", + "version": "2.1.0", + "license": "ISC", + "flagged": false + }, + { + "name": "grpc-google-iam-v1", + "version": "0.14.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "grpcio", + "version": "1.81.1", + "license": "Apache-2.0 AND BSD-3-Clause AND MPL-2.0", + "flagged": true + }, + { + "name": "grpcio", + "version": "1.82.1", + "license": "Apache-2.0 AND BSD-3-Clause AND MPL-2.0", + "flagged": true + }, + { + "name": "grpcio-status", + "version": "1.81.1", + "license": "Apache-2.0 AND BSD-3-Clause AND MPL-2.0", + "flagged": true + }, + { + "name": "h11", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "has-bigints", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "has-flag", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "has-property-descriptors", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "has-proto", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "has-symbols", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "has-tostringtag", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "hasown", + "version": "2.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "hermes-estree", + "version": "0.25.1", + "license": "MIT", + "flagged": false + }, + { + "name": "hermes-parser", + "version": "0.25.1", + "license": "MIT", + "flagged": false + }, + { + "name": "hf-xet", + "version": "1.5.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "html-encoding-sniffer", + "version": "6.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "html-escaper", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "httpcore", + "version": "1.0.9", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpcore2", + "version": "2.5.0", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httplib2", + "version": "0.32.0", + "license": "MIT", + "flagged": false + }, + { + "name": "httpx", + "version": "0.28.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx-sse", + "version": "0.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "httpx2", + "version": "2.5.0", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "huggingface-hub", + "version": "1.23.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "icalendar", + "version": "7.2.0", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "ignore", + "version": "5.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ignore", + "version": "7.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "import-fresh", + "version": "3.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "importlib-metadata", + "version": "8.9.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "imurmurhash", + "version": "0.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "internal-slot", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-array-buffer", + "version": "3.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "is-async-function", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-bigint", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-boolean-object", + "version": "1.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "is-bun-module", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-callable", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "is-core-module", + "version": "2.16.2", + "license": "MIT", + "flagged": false + }, + { + "name": "is-data-view", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "is-date-object", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-document.all", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-extglob", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-finalizationregistry", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-generator-function", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "is-glob", + "version": "4.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "is-map", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "is-negative-zero", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "is-number", + "version": "7.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-number-object", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-potential-custom-element-name", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-regex", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-set", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "is-shared-array-buffer", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "is-string", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-symbol", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-typed-array", + "version": "1.1.15", + "license": "MIT", + "flagged": false + }, + { + "name": "is-weakmap", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "is-weakref", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "is-weakset", + "version": "2.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "isarray", + "version": "2.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "isexe", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "istanbul-lib-coverage", + "version": "3.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-lib-report", + "version": "3.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-reports", + "version": "3.2.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "iterator.prototype", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "jinja2", + "version": "3.1.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "jiter", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jiti", + "version": "2.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "js-tokens", + "version": "10.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "js-tokens", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "js-yaml", + "version": "4.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jschema-to-python", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "jsdom", + "version": "30.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jsesc", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "json-buffer", + "version": "3.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "json-schema-traverse", + "version": "0.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "json-stable-stringify-without-jsonify", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "json5", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "json5", + "version": "2.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonpatch", + "version": "1.33", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "jsonpickle", + "version": "4.1.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "jsonpointer", + "version": "3.1.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "jsonschema", + "version": "4.26.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "jsx-ast-utils", + "version": "3.3.5", + "license": "MIT", + "flagged": false + }, + { + "name": "keycharm", + "version": "0.4.0", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "keyv", + "version": "4.5.4", + "license": "MIT", + "flagged": false + }, + { + "name": "langchain-core", + "version": "1.4.8", + "license": "MIT", + "flagged": false + }, + { + "name": "langchain-core", + "version": "1.4.9", + "license": "MIT", + "flagged": false + }, + { + "name": "langchain-protocol", + "version": "0.0.18", + "license": "MIT", + "flagged": false + }, + { + "name": "langchain-text-splitters", + "version": "1.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "langsmith", + "version": "0.10.1", + "license": "MIT", + "flagged": false + }, + { + "name": "langsmith", + "version": "0.10.2", + "license": "MIT", + "flagged": false + }, + { + "name": "langsmith", + "version": "0.9.4", + "license": "MIT", + "flagged": false + }, + { + "name": "language-subtag-registry", + "version": "0.3.23", + "license": "CC0-1.0", + "flagged": false + }, + { + "name": "language-tags", + "version": "1.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "levn", + "version": "0.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "lightningcss", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-android-arm64", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-arm64", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-x64", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-freebsd-x64", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-gnu", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-musl", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-gnu", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-musl", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-arm64-msvc", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-x64-msvc", + "version": "1.32.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "linkify-it-py", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "litellm", + "version": "1.89.2", + "license": "MIT", + "flagged": false + }, + { + "name": "locate-path", + "version": "6.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "lodash.merge", + "version": "4.6.2", + "license": "MIT", + "flagged": false + }, + { + "name": "logfire-api", + "version": "4.37.0", + "license": "MIT", + "flagged": false + }, + { + "name": "loose-envify", + "version": "1.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "lru-cache", + "version": "11.5.2", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "lru-cache", + "version": "5.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "lucide-react", + "version": "1.27.0", + "license": "ISC", + "flagged": false + }, + { + "name": "magic-string", + "version": "0.30.21", + "license": "MIT", + "flagged": false + }, + { + "name": "magicast", + "version": "0.5.3", + "license": "MIT", + "flagged": false + }, + { + "name": "make-dir", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mako", + "version": "1.3.12", + "license": "MIT", + "flagged": false + }, + { + "name": "markdown-it-py", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "markupsafe", + "version": "3.0.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "math-intrinsics", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mcp", + "version": "1.28.1", + "license": "MIT AND Python-2.0", + "flagged": false + }, + { + "name": "mdit-py-plugins", + "version": "0.6.1", + "license": "MIT", + "flagged": false + }, + { + "name": "mdn-data", + "version": "2.27.1", + "license": "CC0-1.0", + "flagged": false + }, + { + "name": "mdurl", + "version": "0.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "merge2", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "micromatch", + "version": "4.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "minimatch", + "version": "10.2.6", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "minimatch", + "version": "3.1.5", + "license": "ISC", + "flagged": false + }, + { + "name": "minimist", + "version": "1.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "ms", + "version": "2.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "multidict", + "version": "6.7.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "nanoid", + "version": "3.3.18", + "license": "MIT", + "flagged": false + }, + { + "name": "napi-postinstall", + "version": "0.3.4", + "license": "MIT", + "flagged": false + }, + { + "name": "naruon-backend", + "version": "0.14.4", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "natural-compare", + "version": "1.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "next", + "version": "16.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "node-exports-info", + "version": "1.6.2", + "license": "MIT", + "flagged": false + }, + { + "name": "node-releases", + "version": "2.0.51", + "license": "MIT", + "flagged": false + }, + { + "name": "numpy", + "version": "2.5.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "oauthlib", + "version": "3.3.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "object-assign", + "version": "4.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "object-inspect", + "version": "1.13.4", + "license": "MIT", + "flagged": false + }, + { + "name": "object-keys", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "object.assign", + "version": "4.1.7", + "license": "MIT", + "flagged": false + }, + { + "name": "object.entries", + "version": "1.1.9", + "license": "MIT", + "flagged": false + }, + { + "name": "object.fromentries", + "version": "2.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "object.groupby", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "object.values", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "obug", + "version": "2.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "openai", + "version": "2.44.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "openai", + "version": "2.45.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "openai-agents", + "version": "0.14.6", + "license": "MIT", + "flagged": false + }, + { + "name": "opentelemetry-api", + "version": "1.43.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-exporter-otlp", + "version": "1.43.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-exporter-otlp-proto-common", + "version": "1.43.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-exporter-otlp-proto-grpc", + "version": "1.43.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-exporter-otlp-proto-http", + "version": "1.43.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-instrumentation", + "version": "0.64b0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-instrumentation-asgi", + "version": "0.64b0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-instrumentation-fastapi", + "version": "0.64b0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-proto", + "version": "1.43.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-sdk", + "version": "1.43.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-semantic-conventions", + "version": "0.64b0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-util-http", + "version": "0.64b0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "optionator", + "version": "0.9.4", + "license": "MIT", + "flagged": false + }, + { + "name": "orjson", + "version": "3.11.9", + "license": "Apache-2.0 AND MIT AND MPL-2.0", + "flagged": true + }, + { + "name": "own-keys", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "p-limit", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "p-locate", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "packaging", + "version": "26.2", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "parent-module", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "parse5", + "version": "8.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "path-exists", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "path-key", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "path-parse", + "version": "1.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "pathe", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "pbr", + "version": "7.0.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pgvector", + "version": "0.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "pgvector", + "version": "0.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "picocolors", + "version": "1.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "picomatch", + "version": "2.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "picomatch", + "version": "4.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "platformdirs", + "version": "4.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "playwright", + "version": "1.62.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "playwright-core", + "version": "1.62.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "possible-typed-array-names", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "postcss", + "version": "8.5.24", + "license": "MIT", + "flagged": false + }, + { + "name": "prelude-ls", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "prometheus-client", + "version": "0.25.0", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "prometheus-fastapi-instrumentator", + "version": "8.0.2", + "license": "BSD-2-Clause AND BSD-3-Clause AND ISC AND MIT", + "flagged": false + }, + { + "name": "prop-types", + "version": "15.8.1", + "license": "MIT", + "flagged": false + }, + { + "name": "propcache", + "version": "0.5.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "proto-plus", + "version": "1.28.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "proto-plus", + "version": "1.28.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "protobuf", + "version": "6.33.6", + "license": "BSD-3-Clause AND LicenseRef-scancode-protobuf", + "flagged": false + }, + { + "name": "protobuf", + "version": "7.35.1", + "license": "BSD-3-Clause AND LicenseRef-scancode-protobuf", + "flagged": false + }, + { + "name": "punycode", + "version": "2.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pure-rand", + "version": "8.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "pyasn1", + "version": "0.6.3", + "license": "BSD-2-Clause AND BSD-3-Clause AND MIT", + "flagged": false + }, + { + "name": "pyasn1", + "version": "0.6.4", + "license": "BSD-2-Clause AND BSD-3-Clause AND MIT", + "flagged": false + }, + { + "name": "pyasn1-modules", + "version": "0.4.2", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "pycparser", + "version": "3.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pydantic", + "version": "2.13.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-ai-slim", + "version": "2.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-core", + "version": "2.46.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-graph", + "version": "2.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-settings", + "version": "2.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pyjwt", + "version": "2.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pyparsing", + "version": "3.3.2", + "license": "MIT AND Python-2.0", + "flagged": false + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest-asyncio", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "python-dotenv", + "version": "1.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "python-multipart", + "version": "0.0.32", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pyyaml", + "version": "6.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "queue-microtask", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "rankweave", + "version": "0.1.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "react", + "version": "19.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "react-dom", + "version": "19.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "react-is", + "version": "16.13.1", + "license": "MIT", + "flagged": false + }, + { + "name": "react-resizable-panels", + "version": "4.12.2", + "license": "MIT", + "flagged": false + }, + { + "name": "referencing", + "version": "0.37.0", + "license": "MIT", + "flagged": false + }, + { + "name": "reflect.getprototypeof", + "version": "1.0.10", + "license": "MIT", + "flagged": false + }, + { + "name": "regex", + "version": "2026.6.28", + "license": "CNRI-Python AND Apache-2.0", + "flagged": false + }, + { + "name": "regex", + "version": "2026.7.10", + "license": "CNRI-Python AND Apache-2.0", + "flagged": false + }, + { + "name": "regexp.prototype.flags", + "version": "1.5.4", + "license": "MIT", + "flagged": false + }, + { + "name": "requests", + "version": "2.34.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "requests-oauthlib", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "requests-toolbelt", + "version": "1.0.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "require-from-string", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "reselect", + "version": "5.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "resolve", + "version": "2.0.0-next.7", + "license": "MIT", + "flagged": false + }, + { + "name": "resolve-from", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "resolve-pkg-maps", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "reusify", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "rich", + "version": "15.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "rolldown", + "version": "1.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "rpds-py", + "version": "2026.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "ruff", + "version": "0.15.20", + "license": "MIT", + "flagged": false + }, + { + "name": "ruff", + "version": "0.15.21", + "license": "MIT", + "flagged": false + }, + { + "name": "run-parallel", + "version": "1.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "safe-array-concat", + "version": "1.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "safe-push-apply", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "safe-regex-test", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "sarif-om", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "saxes", + "version": "6.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "scheduler", + "version": "0.27.0", + "license": "MIT", + "flagged": false + }, + { + "name": "semver", + "version": "6.3.1", + "license": "ISC", + "flagged": false + }, + { + "name": "semver", + "version": "7.8.5", + "license": "ISC", + "flagged": false + }, + { + "name": "set-function-length", + "version": "1.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "set-function-name", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "set-proto", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "setuptools", + "version": "82.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "setuptools", + "version": "83.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "sharp", + "version": "0.35.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "shebang-command", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "shebang-regex", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "side-channel", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "side-channel-list", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "side-channel-map", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "side-channel-weakmap", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "siginfo", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "six", + "version": "1.17.0", + "license": "MIT", + "flagged": false + }, + { + "name": "sniffio", + "version": "1.3.1", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "source-map-js", + "version": "1.2.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "sqlalchemy", + "version": "2.0.51", + "license": "MIT", + "flagged": false + }, + { + "name": "sse-starlette", + "version": "3.4.5", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "stable-hash", + "version": "0.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "stackback", + "version": "0.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "starlette", + "version": "1.3.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "std-env", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "step-security/harden-runner", + "version": "bf7454d06d71f1098171f2acdf0cd4708d7b5920", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "stevedore", + "version": "5.9.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "stop-iteration-iterator", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "string.prototype.includes", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "string.prototype.matchall", + "version": "4.0.12", + "license": "MIT", + "flagged": false + }, + { + "name": "string.prototype.repeat", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "string.prototype.trim", + "version": "1.2.11", + "license": "MIT", + "flagged": false + }, + { + "name": "string.prototype.trimend", + "version": "1.0.10", + "license": "MIT", + "flagged": false + }, + { + "name": "string.prototype.trimstart", + "version": "1.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-bom", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-json-comments", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "strix-agent", + "version": "1.0.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "styled-jsx", + "version": "5.1.6", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-color", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-preserve-symlinks-flag", + "version": "1.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "symbol-tree", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "tailwind-merge", + "version": "3.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tailwindcss", + "version": "4.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tapable", + "version": "2.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tenacity", + "version": "9.1.4", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "textual", + "version": "8.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "tiktoken", + "version": "0.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinybench", + "version": "2.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyexec", + "version": "1.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyglobby", + "version": "0.2.17", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyrainbow", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tldts", + "version": "7.4.9", + "license": "MIT", + "flagged": false + }, + { + "name": "tldts-core", + "version": "7.4.9", + "license": "MIT", + "flagged": false + }, + { + "name": "to-regex-range", + "version": "5.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tokenizers", + "version": "0.23.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "tough-cookie", + "version": "6.0.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "tqdm", + "version": "4.68.3", + "license": "MIT AND MPL-2.0", + "flagged": true + }, + { + "name": "tqdm", + "version": "4.68.4", + "license": "MIT AND MPL-2.0", + "flagged": true + }, + { + "name": "tr46", + "version": "6.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "truststore", + "version": "0.10.4", + "license": "MIT", + "flagged": false + }, + { + "name": "ts-api-utils", + "version": "2.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tsconfig-paths", + "version": "3.15.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tslib", + "version": "2.8.1", + "license": "0BSD", + "flagged": false + }, + { + "name": "tw-animate-css", + "version": "1.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "type-check", + "version": "0.4.0", + "license": "MIT", + "flagged": false + }, + { + "name": "typed-array-buffer", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "typed-array-byte-length", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "typed-array-byte-offset", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "typed-array-length", + "version": "1.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "types-requests", + "version": "2.33.0.20260712", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "typescript", + "version": "6.0.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "typescript-eslint", + "version": "8.65.0", + "license": "MIT", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.15.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "typing-inspection", + "version": "0.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "tzdata", + "version": "2026.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "tzdata", + "version": "2026.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "uc-micro-py", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "unbox-primitive", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "undici", + "version": "8.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "undici-types", + "version": "8.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "unrs-resolver", + "version": "1.12.2", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "update-browserslist-db", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "uri-js", + "version": "4.4.1", + "license": "BSD-2-Clause AND BSD-2-Clause-Views", + "flagged": false + }, + { + "name": "uritemplate", + "version": "4.2.0", + "license": "Apache-2.0 OR BSD-3-Clause OR (Apache-2.0 AND BSD-3-Clause)", + "flagged": false + }, + { + "name": "urllib3", + "version": "2.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "use-sync-external-store", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "uuid", + "version": "14.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "uuid-utils", + "version": "0.16.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "uuid-utils", + "version": "0.17.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "uvicorn", + "version": "0.49.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "uvicorn", + "version": "0.51.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "vis-data", + "version": "8.0.4", + "license": "(Apache-2.0 OR MIT)", + "flagged": false + }, + { + "name": "vis-network", + "version": "10.1.0", + "license": "MIT OR (Apache-2.0 AND MIT)", + "flagged": false + }, + { + "name": "vis-util", + "version": "6.0.0", + "license": "(Apache-2.0 OR MIT)", + "flagged": false + }, + { + "name": "vite", + "version": "8.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "vitest", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "w3c-xmlserializer", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "webidl-conversions", + "version": "8.0.1", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "websockets", + "version": "15.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "websockets", + "version": "16.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "websockets", + "version": "16.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "whatwg-mimetype", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "whatwg-url", + "version": "16.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "whatwg-url", + "version": "17.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "wheel", + "version": "0.47.0", + "license": "MIT", + "flagged": false + }, + { + "name": "which", + "version": "2.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "which-boxed-primitive", + "version": "1.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "which-builtin-type", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "which-collection", + "version": "1.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "which-typed-array", + "version": "1.1.22", + "license": "MIT", + "flagged": false + }, + { + "name": "why-is-node-running", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "word-wrap", + "version": "1.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "wrapt", + "version": "2.2.2", + "license": "BSD-2-Clause AND BSD-3-Clause AND Python-2.0 AND Ruby", + "flagged": false + }, + { + "name": "xml-name-validator", + "version": "5.0.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "xmlchars", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "xxhash", + "version": "3.8.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "xxhash", + "version": "3.8.1", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "yallist", + "version": "3.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "yarl", + "version": "1.24.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "yocto-queue", + "version": "0.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "zipp", + "version": "4.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "zod", + "version": "4.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "zod-validation-error", + "version": "4.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "zstandard", + "version": "0.25.0", + "license": "BSD-3-Clause", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/newsdom-api", + "error": null, + "component_count": 87, + "components": [ + { + "name": "actions/attest-build-provenance", + "version": "0f67c3f4856b2e3261c31976d6725780e5e4c373", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/dependency-review-action", + "version": "a1d282b36b6f3519aa1f3fc636f609c47dddb294", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/deploy-pages", + "version": "cd2ce8fcbc39b97be8ca5fce6e763baed58fa128", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "altgraph", + "version": "0.17.5", + "license": "MIT AND MIT-0", + "flagged": false + }, + { + "name": "annotated-doc", + "version": "0.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "annotated-types", + "version": "0.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "anyio", + "version": "4.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "astral-sh/setup-uv", + "version": "c771a70e6277c0a99b617c7a806ffedaca235ff9", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "atheris", + "version": "3.0.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "atheris", + "version": "3.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "babel", + "version": "2.18.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "backports-asyncio-runner", + "version": "1.2.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "backrefs", + "version": "5.9", + "license": "MIT", + "flagged": false + }, + { + "name": "certifi", + "version": "2026.2.25", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "charset-normalizer", + "version": "3.4.7", + "license": "MIT", + "flagged": false + }, + { + "name": "click", + "version": "8.4.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "coverage", + "version": "7.13.5", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "docker/build-push-action", + "version": "53b7df96c91f9c12dcc8a07bcb9ccacbed38856a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "docker/login-action", + "version": "dbcb813823bdd20940b903addbd779551569679f", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "docker/metadata-action", + "version": "dc802804100637a589fabce1cb79ff13a1411302", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "docker/setup-buildx-action", + "version": "bb05f3f5519dd87d3ba754cc423b652a5edd6d2c", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "docker/setup-qemu-action", + "version": "96fe6ef7f33517b61c61be40b68a1882f3264fb8", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "exceptiongroup", + "version": "1.3.1", + "license": "MIT AND Python-2.0", + "flagged": false + }, + { + "name": "fastapi", + "version": "0.135.3", + "license": "MIT", + "flagged": false + }, + { + "name": "ghp-import", + "version": "2.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "github/codeql-action/analyze", + "version": "f205ea1c3313d32999d8d6a48b4f6530d4437b38", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/autobuild", + "version": "f205ea1c3313d32999d8d6a48b4f6530d4437b38", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/init", + "version": "f205ea1c3313d32999d8d6a48b4f6530d4437b38", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/upload-sarif", + "version": "f205ea1c3313d32999d8d6a48b4f6530d4437b38", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "google/clusterfuzzlite/actions/build_fuzzers", + "version": "52ecc61cb587ee99c26825a112a21abf19c7448c", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "google/clusterfuzzlite/actions/run_fuzzers", + "version": "52ecc61cb587ee99c26825a112a21abf19c7448c", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "h11", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "httpcore", + "version": "1.0.9", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpcore2", + "version": "2.4.0", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx", + "version": "0.28.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx2", + "version": "2.4.0", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jinja2", + "version": "3.1.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "macholib", + "version": "1.16.4", + "license": "MIT", + "flagged": false + }, + { + "name": "markdown", + "version": "3.10.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "markupsafe", + "version": "3.0.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "mergedeep", + "version": "1.3.4", + "license": "MIT", + "flagged": false + }, + { + "name": "mkdocs", + "version": "1.6.1", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "mkdocs-get-deps", + "version": "0.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "mkdocs-material", + "version": "9.7.7", + "license": "MIT", + "flagged": false + }, + { + "name": "mkdocs-material-extensions", + "version": "1.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "newsdom-api", + "version": "0.2.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ossf/scorecard-action", + "version": "2d1146689b8cda280b9bc96326124645441f03bc", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "packaging", + "version": "26.0", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "paginate", + "version": "0.5.7", + "license": "MIT", + "flagged": false + }, + { + "name": "pathspec", + "version": "1.0.4", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "pefile", + "version": "2023.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "pillow", + "version": "12.3.0", + "license": "MIT-CMU", + "flagged": false + }, + { + "name": "platformdirs", + "version": "4.9.6", + "license": "MIT", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic", + "version": "2.12.5", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-core", + "version": "2.41.5", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pyinstaller", + "version": "6.21.0", + "license": "GPL-2.0-only AND GPL-2.0-or-later", + "flagged": true + }, + { + "name": "pyinstaller-hooks-contrib", + "version": "2026.6", + "license": "Apache-2.0 AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later", + "flagged": true + }, + { + "name": "pymdown-extensions", + "version": "11.0.1", + "license": "BSD-3-Clause AND MIT", + "flagged": false + }, + { + "name": "pypdf", + "version": "6.15.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pytest", + "version": "9.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest-asyncio", + "version": "1.3.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pytest-cov", + "version": "7.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "python-multipart", + "version": "0.0.31", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pywin32-ctypes", + "version": "0.2.3", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pyyaml", + "version": "6.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "pyyaml-env-tag", + "version": "1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "reportlab", + "version": "4.4.10", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "requests", + "version": "2.33.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "setuptools", + "version": "83.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "six", + "version": "1.17.0", + "license": "MIT", + "flagged": false + }, + { + "name": "starlette", + "version": "1.3.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "tomli", + "version": "2.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "truststore", + "version": "0.10.4", + "license": "MIT", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.15.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "typing-inspection", + "version": "0.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "urllib3", + "version": "2.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "uvicorn", + "version": "0.44.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "watchdog", + "version": "6.0.0", + "license": "Apache-2.0 AND Python-2.0", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/noema", + "error": null, + "component_count": 202, + "components": [ + { + "name": "@babel/helper-string-parser", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-validator-identifier", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/parser", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/types", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@bcoe/v8-coverage", + "version": "1.0.2", + "license": "ISC AND MIT", + "flagged": false + }, + { + "name": "@cloudflare/workerd-darwin-64", + "version": "1.20260625.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@cloudflare/workerd-darwin-arm64", + "version": "1.20260625.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@cloudflare/workerd-linux-64", + "version": "1.20260625.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@cloudflare/workerd-linux-arm64", + "version": "1.20260625.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@cloudflare/workerd-windows-64", + "version": "1.20260625.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@cloudflare/workers-types", + "version": "4.20260630.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "@colbymchenry/codegraph", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@colbymchenry/codegraph-darwin-arm64", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@colbymchenry/codegraph-darwin-x64", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@colbymchenry/codegraph-linux-arm64", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@colbymchenry/codegraph-linux-x64", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@colbymchenry/codegraph-win32-arm64", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@colbymchenry/codegraph-win32-x64", + "version": "1.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/core", + "version": "2.0.0-alpha.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/runtime", + "version": "2.0.0-alpha.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/wasi-threads", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/aix-ppc64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-arm", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/android-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/darwin-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/darwin-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/freebsd-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/freebsd-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-arm", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-ia32", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-loong64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-mips64el", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-ppc64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-riscv64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-s390x", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/linux-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/netbsd-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/netbsd-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openbsd-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openbsd-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/openharmony-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/sunos-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-arm64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-ia32", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@esbuild/win32-x64", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/resolve-uri", + "version": "3.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/sourcemap-codec", + "version": "1.5.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/trace-mapping", + "version": "0.3.31", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/wasm-runtime", + "version": "1.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-project/types", + "version": "0.148.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-android-arm-eabi", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-android-arm64", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-darwin-arm64", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-darwin-x64", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-freebsd-x64", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm-gnueabihf", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm64-gnu", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm64-musl", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-ppc64-gnu", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-s390x-gnu", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-x64-gnu", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-x64-musl", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-openharmony-arm64", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-wasm32-wasi", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-win32-arm64-msvc", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-win32-x64-msvc", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/pluginutils", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@standard-schema/spec", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@tybys/wasm-util", + "version": "0.10.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/chai", + "version": "5.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/deep-eql", + "version": "4.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/estree", + "version": "1.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/coverage-v8", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/expect", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/mocker", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/pretty-format", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/runner", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/snapshot", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/spy", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/utils", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "actions/attest", + "version": "59d89421af93a897026c735860bf21b6eb4f7b26", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "11bd71901bbe5b1630ceea73d27597364c9af683", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "de0fac2e4500dabe0009e67214ff5f5447ce83dd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/create-github-app-token", + "version": "bcd2ba49218906704ab6c1aa796996da409d3eb1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/download-artifact", + "version": "d3f86a106a0bac45b974a628896c90dbdf5c8093", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "a26af69be951a213d495a4c3e4e4022e16d87065", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "annotated-types", + "version": "0.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "anyio", + "version": "4.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "aquasecurity/setup-trivy", + "version": "81e514348e19b6112ce2a7e3ecbafe19c1e1f567", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "assertion-error", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ast-v8-to-istanbul", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "attrs", + "version": "26.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "chai", + "version": "6.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "charset-normalizer", + "version": "3.4.9", + "license": "MIT", + "flagged": false + }, + { + "name": "click", + "version": "8.4.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "convert-source-map", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "coverage", + "version": "7.15.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "detect-libc", + "version": "2.1.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "distro", + "version": "1.9.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "docker/build-push-action", + "version": "d08e5c354a6adb9ed34480a06d141179aa583294", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "docker/setup-buildx-action", + "version": "37fe631027851001ddb9b187196cc803df7f5f0e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "es-module-lexer", + "version": "2.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "esbuild", + "version": "0.28.1", + "license": "MIT", + "flagged": false + }, + { + "name": "estree-walker", + "version": "3.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "expect-type", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "fdir", + "version": "6.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fsevents", + "version": "2.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "genai-prices", + "version": "0.0.71", + "license": "MIT", + "flagged": false + }, + { + "name": "griffelib", + "version": "2.1.0", + "license": "ISC", + "flagged": false + }, + { + "name": "h11", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "has-flag", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "html-escaper", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "httpcore", + "version": "1.0.9", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpcore2", + "version": "2.12.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx", + "version": "0.28.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx2", + "version": "2.12.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "interrogate", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "interrogate", + "version": "1.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "istanbul-lib-coverage", + "version": "3.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-lib-report", + "version": "3.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-reports", + "version": "3.2.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "jiter", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "js-tokens", + "version": "10.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "lightningcss", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "logfire-api", + "version": "4.37.0", + "license": "MIT", + "flagged": false + }, + { + "name": "magic-string", + "version": "0.30.21", + "license": "MIT", + "flagged": false + }, + { + "name": "magicast", + "version": "0.5.3", + "license": "MIT", + "flagged": false + }, + { + "name": "make-dir", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "nanoid", + "version": "3.3.18", + "license": "MIT", + "flagged": false + }, + { + "name": "obug", + "version": "2.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "openai", + "version": "2.45.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-api", + "version": "1.43.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "packaging", + "version": "26.2", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "pathe", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "picocolors", + "version": "1.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "picomatch", + "version": "4.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "picomatch", + "version": "4.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "postcss", + "version": "8.5.28", + "license": "MIT", + "flagged": false + }, + { + "name": "py", + "version": "1.11.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pydantic", + "version": "2.13.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-ai-slim", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pydantic-ai-slim", + "version": "2.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-core", + "version": "2.46.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-graph", + "version": "2.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pytest", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest-cov", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest-cov", + "version": "7.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "regex", + "version": "2026.7.10", + "license": "CNRI-Python AND Apache-2.0", + "flagged": false + }, + { + "name": "requests", + "version": "2.34.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "rolldown", + "version": "1.2.7", + "license": "MIT", + "flagged": false + }, + { + "name": "semver", + "version": "7.8.5", + "license": "ISC", + "flagged": false + }, + { + "name": "setuptools", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "siginfo", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "sigstore/cosign-installer", + "version": "6f9f17788090df1f26f669e9d70d6ae9567deba6", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "sniffio", + "version": "1.3.1", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "source-map-js", + "version": "1.2.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "stackback", + "version": "0.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "std-env", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-color", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tabulate", + "version": "0.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tiktoken", + "version": "0.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinybench", + "version": "2.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyexec", + "version": "1.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyglobby", + "version": "0.2.17", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyrainbow", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tomli", + "version": "2.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tqdm", + "version": "4.68.4", + "license": "MIT AND MPL-2.0", + "flagged": true + }, + { + "name": "truststore", + "version": "0.10.4", + "license": "MIT", + "flagged": false + }, + { + "name": "tslib", + "version": "2.8.1", + "license": "0BSD", + "flagged": false + }, + { + "name": "typescript", + "version": "5.9.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "typing-inspection", + "version": "0.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "urllib3", + "version": "2.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "vite", + "version": "8.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "vitest", + "version": "4.1.11", + "license": "MIT", + "flagged": false + }, + { + "name": "why-is-node-running", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "workerd", + "version": "1.20260625.1", + "license": "Apache-2.0", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/Orgmetra", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/OriginWeave", + "error": null, + "component_count": 79, + "components": [ + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "asn1-rs", + "version": "0.7.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "asn1-rs-derive", + "version": "0.6.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "asn1-rs-impl", + "version": "0.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "autocfg", + "version": "1.5.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "base64", + "version": "0.22.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bit-vec", + "version": "0.9.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "block-buffer", + "version": "0.10.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cc", + "version": "1.4.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cfg-if", + "version": "1.0.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cpufeatures", + "version": "0.2.17", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "crypto-common", + "version": "0.1.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "data-encoding", + "version": "2.11.1", + "license": "MIT", + "flagged": false + }, + { + "name": "der-parser", + "version": "10.0.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "deranged", + "version": "0.5.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "digest", + "version": "0.10.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "displaydoc", + "version": "0.2.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dtolnay/rust-toolchain", + "version": "4be7066ada62dd38de10e7b70166bc74ed198c30", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "find-msvc-tools", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "generic-array", + "version": "0.14.7", + "license": "MIT", + "flagged": false + }, + { + "name": "getrandom", + "version": "0.2.17", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "lazy_static", + "version": "1.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "libc", + "version": "0.2.189", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "memchr", + "version": "2.8.3", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "minimal-lexical", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "nom", + "version": "7.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "num-bigint", + "version": "0.4.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-conv", + "version": "0.2.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-integer", + "version": "0.1.46", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "num-traits", + "version": "0.2.19", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "oid-registry", + "version": "0.8.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "once_cell", + "version": "1.21.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "pem", + "version": "3.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "powerfmt", + "version": "0.2.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro2", + "version": "1.0.107", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "quote", + "version": "1.0.47", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rcgen", + "version": "0.14.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ring", + "version": "0.17.14", + "license": "Apache-2.0 AND ISC", + "flagged": false + }, + { + "name": "rusticata-macros", + "version": "4.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rustls", + "version": "0.23.42", + "license": "Apache-2.0 OR ISC OR MIT", + "flagged": false + }, + { + "name": "rustls-pki-types", + "version": "1.15.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rustls-webpki", + "version": "0.103.13", + "license": "ISC", + "flagged": false + }, + { + "name": "serde", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_core", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_derive", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "sha2", + "version": "0.10.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "shlex", + "version": "2.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "step-security/harden-runner", + "version": "bf7454d06d71f1098171f2acdf0cd4708d7b5920", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "subtle", + "version": "2.6.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "syn", + "version": "2.0.119", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "syn", + "version": "3.0.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "synstructure", + "version": "0.13.2", + "license": "MIT", + "flagged": false + }, + { + "name": "thiserror", + "version": "2.0.19", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror-impl", + "version": "2.0.19", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "time", + "version": "0.3.55", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "time-core", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "time-macros", + "version": "0.2.32", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "tinyvec", + "version": "1.10.0", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tinyvec_macros", + "version": "0.1.1", + "license": "MIT OR Apache-2.0 OR Zlib", + "flagged": false + }, + { + "name": "typenum", + "version": "1.20.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unicode-ident", + "version": "1.0.24", + "license": "(MIT OR Apache-2.0) AND Unicode-3.0", + "flagged": false + }, + { + "name": "unicode-normalization", + "version": "0.1.25", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "untrusted", + "version": "0.9.0", + "license": "ISC", + "flagged": false + }, + { + "name": "version_check", + "version": "0.9.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasi", + "version": "0.11.1+wasi-snapshot-preview1", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "windows-sys", + "version": "0.52.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-targets", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_gnullvm", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_msvc", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_gnu", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_gnullvm", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_msvc", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnu", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnullvm", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_msvc", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "x509-parser", + "version": "0.18.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "yasna", + "version": "0.6.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "zeroize", + "version": "1.9.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/pg-erd-cloud", + "error": null, + "component_count": 217, + "components": [ + { + "name": "@adobe/css-tools", + "version": "4.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@asamuzakjp/css-color", + "version": "6.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@asamuzakjp/dom-selector", + "version": "8.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/code-frame", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-string-parser", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/helper-validator-identifier", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/parser", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/runtime", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@babel/types", + "version": "7.29.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@bcoe/v8-coverage", + "version": "1.0.2", + "license": "ISC AND MIT", + "flagged": false + }, + { + "name": "@bramus/specificity", + "version": "2.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/color-helpers", + "version": "6.1.0", + "license": "MIT-0", + "flagged": false + }, + { + "name": "@csstools/css-calc", + "version": "3.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/css-color-parser", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/css-parser-algorithms", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@csstools/css-syntax-patches-for-csstree", + "version": "1.1.7", + "license": "MIT-0", + "flagged": false + }, + { + "name": "@csstools/css-tokenizer", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/core", + "version": "2.0.0-alpha.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/runtime", + "version": "2.0.0-alpha.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@emnapi/wasi-threads", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@exodus/bytes", + "version": "1.15.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/resolve-uri", + "version": "3.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/sourcemap-codec", + "version": "1.5.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/trace-mapping", + "version": "0.3.31", + "license": "MIT", + "flagged": false + }, + { + "name": "@napi-rs/wasm-runtime", + "version": "1.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@oxc-project/types", + "version": "0.142.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-android-arm64", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-darwin-arm64", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-darwin-x64", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-freebsd-x64", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm-gnueabihf", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm64-gnu", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-arm64-musl", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-ppc64-gnu", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-s390x-gnu", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-x64-gnu", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-linux-x64-musl", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-openharmony-arm64", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-wasm32-wasi", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-win32-arm64-msvc", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/binding-win32-x64-msvc", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@rolldown/pluginutils", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@standard-schema/spec", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/dom", + "version": "10.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/jest-dom", + "version": "6.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/react", + "version": "16.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@testing-library/user-event", + "version": "14.6.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@tybys/wasm-util", + "version": "0.10.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/aria-query", + "version": "5.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/chai", + "version": "5.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/d3-color", + "version": "3.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/d3-drag", + "version": "3.0.7", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/d3-interpolate", + "version": "3.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/d3-selection", + "version": "3.0.11", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/d3-transition", + "version": "3.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/d3-zoom", + "version": "3.0.8", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/deep-eql", + "version": "4.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/estree", + "version": "1.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react", + "version": "19.2.18", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/react-dom", + "version": "19.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/coverage-v8", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/expect", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/mocker", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/pretty-format", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/runner", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/snapshot", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/spy", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/utils", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "@xyflow/react", + "version": "12.11.3", + "license": "MIT", + "flagged": false + }, + { + "name": "@xyflow/system", + "version": "0.0.80", + "license": "MIT", + "flagged": false + }, + { + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "ece7cb06caefa5fff74198d8649806c4678c61a1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "aiohttp", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "alembic", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ansi-regex", + "version": "5.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "5.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "aria-query", + "version": "5.3.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "assertion-error", + "version": "2.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "ast-v8-to-istanbul", + "version": "1.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "asyncpg", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "asyncpg-stubs", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "bidi-js", + "version": "1.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "chai", + "version": "6.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "classcat", + "version": "5.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "convert-source-map", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "cryptography", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "css-tree", + "version": "3.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "css.escape", + "version": "1.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "csstype", + "version": "3.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "d3-color", + "version": "3.1.0", + "license": "ISC", + "flagged": false + }, + { + "name": "d3-dispatch", + "version": "3.0.1", + "license": "ISC", + "flagged": false + }, + { + "name": "d3-drag", + "version": "3.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "d3-ease", + "version": "3.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "d3-interpolate", + "version": "3.0.1", + "license": "ISC", + "flagged": false + }, + { + "name": "d3-selection", + "version": "3.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "d3-timer", + "version": "3.0.1", + "license": "ISC", + "flagged": false + }, + { + "name": "d3-transition", + "version": "3.0.1", + "license": "ISC", + "flagged": false + }, + { + "name": "d3-zoom", + "version": "3.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "data-urls", + "version": "7.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "decimal.js", + "version": "10.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "dequal", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "detect-libc", + "version": "2.1.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "dom-accessibility-api", + "version": "0.5.16", + "license": "MIT", + "flagged": false + }, + { + "name": "dom-accessibility-api", + "version": "0.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "entities", + "version": "8.0.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "es-module-lexer", + "version": "2.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "estree-walker", + "version": "3.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "expect-type", + "version": "1.3.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "fast-check", + "version": "4.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fastapi", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "fdir", + "version": "6.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fsevents", + "version": "2.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "github/codeql-action/analyze", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/autobuild", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/init", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "has-flag", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "html-encoding-sniffer", + "version": "6.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "html-escaper", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "httpx", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "hypercorn", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "indent-string", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "is-potential-custom-element-name", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "istanbul-lib-coverage", + "version": "3.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-lib-report", + "version": "3.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-reports", + "version": "3.2.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "js-tokens", + "version": "10.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "js-tokens", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jsdom", + "version": "30.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "lightningcss", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-android-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-arm64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-darwin-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-freebsd-x64", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm-gnueabihf", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-arm64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-gnu", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-linux-x64-musl", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-arm64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lightningcss-win32-x64-msvc", + "version": "1.33.0", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "lru-cache", + "version": "11.5.2", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "lz-string", + "version": "1.5.0", + "license": "MIT", + "flagged": false + }, + { + "name": "magic-string", + "version": "0.30.21", + "license": "MIT", + "flagged": false + }, + { + "name": "magicast", + "version": "0.5.3", + "license": "MIT", + "flagged": false + }, + { + "name": "make-dir", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mdn-data", + "version": "2.27.1", + "license": "CC0-1.0", + "flagged": false + }, + { + "name": "min-indent", + "version": "1.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "mypy", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "nanoid", + "version": "3.3.16", + "license": "MIT", + "flagged": false + }, + { + "name": "obug", + "version": "2.1.3", + "license": "MIT", + "flagged": false + }, + { + "name": "parse5", + "version": "8.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pathe", + "version": "2.0.3", + "license": "MIT", + "flagged": false + }, + { + "name": "picocolors", + "version": "1.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "picomatch", + "version": "4.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "postcss", + "version": "8.5.25", + "license": "MIT", + "flagged": false + }, + { + "name": "pretty-format", + "version": "27.5.1", + "license": "MIT", + "flagged": false + }, + { + "name": "prometheus-client", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "psycopg", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "punycode", + "version": "2.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pure-rand", + "version": "8.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pydantic-settings", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pyjwt", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest-asyncio", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest-cov", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "python-jose", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "python-multipart", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "react", + "version": "19.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "react-dom", + "version": "19.2.8", + "license": "MIT", + "flagged": false + }, + { + "name": "react-is", + "version": "17.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "redent", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "redis", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "requests", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "require-from-string", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "rolldown", + "version": "1.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "saxes", + "version": "6.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "scheduler", + "version": "0.27.0", + "license": "MIT", + "flagged": false + }, + { + "name": "semver", + "version": "7.8.5", + "license": "ISC", + "flagged": false + }, + { + "name": "setuptools", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "siginfo", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "snowflake-connector-python", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "source-map-js", + "version": "1.2.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "sqlalchemy", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "stackback", + "version": "0.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "starlette", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "std-env", + "version": "4.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "step-security/harden-runner", + "version": "b09bb98e06d4d774595224525879c09bc6e98c40", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "strip-indent", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-color", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "symbol-tree", + "version": "3.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "tinybench", + "version": "2.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyexec", + "version": "1.2.4", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyglobby", + "version": "0.2.17", + "license": "MIT", + "flagged": false + }, + { + "name": "tinyrainbow", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tldts", + "version": "7.4.10", + "license": "MIT", + "flagged": false + }, + { + "name": "tldts-core", + "version": "7.4.10", + "license": "MIT", + "flagged": false + }, + { + "name": "tough-cookie", + "version": "6.0.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "tr46", + "version": "6.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tslib", + "version": "2.8.1", + "license": "0BSD", + "flagged": false + }, + { + "name": "types-python-jose", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "typescript", + "version": "6.0.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "undici", + "version": "8.10.0", + "license": "MIT", + "flagged": false + }, + { + "name": "urllib3", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "use-sync-external-store", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "vite", + "version": "8.2.1", + "license": "MIT", + "flagged": false + }, + { + "name": "vitest", + "version": "4.1.10", + "license": "MIT", + "flagged": false + }, + { + "name": "w3c-xmlserializer", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "webidl-conversions", + "version": "8.0.1", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "whatwg-mimetype", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "whatwg-url", + "version": "16.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "whatwg-url", + "version": "17.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "why-is-node-running", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "xml-name-validator", + "version": "5.0.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "xmlchars", + "version": "2.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "zustand", + "version": "4.5.7", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/pg-llm-batch", + "error": null, + "component_count": 41, + "components": [ + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "5fda3b95a4ea91299a34e894583c3862153e4b97", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "aiohappyeyeballs", + "version": "2.7.1", + "license": "0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0", + "flagged": true + }, + { + "name": "aiohttp", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "aiohttp", + "version": "3.14.3", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "aiosignal", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "astral-sh/setup-uv", + "version": "c771a70e6277c0a99b617c7a806ffedaca235ff9", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "async-timeout", + "version": "5.0.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "attrs", + "version": "26.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "backports-asyncio-runner", + "version": "1.2.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "cffi", + "version": "2.1.0", + "license": "MIT-0", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "cryptography", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "cryptography", + "version": "50.0.0", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "exceptiongroup", + "version": "1.3.1", + "license": "MIT AND Python-2.0", + "flagged": false + }, + { + "name": "frozenlist", + "version": "1.8.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "multidict", + "version": "6.7.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "opentelemetry-api", + "version": "1.44.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "packaging", + "version": "26.2", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "pg-llm-batch", + "version": "0.1.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "propcache", + "version": "0.5.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "psycopg", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "psycopg", + "version": "3.3.4", + "license": "LGPL-3.0 AND LGPL-3.0-only", + "flagged": true + }, + { + "name": "psycopg-binary", + "version": "3.3.4", + "license": "GPL-3.0-or-later", + "flagged": true + }, + { + "name": "pycparser", + "version": "3.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pytest", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest-asyncio", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest-asyncio", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "ruff", + "version": "0.16.1", + "license": "MIT", + "flagged": false + }, + { + "name": "setuptools", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "step-security/harden-runner", + "version": "b09bb98e06d4d774595224525879c09bc6e98c40", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "tomli", + "version": "2.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "tzdata", + "version": "2026.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "yarl", + "version": "1.24.2", + "license": "Apache-2.0", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/pingora-gateway", + "error": null, + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/PolicyWeave", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/procurement-workbench", + "error": null, + "component_count": 46, + "components": [ + { + "name": "annotated-doc", + "version": "0.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "annotated-types", + "version": "0.8.0", + "license": "MIT", + "flagged": false + }, + { + "name": "anyio", + "version": "4.15.1", + "license": "MIT", + "flagged": false + }, + { + "name": "attrs", + "version": "26.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "beautifulsoup4", + "version": "4.15.0", + "license": "MIT", + "flagged": false + }, + { + "name": "certifi", + "version": "2026.7.22", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "cffi", + "version": "2.1.1", + "license": "MIT-0", + "flagged": false + }, + { + "name": "click", + "version": "8.5.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "cryptography", + "version": "50.0.2", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "defusedxml", + "version": "0.7.1", + "license": "PSF-2.0", + "flagged": false + }, + { + "name": "fastapi", + "version": "0.142.2", + "license": "MIT", + "flagged": false + }, + { + "name": "greenlet", + "version": "3.5.6", + "license": "MIT AND PSF-2.0", + "flagged": false + }, + { + "name": "h11", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "httpcore", + "version": "1.0.9", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx", + "version": "0.28.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx-sse", + "version": "0.4.3", + "license": "MIT", + "flagged": false + }, + { + "name": "idna", + "version": "3.20", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonschema", + "version": "4.26.0", + "license": "MIT", + "flagged": false + }, + { + "name": "jsonschema-specifications", + "version": "2025.9.1", + "license": "MIT", + "flagged": false + }, + { + "name": "mcp", + "version": "1.30.0", + "license": "MIT", + "flagged": false + }, + { + "name": "opentelemetry-api", + "version": "1.45.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "packaging", + "version": "26.3", + "license": "Apache-2.0 OR BSD-2-Clause", + "flagged": false + }, + { + "name": "playwright", + "version": "1.63.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pycparser", + "version": "3.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pydantic", + "version": "2.13.5", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-core", + "version": "2.46.5", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-settings", + "version": "2.15.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pyee", + "version": "13.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.21.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pyjwt", + "version": "2.15.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "python-dotenv", + "version": "1.2.4", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "python-multipart", + "version": "0.0.32", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pywin32", + "version": "312", + "license": "PSF-2.0", + "flagged": false + }, + { + "name": "referencing", + "version": "0.37.0", + "license": "MIT", + "flagged": false + }, + { + "name": "rpds-py", + "version": "2026.6.3", + "license": "MIT", + "flagged": false + }, + { + "name": "ruff", + "version": "0.16.10", + "license": "MIT", + "flagged": false + }, + { + "name": "soupsieve", + "version": "2.10", + "license": "MIT", + "flagged": false + }, + { + "name": "sse-starlette", + "version": "3.5.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "starlette", + "version": "1.7.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "typing-inspection", + "version": "0.4.4", + "license": "MIT", + "flagged": false + }, + { + "name": "uvicorn", + "version": "0.54.0", + "license": "BSD-3-Clause", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/psychometrics-commons", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/quarantine-sandbox-runtime", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/RankWeave", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/saju-caldav", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/scopeweave", + "error": null, + "component_count": 72, + "components": [ + { + "name": "@bcoe/v8-coverage", + "version": "1.0.2", + "license": "ISC AND MIT", + "flagged": false + }, + { + "name": "@hono/node-server", + "version": "2.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@istanbuljs/schema", + "version": "0.1.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/resolve-uri", + "version": "3.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/sourcemap-codec", + "version": "1.5.5", + "license": "MIT", + "flagged": false + }, + { + "name": "@jridgewell/trace-mapping", + "version": "0.3.31", + "license": "MIT", + "flagged": false + }, + { + "name": "@playwright/test", + "version": "1.62.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "@types/istanbul-lib-coverage", + "version": "2.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/configure-pages", + "version": "45bfe0192ca1faeb007ade9deae92b16b8254a0d", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/dependency-review-action", + "version": "a1d282b36b6f3519aa1f3fc636f609c47dddb294", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/deploy-pages", + "version": "cd2ce8fcbc39b97be8ca5fce6e763baed58fa128", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "2028fbc5c25fe9cf00d9f06a71cc4710d4507903", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-node", + "version": "39370e3970a6d050c480ffad4ff0ed4d3fdee5af", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-pages-artifact", + "version": "fc324d3547104276b827a68afc52ff2a11cc49c9", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "ansi-regex", + "version": "6.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "ansi-styles", + "version": "6.2.3", + "license": "MIT", + "flagged": false + }, + { + "name": "balanced-match", + "version": "4.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "brace-expansion", + "version": "5.0.9", + "license": "MIT", + "flagged": false + }, + { + "name": "c8", + "version": "12.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "cliui", + "version": "9.0.1", + "license": "ISC", + "flagged": false + }, + { + "name": "convert-source-map", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "cross-spawn", + "version": "7.0.6", + "license": "MIT", + "flagged": false + }, + { + "name": "emoji-regex", + "version": "10.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "escalade", + "version": "3.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "fast-check", + "version": "4.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "find-up", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "foreground-child", + "version": "3.3.1", + "license": "ISC", + "flagged": false + }, + { + "name": "fsevents", + "version": "2.3.2", + "license": "MIT", + "flagged": false + }, + { + "name": "get-caller-file", + "version": "2.0.5", + "license": "ISC", + "flagged": false + }, + { + "name": "get-east-asian-width", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "github/codeql-action/analyze", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "github/codeql-action/init", + "version": "8aad20d150bbac5944a9f9d289da16a4b0d87c1e", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "glob", + "version": "13.0.6", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml", + "version": "3a7550f43ba5b58905a821ce3a0ed24c4858b3f4", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "has-flag", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hono", + "version": "4.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "html-escaper", + "version": "2.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "isexe", + "version": "2.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "istanbul-lib-coverage", + "version": "3.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-lib-report", + "version": "3.0.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "istanbul-reports", + "version": "3.2.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "locate-path", + "version": "6.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "lru-cache", + "version": "11.5.2", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "make-dir", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "minimatch", + "version": "10.2.6", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "minipass", + "version": "7.1.3", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "p-limit", + "version": "3.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "p-locate", + "version": "5.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "path-exists", + "version": "4.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "path-key", + "version": "3.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "path-scurry", + "version": "2.0.2", + "license": "BlueOak-1.0.0", + "flagged": false + }, + { + "name": "playwright", + "version": "1.62.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "playwright-core", + "version": "1.62.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "pure-rand", + "version": "8.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "semver", + "version": "7.8.5", + "license": "ISC", + "flagged": false + }, + { + "name": "shebang-command", + "version": "2.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "shebang-regex", + "version": "3.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "signal-exit", + "version": "4.1.0", + "license": "ISC", + "flagged": false + }, + { + "name": "string-width", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "string-width", + "version": "8.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "strip-ansi", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "supports-color", + "version": "7.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "test-exclude", + "version": "8.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "v8-to-istanbul", + "version": "9.3.0", + "license": "ISC", + "flagged": false + }, + { + "name": "which", + "version": "2.0.2", + "license": "ISC", + "flagged": false + }, + { + "name": "wrap-ansi", + "version": "9.0.2", + "license": "MIT", + "flagged": false + }, + { + "name": "y18n", + "version": "5.0.8", + "license": "ISC", + "flagged": false + }, + { + "name": "yargs", + "version": "18.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "yargs-parser", + "version": "21.1.1", + "license": "ISC", + "flagged": false + }, + { + "name": "yargs-parser", + "version": "22.0.0", + "license": "ISC", + "flagged": false + }, + { + "name": "yocto-queue", + "version": "0.1.0", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/semantic-data-portal", + "error": null, + "component_count": 45, + "components": [ + { + "name": "actions/checkout", + "version": "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "ece7cb06caefa5fff74198d8649806c4678c61a1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "annotated-doc", + "version": "0.0.4", + "license": "MIT", + "flagged": false + }, + { + "name": "annotated-types", + "version": "0.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "anyio", + "version": "4.14.1", + "license": "MIT", + "flagged": false + }, + { + "name": "anyio", + "version": "4.14.2", + "license": "MIT", + "flagged": false + }, + { + "name": "atheris", + "version": "3.0.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "cffi", + "version": "2.1.0", + "license": "MIT-0", + "flagged": false + }, + { + "name": "click", + "version": "8.4.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "cryptography", + "version": "49.0.0", + "license": "BSD-3-Clause OR Apache-2.0", + "flagged": false + }, + { + "name": "fastapi", + "version": "0.139.0", + "license": "MIT", + "flagged": false + }, + { + "name": "github/codeql-action/upload-sarif", + "version": "54f647b7e1bb85c95cddabcd46b0c578ec92bc1a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "greenlet", + "version": "3.2.5", + "license": "MIT AND PSF-2.0", + "flagged": false + }, + { + "name": "greenlet", + "version": "3.5.3", + "license": "MIT AND PSF-2.0", + "flagged": false + }, + { + "name": "h11", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "httpcore", + "version": "1.0.9", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx", + "version": "0.28.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "hypothesis", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "hypothesis", + "version": "6.156.6", + "license": "MPL-2.0 AND MPL-1.1", + "flagged": true + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "ossf/scorecard-action", + "version": "4eaacf0543bb3f2c246792bd56e8cdeffafb205a", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "packaging", + "version": "26.2", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "psycopg", + "version": "3.3.4", + "license": "LGPL-3.0 AND LGPL-3.0-only", + "flagged": true + }, + { + "name": "psycopg-binary", + "version": "3.3.4", + "license": "GPL-3.0-or-later", + "flagged": true + }, + { + "name": "pycparser", + "version": "3.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pydantic", + "version": "2.13.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-core", + "version": "2.46.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pyjwt", + "version": "2.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "setuptools", + "version": "80.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "sortedcontainers", + "version": "2.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "sqlalchemy", + "version": "", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "sqlalchemy", + "version": "2.0.51", + "license": "MIT", + "flagged": false + }, + { + "name": "starlette", + "version": "1.3.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "typing-inspection", + "version": "0.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "tzdata", + "version": "2026.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "tzdata", + "version": "2026.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "uvicorn", + "version": "0.51.0", + "license": "BSD-3-Clause", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/supply-chain-control-plane", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/TEPP", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/ThreadWeave", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/tokyo-travel-2026", + "error": null, + "component_count": 3, + "components": [ + { + "name": "fonttools", + "version": "4.66.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pillow", + "version": "12.3.0", + "license": "MIT-CMU", + "flagged": false + }, + { + "name": "pymupdf", + "version": "1.28.2", + "license": "AGPL-3.0 AND AGPL-3.0-only AND AGPL-3.0-or-later", + "flagged": true + } + ] + }, + { + "repo": "ContextualWisdomLab/traveling-agent", + "error": null, + "component_count": 1, + "components": [ + { + "name": "ContextualWisdomLab/.github/.github/workflows/traveling-agent-poc-quality.yml", + "version": "f52a0d71b3ce2b60d738f4326f0f9906af9ea1b9", + "license": "NOASSERTION", + "flagged": true + } + ] + }, + { + "repo": "ContextualWisdomLab/Veilpick", + "error": null, + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/wardnet", + "error": null, + "component_count": 185, + "components": [ + { + "name": "actions/cache", + "version": "0057852bfaa89a56745cba8c7296529d2fc39830", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "arbitrary", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "atomic-waker", + "version": "1.1.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "autocfg", + "version": "1.5.1", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "axum", + "version": "0.8.9", + "license": "MIT", + "flagged": false + }, + { + "name": "axum-core", + "version": "0.5.6", + "license": "MIT", + "flagged": false + }, + { + "name": "base64", + "version": "0.22.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bit-set", + "version": "0.8.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bit-vec", + "version": "0.8.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "bitflags", + "version": "2.13.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bumpalo", + "version": "3.20.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "bytes", + "version": "1.12.1", + "license": "MIT", + "flagged": false + }, + { + "name": "cc", + "version": "1.4.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cfg-if", + "version": "1.0.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cfg_aliases", + "version": "0.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "chacha20", + "version": "0.10.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "cpufeatures", + "version": "0.3.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "displaydoc", + "version": "0.2.7", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "dtolnay/rust-toolchain", + "version": "4be7066ada62dd38de10e7b70166bc74ed198c30", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "dtolnay/rust-toolchain", + "version": "efcb852328a9f50117170cc43094fb6f09eaf1ae", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "errno", + "version": "0.3.14", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "fastrand", + "version": "2.5.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "find-msvc-tools", + "version": "0.1.9", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "fnv", + "version": "1.0.7", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "form_urlencoded", + "version": "1.2.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-channel", + "version": "0.3.33", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-core", + "version": "0.3.34", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-io", + "version": "0.3.34", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-macro", + "version": "0.3.34", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-sink", + "version": "0.3.34", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-task", + "version": "0.3.34", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "futures-util", + "version": "0.3.34", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "getrandom", + "version": "0.2.17", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "getrandom", + "version": "0.3.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "getrandom", + "version": "0.4.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "github/codeql-action/upload-sarif", + "version": "ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "http", + "version": "1.5.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "http-body", + "version": "1.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "http-body-util", + "version": "0.1.4", + "license": "MIT", + "flagged": false + }, + { + "name": "httparse", + "version": "1.10.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "httpdate", + "version": "1.0.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "hyper", + "version": "1.11.0", + "license": "MIT", + "flagged": false + }, + { + "name": "hyper-rustls", + "version": "0.27.9", + "license": "Apache-2.0 OR ISC OR MIT", + "flagged": false + }, + { + "name": "hyper-util", + "version": "0.1.20", + "license": "MIT", + "flagged": false + }, + { + "name": "icu_collections", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_locale_core", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_normalizer", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_normalizer_data", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_properties", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_properties_data", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "icu_provider", + "version": "2.2.0", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "idna", + "version": "1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "idna_adapter", + "version": "1.2.2", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "ipnet", + "version": "2.12.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "itoa", + "version": "1.0.18", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "js-sys", + "version": "0.3.103", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "libc", + "version": "0.2.189", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "libfuzzer-sys", + "version": ">= 0.4.0,< 0.5.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "linux-raw-sys", + "version": "0.12.1", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "litemap", + "version": "0.8.2", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "log", + "version": "0.4.33", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "lru-slab", + "version": "0.1.2", + "license": "MIT OR Apache-2.0 OR Zlib", + "flagged": false + }, + { + "name": "matchit", + "version": "0.8.4", + "license": "MIT AND BSD-3-Clause", + "flagged": false + }, + { + "name": "memchr", + "version": "2.8.3", + "license": "Unlicense OR MIT", + "flagged": false + }, + { + "name": "mime", + "version": "0.3.17", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "mime_guess", + "version": "2.0.5", + "license": "MIT", + "flagged": false + }, + { + "name": "mio", + "version": "1.2.2", + "license": "MIT", + "flagged": false + }, + { + "name": "num-traits", + "version": "0.2.19", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "once_cell", + "version": "1.21.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ossf/scorecard-action", + "version": "2d1146689b8cda280b9bc96326124645441f03bc", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "percent-encoding", + "version": "2.3.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "percent-encoding", + "version": ">= 2.0.0,< 3.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pin-project-lite", + "version": "0.2.17", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "potential_utf", + "version": "0.1.5", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "ppv-lite86", + "version": "0.2.21", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proc-macro2", + "version": "1.0.107", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proptest", + "version": "1.11.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "proptest", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "quick-error", + "version": "1.2.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "quinn", + "version": "0.11.11", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "quinn-proto", + "version": "0.11.16", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "quinn-udp", + "version": "0.5.15", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "quote", + "version": "1.0.47", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "r-efi", + "version": "5.3.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later", + "flagged": true + }, + { + "name": "r-efi", + "version": "6.0.0", + "license": "MIT OR Apache-2.0 OR LGPL-2.1-or-later", + "flagged": true + }, + { + "name": "rand", + "version": "0.10.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rand", + "version": "0.9.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rand_chacha", + "version": "0.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rand_core", + "version": "0.10.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rand_core", + "version": "0.9.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rand_pcg", + "version": "0.10.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rand_xorshift", + "version": "0.4.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "regex-syntax", + "version": "0.8.11", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "reqwest", + "version": "0.12.28", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ring", + "version": "0.17.14", + "license": "Apache-2.0 AND ISC", + "flagged": false + }, + { + "name": "rustc-hash", + "version": "2.1.3", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "rustix", + "version": "1.1.4", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "rustls", + "version": "0.23.43", + "license": "Apache-2.0 OR ISC OR MIT", + "flagged": false + }, + { + "name": "rustls-pki-types", + "version": "1.15.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rustls-webpki", + "version": "0.103.13", + "license": "ISC", + "flagged": false + }, + { + "name": "rustversion", + "version": "1.0.23", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "rusty-fork", + "version": "0.3.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "ryu", + "version": "1.0.23", + "license": "Apache-2.0 OR BSL-1.0", + "flagged": false + }, + { + "name": "serde", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "serde_core", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_derive", + "version": "1.0.229", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_json", + "version": "1.0.151", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_json", + "version": ">= 1.0.0,< 2.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "serde_path_to_error", + "version": "0.1.20", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "serde_urlencoded", + "version": "0.7.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "shlex", + "version": "2.0.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "signal-hook-registry", + "version": "1.4.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "slab", + "version": "0.4.12", + "license": "MIT", + "flagged": false + }, + { + "name": "smallvec", + "version": "1.15.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "socket2", + "version": "0.6.5", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "stable_deref_trait", + "version": "1.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "subtle", + "version": "2.6.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "syn", + "version": "2.0.119", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "syn", + "version": "3.0.3", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "sync_wrapper", + "version": "1.0.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "synstructure", + "version": "0.13.2", + "license": "MIT", + "flagged": false + }, + { + "name": "tempfile", + "version": "3.27.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror", + "version": "2.0.19", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "thiserror-impl", + "version": "2.0.19", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "tinystr", + "version": "0.8.3", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "tinyvec", + "version": "1.12.0", + "license": "Zlib OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "tinyvec_macros", + "version": "0.1.1", + "license": "MIT OR Apache-2.0 OR Zlib", + "flagged": false + }, + { + "name": "tokio", + "version": "1.53.1", + "license": "MIT", + "flagged": false + }, + { + "name": "tokio-macros", + "version": "2.7.2", + "license": "MIT", + "flagged": false + }, + { + "name": "tokio-rustls", + "version": "0.26.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "tokio-util", + "version": "0.7.19", + "license": "MIT", + "flagged": false + }, + { + "name": "tower", + "version": "0.5.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tower-http", + "version": "0.6.11", + "license": "MIT", + "flagged": false + }, + { + "name": "tower-layer", + "version": "0.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tower-service", + "version": "0.3.3", + "license": "MIT", + "flagged": false + }, + { + "name": "tracing", + "version": "0.1.44", + "license": "MIT", + "flagged": false + }, + { + "name": "tracing-core", + "version": "0.1.36", + "license": "MIT", + "flagged": false + }, + { + "name": "try-lock", + "version": "0.2.5", + "license": "MIT", + "flagged": false + }, + { + "name": "unarray", + "version": "0.1.4", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unicase", + "version": "2.9.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "unicode-ident", + "version": "1.0.24", + "license": "(MIT OR Apache-2.0) AND Unicode-3.0", + "flagged": false + }, + { + "name": "untrusted", + "version": "0.9.0", + "license": "ISC", + "flagged": false + }, + { + "name": "url", + "version": "2.5.8", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "utf8_iter", + "version": "1.0.4", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "wait-timeout", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "want", + "version": "0.3.1", + "license": "MIT", + "flagged": false + }, + { + "name": "wasi", + "version": "0.11.1+wasi-snapshot-preview1", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "wasip2", + "version": "1.0.4+wasi-0.2.12", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "wasm-bindgen", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-futures", + "version": "0.4.76", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-macro", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-macro-support", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-bindgen-shared", + "version": "0.2.126", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wasm-streams", + "version": "0.4.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "web-sys", + "version": "0.3.103", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "web-time", + "version": "1.1.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "webpki-roots", + "version": "1.0.9", + "license": "CDLA-Permissive-2.0", + "flagged": false + }, + { + "name": "windows-link", + "version": "0.2.1", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-sys", + "version": "0.52.0", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-sys", + "version": "0.61.2", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows-targets", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_gnullvm", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_aarch64_msvc", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_gnu", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_gnullvm", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_i686_msvc", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnu", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_gnullvm", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "windows_x86_64_msvc", + "version": "0.52.6", + "license": "MIT OR Apache-2.0", + "flagged": false + }, + { + "name": "wit-bindgen", + "version": "0.57.1", + "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "writeable", + "version": "0.6.3", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "yoke", + "version": "0.8.3", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "yoke-derive", + "version": "0.8.2", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zerocopy", + "version": "0.8.55", + "license": "BSD-2-Clause OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "zerocopy-derive", + "version": "0.8.55", + "license": "BSD-2-Clause OR Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "zerofrom", + "version": "0.1.8", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zerofrom-derive", + "version": "0.1.7", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zeroize", + "version": "1.9.0", + "license": "Apache-2.0 OR MIT", + "flagged": false + }, + { + "name": "zerotrie", + "version": "0.2.4", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zerovec", + "version": "0.11.6", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zerovec-derive", + "version": "0.11.3", + "license": "Unicode-3.0", + "flagged": false + }, + { + "name": "zmij", + "version": "1.0.23", + "license": "MIT", + "flagged": false + } + ] + }, + { + "repo": "ContextualWisdomLab/xtrm-lead-pi-outbound", + "error": "gh: Not Found (HTTP 404)", + "component_count": 0, + "components": [] + }, + { + "repo": "ContextualWisdomLab/xtrmLLMBatchPython", + "error": null, + "component_count": 82, + "components": [ + { + "name": "@types/express", + "version": "^4.17.0", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/inquirer", + "version": "^8.2.12", + "license": "MIT", + "flagged": false + }, + { + "name": "@types/node", + "version": "^24.0.1", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/coverage-v8", + "version": "^3.2.6", + "license": "MIT", + "flagged": false + }, + { + "name": "@vitest/ui", + "version": "^3.2.6", + "license": "MIT", + "flagged": false + }, + { + "name": "actions/checkout", + "version": "3d3c42e5aac5ba805825da76410c181273ba90b1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/setup-python", + "version": "a26af69be951a213d495a4c3e4e4022e16d87065", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "actions/upload-artifact", + "version": "ea165f8d65b6e75b540449e92b4886f43607fa02", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "aiofiles", + "version": "25.1.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "aiohappyeyeballs", + "version": "2.7.1", + "license": "0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0", + "flagged": true + }, + { + "name": "aiohttp", + "version": "3.14.1", + "license": "Apache-2.0 AND MIT", + "flagged": false + }, + { + "name": "aioresponses", + "version": "0.7.9", + "license": "MIT", + "flagged": false + }, + { + "name": "aiosignal", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "aiosqlite", + "version": "0.22.1", + "license": "MIT", + "flagged": false + }, + { + "name": "annotated-types", + "version": "0.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "anyio", + "version": "4.14.1", + "license": "MIT", + "flagged": false + }, + { + "name": "astral-sh/setup-uv", + "version": "c771a70e6277c0a99b617c7a806ffedaca235ff9", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "async-timeout", + "version": "5.0.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "attrs", + "version": "26.1.0", + "license": "MIT", + "flagged": false + }, + { + "name": "axios", + "version": "^1.9.0", + "license": "MIT", + "flagged": false + }, + { + "name": "backports-asyncio-runner", + "version": "1.2.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "certifi", + "version": "2026.6.17", + "license": "MPL-2.0", + "flagged": true + }, + { + "name": "cffi", + "version": "2.0.0", + "license": "MIT-0", + "flagged": false + }, + { + "name": "charset-normalizer", + "version": "3.4.7", + "license": "MIT", + "flagged": false + }, + { + "name": "click", + "version": "8.4.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "colorama", + "version": "0.4.6", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "commander", + "version": "^14.0.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "cryptography", + "version": "49.0.0", + "license": "BSD-3-Clause OR Apache-2.0", + "flagged": false + }, + { + "name": "eslint", + "version": "^9.27.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "et-xmlfile", + "version": "2.0.0", + "license": "0BSD AND BSD-3-Clause AND MIT AND Python-2.0", + "flagged": false + }, + { + "name": "exceptiongroup", + "version": "1.3.1", + "license": "MIT AND Python-2.0", + "flagged": false + }, + { + "name": "express", + "version": "^4.18.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "extract-zip", + "version": "^2.0.1", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "frozenlist", + "version": "1.8.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "github.com/lib/pq", + "version": "v1.12.3", + "license": "MIT", + "flagged": false + }, + { + "name": "h11", + "version": "0.16.0", + "license": "MIT", + "flagged": false + }, + { + "name": "httpcore", + "version": "1.0.9", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "httpx", + "version": "0.28.1", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "idna", + "version": "3.18", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "iniconfig", + "version": "2.3.0", + "license": "MIT", + "flagged": false + }, + { + "name": "inquirer", + "version": "^8.2.6", + "license": "MIT", + "flagged": false + }, + { + "name": "markdown-it-py", + "version": "4.2.0", + "license": "MIT", + "flagged": false + }, + { + "name": "mdurl", + "version": "0.1.2", + "license": "MIT", + "flagged": false + }, + { + "name": "multidict", + "version": "6.7.1", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "numpy", + "version": "2.2.6", + "license": "Apache-2.0 AND BSD-3-Clause AND MIT AND Zlib", + "flagged": false + }, + { + "name": "numpy", + "version": "2.4.6", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "numpy", + "version": "2.5.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "open", + "version": "^10.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "openpyxl", + "version": "3.1.5", + "license": "MIT", + "flagged": false + }, + { + "name": "packaging", + "version": "26.2", + "license": "Apache-2.0 AND BSD-2-Clause", + "flagged": false + }, + { + "name": "pandas", + "version": "2.3.3", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "pandas", + "version": "3.0.3", + "license": "BSD-2-Clause AND BSD-3-Clause", + "flagged": false + }, + { + "name": "pino", + "version": "^9.7.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pluggy", + "version": "1.6.0", + "license": "MIT", + "flagged": false + }, + { + "name": "propcache", + "version": "0.5.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "psycopg", + "version": "3.3.4", + "license": "LGPL-3.0 AND LGPL-3.0-only", + "flagged": true + }, + { + "name": "psycopg-binary", + "version": "3.3.4", + "license": "GPL-3.0-or-later", + "flagged": true + }, + { + "name": "pycparser", + "version": "3.0", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pydantic", + "version": "2.13.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pydantic-core", + "version": "2.46.4", + "license": "MIT", + "flagged": false + }, + { + "name": "pygments", + "version": "2.20.0", + "license": "BSD-2-Clause", + "flagged": false + }, + { + "name": "pypa/gh-action-pip-audit", + "version": "1220774d901786e6f652ae159f7b6bc8fea6d266", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "pytest", + "version": "9.1.1", + "license": "MIT", + "flagged": false + }, + { + "name": "pytest-asyncio", + "version": "1.4.0", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "python-dateutil", + "version": "2.9.0.post0", + "license": "Apache-2.0 OR BSD-3-Clause", + "flagged": false + }, + { + "name": "python-dotenv", + "version": "1.2.2", + "license": "BSD-3-Clause", + "flagged": false + }, + { + "name": "pytz", + "version": "2026.2", + "license": "MIT AND ZPL-2.1", + "flagged": false + }, + { + "name": "regex", + "version": "2026.6.28", + "license": "CNRI-Python AND Apache-2.0", + "flagged": false + }, + { + "name": "requests", + "version": "2.34.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "rich", + "version": "15.0.0", + "license": "MIT", + "flagged": false + }, + { + "name": "six", + "version": "1.17.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tiktoken", + "version": "0.13.0", + "license": "MIT", + "flagged": false + }, + { + "name": "tomli", + "version": "2.4.1", + "license": "MIT", + "flagged": false + }, + { + "name": "typescript", + "version": "^5.8.3", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "typing-extensions", + "version": "4.16.0", + "license": "Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD", + "flagged": true + }, + { + "name": "typing-inspection", + "version": "0.4.2", + "license": "MIT", + "flagged": false + }, + { + "name": "tzdata", + "version": "2026.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "urllib3", + "version": "2.7.0", + "license": "MIT", + "flagged": false + }, + { + "name": "vitest", + "version": "^3.2.6", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "xtrmllmbatch", + "version": "0.1.0", + "license": "NOASSERTION", + "flagged": true + }, + { + "name": "yarl", + "version": "1.24.2", + "license": "Apache-2.0", + "flagged": false + }, + { + "name": "zod", + "version": "^3.25.67", + "license": "NOASSERTION", + "flagged": true + } + ] + } + ] } diff --git a/docs/sbom/inventory.md b/docs/sbom/inventory.md index 8892aa3b33..3517c2ffef 100644 --- a/docs/sbom/inventory.md +++ b/docs/sbom/inventory.md @@ -1,6 +1,6 @@ # Organization SBOM inventory -Generated: pending first scheduled run +Generated: 2026-10-16T20:40:32Z One central view of every managed repository's software components, versions, and licenses. Feeds license and vulnerability governance @@ -8,18 +8,8177 @@ alongside the central Security Scan. ## Summary -- Repositories: 0 -- Components: 0 +- Repositories: 76 +- Components: 6967 - Policy: commercial-license-only -- Flagged licenses: 0 +- Flagged licenses: 729 ## License roll-up | License | Components | | --- | ---: | +| (Apache-2.0 OR MIT) | 2 | +| (Apache-2.0 OR MIT) AND BSD-3-Clause | 1 | +| (MIT OR Apache-2.0) AND NCSA | 1 | +| (MIT OR Apache-2.0) AND Unicode-3.0 | 9 | +| (MIT OR CC0-1.0) | 1 | +| (MIT OR GPL-3.0-or-later) ⚠️ | 1 | +| 0BSD | 8 | +| 0BSD AND BSD-2-Clause AND BSD-3-Clause AND BSD-4-Clause AND LicenseRef-scancode-python-cwi AND LicenseRef-scancode-secret-labs-2011 AND LicenseRef-scancode-unicode AND MIT AND Python-2.0 | 1 | +| 0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0 ⚠️ | 5 | +| 0BSD AND BSD-3-Clause AND MIT AND Python-2.0 | 2 | +| 0BSD OR MIT OR Apache-2.0 | 2 | +| AGPL-3.0 AND AGPL-3.0-only AND AGPL-3.0-or-later ⚠️ | 1 | +| Apache-2.0 | 368 | +| Apache-2.0 AND BSD-2-Clause | 21 | +| Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND CC0-1.0 AND MIT AND OFL-1.1 | 1 | +| Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND LicenseRef-scancode-public-domain AND MIT | 1 | +| Apache-2.0 AND BSD-3-Clause AND MIT AND Zlib | 2 | +| Apache-2.0 AND BSD-3-Clause AND MPL-2.0 ⚠️ | 5 | +| Apache-2.0 AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later ⚠️ | 1 | +| Apache-2.0 AND GPL-1.0-or-later AND MIT ⚠️ | 2 | +| Apache-2.0 AND ISC | 6 | +| Apache-2.0 AND LGPL-3.0-or-later ⚠️ | 3 | +| Apache-2.0 AND LGPL-3.0-or-later AND MIT ⚠️ | 1 | +| Apache-2.0 AND MIT | 24 | +| Apache-2.0 AND MIT AND MPL-2.0 ⚠️ | 1 | +| Apache-2.0 AND Python-2.0 | 6 | +| Apache-2.0 OR BSD-2-Clause | 7 | +| Apache-2.0 OR BSD-3-Clause | 14 | +| Apache-2.0 OR BSD-3-Clause OR (Apache-2.0 AND BSD-3-Clause) | 1 | +| Apache-2.0 OR BSL-1.0 | 3 | +| Apache-2.0 OR ISC OR MIT | 4 | +| Apache-2.0 OR MIT | 144 | +| Apache-2.0 WITH LLVM-exception | 6 | +| Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | 20 | +| BSD-2-Clause | 72 | +| BSD-2-Clause AND BSD-2-Clause-Views | 2 | +| BSD-2-Clause AND BSD-3-Clause | 63 | +| BSD-2-Clause AND BSD-3-Clause AND CC-PDDC AND GPL-1.0-or-later AND GPL-3.0-only AND GPL-3.0-or-later AND LicenseRef-scancode-other-copyleft AND LicenseRef-scancode-public-domain ⚠️ | 1 | +| BSD-2-Clause AND BSD-3-Clause AND ISC AND MIT | 1 | +| BSD-2-Clause AND BSD-3-Clause AND MIT | 5 | +| BSD-2-Clause AND BSD-3-Clause AND Python-2.0 AND Ruby | 1 | +| BSD-2-Clause AND JSON | 1 | +| BSD-2-Clause AND MIT AND Python-2.0 AND Python-2.0.1 | 2 | +| BSD-2-Clause OR Apache-2.0 OR MIT | 8 | +| BSD-3-Clause | 224 | +| BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0 | 3 | +| BSD-3-Clause AND GPL-1.0-or-later ⚠️ | 3 | +| BSD-3-Clause AND LicenseRef-scancode-protobuf | 5 | +| BSD-3-Clause AND MIT | 4 | +| BSD-3-Clause AND Python-2.0 | 1 | +| BSD-3-Clause OR Apache-2.0 | 5 | +| BSD-3-Clause OR GPL-2.0-only ⚠️ | 1 | +| BSD-3-Clause OR MIT OR Apache-2.0 | 4 | +| BlueOak-1.0.0 | 42 | +| CC-BY-3.0 | 1 | +| CC-BY-4.0 | 5 | +| CC0-1.0 | 6 | +| CC0-1.0 AND MIT | 6 | +| CC0-1.0 AND Unlicense | 1 | +| CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception | 1 | +| CC0-1.0 OR MIT-0 OR Apache-2.0 | 3 | +| CDLA-Permissive-2.0 | 2 | +| CNRI-Python AND Apache-2.0 | 6 | +| EPL-2.0 ⚠️ | 5 | +| EPL-2.0 OR (Apache-2.0 AND EPL-2.0) ⚠️ | 2 | +| GPL-2.0-only AND GPL-2.0-or-later ⚠️ | 1 | +| GPL-3.0 AND GPL-3.0-only AND GPL-3.0-or-later ⚠️ | 1 | +| GPL-3.0-or-later ⚠️ | 6 | +| ISC | 175 | +| ISC AND MIT | 10 | +| ISC AND MPL-2.0 ⚠️ | 1 | +| JSON AND MIT | 1 | +| LGPL-2.1-or-later ⚠️ | 2 | +| LGPL-3.0 AND LGPL-3.0-only ⚠️ | 5 | +| LGPL-3.0 AND LGPL-3.0-only AND LGPL-3.0-or-later ⚠️ | 2 | +| LGPL-3.0-only ⚠️ | 2 | +| LGPL-3.0-or-later ⚠️ | 10 | +| LicenseRef-NVIDIA-Proprietary | 7 | +| LicenseRef-bad-apache-2.0mit | 3 | +| LicenseRef-bad-bsd-2-clause-and-apache-2.0-and-llvm-exception-and-bsd-2-clause | 1 | +| LicenseRef-bad-bsd-3-clausemit | 1 | +| LicenseRef-bad-non-standard | 2 | +| LicenseRef-github-OTHER | 1 | +| LicenseRef-scancode-public-domain AND Unlicense | 1 | +| LicenseRef-scancode-unicode AND MIT | 3 | +| LicenseRef-scancode-unknown | 4 | +| MIT | 3722 | +| MIT AND Apache-2.0 | 1 | +| MIT AND BSD-3-Clause | 1 | +| MIT AND MIT-0 | 1 | +| MIT AND MPL-2.0 ⚠️ | 9 | +| MIT AND PSF-2.0 | 5 | +| MIT AND Python-2.0 | 13 | +| MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause AND MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause | 2 | +| MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause | 1 | +| MIT AND ZPL-2.1 | 1 | +| MIT AND Zlib | 1 | +| MIT OR (Apache-2.0 AND MIT) | 1 | +| MIT OR (CC0-1.0 AND MIT) | 1 | +| MIT OR Apache-2.0 | 963 | +| MIT OR Apache-2.0 OR LGPL-2.1-or-later ⚠️ | 8 | +| MIT OR Apache-2.0 OR Zlib | 10 | +| MIT OR Zlib OR Apache-2.0 | 2 | +| MIT-0 | 19 | +| MIT-CMU | 6 | +| MPL-2.0 ⚠️ | 133 | +| MPL-2.0 AND MPL-1.1 ⚠️ | 2 | +| NOASSERTION ⚠️ | 495 | +| OFL-1.1 | 2 | +| PSF-2.0 | 11 | +| Python-2.0 | 3 | +| Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD ⚠️ | 18 | +| Python-2.0 AND LGPL-2.1-or-later ⚠️ | 1 | +| Python-2.0 AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | 1 | +| Unicode-3.0 | 54 | +| Unlicense | 2 | +| Unlicense AND Unlicense AND GPL-3.0-or-later AND MPL-2.0 AND MIT AND BSD-3-Clause AND Apache-2.0 AND MIT AND GPL-2.0-or-later AND BSD-3-Clause AND MIT AND LGPL-2.1-only AND BSD-2-Clause AND GPL-2.0-or-later ⚠️ | 1 | +| Unlicense OR MIT | 26 | +| Zlib | 5 | +| Zlib OR Apache-2.0 OR MIT | 46 | ## Flagged components (policy violations) -No copyleft or NOASSERTION components detected. +| Repository | Component | Version | License | +| --- | --- | --- | --- | +| ContextualWisdomLab/.github | actions/attest | v4.1.0 | NOASSERTION | +| ContextualWisdomLab/.github | actions/checkout | 11d5960a326750d5838078e36cf38b85af677262 | NOASSERTION | +| ContextualWisdomLab/.github | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/.github | actions/checkout | 631c942040754b6e095e929c1677c07e10ed4f87 | NOASSERTION | +| ContextualWisdomLab/.github | actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | +| ContextualWisdomLab/.github | actions/checkout | de0fac2e4500dabe0009e67214ff5f5447ce83dd | NOASSERTION | +| ContextualWisdomLab/.github | actions/checkout | fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 | NOASSERTION | +| ContextualWisdomLab/.github | actions/checkout | v7.0.0 | NOASSERTION | +| ContextualWisdomLab/.github | actions/checkout | v7.0.1 | NOASSERTION | +| ContextualWisdomLab/.github | actions/create-github-app-token | v3.2.0 | NOASSERTION | +| ContextualWisdomLab/.github | actions/dependency-review-action | v5.0.0 | NOASSERTION | +| ContextualWisdomLab/.github | actions/download-artifact | v8.0.1 | NOASSERTION | +| ContextualWisdomLab/.github | actions/setup-node | 48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e | NOASSERTION | +| ContextualWisdomLab/.github | actions/setup-node | 49933ea5288caeca8642d1e84afbd3f7d6820020 | NOASSERTION | +| ContextualWisdomLab/.github | actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | +| ContextualWisdomLab/.github | actions/setup-python | ece7cb06caefa5fff74198d8649806c4678c61a1 | NOASSERTION | +| ContextualWisdomLab/.github | actions/setup-python | v7.0.0 | NOASSERTION | +| ContextualWisdomLab/.github | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/.github | actions/upload-artifact | b7c566a772e6b6bfb58ed0dc250532a479d7789f | NOASSERTION | +| ContextualWisdomLab/.github | actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | +| ContextualWisdomLab/.github | actions/upload-artifact | v6.0.0 | NOASSERTION | +| ContextualWisdomLab/.github | actions/upload-artifact | v7.0.0 | NOASSERTION | +| ContextualWisdomLab/.github | actions/upload-artifact | v7.0.1 | NOASSERTION | +| ContextualWisdomLab/.github | aiohappyeyeballs | 2.7.1 | 0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0 | +| ContextualWisdomLab/.github | anchore/sbom-action | v0.24.0 | NOASSERTION | +| ContextualWisdomLab/.github | aquasecurity/trivy-action | v0.36.0 | NOASSERTION | +| ContextualWisdomLab/.github | astral-sh/setup-uv | 37802adc94f370d6bfd71619e3f0bf239e1f3b78 | NOASSERTION | +| ContextualWisdomLab/.github | astral-sh/setup-uv | bec219d24cd3e171d82865faccec33120bb574f4 | NOASSERTION | +| ContextualWisdomLab/.github | astral-sh/setup-uv | d0cc045d04ccac9d8b7881df0226f9e82c39688e | NOASSERTION | +| ContextualWisdomLab/.github | caido-server-auth | 0.1.2 | NOASSERTION | +| ContextualWisdomLab/.github | certifi | 2026.6.17 | MPL-2.0 | +| ContextualWisdomLab/.github | cloudflare/wrangler-action | v4.0.0 | NOASSERTION | +| ContextualWisdomLab/.github | contextualwisdomlab-opencode-codegraph-tooling | — | NOASSERTION | +| ContextualWisdomLab/.github | ContextualWisdomLab/.github/.github/workflows/release-dependency-license-strix-gate.yml | e249236e3f420c4e9c470f26925640dae59f0d93 | NOASSERTION | +| ContextualWisdomLab/.github | coverage-cargo-fixtures | 0.0.0 | NOASSERTION | +| ContextualWisdomLab/.github | cvss | 3.6 | LGPL-3.0 AND LGPL-3.0-only AND LGPL-3.0-or-later | +| ContextualWisdomLab/.github | dtolnay/rust-toolchain | 4be7066ada62dd38de10e7b70166bc74ed198c30 | NOASSERTION | +| ContextualWisdomLab/.github | github/codeql-action | v4.37.9 | NOASSERTION | +| ContextualWisdomLab/.github | github/codeql-action/analyze | ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd | NOASSERTION | +| ContextualWisdomLab/.github | github/codeql-action/init | ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd | NOASSERTION | +| ContextualWisdomLab/.github | google/clusterfuzzlite/actions/build_fuzzers | 884713a6c30a92e5e8544c39945cd7cb630abcd1 | NOASSERTION | +| ContextualWisdomLab/.github | google/clusterfuzzlite/actions/run_fuzzers | 884713a6c30a92e5e8544c39945cd7cb630abcd1 | NOASSERTION | +| ContextualWisdomLab/.github | google/osv-scanner-action | v2.3.8 | NOASSERTION | +| ContextualWisdomLab/.github | google/osv-scanner-action | v2.5.1 | NOASSERTION | +| ContextualWisdomLab/.github | grpcio | 1.81.1 | Apache-2.0 AND BSD-3-Clause AND MPL-2.0 | +| ContextualWisdomLab/.github | grpcio-status | 1.81.1 | Apache-2.0 AND BSD-3-Clause AND MPL-2.0 | +| ContextualWisdomLab/.github | hypothesis | 6.168.0 | MPL-2.0 | +| ContextualWisdomLab/.github | interrogate | — | NOASSERTION | +| ContextualWisdomLab/.github | jszip | 3.10.2 | (MIT OR GPL-3.0-or-later) | +| ContextualWisdomLab/.github | noema-document-reader-runtime | 1.0.0 | NOASSERTION | +| ContextualWisdomLab/.github | ossf/scorecard-action | v2.4.3 | NOASSERTION | +| ContextualWisdomLab/.github | PyO3/maturin-action | e83996d129638aa358a18fbd1dfb82f0b0fb5d3b | NOASSERTION | +| ContextualWisdomLab/.github | pypa/gh-action-pypi-publish | dc37677b2e1c63e2034f94d8a5b11f265b73ba33 | NOASSERTION | +| ContextualWisdomLab/.github | pytest | — | NOASSERTION | +| ContextualWisdomLab/.github | pytest-cov | — | NOASSERTION | +| ContextualWisdomLab/.github | r-lib/actions | 6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 | NOASSERTION | +| ContextualWisdomLab/.github | step-security/harden-runner | bf7454d06d71f1098171f2acdf0cd4708d7b5920 | NOASSERTION | +| ContextualWisdomLab/.github | step-security/harden-runner | v2.13.2 | NOASSERTION | +| ContextualWisdomLab/.github | step-security/harden-runner | v2.20.0 | NOASSERTION | +| ContextualWisdomLab/.github | step-security/harden-runner | v2.20.1 | NOASSERTION | +| ContextualWisdomLab/.github | tqdm | 4.68.3 | MIT AND MPL-2.0 | +| ContextualWisdomLab/.github | typing-extensions | 4.15.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/accounting-information-platform | accounting-information-platform | 0.1.0 | NOASSERTION | +| ContextualWisdomLab/accounting-information-platform | actions/attest | 508db95dd578ae2727ebd6217d5ba78e4fbda05d | NOASSERTION | +| ContextualWisdomLab/accounting-information-platform | actions/checkout | 631c942040754b6e095e929c1677c07e10ed4f87 | NOASSERTION | +| ContextualWisdomLab/accounting-information-platform | actions/download-artifact | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c | NOASSERTION | +| ContextualWisdomLab/accounting-information-platform | actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | +| ContextualWisdomLab/accounting-information-platform | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/accounting-information-platform | aquasecurity/trivy-action | a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 | NOASSERTION | +| ContextualWisdomLab/accounting-information-platform | psycopg | 3.3.4 | LGPL-3.0 AND LGPL-3.0-only | +| ContextualWisdomLab/accounting-information-platform | psycopg-binary | 3.3.4 | GPL-3.0-or-later | +| ContextualWisdomLab/aFIPC | actions/checkout | de0fac2e4500dabe0009e67214ff5f5447ce83dd | NOASSERTION | +| ContextualWisdomLab/aFIPC | r-lib/actions/check-r-package | 6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 | NOASSERTION | +| ContextualWisdomLab/aFIPC | r-lib/actions/setup-r | d3c5be51b12e724e68f33216ca3c148b66d5f0b6 | NOASSERTION | +| ContextualWisdomLab/aFIPC | r-lib/actions/setup-r-dependencies | d3c5be51b12e724e68f33216ca3c148b66d5f0b6 | NOASSERTION | +| ContextualWisdomLab/aFIPC | step-security/harden-runner | bf7454d06d71f1098171f2acdf0cd4708d7b5920 | NOASSERTION | +| ContextualWisdomLab/appguardrail | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/appguardrail | actions/create-github-app-token | bcd2ba49218906704ab6c1aa796996da409d3eb1 | NOASSERTION | +| ContextualWisdomLab/appguardrail | actions/download-artifact | 37930b1c2abaa49bbe596cd826c3c89aef350131 | NOASSERTION | +| ContextualWisdomLab/appguardrail | actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | +| ContextualWisdomLab/appguardrail | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/appguardrail | anomalyco/opencode/github | 77fc88c8ade8e5a620ebbe1197f3a572d29ae91a | NOASSERTION | +| ContextualWisdomLab/appguardrail | aquasecurity/trivy-action | ed142fd0673e97e23eac54620cfb913e5ce36c25 | NOASSERTION | +| ContextualWisdomLab/appguardrail | backports-tarfile | 1.2.0 | NOASSERTION | +| ContextualWisdomLab/appguardrail | certifi | 2026.6.17 | MPL-2.0 | +| ContextualWisdomLab/appguardrail | chardet | 5.2.0 | LGPL-2.1-or-later | +| ContextualWisdomLab/appguardrail | docutils | 0.23 | BSD-2-Clause AND BSD-3-Clause AND CC-PDDC AND GPL-1.0-or-later AND GPL-3.0-only AND GPL-3.0-or-later AND LicenseRef-scancode-other-copyleft AND LicenseRef-scancode-public-domain | +| ContextualWisdomLab/appguardrail | fqdn | 1.5.1 | MPL-2.0 | +| ContextualWisdomLab/appguardrail | github/codeql-action/upload-sarif | 5595ccaf912efad79be6eef63a5619ff05969be3 | NOASSERTION | +| ContextualWisdomLab/appguardrail | lark | 1.3.1 | MIT AND MPL-2.0 | +| ContextualWisdomLab/appguardrail | lxml | 6.1.1 | BSD-3-Clause AND GPL-1.0-or-later | +| ContextualWisdomLab/appguardrail | pypa/gh-action-pypi-publish | dc37677b2e1c63e2034f94d8a5b11f265b73ba33 | NOASSERTION | +| ContextualWisdomLab/appguardrail | rfc3987-syntax | 1.1.0 | Apache-2.0 AND GPL-1.0-or-later AND MIT | +| ContextualWisdomLab/appguardrail | setuptools | — | NOASSERTION | +| ContextualWisdomLab/appguardrail | typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/bandscope | @bandscope/desktop | 0.1.0 | NOASSERTION | +| ContextualWisdomLab/bandscope | @bandscope/shared-types | 0.1.0 | NOASSERTION | +| ContextualWisdomLab/bandscope | @tauri-apps/api | ^2.11.0 | NOASSERTION | +| ContextualWisdomLab/bandscope | @tauri-apps/cli | ^2.11.4 | NOASSERTION | +| ContextualWisdomLab/bandscope | actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | +| ContextualWisdomLab/bandscope | actions/download-artifact | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c | NOASSERTION | +| ContextualWisdomLab/bandscope | actions/setup-node | 48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e | NOASSERTION | +| ContextualWisdomLab/bandscope | actions/setup-python | a309ff8b426b58ec0e2a45f0f869d46889d02405 | NOASSERTION | +| ContextualWisdomLab/bandscope | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/bandscope | anchore/sbom-action | e22c389904149dbc22b58101806040fa8d37a610 | NOASSERTION | +| ContextualWisdomLab/bandscope | aquasecurity/trivy-action | ed142fd0673e97e23eac54620cfb913e5ce36c25 | NOASSERTION | +| ContextualWisdomLab/bandscope | astral-sh/setup-uv | 11f9893b081a58869d3b5fccaea48c9e9e46f990 | NOASSERTION | +| ContextualWisdomLab/bandscope | bandscope-analysis | 0.1.0 | NOASSERTION | +| ContextualWisdomLab/bandscope | bandscope-workspace | 0.1.0 | NOASSERTION | +| ContextualWisdomLab/bandscope | certifi | 2026.2.25 | MPL-2.0 | +| ContextualWisdomLab/bandscope | cssparser | 0.36.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | cssparser-macros | 0.6.1 | MPL-2.0 | +| ContextualWisdomLab/bandscope | dtoa-short | 0.3.5 | MPL-2.0 | +| ContextualWisdomLab/bandscope | eslint | ^10.7.0 | NOASSERTION | +| ContextualWisdomLab/bandscope | github/codeql-action/upload-sarif | 99df26d4f13ea111d4ec1a7dddef6063f76b97e9 | NOASSERTION | +| ContextualWisdomLab/bandscope | lameenc | 1.8.4 | GPL-3.0-or-later | +| ContextualWisdomLab/bandscope | lightningcss | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-android-arm64 | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-android-arm64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-darwin-arm64 | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-darwin-arm64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-darwin-x64 | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-darwin-x64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-freebsd-x64 | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-freebsd-x64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-linux-arm-gnueabihf | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-linux-arm-gnueabihf | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-linux-arm64-gnu | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-linux-arm64-gnu | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-linux-arm64-musl | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-linux-arm64-musl | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-linux-x64-gnu | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-linux-x64-gnu | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-linux-x64-musl | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-linux-x64-musl | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-win32-arm64-msvc | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-win32-arm64-msvc | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-win32-x64-msvc | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lightningcss-win32-x64-msvc | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | lucide-react | ^1.24.0 | NOASSERTION | +| ContextualWisdomLab/bandscope | maturin | — | NOASSERTION | +| ContextualWisdomLab/bandscope | option-ext | 0.2.0 | MPL-2.0 | +| ContextualWisdomLab/bandscope | ossf/scorecard-action | 4eaacf0543bb3f2c246792bd56e8cdeffafb205a | NOASSERTION | +| ContextualWisdomLab/bandscope | pathspec | 1.0.4 | MPL-2.0 | +| ContextualWisdomLab/bandscope | r-efi | 5.3.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | +| ContextualWisdomLab/bandscope | r-efi | 6.0.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | +| ContextualWisdomLab/bandscope | react | ^19.2.4 | NOASSERTION | +| ContextualWisdomLab/bandscope | react-dom | ^19.2.7 | NOASSERTION | +| ContextualWisdomLab/bandscope | selectors | 0.36.1 | MPL-2.0 | +| ContextualWisdomLab/bandscope | serde | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/bandscope | serde_json | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/bandscope | soxr | 1.0.0 | Python-2.0 AND LGPL-2.1-or-later | +| ContextualWisdomLab/bandscope | storybook | ^10.4.6 | NOASSERTION | +| ContextualWisdomLab/bandscope | time | >= 0.3.0,< 0.4.0 | NOASSERTION | +| ContextualWisdomLab/bandscope | tqdm | 4.68.3 | MIT AND MPL-2.0 | +| ContextualWisdomLab/bandscope | typing-extensions | 4.15.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/bandscope | url | >= 2.5.8,< 3.0.0 | NOASSERTION | +| ContextualWisdomLab/bandscope | uuid | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/bandscope | vite | ^8.1.4 | NOASSERTION | +| ContextualWisdomLab/bandscope | vitest | ^4.1.10 | NOASSERTION | +| ContextualWisdomLab/bandscope | yt-dlp | 2026.7.4 | Unlicense AND Unlicense AND GPL-3.0-or-later AND MPL-2.0 AND MIT AND BSD-3-Clause AND Apache-2.0 AND MIT AND GPL-2.0-or-later AND BSD-3-Clause AND MIT AND LGPL-2.1-only AND BSD-2-Clause AND GPL-2.0-or-later | +| ContextualWisdomLab/clearfolio | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/clearfolio | actions/setup-java | b6effb05e454b25005698d916606bdc6ffcbf961 | NOASSERTION | +| ContextualWisdomLab/clearfolio | actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | +| ContextualWisdomLab/clearfolio | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/clearfolio | org.apache.maven.plugins:maven-compiler-plugin | — | NOASSERTION | +| ContextualWisdomLab/clearfolio | org.apache.maven.plugins:maven-surefire-plugin | — | NOASSERTION | +| ContextualWisdomLab/clearfolio | org.jacoco:jacoco-maven-plugin | 0.8.15 | EPL-2.0 OR (Apache-2.0 AND EPL-2.0) | +| ContextualWisdomLab/clearfolio | org.springframework.boot:spring-boot-maven-plugin | — | NOASSERTION | +| ContextualWisdomLab/clearfolio | org.springframework.boot:spring-boot-starter-log4j2 | — | NOASSERTION | +| ContextualWisdomLab/clearfolio | org.springframework.boot:spring-boot-starter-test | — | NOASSERTION | +| ContextualWisdomLab/clearfolio | org.springframework.boot:spring-boot-starter-validation | — | NOASSERTION | +| ContextualWisdomLab/clearfolio | org.springframework.boot:spring-boot-starter-webflux | — | NOASSERTION | +| ContextualWisdomLab/codec-carver | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/codec-carver | actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | +| ContextualWisdomLab/codec-carver | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/codec-carver | aiofiles | — | NOASSERTION | +| ContextualWisdomLab/codec-carver | aiohappyeyeballs | 2.7.1 | 0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0 | +| ContextualWisdomLab/codec-carver | certifi | 2026.6.17 | MPL-2.0 | +| ContextualWisdomLab/codec-carver | coverage | — | NOASSERTION | +| ContextualWisdomLab/codec-carver | fastapi | — | NOASSERTION | +| ContextualWisdomLab/codec-carver | httpx | — | NOASSERTION | +| ContextualWisdomLab/codec-carver | hypothesis | — | NOASSERTION | +| ContextualWisdomLab/codec-carver | hypothesis | 6.165.0 | MPL-2.0 | +| ContextualWisdomLab/codec-carver | mcp | — | NOASSERTION | +| ContextualWisdomLab/codec-carver | python-multipart | — | NOASSERTION | +| ContextualWisdomLab/codec-carver | tqdm | 4.68.4 | MIT AND MPL-2.0 | +| ContextualWisdomLab/codec-carver | typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/codec-carver | uvicorn | — | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | actions/download-artifact | 634f93cb2916e3fdff6788551b99b062d0335ce0 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | actions/setup-node | 820762786026740c76f36085b0efc47a31fe5020 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | actions/setup-python | ece7cb06caefa5fff74198d8649806c4678c61a1 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | actions/upload-artifact | 330a01c490aca151604b8cf639adc76d48f6c5d4 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | astral-sh/setup-uv | 20cfd1bf945f4377ade1205e4dbc17946fc9a30d | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | axe-core | 4.13.0 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | certifi | 2026.6.17 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | certifi | 2026.7.22 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | chardet | 5.2.0 | LGPL-2.1-or-later | +| ContextualWisdomLab/contextual-orchestrator | contextual-orchestrator | 0.2.0 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | docker/setup-compose-action | 54042514f505b273907334ae2b9cdbb9a0213c1a | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | dtolnay/rust-toolchain | 6bed0761d98439e5a578e2877258200ad565ba87 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | egressweave | 0.1.0 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | fqdn | 1.5.1 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | github/codeql-action/analyze | b96794f015dfd88f77b49b1c93e0fa7110f94c63 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | github/codeql-action/init | b96794f015dfd88f77b49b1c93e0fa7110f94c63 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | hypothesis | 6.165.10 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lark | 1.3.1 | MIT AND MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lightningcss | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lightningcss-android-arm64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lightningcss-darwin-arm64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lightningcss-darwin-x64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lightningcss-freebsd-x64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lightningcss-linux-arm-gnueabihf | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lightningcss-linux-arm64-gnu | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lightningcss-linux-arm64-musl | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lightningcss-linux-x64-gnu | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lightningcss-linux-x64-musl | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lightningcss-win32-arm64-msvc | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lightningcss-win32-x64-msvc | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | lxml | 6.1.1 | BSD-3-Clause AND GPL-1.0-or-later | +| ContextualWisdomLab/contextual-orchestrator | maturin | — | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | opencode-darwin-arm64 | 1.18.22 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | opencode-darwin-x64 | 1.18.22 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | opencode-darwin-x64-baseline | 1.18.22 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | opencode-linux-arm64 | 1.18.22 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | opencode-linux-arm64-musl | 1.18.22 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | opencode-linux-x64 | 1.18.22 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | opencode-linux-x64-baseline | 1.18.22 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | opencode-linux-x64-baseline-musl | 1.18.22 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | opencode-linux-x64-musl | 1.18.22 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | opencode-windows-x64 | 1.18.22 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | opencode-windows-x64-baseline | 1.18.22 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | playwright | latest | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | psycopg | 3.3.4 | LGPL-3.0 AND LGPL-3.0-only | +| ContextualWisdomLab/contextual-orchestrator | psycopg-binary | 3.3.4 | GPL-3.0-or-later | +| ContextualWisdomLab/contextual-orchestrator | pyo3 | >= 0.29.0,< 0.30.0 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | rayon | >= 1.10.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | react | ^19.2.8 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | react-dom | ^19.2.8 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | rfc3987-syntax | 1.1.0 | Apache-2.0 AND GPL-1.0-or-later AND MIT | +| ContextualWisdomLab/contextual-orchestrator | serde | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | serde_json | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | storybook | ^10.5.10 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | tiktoken-rs | >= 0.7.0,< 0.8.0 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | tqdm | 4.70.0 | MIT AND MPL-2.0 | +| ContextualWisdomLab/contextual-orchestrator | typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/contextual-orchestrator | vite | ^8.2.2 | NOASSERTION | +| ContextualWisdomLab/contextual-orchestrator | vitest | latest | NOASSERTION | +| ContextualWisdomLab/ContextualWisdomLab.github.io | actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | +| ContextualWisdomLab/ContextualWisdomLab.github.io | github/codeql-action/analyze | cdf488f595d80d6e07e03d4674febd5ab45fa938 | NOASSERTION | +| ContextualWisdomLab/ContextualWisdomLab.github.io | github/codeql-action/init | cdf488f595d80d6e07e03d4674febd5ab45fa938 | NOASSERTION | +| ContextualWisdomLab/disksage | actions/attest | 1e69f48acb82d1966a394da916b4c1698aa569d6 | NOASSERTION | +| ContextualWisdomLab/disksage | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/disksage | actions/download-artifact | 37930b1c2abaa49bbe596cd826c3c89aef350131 | NOASSERTION | +| ContextualWisdomLab/disksage | actions/download-artifact | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c | NOASSERTION | +| ContextualWisdomLab/disksage | actions/setup-node | 820762786026740c76f36085b0efc47a31fe5020 | NOASSERTION | +| ContextualWisdomLab/disksage | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/disksage | cssparser | 0.36.0 | MPL-2.0 | +| ContextualWisdomLab/disksage | cssparser-macros | 0.6.1 | MPL-2.0 | +| ContextualWisdomLab/disksage | dtoa-short | 0.3.5 | MPL-2.0 | +| ContextualWisdomLab/disksage | dtolnay/rust-toolchain | 4be7066ada62dd38de10e7b70166bc74ed198c30 | NOASSERTION | +| ContextualWisdomLab/disksage | ilammy/msvc-dev-cmd | 0b201ec74fa43914dc39ae48a89fd1d8cb592756 | NOASSERTION | +| ContextualWisdomLab/disksage | jakoch/install-vulkan-sdk-action | 37effcfa045411f8bfbbda26df2fd1b3bf3436fa | NOASSERTION | +| ContextualWisdomLab/disksage | Jimver/cuda-toolkit | b8bf9c6c28f8a92fbb04dcfcaee872e60c57462d | NOASSERTION | +| ContextualWisdomLab/disksage | mlsirm-core | 0.7.0 | NOASSERTION | +| ContextualWisdomLab/disksage | option-ext | 0.2.0 | MPL-2.0 | +| ContextualWisdomLab/disksage | r-efi | 5.3.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | +| ContextualWisdomLab/disksage | r-efi | 6.0.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | +| ContextualWisdomLab/disksage | selectors | 0.36.1 | MPL-2.0 | +| ContextualWisdomLab/disksage | softprops/action-gh-release | 3d0d9888cb7fd7b750713d6e236d1fcb99157228 | NOASSERTION | +| ContextualWisdomLab/disksage | Swatinem/rust-cache | 6323deb102c322ba6fcbdcafc7e3dddab59af2b6 | NOASSERTION | +| ContextualWisdomLab/EgressWeave | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/EgressWeave | actions/download-artifact | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c | NOASSERTION | +| ContextualWisdomLab/EgressWeave | actions/download-artifact | d3f86a106a0bac45b974a628896c90dbdf5c8093 | NOASSERTION | +| ContextualWisdomLab/EgressWeave | actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | +| ContextualWisdomLab/EgressWeave | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/EgressWeave | actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | +| ContextualWisdomLab/EgressWeave | backports-asyncio-runner | 1.2.0 | NOASSERTION | +| ContextualWisdomLab/EgressWeave | certifi | 2026.7.22 | MPL-2.0 | +| ContextualWisdomLab/EgressWeave | coverage | — | NOASSERTION | +| ContextualWisdomLab/EgressWeave | hatchling | — | NOASSERTION | +| ContextualWisdomLab/EgressWeave | idna | — | NOASSERTION | +| ContextualWisdomLab/EgressWeave | pathspec | — | NOASSERTION | +| ContextualWisdomLab/EgressWeave | pypa/gh-action-pypi-publish | dc37677b2e1c63e2034f94d8a5b11f265b73ba33 | NOASSERTION | +| ContextualWisdomLab/EgressWeave | pytest | — | NOASSERTION | +| ContextualWisdomLab/EgressWeave | pytest-asyncio | — | NOASSERTION | +| ContextualWisdomLab/EgressWeave | step-security/harden-runner | bf7454d06d71f1098171f2acdf0cd4708d7b5920 | NOASSERTION | +| ContextualWisdomLab/EgressWeave | trove-classifiers | — | NOASSERTION | +| ContextualWisdomLab/EgressWeave | typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/fast-mlsirm | bytemuck | >= 1.25.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-cflite_pr.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-ci.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-codeql.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-hourly-pr-governance.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-publish-pypi.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-pypi-gap-guard.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-release-tag.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-statistical-studies.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | hypothesis | 6.156.6 | MPL-2.0 AND MPL-1.1 | +| ContextualWisdomLab/fast-mlsirm | hypothesis | 6.168.0 | MPL-2.0 | +| ContextualWisdomLab/fast-mlsirm | maturin | — | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | numpy | — | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | pollster | >= 1.0.1,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | proptest | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | pytest | — | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | r-efi | 5.3.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | +| ContextualWisdomLab/fast-mlsirm | r-efi | 6.0.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | +| ContextualWisdomLab/fast-mlsirm | serde | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | serde_json | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | sha2 | >= 0.10.9,< 0.11.0 | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | time | >= 0.3.0,< 0.4.0 | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | uuid | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/fast-mlsirm | wgpu | >= 30.0.0,< 31.0.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | axum | >= 0.7.0,< 0.8.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | base64 | >= 0.22.0,< 0.23.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | cryptography | — | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | ext-curl | >= 0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | ext-dom | >= 0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | ext-mcrypt | >= 0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | ext-openssl | >= 0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | fastapi | — | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | flate2 | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | form_urlencoded | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | httpx | — | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | itsdangerous | — | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | jinja2 | — | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | jsonwebtoken | >= 10.3.0,< 11.0.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | lxml | — | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | Microsoft.AspNet.Mvc | 5.2.9 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | Microsoft.AspNet.Mvc.ko | 5.2.9 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | Microsoft.AspNet.WebPages | 3.2.9 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | pem | >= 3.0.0,< 4.0.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | php | >= 5.3.2 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | phploc/phploc | >= 0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | pyjwt | — | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | pytest | — | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | python-multipart | — | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | rand | >= 0.8.0,< 0.9.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | reqwest | >= 0.12.0,< 0.13.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | roxmltree | >= 0.20.0,< 0.21.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | rsa | >= 0.9.0,< 0.10.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | rust-xmlsec | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | sebastian/phpcpd | >= 0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | serde | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | serde_json | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | sha2 | >= 0.10.0,< 0.11.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | signxml | — | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | System.IdentityModel.Tokens.Jwt | 5.4.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | thiserror | >= 2.0.0,< 3.0.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | time | >= 0.3.0,< 0.4.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | tokio | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | tower-sessions | >= 0.13.0,< 0.14.0 | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | uvicorn | — | NOASSERTION | +| ContextualWisdomLab/feelanet-adfs | x509-parser | >= 0.16.0,< 0.17.0 | NOASSERTION | +| ContextualWisdomLab/four-pillars | actions/checkout | 11d5960a326750d5838078e36cf38b85af677262 | NOASSERTION | +| ContextualWisdomLab/four-pillars | actions/create-github-app-token | bcd2ba49218906704ab6c1aa796996da409d3eb1 | NOASSERTION | +| ContextualWisdomLab/four-pillars | actions/download-artifact | d3f86a106a0bac45b974a628896c90dbdf5c8093 | NOASSERTION | +| ContextualWisdomLab/four-pillars | actions/setup-python | a26af69be951a213d495a4c3e4e4022e16d87065 | NOASSERTION | +| ContextualWisdomLab/four-pillars | actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | +| ContextualWisdomLab/four-pillars | certifi | 2026.7.22 | MPL-2.0 | +| ContextualWisdomLab/four-pillars | fastapi | >= 0.115,< 1 | NOASSERTION | +| ContextualWisdomLab/four-pillars | httpx | >= 0.27,< 1 | NOASSERTION | +| ContextualWisdomLab/four-pillars | jinja2 | >= 3.1,< 4 | NOASSERTION | +| ContextualWisdomLab/four-pillars | korean-lunar-calendar | >= 0.3.1,< 1 | NOASSERTION | +| ContextualWisdomLab/four-pillars | pathspec | 1.1.1 | MPL-2.0 | +| ContextualWisdomLab/four-pillars | pydantic | >= 2.10,< 3 | NOASSERTION | +| ContextualWisdomLab/four-pillars | pydantic-settings | >= 2.7,< 3 | NOASSERTION | +| ContextualWisdomLab/four-pillars | python-multipart | >= 0.0.20,< 1 | NOASSERTION | +| ContextualWisdomLab/four-pillars | reportlab | >= 4.2,< 5 | NOASSERTION | +| ContextualWisdomLab/four-pillars | typer | >= 0.15,< 1 | NOASSERTION | +| ContextualWisdomLab/four-pillars | typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/four-pillars | uvicorn | — | NOASSERTION | +| ContextualWisdomLab/gyeot | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/gyeot | actions/setup-node | 249970729cb0ef3589644e2896645e5dc5ba9c38 | NOASSERTION | +| ContextualWisdomLab/gyeot | actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | +| ContextualWisdomLab/gyeot | lightningcss | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/gyeot | lightningcss-android-arm64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/gyeot | lightningcss-darwin-arm64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/gyeot | lightningcss-darwin-x64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/gyeot | lightningcss-freebsd-x64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/gyeot | lightningcss-linux-arm-gnueabihf | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/gyeot | lightningcss-linux-arm64-gnu | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/gyeot | lightningcss-linux-arm64-musl | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/gyeot | lightningcss-linux-x64-gnu | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/gyeot | lightningcss-linux-x64-musl | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/gyeot | lightningcss-win32-arm64-msvc | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/gyeot | lightningcss-win32-x64-msvc | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/gyeot | node-forge | 1.4.0 | BSD-3-Clause OR GPL-2.0-only | +| ContextualWisdomLab/hyosung-itx-slogan-brief | actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | +| ContextualWisdomLab/hyosung-itx-slogan-brief | actions/setup-node | 49933ea5288caeca8642d1e84afbd3f7d6820020 | NOASSERTION | +| ContextualWisdomLab/inkspan | actions/attest | 59d89421af93a897026c735860bf21b6eb4f7b26 | NOASSERTION | +| ContextualWisdomLab/inkspan | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/inkspan | actions/download-artifact | 37930b1c2abaa49bbe596cd826c3c89aef350131 | NOASSERTION | +| ContextualWisdomLab/inkspan | actions/setup-node | 820762786026740c76f36085b0efc47a31fe5020 | NOASSERTION | +| ContextualWisdomLab/inkspan | actions/setup-python | a26af69be951a213d495a4c3e4e4022e16d87065 | NOASSERTION | +| ContextualWisdomLab/inkspan | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/inkspan | lxml | 6.1.0 | BSD-3-Clause AND GPL-1.0-or-later | +| ContextualWisdomLab/inkspan | openpyxl | >= 3.1.5,< 4 | NOASSERTION | +| ContextualWisdomLab/inkspan | pnpm/action-setup | 0977fd99725f1db4007ccb2928dbb4e90d06cc86 | NOASSERTION | +| ContextualWisdomLab/inkspan | pypa/gh-action-pypi-publish | dc37677b2e1c63e2034f94d8a5b11f265b73ba33 | NOASSERTION | +| ContextualWisdomLab/inkspan | python-docx | >= 1.2.0,< 2 | NOASSERTION | +| ContextualWisdomLab/inkspan | python-pptx | >= 1.0.2,< 2 | NOASSERTION | +| ContextualWisdomLab/inkspan | sigstore/cosign-installer | 6f9f17788090df1f26f669e9d70d6ae9567deba6 | NOASSERTION | +| ContextualWisdomLab/inkspan | typing-extensions | 4.15.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/k-csap-skills | actions/checkout | 4.*.* | NOASSERTION | +| ContextualWisdomLab/k-csap-skills | astral-sh/setup-uv | 6.*.* | NOASSERTION | +| ContextualWisdomLab/kaefa | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/kaefa | r-lib/actions/check-r-package | 6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 | NOASSERTION | +| ContextualWisdomLab/kaefa | r-lib/actions/setup-pandoc | d3c5be51b12e724e68f33216ca3c148b66d5f0b6 | NOASSERTION | +| ContextualWisdomLab/kaefa | r-lib/actions/setup-r | 6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 | NOASSERTION | +| ContextualWisdomLab/kaefa | r-lib/actions/setup-r-dependencies | 6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 | NOASSERTION | +| ContextualWisdomLab/keyverse | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/keyverse | actions/download-artifact | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c | NOASSERTION | +| ContextualWisdomLab/keyverse | actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | +| ContextualWisdomLab/keyverse | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/keyverse | astral-sh/setup-uv | 20cfd1bf945f4377ade1205e4dbc17946fc9a30d | NOASSERTION | +| ContextualWisdomLab/keyverse | certifi | 2026.7.22 | MPL-2.0 | +| ContextualWisdomLab/keyverse | cwl-idp-account-unification | 0.1.0 | NOASSERTION | +| ContextualWisdomLab/keyverse | step-security/harden-runner | 05e31511f85b41b11d1cf0ef85d0992719546e2c | NOASSERTION | +| ContextualWisdomLab/keyverse | typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/late-life-anxiety-reanalysis | fast-mlsirm | — | NOASSERTION | +| ContextualWisdomLab/law-ai-agent | ContextualWisdomLab/.github/.github/workflows/law-ai-agent-ci.yml | 8dff0538c55b99df3f6f19b25a06633fc72b38ef | NOASSERTION | +| ContextualWisdomLab/law-ai-agent | pathspec | 1.1.1 | MPL-2.0 | +| ContextualWisdomLab/law-ai-agent | psycopg | 3.3.6 | LGPL-3.0-only | +| ContextualWisdomLab/law-ai-agent | psycopg-binary | 3.3.6 | LGPL-3.0-only | +| ContextualWisdomLab/law-ai-agent | typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/linux-cluster-ops | actions/attest-build-provenance | a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 | NOASSERTION | +| ContextualWisdomLab/linux-cluster-ops | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/linux-cluster-ops | actions/setup-python | ece7cb06caefa5fff74198d8649806c4678c61a1 | NOASSERTION | +| ContextualWisdomLab/linux-cluster-ops | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/mcp-shared-gateway | actions/checkout | 11bd71901bbe5b1630ceea73d27597364c9af683 | NOASSERTION | +| ContextualWisdomLab/mcp-shared-gateway | actions/setup-node | 49933ea5288caeca8642d1e84afbd3f7d6820020 | NOASSERTION | +| ContextualWisdomLab/mcp-shared-gateway | actions/setup-python | a26af69be951a213d495a4c3e4e4022e16d87065 | NOASSERTION | +| ContextualWisdomLab/mightyETL | actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | +| ContextualWisdomLab/mightyETL | actions/dependency-review-action | a1d282b36b6f3519aa1f3fc636f609c47dddb294 | NOASSERTION | +| ContextualWisdomLab/mightyETL | actions/setup-java | 1bcf9fb12cf4aa7d266a90ae39939e61372fe520 | NOASSERTION | +| ContextualWisdomLab/mightyETL | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/mightyETL | com.fasterxml.jackson.core:jackson-databind | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | com.h2database:h2 | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | github/codeql-action/analyze | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | +| ContextualWisdomLab/mightyETL | github/codeql-action/autobuild | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | +| ContextualWisdomLab/mightyETL | github/codeql-action/init | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | +| ContextualWisdomLab/mightyETL | github/codeql-action/upload-sarif | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | +| ContextualWisdomLab/mightyETL | io.micrometer:micrometer-tracing-bridge-brave | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | io.zipkin.reporter2:zipkin-reporter-brave | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.apache.maven.plugins:maven-dependency-plugin | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.flywaydb:flyway-core | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.flywaydb:flyway-database-postgresql | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.jacoco:jacoco-maven-plugin | 0.8.15 | EPL-2.0 OR (Apache-2.0 AND EPL-2.0) | +| ContextualWisdomLab/mightyETL | org.junit.jupiter:junit-jupiter-api | 5.12.2 | EPL-2.0 | +| ContextualWisdomLab/mightyETL | org.junit.jupiter:junit-jupiter-engine | 5.12.2 | EPL-2.0 | +| ContextualWisdomLab/mightyETL | org.junit.platform:junit-platform-commons | 1.12.2 | EPL-2.0 | +| ContextualWisdomLab/mightyETL | org.junit.platform:junit-platform-engine | 1.12.2 | EPL-2.0 | +| ContextualWisdomLab/mightyETL | org.junit.platform:junit-platform-launcher | 1.12.2 | EPL-2.0 | +| ContextualWisdomLab/mightyETL | org.postgresql:postgresql | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.boot:spring-boot-configuration-processor | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.boot:spring-boot-maven-plugin | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.boot:spring-boot-starter-actuator | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.boot:spring-boot-starter-aop | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.boot:spring-boot-starter-data-jpa | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.boot:spring-boot-starter-security | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.boot:spring-boot-starter-test | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.boot:spring-boot-starter-web | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.cloud:spring-cloud-config-server | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.cloud:spring-cloud-starter-gateway | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.cloud:spring-cloud-starter-netflix-eureka-client | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.cloud:spring-cloud-starter-netflix-eureka-server | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.kafka:spring-kafka | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.retry:spring-retry | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | org.springframework.security:spring-security-test | — | NOASSERTION | +| ContextualWisdomLab/mightyETL | ossf/scorecard-action | 4eaacf0543bb3f2c246792bd56e8cdeffafb205a | NOASSERTION | +| ContextualWisdomLab/naruon | @img/sharp-libvips-darwin-arm64 | 1.3.0 | LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | @img/sharp-libvips-darwin-x64 | 1.3.0 | LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | @img/sharp-libvips-linux-arm | 1.3.0 | LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | @img/sharp-libvips-linux-arm64 | 1.3.0 | LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | @img/sharp-libvips-linux-ppc64 | 1.3.0 | LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | @img/sharp-libvips-linux-riscv64 | 1.3.0 | LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | @img/sharp-libvips-linux-s390x | 1.3.0 | LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | @img/sharp-libvips-linux-x64 | 1.3.0 | LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | @img/sharp-libvips-linuxmusl-arm64 | 1.3.0 | LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | @img/sharp-libvips-linuxmusl-x64 | 1.3.0 | LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | @img/sharp-wasm32 | 0.35.0 | Apache-2.0 AND LGPL-3.0-or-later AND MIT | +| ContextualWisdomLab/naruon | @img/sharp-win32-arm64 | 0.35.0 | Apache-2.0 AND LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | @img/sharp-win32-ia32 | 0.35.0 | Apache-2.0 AND LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | @img/sharp-win32-x64 | 0.35.0 | Apache-2.0 AND LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | actions/cache | 55cc8345863c7cc4c66a329aec7e433d2d1c52a9 | NOASSERTION | +| ContextualWisdomLab/naruon | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/naruon | actions/dependency-review-action | a1d282b36b6f3519aa1f3fc636f609c47dddb294 | NOASSERTION | +| ContextualWisdomLab/naruon | actions/setup-node | 820762786026740c76f36085b0efc47a31fe5020 | NOASSERTION | +| ContextualWisdomLab/naruon | actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | +| ContextualWisdomLab/naruon | aiohappyeyeballs | 2.7.1 | 0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0 | +| ContextualWisdomLab/naruon | aioimaplib | 2.0.1 | GPL-3.0 AND GPL-3.0-only AND GPL-3.0-or-later | +| ContextualWisdomLab/naruon | axe-core | 4.12.1 | MPL-2.0 | +| ContextualWisdomLab/naruon | caido-server-auth | 0.1.2 | NOASSERTION | +| ContextualWisdomLab/naruon | certifi | 2026.6.17 | MPL-2.0 | +| ContextualWisdomLab/naruon | cvss | 3.6 | LGPL-3.0 AND LGPL-3.0-only AND LGPL-3.0-or-later | +| ContextualWisdomLab/naruon | dnspython | 2.8.0 | ISC AND MPL-2.0 | +| ContextualWisdomLab/naruon | docker/build-push-action | 53b7df96c91f9c12dcc8a07bcb9ccacbed38856a | NOASSERTION | +| ContextualWisdomLab/naruon | docker/login-action | dbcb813823bdd20940b903addbd779551569679f | NOASSERTION | +| ContextualWisdomLab/naruon | docker/metadata-action | dc802804100637a589fabce1cb79ff13a1411302 | NOASSERTION | +| ContextualWisdomLab/naruon | docker/setup-buildx-action | bb05f3f5519dd87d3ba754cc423b652a5edd6d2c | NOASSERTION | +| ContextualWisdomLab/naruon | docker/setup-qemu-action | 96fe6ef7f33517b61c61be40b68a1882f3264fb8 | NOASSERTION | +| ContextualWisdomLab/naruon | github/codeql-action/upload-sarif | f205ea1c3313d32999d8d6a48b4f6530d4437b38 | NOASSERTION | +| ContextualWisdomLab/naruon | grpcio | 1.81.1 | Apache-2.0 AND BSD-3-Clause AND MPL-2.0 | +| ContextualWisdomLab/naruon | grpcio | 1.82.1 | Apache-2.0 AND BSD-3-Clause AND MPL-2.0 | +| ContextualWisdomLab/naruon | grpcio-status | 1.81.1 | Apache-2.0 AND BSD-3-Clause AND MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-android-arm64 | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-android-arm64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-darwin-arm64 | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-darwin-arm64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-darwin-x64 | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-darwin-x64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-freebsd-x64 | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-freebsd-x64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-linux-arm-gnueabihf | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-linux-arm-gnueabihf | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-linux-arm64-gnu | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-linux-arm64-gnu | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-linux-arm64-musl | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-linux-arm64-musl | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-linux-x64-gnu | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-linux-x64-gnu | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-linux-x64-musl | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-linux-x64-musl | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-win32-arm64-msvc | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-win32-arm64-msvc | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-win32-x64-msvc | 1.32.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | lightningcss-win32-x64-msvc | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/naruon | naruon-backend | 0.14.4 | NOASSERTION | +| ContextualWisdomLab/naruon | orjson | 3.11.9 | Apache-2.0 AND MIT AND MPL-2.0 | +| ContextualWisdomLab/naruon | rankweave | 0.1.0 | NOASSERTION | +| ContextualWisdomLab/naruon | step-security/harden-runner | bf7454d06d71f1098171f2acdf0cd4708d7b5920 | NOASSERTION | +| ContextualWisdomLab/naruon | tqdm | 4.68.3 | MIT AND MPL-2.0 | +| ContextualWisdomLab/naruon | tqdm | 4.68.4 | MIT AND MPL-2.0 | +| ContextualWisdomLab/naruon | typing-extensions | 4.15.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/naruon | typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/newsdom-api | actions/attest-build-provenance | 0f67c3f4856b2e3261c31976d6725780e5e4c373 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | actions/dependency-review-action | a1d282b36b6f3519aa1f3fc636f609c47dddb294 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | actions/deploy-pages | cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/newsdom-api | astral-sh/setup-uv | c771a70e6277c0a99b617c7a806ffedaca235ff9 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | backports-asyncio-runner | 1.2.0 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | certifi | 2026.2.25 | MPL-2.0 | +| ContextualWisdomLab/newsdom-api | docker/build-push-action | 53b7df96c91f9c12dcc8a07bcb9ccacbed38856a | NOASSERTION | +| ContextualWisdomLab/newsdom-api | docker/login-action | dbcb813823bdd20940b903addbd779551569679f | NOASSERTION | +| ContextualWisdomLab/newsdom-api | docker/metadata-action | dc802804100637a589fabce1cb79ff13a1411302 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | docker/setup-buildx-action | bb05f3f5519dd87d3ba754cc423b652a5edd6d2c | NOASSERTION | +| ContextualWisdomLab/newsdom-api | docker/setup-qemu-action | 96fe6ef7f33517b61c61be40b68a1882f3264fb8 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | github/codeql-action/analyze | f205ea1c3313d32999d8d6a48b4f6530d4437b38 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | github/codeql-action/autobuild | f205ea1c3313d32999d8d6a48b4f6530d4437b38 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | github/codeql-action/init | f205ea1c3313d32999d8d6a48b4f6530d4437b38 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | github/codeql-action/upload-sarif | f205ea1c3313d32999d8d6a48b4f6530d4437b38 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | google/clusterfuzzlite/actions/build_fuzzers | 52ecc61cb587ee99c26825a112a21abf19c7448c | NOASSERTION | +| ContextualWisdomLab/newsdom-api | google/clusterfuzzlite/actions/run_fuzzers | 52ecc61cb587ee99c26825a112a21abf19c7448c | NOASSERTION | +| ContextualWisdomLab/newsdom-api | newsdom-api | 0.2.0 | NOASSERTION | +| ContextualWisdomLab/newsdom-api | ossf/scorecard-action | 2d1146689b8cda280b9bc96326124645441f03bc | NOASSERTION | +| ContextualWisdomLab/newsdom-api | pathspec | 1.0.4 | MPL-2.0 | +| ContextualWisdomLab/newsdom-api | pyinstaller | 6.21.0 | GPL-2.0-only AND GPL-2.0-or-later | +| ContextualWisdomLab/newsdom-api | pyinstaller-hooks-contrib | 2026.6 | Apache-2.0 AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later | +| ContextualWisdomLab/newsdom-api | typing-extensions | 4.15.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/noema | actions/attest | 59d89421af93a897026c735860bf21b6eb4f7b26 | NOASSERTION | +| ContextualWisdomLab/noema | actions/checkout | 11bd71901bbe5b1630ceea73d27597364c9af683 | NOASSERTION | +| ContextualWisdomLab/noema | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/noema | actions/checkout | de0fac2e4500dabe0009e67214ff5f5447ce83dd | NOASSERTION | +| ContextualWisdomLab/noema | actions/create-github-app-token | bcd2ba49218906704ab6c1aa796996da409d3eb1 | NOASSERTION | +| ContextualWisdomLab/noema | actions/download-artifact | d3f86a106a0bac45b974a628896c90dbdf5c8093 | NOASSERTION | +| ContextualWisdomLab/noema | actions/setup-node | 48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e | NOASSERTION | +| ContextualWisdomLab/noema | actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | +| ContextualWisdomLab/noema | actions/setup-python | a26af69be951a213d495a4c3e4e4022e16d87065 | NOASSERTION | +| ContextualWisdomLab/noema | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/noema | aquasecurity/setup-trivy | 81e514348e19b6112ce2a7e3ecbafe19c1e1f567 | NOASSERTION | +| ContextualWisdomLab/noema | certifi | 2026.6.17 | MPL-2.0 | +| ContextualWisdomLab/noema | docker/build-push-action | d08e5c354a6adb9ed34480a06d141179aa583294 | NOASSERTION | +| ContextualWisdomLab/noema | docker/setup-buildx-action | 37fe631027851001ddb9b187196cc803df7f5f0e | NOASSERTION | +| ContextualWisdomLab/noema | interrogate | — | NOASSERTION | +| ContextualWisdomLab/noema | lightningcss | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/noema | lightningcss-android-arm64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/noema | lightningcss-darwin-arm64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/noema | lightningcss-darwin-x64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/noema | lightningcss-freebsd-x64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/noema | lightningcss-linux-arm-gnueabihf | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/noema | lightningcss-linux-arm64-gnu | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/noema | lightningcss-linux-arm64-musl | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/noema | lightningcss-linux-x64-gnu | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/noema | lightningcss-linux-x64-musl | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/noema | lightningcss-win32-arm64-msvc | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/noema | lightningcss-win32-x64-msvc | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/noema | pydantic | — | NOASSERTION | +| ContextualWisdomLab/noema | pydantic-ai-slim | — | NOASSERTION | +| ContextualWisdomLab/noema | pytest | — | NOASSERTION | +| ContextualWisdomLab/noema | pytest-cov | — | NOASSERTION | +| ContextualWisdomLab/noema | setuptools | — | NOASSERTION | +| ContextualWisdomLab/noema | sigstore/cosign-installer | 6f9f17788090df1f26f669e9d70d6ae9567deba6 | NOASSERTION | +| ContextualWisdomLab/noema | tqdm | 4.68.4 | MIT AND MPL-2.0 | +| ContextualWisdomLab/noema | typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/OriginWeave | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/OriginWeave | actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | +| ContextualWisdomLab/OriginWeave | dtolnay/rust-toolchain | 4be7066ada62dd38de10e7b70166bc74ed198c30 | NOASSERTION | +| ContextualWisdomLab/OriginWeave | step-security/harden-runner | bf7454d06d71f1098171f2acdf0cd4708d7b5920 | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | actions/setup-node | 48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | actions/setup-python | ece7cb06caefa5fff74198d8649806c4678c61a1 | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | aiohttp | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | alembic | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | asyncpg | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | asyncpg-stubs | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | cryptography | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | fastapi | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | github/codeql-action/analyze | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | github/codeql-action/autobuild | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | github/codeql-action/init | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | httpx | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | hypercorn | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | lightningcss | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/pg-erd-cloud | lightningcss-android-arm64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/pg-erd-cloud | lightningcss-darwin-arm64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/pg-erd-cloud | lightningcss-darwin-x64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/pg-erd-cloud | lightningcss-freebsd-x64 | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/pg-erd-cloud | lightningcss-linux-arm-gnueabihf | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/pg-erd-cloud | lightningcss-linux-arm64-gnu | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/pg-erd-cloud | lightningcss-linux-arm64-musl | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/pg-erd-cloud | lightningcss-linux-x64-gnu | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/pg-erd-cloud | lightningcss-linux-x64-musl | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/pg-erd-cloud | lightningcss-win32-arm64-msvc | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/pg-erd-cloud | lightningcss-win32-x64-msvc | 1.33.0 | MPL-2.0 | +| ContextualWisdomLab/pg-erd-cloud | mypy | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | prometheus-client | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | psycopg | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | pydantic | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | pydantic-settings | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | pyjwt | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | pytest | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | pytest-asyncio | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | pytest-cov | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | python-jose | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | python-multipart | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | redis | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | requests | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | setuptools | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | snowflake-connector-python | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | sqlalchemy | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | starlette | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | step-security/harden-runner | b09bb98e06d4d774595224525879c09bc6e98c40 | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | types-python-jose | — | NOASSERTION | +| ContextualWisdomLab/pg-erd-cloud | urllib3 | — | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | aiohappyeyeballs | 2.7.1 | 0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0 | +| ContextualWisdomLab/pg-llm-batch | aiohttp | — | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | astral-sh/setup-uv | c771a70e6277c0a99b617c7a806ffedaca235ff9 | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | backports-asyncio-runner | 1.2.0 | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | cryptography | — | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | pg-llm-batch | 0.1.0 | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | psycopg | — | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | psycopg | 3.3.4 | LGPL-3.0 AND LGPL-3.0-only | +| ContextualWisdomLab/pg-llm-batch | psycopg-binary | 3.3.4 | GPL-3.0-or-later | +| ContextualWisdomLab/pg-llm-batch | pytest | — | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | pytest-asyncio | — | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | setuptools | — | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | step-security/harden-runner | b09bb98e06d4d774595224525879c09bc6e98c40 | NOASSERTION | +| ContextualWisdomLab/pg-llm-batch | typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/procurement-workbench | certifi | 2026.7.22 | MPL-2.0 | +| ContextualWisdomLab/procurement-workbench | typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/scopeweave | actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | +| ContextualWisdomLab/scopeweave | actions/configure-pages | 45bfe0192ca1faeb007ade9deae92b16b8254a0d | NOASSERTION | +| ContextualWisdomLab/scopeweave | actions/dependency-review-action | a1d282b36b6f3519aa1f3fc636f609c47dddb294 | NOASSERTION | +| ContextualWisdomLab/scopeweave | actions/deploy-pages | cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 | NOASSERTION | +| ContextualWisdomLab/scopeweave | actions/setup-node | 2028fbc5c25fe9cf00d9f06a71cc4710d4507903 | NOASSERTION | +| ContextualWisdomLab/scopeweave | actions/setup-node | 39370e3970a6d050c480ffad4ff0ed4d3fdee5af | NOASSERTION | +| ContextualWisdomLab/scopeweave | actions/upload-pages-artifact | fc324d3547104276b827a68afc52ff2a11cc49c9 | NOASSERTION | +| ContextualWisdomLab/scopeweave | github/codeql-action/analyze | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | +| ContextualWisdomLab/scopeweave | github/codeql-action/init | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | +| ContextualWisdomLab/scopeweave | google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml | 3a7550f43ba5b58905a821ce3a0ed24c4858b3f4 | NOASSERTION | +| ContextualWisdomLab/semantic-data-portal | actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | +| ContextualWisdomLab/semantic-data-portal | actions/setup-python | ece7cb06caefa5fff74198d8649806c4678c61a1 | NOASSERTION | +| ContextualWisdomLab/semantic-data-portal | actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | +| ContextualWisdomLab/semantic-data-portal | certifi | 2026.6.17 | MPL-2.0 | +| ContextualWisdomLab/semantic-data-portal | github/codeql-action/upload-sarif | 54f647b7e1bb85c95cddabcd46b0c578ec92bc1a | NOASSERTION | +| ContextualWisdomLab/semantic-data-portal | hypothesis | — | NOASSERTION | +| ContextualWisdomLab/semantic-data-portal | hypothesis | 6.156.6 | MPL-2.0 AND MPL-1.1 | +| ContextualWisdomLab/semantic-data-portal | ossf/scorecard-action | 4eaacf0543bb3f2c246792bd56e8cdeffafb205a | NOASSERTION | +| ContextualWisdomLab/semantic-data-portal | psycopg | 3.3.4 | LGPL-3.0 AND LGPL-3.0-only | +| ContextualWisdomLab/semantic-data-portal | psycopg-binary | 3.3.4 | GPL-3.0-or-later | +| ContextualWisdomLab/semantic-data-portal | sqlalchemy | — | NOASSERTION | +| ContextualWisdomLab/semantic-data-portal | typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/tokyo-travel-2026 | pymupdf | 1.28.2 | AGPL-3.0 AND AGPL-3.0-only AND AGPL-3.0-or-later | +| ContextualWisdomLab/traveling-agent | ContextualWisdomLab/.github/.github/workflows/traveling-agent-poc-quality.yml | f52a0d71b3ce2b60d738f4326f0f9906af9ea1b9 | NOASSERTION | +| ContextualWisdomLab/wardnet | actions/cache | 0057852bfaa89a56745cba8c7296529d2fc39830 | NOASSERTION | +| ContextualWisdomLab/wardnet | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/wardnet | actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | +| ContextualWisdomLab/wardnet | arbitrary | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/wardnet | dtolnay/rust-toolchain | 4be7066ada62dd38de10e7b70166bc74ed198c30 | NOASSERTION | +| ContextualWisdomLab/wardnet | dtolnay/rust-toolchain | efcb852328a9f50117170cc43094fb6f09eaf1ae | NOASSERTION | +| ContextualWisdomLab/wardnet | github/codeql-action/upload-sarif | ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd | NOASSERTION | +| ContextualWisdomLab/wardnet | libfuzzer-sys | >= 0.4.0,< 0.5.0 | NOASSERTION | +| ContextualWisdomLab/wardnet | ossf/scorecard-action | 2d1146689b8cda280b9bc96326124645441f03bc | NOASSERTION | +| ContextualWisdomLab/wardnet | percent-encoding | >= 2.0.0,< 3.0.0 | NOASSERTION | +| ContextualWisdomLab/wardnet | proptest | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/wardnet | r-efi | 5.3.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | +| ContextualWisdomLab/wardnet | r-efi | 6.0.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | +| ContextualWisdomLab/wardnet | serde | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/wardnet | serde_json | >= 1.0.0,< 2.0.0 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | actions/setup-python | a26af69be951a213d495a4c3e4e4022e16d87065 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | aiohappyeyeballs | 2.7.1 | 0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0 | +| ContextualWisdomLab/xtrmLLMBatchPython | astral-sh/setup-uv | c771a70e6277c0a99b617c7a806ffedaca235ff9 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | backports-asyncio-runner | 1.2.0 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | certifi | 2026.6.17 | MPL-2.0 | +| ContextualWisdomLab/xtrmLLMBatchPython | commander | ^14.0.0 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | eslint | ^9.27.0 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | express | ^4.18.0 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | extract-zip | ^2.0.1 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | pino | ^9.7.0 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | psycopg | 3.3.4 | LGPL-3.0 AND LGPL-3.0-only | +| ContextualWisdomLab/xtrmLLMBatchPython | psycopg-binary | 3.3.4 | GPL-3.0-or-later | +| ContextualWisdomLab/xtrmLLMBatchPython | pypa/gh-action-pip-audit | 1220774d901786e6f652ae159f7b6bc8fea6d266 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | +| ContextualWisdomLab/xtrmLLMBatchPython | vitest | ^3.2.6 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | xtrmllmbatch | 0.1.0 | NOASSERTION | +| ContextualWisdomLab/xtrmLLMBatchPython | zod | ^3.25.67 | NOASSERTION | ## Per-repository components + +### ContextualWisdomLab/.github + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| @colbymchenry/codegraph | 1.4.1 | MIT | no | +| @colbymchenry/codegraph-darwin-arm64 | 1.4.1 | MIT | no | +| @colbymchenry/codegraph-darwin-x64 | 1.4.1 | MIT | no | +| @colbymchenry/codegraph-linux-arm64 | 1.4.1 | MIT | no | +| @colbymchenry/codegraph-linux-x64 | 1.4.1 | MIT | no | +| @colbymchenry/codegraph-win32-arm64 | 1.4.1 | MIT | no | +| @colbymchenry/codegraph-win32-x64 | 1.4.1 | MIT | no | +| @hono/node-server | 2.1.1 | MIT | no | +| @modelcontextprotocol/sdk | 1.30.0 | MIT | no | +| @rhwp/core | 0.7.7 | MIT | no | +| accepts | 2.0.0 | MIT | no | +| actions/attest | v4.1.0 | NOASSERTION | yes | +| actions/checkout | 11d5960a326750d5838078e36cf38b85af677262 | NOASSERTION | yes | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/checkout | 631c942040754b6e095e929c1677c07e10ed4f87 | NOASSERTION | yes | +| actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | yes | +| actions/checkout | de0fac2e4500dabe0009e67214ff5f5447ce83dd | NOASSERTION | yes | +| actions/checkout | fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 | NOASSERTION | yes | +| actions/checkout | v7.0.0 | NOASSERTION | yes | +| actions/checkout | v7.0.1 | NOASSERTION | yes | +| actions/create-github-app-token | v3.2.0 | NOASSERTION | yes | +| actions/dependency-review-action | v5.0.0 | NOASSERTION | yes | +| actions/download-artifact | v8.0.1 | NOASSERTION | yes | +| actions/setup-node | 48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e | NOASSERTION | yes | +| actions/setup-node | 49933ea5288caeca8642d1e84afbd3f7d6820020 | NOASSERTION | yes | +| actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | yes | +| actions/setup-python | ece7cb06caefa5fff74198d8649806c4678c61a1 | NOASSERTION | yes | +| actions/setup-python | v7.0.0 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| actions/upload-artifact | b7c566a772e6b6bfb58ed0dc250532a479d7789f | NOASSERTION | yes | +| actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | yes | +| actions/upload-artifact | v6.0.0 | NOASSERTION | yes | +| actions/upload-artifact | v7.0.0 | NOASSERTION | yes | +| actions/upload-artifact | v7.0.1 | NOASSERTION | yes | +| aiohappyeyeballs | 2.7.1 | 0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0 | yes | +| aiohttp | 3.14.3 | Apache-2.0 AND MIT | no | +| aiosignal | 1.4.0 | Apache-2.0 | no | +| ajv | 8.20.0 | MIT | no | +| ajv-formats | 3.0.1 | MIT | no | +| anchore/sbom-action | v0.24.0 | NOASSERTION | yes | +| annotated-doc | 0.0.4 | MIT | no | +| annotated-types | 0.7.0 | MIT | no | +| anyio | 4.14.2 | MIT | no | +| aquasecurity/trivy-action | v0.36.0 | NOASSERTION | yes | +| astral-sh/setup-uv | 37802adc94f370d6bfd71619e3f0bf239e1f3b78 | NOASSERTION | yes | +| astral-sh/setup-uv | bec219d24cd3e171d82865faccec33120bb574f4 | NOASSERTION | yes | +| astral-sh/setup-uv | d0cc045d04ccac9d8b7881df0226f9e82c39688e | NOASSERTION | yes | +| attrs | 26.1.0 | MIT | no | +| autocfg | 1.5.1 | Apache-2.0 OR MIT | no | +| backoff | 2.2.1 | MIT | no | +| bandit | 1.9.4 | Apache-2.0 | no | +| body-parser | 2.3.0 | MIT | no | +| boolean-py | 5.0 | BSD-2-Clause | no | +| bytes | 3.1.2 | MIT | no | +| cachecontrol | 0.14.4 | Apache-2.0 | no | +| caido-sdk-client | 0.2.0 | MIT | no | +| caido-server-auth | 0.1.2 | NOASSERTION | yes | +| call-bind-apply-helpers | 1.0.2 | MIT | no | +| call-bound | 1.0.4 | MIT | no | +| certifi | 2026.6.17 | MPL-2.0 | yes | +| cffi | 2.0.0 | MIT-0 | no | +| cfg-if | 1.0.5 | MIT OR Apache-2.0 | no | +| charset-normalizer | 3.5.1 | MIT | no | +| click | 8.4.1 | BSD-3-Clause | no | +| click | 8.5.0 | BSD-3-Clause | no | +| cloudflare/wrangler-action | v4.0.0 | NOASSERTION | yes | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| content-disposition | 1.1.0 | MIT | no | +| content-type | 1.0.5 | MIT | no | +| content-type | 2.1.0 | MIT | no | +| contextualwisdomlab-opencode-codegraph-tooling | — | NOASSERTION | yes | +| ContextualWisdomLab/.github/.github/workflows/release-dependency-license-strix-gate.yml | e249236e3f420c4e9c470f26925640dae59f0d93 | NOASSERTION | yes | +| cookie | 0.7.2 | MIT | no | +| cookie-signature | 1.2.2 | MIT | no | +| core-util-is | 1.0.3 | MIT | no | +| cors | 2.8.6 | MIT | no | +| coverage | 7.15.4 | Apache-2.0 | no | +| coverage-cargo-fixtures | 0.0.0 | NOASSERTION | yes | +| cross-spawn | 7.0.6 | MIT | no | +| cryptography | 50.0.0 | Apache-2.0 OR BSD-3-Clause | no | +| cvss | 3.6 | LGPL-3.0 AND LGPL-3.0-only AND LGPL-3.0-or-later | yes | +| cyclonedx-python-lib | 9.1.0 | Apache-2.0 AND Python-2.0 | no | +| debug | 4.4.3 | MIT | no | +| defusedxml | 0.7.1 | PSF-2.0 | no | +| depd | 2.0.0 | MIT | no | +| distro | 1.9.0 | Apache-2.0 | no | +| docker | 7.1.0 | Apache-2.0 | no | +| docstring-parser | 0.18.0 | MIT | no | +| dtolnay/rust-toolchain | 4be7066ada62dd38de10e7b70166bc74ed198c30 | NOASSERTION | yes | +| dunder-proto | 1.0.1 | MIT | no | +| ee-first | 1.1.1 | MIT | no | +| encodeurl | 2.0.0 | MIT | no | +| es-define-property | 1.0.1 | MIT | no | +| es-errors | 1.3.0 | MIT | no | +| es-object-atoms | 1.1.2 | MIT | no | +| escape-html | 1.0.3 | MIT | no | +| etag | 1.8.1 | MIT | no | +| eventsource | 3.0.7 | MIT | no | +| eventsource-parser | 3.1.1 | MIT | no | +| express | 5.2.1 | MIT | no | +| express-rate-limit | 8.7.0 | MIT | no | +| fast-deep-equal | 3.1.3 | MIT | no | +| fast-uri | 3.1.7 | BSD-3-Clause | no | +| fastuuid | 0.14.0 | BSD-2-Clause AND BSD-3-Clause | no | +| filelock | 3.29.4 | MIT | no | +| filelock | 3.29.7 | MIT | no | +| finalhandler | 2.1.1 | MIT | no | +| forwarded | 0.2.0 | MIT | no | +| fresh | 2.0.0 | MIT | no | +| frozenlist | 1.8.0 | Apache-2.0 | no | +| fsspec | 2026.6.0 | BSD-3-Clause | no | +| function-bind | 1.1.2 | MIT | no | +| get-intrinsic | 1.3.0 | MIT | no | +| get-proto | 1.0.1 | MIT | no | +| github/codeql-action | v4.37.9 | NOASSERTION | yes | +| github/codeql-action/analyze | ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd | NOASSERTION | yes | +| github/codeql-action/init | ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd | NOASSERTION | yes | +| google-api-core | 2.33.0 | Apache-2.0 | no | +| google-auth | 2.55.1 | Apache-2.0 | no | +| google-cloud-aiplatform | 1.133.0 | Apache-2.0 | no | +| google-cloud-bigquery | 3.42.2 | Apache-2.0 | no | +| google-cloud-core | 2.6.0 | Apache-2.0 | no | +| google-cloud-resource-manager | 1.18.0 | Apache-2.0 | no | +| google-cloud-storage | 3.13.1 | Apache-2.0 | no | +| google-crc32c | 1.8.0 | Apache-2.0 | no | +| google-genai | 1.75.0 | Apache-2.0 | no | +| google-resumable-media | 2.10.0 | Apache-2.0 | no | +| google/clusterfuzzlite/actions/build_fuzzers | 884713a6c30a92e5e8544c39945cd7cb630abcd1 | NOASSERTION | yes | +| google/clusterfuzzlite/actions/run_fuzzers | 884713a6c30a92e5e8544c39945cd7cb630abcd1 | NOASSERTION | yes | +| google/osv-scanner-action | v2.3.8 | NOASSERTION | yes | +| google/osv-scanner-action | v2.5.1 | NOASSERTION | yes | +| googleapis-common-protos | 1.75.0 | Apache-2.0 | no | +| gopd | 1.2.0 | MIT | no | +| gql | 4.0.0 | MIT | no | +| graphql-core | 3.2.12 | MIT | no | +| griffelib | 2.1.0 | ISC | no | +| grpc-google-iam-v1 | 0.14.4 | Apache-2.0 | no | +| grpcio | 1.81.1 | Apache-2.0 AND BSD-3-Clause AND MPL-2.0 | yes | +| grpcio-status | 1.81.1 | Apache-2.0 AND BSD-3-Clause AND MPL-2.0 | yes | +| h11 | 0.16.0 | MIT | no | +| has-symbols | 1.1.0 | MIT | no | +| hasown | 2.0.4 | MIT | no | +| heck | 0.5.0 | MIT OR Apache-2.0 | no | +| hf-xet | 1.5.1 | Apache-2.0 | no | +| hono | 4.13.7 | MIT | no | +| http-errors | 2.0.1 | MIT | no | +| httpcore | 1.0.9 | BSD-2-Clause AND BSD-3-Clause | no | +| httpcore2 | 2.12.0 | BSD-3-Clause | no | +| httpx | 0.28.1 | BSD-3-Clause | no | +| httpx-sse | 0.4.3 | MIT | no | +| httpx2 | 2.12.0 | BSD-3-Clause | no | +| huggingface-hub | 1.20.0 | Apache-2.0 | no | +| hwp-mcp | 0.3.0 | MIT | no | +| hypothesis | 6.168.0 | MPL-2.0 | yes | +| iconv-lite | 0.7.3 | MIT | no | +| idna | 3.18 | BSD-3-Clause | no | +| immediate | 3.0.6 | MIT | no | +| importlib-metadata | 8.9.0 | Apache-2.0 | no | +| indoc | 2.0.7 | MIT OR Apache-2.0 | no | +| inherits | 2.0.4 | ISC | no | +| iniconfig | 2.3.0 | MIT | no | +| iniconfig | 2.3.1 | MIT | no | +| interrogate | — | NOASSERTION | yes | +| interrogate | 1.7.0 | MIT | no | +| ip-address | 10.7.0 | MIT | no | +| ipaddr.js | 1.9.1 | MIT | no | +| is-promise | 4.0.0 | MIT | no | +| isarray | 1.0.0 | MIT | no | +| isexe | 2.0.0 | ISC | no | +| itoa | 1.0.15 | MIT OR Apache-2.0 | no | +| jinja2 | 3.1.6 | BSD-2-Clause AND BSD-3-Clause | no | +| jiter | 0.15.0 | MIT | no | +| jose | 6.2.12 | MIT | no | +| json-schema-traverse | 1.0.0 | MIT | no | +| json-schema-typed | 8.0.2 | BSD-2-Clause AND JSON | no | +| jsonschema | 4.26.0 | MIT | no | +| jsonschema-specifications | 2025.9.1 | MIT | no | +| jszip | 3.10.2 | (MIT OR GPL-3.0-or-later) | yes | +| libc | 0.2.189 | MIT OR Apache-2.0 | no | +| license-expression | 30.4.4 | Apache-2.0 | no | +| lie | 3.3.0 | MIT | no | +| litellm | 1.94.1 | MIT | no | +| markdown-it-py | 4.2.0 | MIT | no | +| markupsafe | 3.0.3 | BSD-3-Clause | no | +| math-intrinsics | 1.1.0 | MIT | no | +| maturin | 1.15.0 | MIT OR Apache-2.0 | no | +| mcp | 1.28.1 | MIT AND Python-2.0 | no | +| mdurl | 0.1.2 | MIT | no | +| media-typer | 1.1.1 | MIT | no | +| memoffset | 0.9.1 | MIT | no | +| merge-descriptors | 2.0.0 | MIT | no | +| mime-db | 1.54.0 | MIT | no | +| mime-types | 3.0.2 | MIT | no | +| ms | 2.1.3 | MIT | no | +| msgpack | 1.2.1 | Apache-2.0 | no | +| multidict | 6.7.1 | Apache-2.0 | no | +| negotiator | 1.1.0 | MIT | no | +| noema-document-reader-runtime | 1.0.0 | NOASSERTION | yes | +| object-assign | 4.1.1 | MIT | no | +| object-inspect | 1.13.4 | MIT | no | +| on-finished | 2.4.1 | MIT | no | +| once | 1.4.0 | ISC | no | +| once_cell | 1.21.4 | MIT OR Apache-2.0 | no | +| openai | 2.54.0 | Apache-2.0 | no | +| openai-agents | 0.19.4 | MIT | no | +| ossf/scorecard-action | v2.4.3 | NOASSERTION | yes | +| packageurl-python | 0.17.6 | MIT | no | +| packaging | 26.2 | Apache-2.0 AND BSD-2-Clause | no | +| packaging | 26.3 | Apache-2.0 OR BSD-2-Clause | no | +| pako | 1.0.11 | MIT AND Zlib | no | +| parseurl | 1.3.3 | MIT | no | +| path-key | 3.1.1 | MIT | no | +| path-to-regexp | 8.4.2 | MIT | no | +| picomatch | 4.0.4 | MIT | no | +| pillow | 12.3.0 | MIT-CMU | no | +| pip | 26.2.1 | MIT | no | +| pip-api | 0.0.34 | Apache-2.0 | no | +| pip-audit | 2.10.1 | Apache-2.0 AND ISC | no | +| pip-requirements-parser | 32.0.1 | Apache-2.0 AND MIT | no | +| pkce-challenge | 5.0.1 | MIT | no | +| platformdirs | 4.10.0 | MIT | no | +| pluggy | 1.6.0 | MIT | no | +| portable-atomic | 1.15.0 | Apache-2.0 OR MIT | no | +| proc-macro2 | 1.0.107 | MIT OR Apache-2.0 | no | +| process-nextick-args | 2.0.1 | MIT | no | +| propcache | 0.5.2 | Apache-2.0 | no | +| proto-plus | 1.28.1 | Apache-2.0 | no | +| protobuf | 6.33.6 | BSD-3-Clause AND LicenseRef-scancode-protobuf | no | +| proxy-addr | 2.0.7 | MIT | no | +| py | 1.11.0 | MIT | no | +| py-serializable | 2.1.0 | Apache-2.0 | no | +| pyasn1 | 0.6.4 | BSD-2-Clause AND BSD-3-Clause AND MIT | no | +| pyasn1-modules | 0.4.2 | BSD-2-Clause AND BSD-3-Clause | no | +| pycparser | 3.0 | BSD-3-Clause | no | +| pydantic | 2.13.4 | MIT | no | +| pydantic-core | 2.46.4 | MIT | no | +| pydantic-settings | 2.14.2 | MIT | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pygments | 2.21.0 | BSD-2-Clause | no | +| pyjwt | 2.13.0 | MIT | no | +| pyo3 | 0.22.6 | MIT OR Apache-2.0 | no | +| pyo3-build-config | 0.22.6 | MIT OR Apache-2.0 | no | +| pyo3-ffi | 0.22.6 | MIT OR Apache-2.0 | no | +| pyo3-macros | 0.22.6 | MIT OR Apache-2.0 | no | +| pyo3-macros-backend | 0.22.6 | MIT OR Apache-2.0 | no | +| PyO3/maturin-action | e83996d129638aa358a18fbd1dfb82f0b0fb5d3b | NOASSERTION | yes | +| pyopenssl | 26.4.0 | Apache-2.0 | no | +| pypa/gh-action-pypi-publish | dc37677b2e1c63e2034f94d8a5b11f265b73ba33 | NOASSERTION | yes | +| pyparsing | 3.3.2 | MIT AND Python-2.0 | no | +| pypdf | 6.16.1 | BSD-3-Clause | no | +| pytest | — | NOASSERTION | yes | +| pytest | 9.1.1 | MIT | no | +| pytest-cov | — | NOASSERTION | yes | +| pytest-cov | 7.1.0 | MIT | no | +| python-dateutil | 2.9.0.post0 | Apache-2.0 OR BSD-3-Clause | no | +| python-dotenv | 1.2.2 | BSD-3-Clause | no | +| python-multipart | 0.0.32 | Apache-2.0 | no | +| pyyaml | 6.0.3 | MIT | no | +| qs | 6.16.0 | BSD-3-Clause | no | +| quote | 1.0.47 | MIT OR Apache-2.0 | no | +| r-lib/actions | 6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 | NOASSERTION | yes | +| range-parser | 1.3.0 | MIT | no | +| raw-body | 3.0.2 | MIT | no | +| readable-stream | 2.3.8 | MIT | no | +| referencing | 0.37.0 | MIT | no | +| regex | 2026.7.19 | CNRI-Python AND Apache-2.0 | no | +| reportlab | 5.0.0 | BSD-2-Clause AND BSD-3-Clause | no | +| requests | 2.34.2 | Apache-2.0 | no | +| require-from-string | 2.0.2 | MIT | no | +| rich | 15.0.0 | MIT | no | +| router | 2.2.0 | MIT | no | +| rpds-py | 2026.5.1 | MIT | no | +| rustversion | 1.0.23 | MIT OR Apache-2.0 | no | +| ryu | 1.0.20 | Apache-2.0 OR BSL-1.0 | no | +| safe-buffer | 5.1.2 | MIT | no | +| safer-buffer | 2.1.2 | MIT | no | +| send | 1.2.1 | MIT | no | +| serve-static | 2.2.1 | MIT | no | +| setimmediate | 1.0.5 | MIT | no | +| setprototypeof | 1.2.0 | ISC | no | +| shebang-command | 2.0.0 | MIT | no | +| shebang-regex | 3.0.0 | MIT | no | +| shellingham | 1.5.4 | ISC | no | +| side-channel | 1.1.1 | MIT | no | +| side-channel-list | 1.0.1 | MIT | no | +| side-channel-map | 1.0.1 | MIT | no | +| side-channel-weakmap | 1.0.2 | MIT | no | +| six | 1.17.0 | MIT | no | +| sniffio | 1.3.1 | Apache-2.0 AND MIT | no | +| sortedcontainers | 2.4.0 | Apache-2.0 | no | +| sse-starlette | 3.4.4 | BSD-3-Clause | no | +| starlette | 1.3.1 | BSD-3-Clause | no | +| statuses | 2.0.2 | MIT | no | +| step-security/harden-runner | bf7454d06d71f1098171f2acdf0cd4708d7b5920 | NOASSERTION | yes | +| step-security/harden-runner | v2.13.2 | NOASSERTION | yes | +| step-security/harden-runner | v2.20.0 | NOASSERTION | yes | +| step-security/harden-runner | v2.20.1 | NOASSERTION | yes | +| stevedore | 5.9.0 | Apache-2.0 | no | +| string_decoder | 1.1.1 | MIT | no | +| strix-agent | 1.5.3 | Apache-2.0 | no | +| syn | 2.0.119 | MIT OR Apache-2.0 | no | +| tabulate | 0.10.0 | MIT | no | +| target-lexicon | 0.12.16 | Apache-2.0 WITH LLVM-exception | no | +| tenacity | 9.1.4 | Apache-2.0 | no | +| tiktoken | 0.13.0 | MIT | no | +| toidentifier | 1.0.1 | MIT | no | +| tokenizers | 0.23.1 | Apache-2.0 | no | +| tomli | 2.4.1 | MIT | no | +| tomli-w | 1.2.0 | MIT | no | +| tqdm | 4.68.3 | MIT AND MPL-2.0 | yes | +| truststore | 0.10.4 | MIT | no | +| type-is | 2.1.0 | MIT | no | +| typer | 0.25.1 | MIT | no | +| typing-extensions | 4.15.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| typing-inspection | 0.4.4 | MIT | no | +| unicode-ident | 1.0.26 | (MIT OR Apache-2.0) AND Unicode-3.0 | no | +| unindent | 0.2.4 | MIT OR Apache-2.0 | no | +| unpipe | 1.0.0 | MIT | no | +| urllib3 | 2.7.0 | MIT | no | +| util-deprecate | 1.0.2 | MIT | no | +| uv | 0.12.7 | MIT OR Apache-2.0 | no | +| uvicorn | 0.49.0 | BSD-3-Clause | no | +| vary | 1.1.2 | MIT | no | +| websockets | 15.0.1 | BSD-3-Clause | no | +| which | 2.0.2 | ISC | no | +| wrappy | 1.0.2 | ISC | no | +| yarl | 1.24.2 | Apache-2.0 | no | +| zipp | 4.1.0 | MIT | no | +| zod | 4.6.4 | MIT | no | +| zod-to-json-schema | 3.25.2 | ISC | no | + +### ContextualWisdomLab/accounting-information-platform + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| accounting-information-platform | 0.1.0 | NOASSERTION | yes | +| actions/attest | 508db95dd578ae2727ebd6217d5ba78e4fbda05d | NOASSERTION | yes | +| actions/checkout | 631c942040754b6e095e929c1677c07e10ed4f87 | NOASSERTION | yes | +| actions/download-artifact | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c | NOASSERTION | yes | +| actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| aquasecurity/trivy-action | a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 | NOASSERTION | yes | +| coverage | 7.15.4 | Apache-2.0 | no | +| packaging | 26.3 | Apache-2.0 OR BSD-2-Clause | no | +| psycopg | 3.3.4 | LGPL-3.0 AND LGPL-3.0-only | yes | +| psycopg-binary | 3.3.4 | GPL-3.0-or-later | yes | +| setuptools | 84.0.0 | MIT | no | +| wheel | 0.48.0 | MIT | no | + +### ContextualWisdomLab/aFIPC + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | de0fac2e4500dabe0009e67214ff5f5447ce83dd | NOASSERTION | yes | +| r-lib/actions/check-r-package | 6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 | NOASSERTION | yes | +| r-lib/actions/setup-r | d3c5be51b12e724e68f33216ca3c148b66d5f0b6 | NOASSERTION | yes | +| r-lib/actions/setup-r-dependencies | d3c5be51b12e724e68f33216ca3c148b66d5f0b6 | NOASSERTION | yes | +| step-security/harden-runner | bf7454d06d71f1098171f2acdf0cd4708d7b5920 | NOASSERTION | yes | + +### ContextualWisdomLab/appguardrail + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/create-github-app-token | bcd2ba49218906704ab6c1aa796996da409d3eb1 | NOASSERTION | yes | +| actions/download-artifact | 37930b1c2abaa49bbe596cd826c3c89aef350131 | NOASSERTION | yes | +| actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| anomalyco/opencode/github | 77fc88c8ade8e5a620ebbe1197f3a572d29ae91a | NOASSERTION | yes | +| aquasecurity/trivy-action | ed142fd0673e97e23eac54620cfb913e5ce36c25 | NOASSERTION | yes | +| arrow | 1.4.0 | Apache-2.0 | no | +| attrs | 26.1.0 | MIT | no | +| backports-tarfile | 1.2.0 | NOASSERTION | yes | +| boolean-py | 5.0 | BSD-2-Clause | no | +| build | 1.5.1 | MIT | no | +| cachecontrol | 0.14.4 | Apache-2.0 | no | +| certifi | 2026.6.17 | MPL-2.0 | yes | +| cffi | 2.0.0 | MIT-0 | no | +| chardet | 5.2.0 | LGPL-2.1-or-later | yes | +| charset-normalizer | 3.4.7 | MIT | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| cryptography | 49.0.0 | BSD-3-Clause OR Apache-2.0 | no | +| cyclonedx-bom | 7.3.0 | Apache-2.0 AND Python-2.0 | no | +| cyclonedx-python-lib | 11.11.0 | Apache-2.0 AND Python-2.0 | no | +| defusedxml | 0.7.1 | PSF-2.0 | no | +| docutils | 0.23 | BSD-2-Clause AND BSD-3-Clause AND CC-PDDC AND GPL-1.0-or-later AND GPL-3.0-only AND GPL-3.0-or-later AND LicenseRef-scancode-other-copyleft AND LicenseRef-scancode-public-domain | yes | +| filelock | 3.29.4 | MIT | no | +| fqdn | 1.5.1 | MPL-2.0 | yes | +| github/codeql-action/upload-sarif | 5595ccaf912efad79be6eef63a5619ff05969be3 | NOASSERTION | yes | +| id | 1.6.1 | Apache-2.0 | no | +| idna | 3.18 | BSD-3-Clause | no | +| importlib-metadata | 9.0.0 | Apache-2.0 | no | +| iniconfig | 2.3.0 | MIT | no | +| isoduration | 20.11.0 | ISC | no | +| jaraco-classes | 3.4.0 | MIT | no | +| jaraco-context | 6.1.2 | MIT | no | +| jaraco-functools | 4.5.0 | MIT | no | +| jeepney | 0.9.0 | MIT | no | +| jsonpointer | 3.1.1 | BSD-3-Clause | no | +| jsonschema | 4.26.0 | MIT | no | +| jsonschema-specifications | 2025.9.1 | MIT | no | +| keyring | 25.7.0 | MIT | no | +| lark | 1.3.1 | MIT AND MPL-2.0 | yes | +| license-expression | 30.4.4 | Apache-2.0 | no | +| lxml | 6.1.1 | BSD-3-Clause AND GPL-1.0-or-later | yes | +| markdown-it-py | 4.2.0 | MIT | no | +| mdurl | 0.1.2 | MIT | no | +| more-itertools | 11.1.0 | MIT | no | +| msgpack | 1.2.1 | Apache-2.0 | no | +| nh3 | 0.3.6 | MIT | no | +| packageurl-python | 0.17.6 | MIT | no | +| packaging | 26.2 | Apache-2.0 AND BSD-2-Clause | no | +| pip-api | 0.0.34 | Apache-2.0 | no | +| pip-audit | 2.10.1 | Apache-2.0 AND ISC | no | +| pip-requirements-parser | 32.0.1 | Apache-2.0 AND MIT | no | +| platformdirs | 4.10.0 | MIT | no | +| pluggy | 1.6.0 | MIT | no | +| py-serializable | 2.1.0 | Apache-2.0 | no | +| pycparser | 3.0 | BSD-3-Clause | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pypa/gh-action-pypi-publish | dc37677b2e1c63e2034f94d8a5b11f265b73ba33 | NOASSERTION | yes | +| pyparsing | 3.3.2 | MIT AND Python-2.0 | no | +| pyproject-hooks | 1.2.0 | MIT | no | +| pytest | 9.1.1 | MIT | no | +| python-dateutil | 2.9.0.post0 | Apache-2.0 OR BSD-3-Clause | no | +| readme-renderer | 45.0 | Apache-2.0 | no | +| referencing | 0.37.0 | MIT | no | +| requests | 2.34.2 | Apache-2.0 | no | +| requests-toolbelt | 1.0.0 | Apache-2.0 | no | +| rfc3339-validator | 0.1.4 | MIT | no | +| rfc3986 | 2.0.0 | Apache-2.0 | no | +| rfc3986-validator | 0.1.1 | MIT | no | +| rfc3987-syntax | 1.1.0 | Apache-2.0 AND GPL-1.0-or-later AND MIT | yes | +| rich | 15.0.0 | MIT | no | +| rpds-py | 0.30.0 | MIT | no | +| secretstorage | 3.5.0 | BSD-3-Clause | no | +| setuptools | — | NOASSERTION | yes | +| six | 1.17.0 | MIT | no | +| sortedcontainers | 2.4.0 | Apache-2.0 | no | +| tomli | 2.4.1 | MIT | no | +| tomli-w | 1.2.0 | MIT | no | +| twine | 6.2.0 | Apache-2.0 | no | +| typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| tzdata | 2026.2 | Apache-2.0 | no | +| uri-template | 1.3.0 | MIT | no | +| urllib3 | 2.7.0 | MIT | no | +| webcolors | 25.10.0 | BSD-3-Clause | no | +| zipp | 4.1.0 | MIT | no | + +### ContextualWisdomLab/bandscope + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| @adobe/css-tools | 4.5.0 | MIT | no | +| @asamuzakjp/css-color | 5.1.11 | MIT | no | +| @asamuzakjp/dom-selector | 7.1.1 | MIT | no | +| @asamuzakjp/generational-cache | 1.0.1 | MIT | no | +| @asamuzakjp/nwsapi | 2.3.9 | MIT | no | +| @babel/code-frame | 7.29.7 | MIT | no | +| @babel/compat-data | 7.29.7 | MIT | no | +| @babel/core | 7.29.7 | MIT | no | +| @babel/generator | 7.29.8 | MIT | no | +| @babel/helper-compilation-targets | 7.29.7 | MIT | no | +| @babel/helper-globals | 7.29.7 | MIT | no | +| @babel/helper-module-imports | 7.29.7 | MIT | no | +| @babel/helper-module-transforms | 7.29.7 | MIT | no | +| @babel/helper-string-parser | 7.29.7 | MIT | no | +| @babel/helper-validator-identifier | 7.29.7 | MIT | no | +| @babel/helper-validator-option | 7.29.7 | MIT | no | +| @babel/helpers | 7.29.7 | MIT | no | +| @babel/parser | 7.29.8 | MIT | no | +| @babel/runtime | 7.29.7 | MIT | no | +| @babel/template | 7.29.7 | MIT | no | +| @babel/traverse | 7.29.8 | MIT | no | +| @babel/types | 7.29.8 | MIT | no | +| @bandscope/desktop | 0.1.0 | NOASSERTION | yes | +| @bandscope/shared-types | 0.1.0 | NOASSERTION | yes | +| @base-ui/react | 1.7.0 | MIT | no | +| @base-ui/react | ^1.5.0 | MIT | no | +| @base-ui/utils | 0.3.2 | MIT | no | +| @bcoe/v8-coverage | 1.0.2 | ISC AND MIT | no | +| @bramus/specificity | 2.4.2 | MIT | no | +| @csstools/color-helpers | 6.1.0 | MIT-0 | no | +| @csstools/css-calc | 3.3.0 | MIT | no | +| @csstools/css-color-parser | 4.1.10 | MIT | no | +| @csstools/css-parser-algorithms | 4.0.0 | MIT | no | +| @csstools/css-syntax-patches-for-csstree | 1.1.7 | MIT-0 | no | +| @csstools/css-tokenizer | 4.0.0 | MIT | no | +| @emnapi/core | 1.11.2 | MIT | no | +| @emnapi/core | 1.9.2 | MIT | no | +| @emnapi/runtime | 1.11.2 | MIT | no | +| @emnapi/runtime | 1.9.2 | MIT | no | +| @emnapi/wasi-threads | 1.2.1 | MIT | no | +| @emnapi/wasi-threads | 1.2.2 | MIT | no | +| @es-joy/jsdoccomment | 0.91.0 | MIT | no | +| @es-joy/resolve.exports | 1.2.0 | MIT | no | +| @esbuild/aix-ppc64 | 0.28.2 | MIT | no | +| @esbuild/android-arm | 0.28.2 | MIT | no | +| @esbuild/android-arm64 | 0.28.2 | MIT | no | +| @esbuild/android-x64 | 0.28.2 | MIT | no | +| @esbuild/darwin-arm64 | 0.28.2 | MIT | no | +| @esbuild/darwin-x64 | 0.28.2 | MIT | no | +| @esbuild/freebsd-arm64 | 0.28.2 | MIT | no | +| @esbuild/freebsd-x64 | 0.28.2 | MIT | no | +| @esbuild/linux-arm | 0.28.2 | MIT | no | +| @esbuild/linux-arm64 | 0.28.2 | MIT | no | +| @esbuild/linux-ia32 | 0.28.2 | MIT | no | +| @esbuild/linux-loong64 | 0.28.2 | MIT | no | +| @esbuild/linux-mips64el | 0.28.2 | MIT | no | +| @esbuild/linux-ppc64 | 0.28.2 | MIT | no | +| @esbuild/linux-riscv64 | 0.28.2 | MIT | no | +| @esbuild/linux-s390x | 0.28.2 | MIT | no | +| @esbuild/linux-x64 | 0.28.2 | MIT | no | +| @esbuild/netbsd-arm64 | 0.28.2 | MIT | no | +| @esbuild/netbsd-x64 | 0.28.2 | MIT | no | +| @esbuild/openbsd-arm64 | 0.28.2 | MIT | no | +| @esbuild/openbsd-x64 | 0.28.2 | MIT | no | +| @esbuild/openharmony-arm64 | 0.28.2 | MIT | no | +| @esbuild/sunos-x64 | 0.28.2 | MIT | no | +| @esbuild/win32-arm64 | 0.28.2 | MIT | no | +| @esbuild/win32-ia32 | 0.28.2 | MIT | no | +| @esbuild/win32-x64 | 0.28.2 | MIT | no | +| @eslint-community/eslint-utils | 4.10.1 | MIT | no | +| @eslint-community/regexpp | 4.12.2 | MIT | no | +| @eslint/config-array | 0.23.5 | Apache-2.0 | no | +| @eslint/config-helpers | 0.7.0 | Apache-2.0 | no | +| @eslint/core | 1.2.1 | Apache-2.0 | no | +| @eslint/js | 10.0.1 | MIT | no | +| @eslint/object-schema | 3.0.5 | Apache-2.0 | no | +| @eslint/plugin-kit | 0.7.2 | Apache-2.0 | no | +| @exodus/bytes | 1.15.1 | MIT | no | +| @floating-ui/core | 1.8.0 | MIT | no | +| @floating-ui/dom | 1.8.0 | MIT | no | +| @floating-ui/react-dom | 2.1.9 | MIT | no | +| @floating-ui/utils | 0.2.12 | MIT | no | +| @fontsource-variable/geist | 5.3.0 | OFL-1.1 | no | +| @fontsource-variable/geist | ^5.2.9 | OFL-1.1 | no | +| @humanfs/core | 0.19.2 | Apache-2.0 | no | +| @humanfs/node | 0.16.8 | Apache-2.0 | no | +| @humanfs/types | 0.15.0 | Apache-2.0 | no | +| @humanwhocodes/module-importer | 1.0.1 | Apache-2.0 | no | +| @humanwhocodes/retry | 0.4.3 | Apache-2.0 | no | +| @joshwooding/vite-plugin-react-docgen-typescript | 0.7.0 | MIT | no | +| @jridgewell/gen-mapping | 0.3.13 | MIT | no | +| @jridgewell/remapping | 2.3.5 | MIT | no | +| @jridgewell/resolve-uri | 3.1.2 | MIT | no | +| @jridgewell/sourcemap-codec | 1.5.5 | MIT | no | +| @jridgewell/trace-mapping | 0.3.31 | MIT | no | +| @napi-rs/canvas | 1.0.5 | MIT | no | +| @napi-rs/canvas-android-arm64 | 1.0.5 | MIT | no | +| @napi-rs/canvas-darwin-arm64 | 1.0.5 | MIT | no | +| @napi-rs/canvas-darwin-x64 | 1.0.5 | MIT | no | +| @napi-rs/canvas-linux-arm-gnueabihf | 1.0.5 | MIT | no | +| @napi-rs/canvas-linux-arm64-gnu | 1.0.5 | MIT | no | +| @napi-rs/canvas-linux-arm64-musl | 1.0.5 | MIT | no | +| @napi-rs/canvas-linux-riscv64-gnu | 1.0.5 | MIT | no | +| @napi-rs/canvas-linux-x64-gnu | 1.0.5 | MIT | no | +| @napi-rs/canvas-linux-x64-musl | 1.0.5 | MIT | no | +| @napi-rs/canvas-win32-arm64-msvc | 1.0.5 | MIT | no | +| @napi-rs/canvas-win32-x64-msvc | 1.0.5 | MIT | no | +| @napi-rs/wasm-runtime | 1.2.2 | MIT | no | +| @oxc-parser/binding-android-arm-eabi | 0.127.0 | MIT | no | +| @oxc-parser/binding-android-arm64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-darwin-arm64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-darwin-x64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-freebsd-x64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-arm-gnueabihf | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-arm-musleabihf | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-arm64-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-arm64-musl | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-ppc64-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-riscv64-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-riscv64-musl | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-s390x-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-x64-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-x64-musl | 0.127.0 | MIT | no | +| @oxc-parser/binding-openharmony-arm64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-wasm32-wasi | 0.127.0 | MIT | no | +| @oxc-parser/binding-win32-arm64-msvc | 0.127.0 | MIT | no | +| @oxc-parser/binding-win32-ia32-msvc | 0.127.0 | MIT | no | +| @oxc-parser/binding-win32-x64-msvc | 0.127.0 | MIT | no | +| @oxc-project/types | 0.127.0 | MIT | no | +| @oxc-project/types | 0.143.0 | MIT | no | +| @oxc-resolver/binding-android-arm-eabi | 11.24.2 | MIT | no | +| @oxc-resolver/binding-android-arm64 | 11.24.2 | MIT | no | +| @oxc-resolver/binding-darwin-arm64 | 11.24.2 | MIT | no | +| @oxc-resolver/binding-darwin-x64 | 11.24.2 | MIT | no | +| @oxc-resolver/binding-freebsd-x64 | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-arm-gnueabihf | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-arm-musleabihf | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-arm64-gnu | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-arm64-musl | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-ppc64-gnu | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-riscv64-gnu | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-riscv64-musl | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-s390x-gnu | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-x64-gnu | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-x64-musl | 11.24.2 | MIT | no | +| @oxc-resolver/binding-openharmony-arm64 | 11.24.2 | MIT | no | +| @oxc-resolver/binding-wasm32-wasi | 11.24.2 | MIT | no | +| @oxc-resolver/binding-win32-arm64-msvc | 11.24.2 | MIT | no | +| @oxc-resolver/binding-win32-x64-msvc | 11.24.2 | MIT | no | +| @rolldown/binding-android-arm64 | 1.2.3 | MIT | no | +| @rolldown/binding-darwin-arm64 | 1.2.3 | MIT | no | +| @rolldown/binding-darwin-x64 | 1.2.3 | MIT | no | +| @rolldown/binding-freebsd-x64 | 1.2.3 | MIT | no | +| @rolldown/binding-linux-arm-gnueabihf | 1.2.3 | MIT | no | +| @rolldown/binding-linux-arm64-gnu | 1.2.3 | MIT | no | +| @rolldown/binding-linux-arm64-musl | 1.2.3 | MIT | no | +| @rolldown/binding-linux-ppc64-gnu | 1.2.3 | MIT | no | +| @rolldown/binding-linux-s390x-gnu | 1.2.3 | MIT | no | +| @rolldown/binding-linux-x64-gnu | 1.2.3 | MIT | no | +| @rolldown/binding-linux-x64-musl | 1.2.3 | MIT | no | +| @rolldown/binding-openharmony-arm64 | 1.2.3 | MIT | no | +| @rolldown/binding-win32-arm64-msvc | 1.2.3 | MIT | no | +| @rolldown/binding-win32-x64-msvc | 1.2.3 | MIT | no | +| @rolldown/pluginutils | 1.0.1 | MIT | no | +| @rollup/pluginutils | 5.4.0 | MIT | no | +| @sindresorhus/base62 | 1.0.0 | MIT | no | +| @standard-schema/spec | 1.1.0 | MIT | no | +| @storybook/builder-vite | 10.5.7 | MIT | no | +| @storybook/csf-plugin | 10.5.7 | MIT | no | +| @storybook/global | 5.0.0 | MIT | no | +| @storybook/icons | 2.1.0 | MIT | no | +| @storybook/react | 10.5.7 | MIT | no | +| @storybook/react-dom-shim | 10.5.7 | MIT | no | +| @storybook/react-vite | 10.5.7 | MIT | no | +| @storybook/react-vite | ^10.4.6 | MIT | no | +| @tailwindcss/node | 4.3.3 | MIT | no | +| @tailwindcss/oxide | 4.3.3 | MIT | no | +| @tailwindcss/oxide-android-arm64 | 4.3.3 | MIT | no | +| @tailwindcss/oxide-darwin-arm64 | 4.3.3 | MIT | no | +| @tailwindcss/oxide-darwin-x64 | 4.3.3 | MIT | no | +| @tailwindcss/oxide-freebsd-x64 | 4.3.3 | MIT | no | +| @tailwindcss/oxide-linux-arm-gnueabihf | 4.3.3 | MIT | no | +| @tailwindcss/oxide-linux-arm64-gnu | 4.3.3 | MIT | no | +| @tailwindcss/oxide-linux-arm64-musl | 4.3.3 | MIT | no | +| @tailwindcss/oxide-linux-x64-gnu | 4.3.3 | MIT | no | +| @tailwindcss/oxide-linux-x64-musl | 4.3.3 | MIT | no | +| @tailwindcss/oxide-wasm32-wasi | 4.3.3 | MIT | no | +| @tailwindcss/oxide-win32-arm64-msvc | 4.3.3 | MIT | no | +| @tailwindcss/oxide-win32-x64-msvc | 4.3.3 | MIT | no | +| @tailwindcss/vite | 4.3.3 | MIT | no | +| @tailwindcss/vite | ^4.3.2 | MIT | no | +| @tauri-apps/api | 2.11.1 | Apache-2.0 OR MIT | no | +| @tauri-apps/api | ^2.11.0 | NOASSERTION | yes | +| @tauri-apps/cli | 2.11.4 | Apache-2.0 OR MIT | no | +| @tauri-apps/cli | ^2.11.4 | NOASSERTION | yes | +| @tauri-apps/cli-darwin-arm64 | 2.11.4 | Apache-2.0 OR MIT | no | +| @tauri-apps/cli-darwin-x64 | 2.11.4 | Apache-2.0 OR MIT | no | +| @tauri-apps/cli-linux-arm-gnueabihf | 2.11.4 | Apache-2.0 OR MIT | no | +| @tauri-apps/cli-linux-arm64-gnu | 2.11.4 | Apache-2.0 OR MIT | no | +| @tauri-apps/cli-linux-arm64-musl | 2.11.4 | Apache-2.0 OR MIT | no | +| @tauri-apps/cli-linux-riscv64-gnu | 2.11.4 | Apache-2.0 OR MIT | no | +| @tauri-apps/cli-linux-x64-gnu | 2.11.4 | Apache-2.0 OR MIT | no | +| @tauri-apps/cli-linux-x64-musl | 2.11.4 | Apache-2.0 OR MIT | no | +| @tauri-apps/cli-win32-arm64-msvc | 2.11.4 | Apache-2.0 OR MIT | no | +| @tauri-apps/cli-win32-ia32-msvc | 2.11.4 | Apache-2.0 OR MIT | no | +| @tauri-apps/cli-win32-x64-msvc | 2.11.4 | Apache-2.0 OR MIT | no | +| @testing-library/dom | 10.4.1 | MIT | no | +| @testing-library/jest-dom | 6.9.1 | MIT | no | +| @testing-library/jest-dom | ^6.6.3 | MIT | no | +| @testing-library/react | 16.3.2 | MIT | no | +| @testing-library/react | ^16.2.0 | MIT | no | +| @testing-library/user-event | 14.6.3 | MIT | no | +| @tybys/wasm-util | 0.10.3 | MIT | no | +| @types/aria-query | 5.0.4 | MIT | no | +| @types/babel__core | 7.20.5 | MIT | no | +| @types/babel__generator | 7.27.0 | MIT | no | +| @types/babel__template | 7.4.4 | MIT | no | +| @types/babel__traverse | 7.28.0 | MIT | no | +| @types/chai | 5.2.3 | MIT | no | +| @types/deep-eql | 4.0.2 | MIT | no | +| @types/doctrine | 0.0.9 | MIT | no | +| @types/esrecurse | 4.3.1 | MIT | no | +| @types/estree | 1.0.9 | MIT | no | +| @types/json-schema | 7.0.15 | MIT | no | +| @types/node | 26.2.0 | MIT | no | +| @types/node | ^26.1.1 | MIT | no | +| @types/react | 19.2.18 | MIT | no | +| @types/react | ^19.2.17 | MIT | no | +| @types/react-dom | 19.2.4 | MIT | no | +| @types/react-dom | ^19.2.3 | MIT | no | +| @types/resolve | 1.20.6 | MIT | no | +| @typescript-eslint/eslint-plugin | 8.66.0 | MIT | no | +| @typescript-eslint/parser | 8.66.0 | MIT | no | +| @typescript-eslint/project-service | 8.66.0 | MIT | no | +| @typescript-eslint/scope-manager | 8.66.0 | MIT | no | +| @typescript-eslint/tsconfig-utils | 8.66.0 | MIT | no | +| @typescript-eslint/type-utils | 8.66.0 | MIT | no | +| @typescript-eslint/types | 8.66.0 | MIT | no | +| @typescript-eslint/typescript-estree | 8.66.0 | MIT | no | +| @typescript-eslint/utils | 8.66.0 | MIT | no | +| @typescript-eslint/visitor-keys | 8.66.0 | MIT | no | +| @vitejs/plugin-react | 6.0.5 | MIT | no | +| @vitejs/plugin-react | ^6.0.2 | MIT | no | +| @vitest/coverage-v8 | 4.1.10 | MIT | no | +| @vitest/coverage-v8 | ^4.1.10 | MIT | no | +| @vitest/expect | 3.2.4 | MIT | no | +| @vitest/expect | 4.1.10 | MIT | no | +| @vitest/mocker | 4.1.10 | MIT | no | +| @vitest/pretty-format | 3.2.4 | MIT | no | +| @vitest/pretty-format | 4.1.10 | MIT | no | +| @vitest/runner | 4.1.10 | MIT | no | +| @vitest/snapshot | 4.1.10 | MIT | no | +| @vitest/spy | 3.2.4 | MIT | no | +| @vitest/spy | 4.1.10 | MIT | no | +| @vitest/utils | 3.2.4 | MIT | no | +| @vitest/utils | 4.1.10 | MIT | no | +| @webcontainer/env | 1.1.1 | MIT | no | +| acorn | 8.18.0 | MIT | no | +| acorn-jsx | 5.3.2 | MIT | no | +| actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | yes | +| actions/download-artifact | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c | NOASSERTION | yes | +| actions/setup-node | 48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e | NOASSERTION | yes | +| actions/setup-python | a309ff8b426b58ec0e2a45f0f869d46889d02405 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| adler2 | 2.0.1 | 0BSD OR MIT OR Apache-2.0 | no | +| aho-corasick | 1.1.4 | Unlicense OR MIT | no | +| ajv | 6.15.0 | MIT | no | +| anchore/sbom-action | e22c389904149dbc22b58101806040fa8d37a610 | NOASSERTION | yes | +| android_system_properties | 0.1.5 | MIT OR Apache-2.0 | no | +| ansi-regex | 5.0.1 | MIT | no | +| ansi-styles | 5.2.0 | MIT | no | +| antlr4-python3-runtime | 4.9.3 | BSD-3-Clause | no | +| anyhow | 1.0.103 | MIT OR Apache-2.0 | no | +| aquasecurity/trivy-action | ed142fd0673e97e23eac54620cfb913e5ce36c25 | NOASSERTION | yes | +| are-docs-informative | 0.0.2 | MIT | no | +| aria-query | 5.3.0 | Apache-2.0 | no | +| assertion-error | 2.0.1 | MIT | no | +| ast-types | 0.16.1 | MIT | no | +| ast-v8-to-istanbul | 1.0.5 | MIT | no | +| astral-sh/setup-uv | 11f9893b081a58869d3b5fccaea48c9e9e46f990 | NOASSERTION | yes | +| atk | 0.18.2 | MIT | no | +| atk-sys | 0.18.2 | MIT | no | +| atomic-waker | 1.1.2 | Apache-2.0 OR MIT | no | +| audioop-lts | 0.2.2 | Python-2.0 AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | no | +| audioread | 3.1.0 | MIT | no | +| autocfg | 1.5.1 | Apache-2.0 OR MIT | no | +| balanced-match | 4.0.4 | MIT | no | +| bandit | 1.9.4 | Apache-2.0 | no | +| bandscope-analysis | 0.1.0 | NOASSERTION | yes | +| bandscope-workspace | 0.1.0 | NOASSERTION | yes | +| base64 | 0.21.7 | MIT OR Apache-2.0 | no | +| base64 | 0.22.1 | MIT OR Apache-2.0 | no | +| baseline-browser-mapping | 2.11.13 | Apache-2.0 | no | +| bidi-js | 1.0.3 | MIT | no | +| bit-set | 0.8.0 | Apache-2.0 OR MIT | no | +| bit-vec | 0.8.0 | Apache-2.0 OR MIT | no | +| bitflags | 1.3.2 | MIT OR Apache-2.0 | no | +| bitflags | 2.13.0 | MIT OR Apache-2.0 | no | +| block-buffer | 0.10.4 | MIT OR Apache-2.0 | no | +| block2 | 0.6.2 | MIT | no | +| brace-expansion | 5.0.9 | MIT | no | +| browserslist | 4.28.8 | MIT | no | +| bs58 | 0.5.1 | MIT OR Apache-2.0 | no | +| bumpalo | 3.20.3 | MIT OR Apache-2.0 | no | +| bundle-name | 4.1.0 | MIT | no | +| bytemuck | 1.25.1 | Zlib OR Apache-2.0 OR MIT | no | +| byteorder | 1.5.0 | Unlicense OR MIT | no | +| bytes | 1.12.1 | MIT | no | +| cairo-rs | 0.18.5 | MIT | no | +| cairo-sys-rs | 0.18.2 | MIT | no | +| camino | 1.2.4 | MIT OR Apache-2.0 | no | +| caniuse-lite | 1.0.30001809 | CC-BY-4.0 | no | +| cargo-platform | 0.1.9 | MIT OR Apache-2.0 | no | +| cargo_metadata | 0.19.2 | MIT | no | +| cargo_toml | 0.22.3 | Apache-2.0 OR MIT | no | +| cc | 1.2.67 | MIT OR Apache-2.0 | no | +| certifi | 2026.2.25 | MPL-2.0 | yes | +| cesu8 | 1.1.0 | Apache-2.0 OR MIT | no | +| cfb | 0.7.3 | MIT | no | +| cffi | 2.0.0 | MIT-0 | no | +| cfg-expr | 0.15.8 | MIT OR Apache-2.0 | no | +| cfg-if | 1.0.4 | MIT OR Apache-2.0 | no | +| chai | 5.3.3 | MIT | no | +| chai | 6.2.2 | MIT | no | +| charset-normalizer | 3.4.6 | MIT | no | +| check-error | 2.1.3 | MIT | no | +| chrono | 0.4.45 | MIT OR Apache-2.0 | no | +| class-variance-authority | 0.7.1 | Apache-2.0 | no | +| class-variance-authority | ^0.7.1 | Apache-2.0 | no | +| cloudpickle | 3.1.2 | BSD-3-Clause | no | +| clsx | 2.1.1 | MIT | no | +| clsx | ^2.1.1 | MIT | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| combine | 4.6.7 | MIT | no | +| comment-parser | 1.4.7 | MIT | no | +| convert-source-map | 2.0.0 | MIT | no | +| cookie | 0.18.1 | MIT OR Apache-2.0 | no | +| core-foundation | 0.10.1 | MIT OR Apache-2.0 | no | +| core-foundation-sys | 0.8.7 | MIT OR Apache-2.0 | no | +| core-graphics | 0.25.0 | MIT OR Apache-2.0 | no | +| core-graphics-types | 0.2.0 | MIT OR Apache-2.0 | no | +| coverage | 7.13.4 | Apache-2.0 | no | +| cpufeatures | 0.2.17 | MIT OR Apache-2.0 | no | +| crc32fast | 1.5.0 | MIT OR Apache-2.0 | no | +| cross-spawn | 7.0.6 | MIT | no | +| crossbeam-channel | 0.5.16 | MIT OR Apache-2.0 | no | +| crossbeam-utils | 0.8.22 | MIT OR Apache-2.0 | no | +| crypto-common | 0.1.7 | MIT OR Apache-2.0 | no | +| css-tree | 3.2.1 | MIT | no | +| css.escape | 1.5.1 | MIT | no | +| cssparser | 0.36.0 | MPL-2.0 | yes | +| cssparser-macros | 0.6.1 | MPL-2.0 | yes | +| csstype | 3.2.3 | MIT | no | +| ctor | 0.8.0 | Apache-2.0 OR MIT | no | +| ctor-proc-macro | 0.0.7 | Apache-2.0 OR MIT | no | +| cuda-bindings | 13.3.1 | Apache-2.0 | no | +| cuda-pathfinder | 1.5.6 | Apache-2.0 | no | +| cuda-toolkit | 13.0.2 | Apache-2.0 | no | +| darling | 0.23.0 | MIT | no | +| darling_core | 0.23.0 | MIT | no | +| darling_macro | 0.23.0 | MIT | no | +| data-urls | 7.0.0 | MIT | no | +| debug | 4.4.3 | MIT | no | +| decimal.js | 10.6.0 | MIT | no | +| decorator | 5.2.1 | BSD-2-Clause AND BSD-3-Clause | no | +| deep-eql | 5.0.2 | MIT | no | +| deep-is | 0.1.4 | MIT | no | +| default-browser | 5.5.0 | MIT | no | +| default-browser-id | 5.0.1 | MIT | no | +| define-lazy-prop | 3.0.0 | MIT | no | +| demucs | 4.0.1 | MIT | no | +| dequal | 2.0.3 | MIT | no | +| deranged | 0.5.8 | MIT OR Apache-2.0 | no | +| derive_more | 2.1.1 | MIT | no | +| derive_more-impl | 2.1.1 | MIT | no | +| detect-libc | 2.1.2 | Apache-2.0 | no | +| digest | 0.10.7 | MIT OR Apache-2.0 | no | +| dirs | 6.0.0 | MIT OR Apache-2.0 | no | +| dirs-sys | 0.5.0 | MIT OR Apache-2.0 | no | +| dispatch2 | 0.3.1 | Zlib OR Apache-2.0 OR MIT | no | +| displaydoc | 0.2.6 | MIT OR Apache-2.0 | no | +| dlib | 0.5.3 | MIT | no | +| dlopen2 | 0.8.2 | MIT | no | +| dlopen2_derive | 0.4.3 | MIT | no | +| doctrine | 3.0.0 | Apache-2.0 AND BSD-2-Clause | no | +| dom-accessibility-api | 0.5.16 | MIT | no | +| dom-accessibility-api | 0.6.3 | MIT | no | +| dom_query | 0.27.0 | MIT | no | +| dora-search | 0.1.12 | MIT | no | +| downcast-rs | 1.2.1 | MIT OR Apache-2.0 | no | +| dpi | 0.1.2 | Apache-2.0 AND MIT | no | +| dtoa | 1.0.11 | MIT OR Apache-2.0 | no | +| dtoa-short | 0.3.5 | MPL-2.0 | yes | +| dtor | 0.3.0 | Apache-2.0 OR MIT | no | +| dtor-proc-macro | 0.0.6 | Apache-2.0 OR MIT | no | +| dunce | 1.0.5 | CC0-1.0 OR MIT-0 OR Apache-2.0 | no | +| dyn-clone | 1.0.20 | MIT OR Apache-2.0 | no | +| einops | 0.8.2 | MIT | no | +| electron-to-chromium | 1.5.403 | ISC | no | +| embed-resource | 3.0.11 | MIT | no | +| embed_plist | 1.2.2 | MIT OR Apache-2.0 | no | +| empathic | 2.0.1 | MIT | no | +| enhanced-resolve | 5.24.5 | MIT | no | +| entities | 8.0.0 | BSD-2-Clause | no | +| equivalent | 1.0.2 | Apache-2.0 OR MIT | no | +| erased-serde | 0.4.10 | MIT OR Apache-2.0 | no | +| errno | 0.3.14 | MIT OR Apache-2.0 | no | +| es-errors | 1.3.0 | MIT | no | +| es-module-lexer | 2.3.1 | MIT | no | +| esbuild | 0.28.2 | MIT | no | +| escalade | 3.2.0 | MIT | no | +| escape-string-regexp | 4.0.0 | MIT | no | +| eslint | 10.8.1 | MIT | no | +| eslint | ^10.7.0 | NOASSERTION | yes | +| eslint-plugin-jsdoc | 63.3.3 | BSD-3-Clause | no | +| eslint-scope | 9.1.2 | BSD-2-Clause | no | +| eslint-visitor-keys | 3.4.3 | Apache-2.0 | no | +| eslint-visitor-keys | 5.0.1 | Apache-2.0 | no | +| espree | 11.2.0 | BSD-2-Clause | no | +| esprima | 4.0.1 | BSD-2-Clause AND BSD-3-Clause | no | +| esquery | 1.7.0 | BSD-3-Clause | no | +| esrecurse | 4.3.0 | BSD-2-Clause | no | +| estraverse | 5.3.0 | BSD-2-Clause | no | +| estree-walker | 2.0.2 | MIT | no | +| estree-walker | 3.0.3 | MIT | no | +| esutils | 2.0.3 | BSD-2-Clause | no | +| expect-type | 1.4.0 | Apache-2.0 | no | +| fast-check | 4.9.0 | MIT | no | +| fast-check | ^4.8.0 | MIT | no | +| fast-deep-equal | 3.1.3 | MIT | no | +| fast-json-stable-stringify | 2.1.0 | MIT | no | +| fast-levenshtein | 2.0.6 | MIT | no | +| fastrand | 2.4.1 | Apache-2.0 OR MIT | no | +| fdeflate | 0.3.7 | MIT OR Apache-2.0 | no | +| fdir | 6.5.0 | MIT | no | +| field-offset | 0.3.6 | MIT OR Apache-2.0 | no | +| file-entry-cache | 8.0.0 | MIT | no | +| filelock | 3.29.5 | MIT | no | +| find-msvc-tools | 0.1.9 | MIT OR Apache-2.0 | no | +| find-up | 5.0.0 | MIT | no | +| flat-cache | 4.0.1 | MIT | no | +| flate2 | 1.1.9 | MIT OR Apache-2.0 | no | +| flatted | 3.4.4 | ISC | no | +| fnv | 1.0.7 | Apache-2.0 OR MIT | no | +| foldhash | 0.2.0 | Zlib | no | +| foreign-types | 0.5.0 | MIT OR Apache-2.0 | no | +| foreign-types-macros | 0.2.3 | MIT OR Apache-2.0 | no | +| foreign-types-shared | 0.3.1 | MIT OR Apache-2.0 | no | +| form_urlencoded | 1.2.2 | MIT OR Apache-2.0 | no | +| fsevents | 2.3.3 | MIT | no | +| fsspec | 2026.6.0 | BSD-3-Clause | no | +| function-bind | 1.1.2 | MIT | no | +| futures-channel | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-core | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-executor | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-io | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-macro | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-sink | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-task | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-util | 0.3.32 | MIT OR Apache-2.0 | no | +| gdk | 0.18.2 | MIT | no | +| gdk-pixbuf | 0.18.5 | MIT | no | +| gdk-pixbuf-sys | 0.18.0 | MIT | no | +| gdk-sys | 0.18.2 | MIT | no | +| gdkwayland-sys | 0.18.2 | MIT | no | +| generic-array | 0.14.7 | MIT | no | +| gensync | 1.0.0-beta.2 | MIT | no | +| getrandom | 0.2.17 | MIT OR Apache-2.0 | no | +| getrandom | 0.3.4 | MIT OR Apache-2.0 | no | +| getrandom | 0.4.3 | MIT OR Apache-2.0 | no | +| gio | 0.18.4 | MIT | no | +| gio-sys | 0.18.1 | MIT | no | +| github/codeql-action/upload-sarif | 99df26d4f13ea111d4ec1a7dddef6063f76b97e9 | NOASSERTION | yes | +| glib | 0.18.5 | MIT | no | +| glib-macros | 0.18.5 | MIT | no | +| glib-sys | 0.18.1 | MIT | no | +| glob | 0.3.3 | MIT OR Apache-2.0 | no | +| glob | 13.0.6 | BlueOak-1.0.0 | no | +| glob-parent | 6.0.2 | ISC | no | +| gobject-sys | 0.18.0 | MIT | no | +| graceful-fs | 4.2.11 | ISC | no | +| gtk | 0.18.2 | MIT | no | +| gtk-sys | 0.18.2 | MIT | no | +| gtk3-macros | 0.18.2 | MIT | no | +| has-flag | 4.0.0 | MIT | no | +| hashbrown | 0.12.3 | MIT OR Apache-2.0 | no | +| hashbrown | 0.17.1 | MIT OR Apache-2.0 | no | +| hasown | 2.0.4 | MIT | no | +| heck | 0.4.1 | MIT OR Apache-2.0 | no | +| heck | 0.5.0 | MIT OR Apache-2.0 | no | +| hex | 0.4.3 | MIT OR Apache-2.0 | no | +| html-encoding-sniffer | 6.0.0 | MIT | no | +| html-entities | 2.6.0 | MIT | no | +| html-escaper | 2.0.2 | MIT | no | +| html5ever | 0.38.0 | MIT OR Apache-2.0 | no | +| http | 1.4.2 | MIT OR Apache-2.0 | no | +| http-body | 1.0.1 | MIT | no | +| http-body-util | 0.1.3 | MIT | no | +| httparse | 1.10.1 | MIT OR Apache-2.0 | no | +| hyper | 1.10.1 | MIT | no | +| hyper-util | 0.1.20 | MIT | no | +| iana-time-zone | 0.1.65 | MIT OR Apache-2.0 | no | +| iana-time-zone-haiku | 0.1.2 | MIT OR Apache-2.0 | no | +| ico | 0.5.0 | MIT | no | +| icu_collections | 2.2.0 | Unicode-3.0 | no | +| icu_locale_core | 2.2.0 | Unicode-3.0 | no | +| icu_normalizer | 2.2.0 | Unicode-3.0 | no | +| icu_normalizer_data | 2.2.0 | Unicode-3.0 | no | +| icu_properties | 2.2.0 | Unicode-3.0 | no | +| icu_properties_data | 2.2.0 | Unicode-3.0 | no | +| icu_provider | 2.2.0 | Unicode-3.0 | no | +| ident_case | 1.0.1 | MIT OR Apache-2.0 | no | +| idna | 1.1.0 | MIT OR Apache-2.0 | no | +| idna | 3.18 | BSD-3-Clause | no | +| idna_adapter | 1.2.2 | Apache-2.0 OR MIT | no | +| ignore | 5.3.2 | MIT | no | +| ignore | 7.0.6 | MIT | no | +| imurmurhash | 0.1.4 | MIT | no | +| indent-string | 4.0.0 | MIT | no | +| indexmap | 1.9.3 | Apache-2.0 OR MIT | no | +| indexmap | 2.14.0 | Apache-2.0 OR MIT | no | +| infer | 0.19.0 | MIT | no | +| iniconfig | 2.3.0 | MIT | no | +| ipnet | 2.12.0 | MIT OR Apache-2.0 | no | +| is-core-module | 2.16.2 | MIT | no | +| is-docker | 3.0.0 | MIT | no | +| is-extglob | 2.1.1 | MIT | no | +| is-glob | 4.0.3 | MIT | no | +| is-inside-container | 1.0.0 | MIT | no | +| is-potential-custom-element-name | 1.0.1 | MIT | no | +| is-wsl | 3.1.1 | MIT | no | +| isexe | 2.0.0 | ISC | no | +| istanbul-lib-coverage | 3.2.2 | BSD-3-Clause | no | +| istanbul-lib-report | 3.0.1 | BSD-3-Clause | no | +| istanbul-reports | 3.2.0 | BSD-3-Clause | no | +| itoa | 1.0.18 | MIT OR Apache-2.0 | no | +| javascriptcore-rs | 1.1.2 | MIT | no | +| javascriptcore-rs-sys | 1.1.1 | MIT | no | +| jinja2 | 3.1.6 | BSD-2-Clause AND BSD-3-Clause | no | +| jiti | 2.7.0 | MIT | no | +| jni | 0.21.1 | MIT OR Apache-2.0 | no | +| jni-sys | 0.3.1 | MIT OR Apache-2.0 | no | +| jni-sys | 0.4.1 | MIT OR Apache-2.0 | no | +| jni-sys-macros | 0.4.1 | MIT OR Apache-2.0 | no | +| joblib | 1.5.3 | BSD-3-Clause | no | +| js-sys | 0.3.103 | MIT OR Apache-2.0 | no | +| js-tokens | 10.0.0 | MIT | no | +| js-tokens | 4.0.0 | MIT | no | +| jsdoc-type-pratt-parser | 8.0.0 | MIT | no | +| jsdom | 29.1.1 | MIT | no | +| jsdom | ^29.1.1 | MIT | no | +| jsesc | 3.1.0 | MIT | no | +| json-buffer | 3.0.1 | MIT | no | +| json-patch | 3.0.1 | MIT OR Apache-2.0 | no | +| json-schema-traverse | 0.4.1 | MIT | no | +| json-stable-stringify-without-jsonify | 1.0.1 | MIT | no | +| json5 | 2.2.3 | MIT | no | +| jsonc-parser | 3.3.1 | MIT | no | +| jsonptr | 0.6.3 | MIT OR Apache-2.0 | no | +| julius | 0.2.8 | MIT | no | +| keyboard-types | 0.7.0 | MIT OR Apache-2.0 | no | +| keyv | 4.5.4 | MIT | no | +| lameenc | 1.8.4 | GPL-3.0-or-later | yes | +| lazy-loader | 0.5 | BSD-3-Clause | no | +| levn | 0.4.1 | MIT | no | +| libc | 0.2.186 | MIT OR Apache-2.0 | no | +| libloading | 0.8.9 | ISC | no | +| libredox | 0.1.18 | MIT | no | +| librosa | 0.11.0 | ISC | no | +| librt | 0.8.1 | BSD-2-Clause AND MIT AND Python-2.0 AND Python-2.0.1 | no | +| lightningcss | 1.32.0 | MPL-2.0 | yes | +| lightningcss | 1.33.0 | MPL-2.0 | yes | +| lightningcss-android-arm64 | 1.32.0 | MPL-2.0 | yes | +| lightningcss-android-arm64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-darwin-arm64 | 1.32.0 | MPL-2.0 | yes | +| lightningcss-darwin-arm64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-darwin-x64 | 1.32.0 | MPL-2.0 | yes | +| lightningcss-darwin-x64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-freebsd-x64 | 1.32.0 | MPL-2.0 | yes | +| lightningcss-freebsd-x64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm-gnueabihf | 1.32.0 | MPL-2.0 | yes | +| lightningcss-linux-arm-gnueabihf | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-gnu | 1.32.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-gnu | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-musl | 1.32.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-musl | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-gnu | 1.32.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-gnu | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-musl | 1.32.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-musl | 1.33.0 | MPL-2.0 | yes | +| lightningcss-win32-arm64-msvc | 1.32.0 | MPL-2.0 | yes | +| lightningcss-win32-arm64-msvc | 1.33.0 | MPL-2.0 | yes | +| lightningcss-win32-x64-msvc | 1.32.0 | MPL-2.0 | yes | +| lightningcss-win32-x64-msvc | 1.33.0 | MPL-2.0 | yes | +| linux-raw-sys | 0.12.1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| litemap | 0.8.2 | Unicode-3.0 | no | +| llvmlite | 0.45.1 | BSD-2-Clause | no | +| locate-path | 6.0.0 | MIT | no | +| lock_api | 0.4.14 | MIT OR Apache-2.0 | no | +| log | 0.4.33 | MIT OR Apache-2.0 | no | +| loupe | 3.2.1 | MIT | no | +| lru-cache | 11.5.2 | BlueOak-1.0.0 | no | +| lru-cache | 5.1.1 | ISC | no | +| lucide-react | 1.30.0 | ISC | no | +| lucide-react | ^1.24.0 | NOASSERTION | yes | +| lz-string | 1.5.0 | MIT | no | +| magic-string | 0.30.21 | MIT | no | +| magicast | 0.5.4 | MIT | no | +| make-dir | 4.0.0 | MIT | no | +| markdown-it-py | 4.0.0 | MIT | no | +| markup5ever | 0.38.0 | MIT OR Apache-2.0 | no | +| markupsafe | 3.0.3 | BSD-3-Clause | no | +| matrixmultiply | 0.3.10 | MIT OR Apache-2.0 | no | +| maturin | — | NOASSERTION | yes | +| mdn-data | 2.27.1 | CC0-1.0 | no | +| mdurl | 0.1.2 | MIT | no | +| memchr | 2.8.3 | Unlicense OR MIT | no | +| memoffset | 0.9.1 | MIT | no | +| mime | 0.3.17 | MIT OR Apache-2.0 | no | +| min-indent | 1.0.1 | MIT | no | +| minimatch | 10.2.6 | BlueOak-1.0.0 | no | +| minimist | 1.2.8 | MIT | no | +| minipass | 7.1.3 | BlueOak-1.0.0 | no | +| miniz_oxide | 0.8.9 | MIT OR Zlib OR Apache-2.0 | no | +| mio | 1.2.1 | MIT | no | +| mpmath | 1.3.0 | BSD-3-Clause | no | +| ms | 2.1.3 | MIT | no | +| msgpack | 1.2.1 | Apache-2.0 | no | +| muda | 0.19.3 | Apache-2.0 OR MIT | no | +| mypy | 1.19.1 | BSD-2-Clause AND MIT AND Python-2.0 AND Python-2.0.1 | no | +| mypy-extensions | 1.1.0 | MIT | no | +| nanoid | 3.3.18 | MIT | no | +| natural-compare | 1.4.0 | MIT | no | +| ndarray | 0.16.1 | MIT OR Apache-2.0 | no | +| ndk | 0.9.0 | MIT OR Apache-2.0 | no | +| ndk-sys | 0.6.0+11769913 | MIT OR Apache-2.0 | no | +| networkx | 3.6.1 | BSD-3-Clause | no | +| new_debug_unreachable | 1.0.6 | MIT | no | +| node-releases | 2.0.53 | MIT | no | +| num-complex | 0.4.6 | MIT OR Apache-2.0 | no | +| num-conv | 0.2.2 | MIT OR Apache-2.0 | no | +| num-integer | 0.1.46 | MIT OR Apache-2.0 | no | +| num-traits | 0.2.19 | MIT OR Apache-2.0 | no | +| num_enum | 0.7.6 | BSD-3-Clause OR MIT OR Apache-2.0 | no | +| num_enum_derive | 0.7.6 | BSD-3-Clause OR MIT OR Apache-2.0 | no | +| numba | 0.62.1 | 0BSD AND BSD-2-Clause AND BSD-3-Clause AND BSD-4-Clause AND LicenseRef-scancode-python-cwi AND LicenseRef-scancode-secret-labs-2011 AND LicenseRef-scancode-unicode AND MIT AND Python-2.0 | no | +| numpy | 0.29.0 | BSD-2-Clause | no | +| numpy | 2.3.5 | Apache-2.0 AND BSD-3-Clause AND MIT AND Zlib | no | +| nvidia-cublas | 13.1.1.3 | Apache-2.0 | no | +| nvidia-cuda-cupti | 13.0.85 | Apache-2.0 | no | +| nvidia-cuda-nvrtc | 13.0.88 | LicenseRef-NVIDIA-Proprietary | no | +| nvidia-cuda-runtime | 13.0.96 | Apache-2.0 | no | +| nvidia-cudnn-cu13 | 9.20.0.48 | Apache-2.0 | no | +| nvidia-cufft | 12.0.0.61 | Apache-2.0 | no | +| nvidia-cufile | 1.15.1.6 | LicenseRef-NVIDIA-Proprietary | no | +| nvidia-curand | 10.4.0.35 | LicenseRef-NVIDIA-Proprietary | no | +| nvidia-cusolver | 12.0.4.66 | LicenseRef-NVIDIA-Proprietary | no | +| nvidia-cusparse | 12.6.3.3 | LicenseRef-NVIDIA-Proprietary | no | +| nvidia-cusparselt-cu13 | 0.8.1 | Apache-2.0 | no | +| nvidia-nccl-cu13 | 2.29.7 | Apache-2.0 | no | +| nvidia-nvjitlink | 13.0.88 | LicenseRef-NVIDIA-Proprietary | no | +| nvidia-nvshmem-cu13 | 3.4.5 | Apache-2.0 | no | +| nvidia-nvtx | 13.0.85 | LicenseRef-NVIDIA-Proprietary | no | +| objc2 | 0.6.4 | MIT | no | +| objc2-app-kit | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-cloud-kit | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-core-data | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-core-foundation | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-core-graphics | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-core-image | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-core-location | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-core-text | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-encode | 4.1.0 | MIT | no | +| objc2-exception-helper | 0.1.1 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-foundation | 0.3.2 | MIT | no | +| objc2-io-surface | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-quartz-core | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-ui-kit | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-user-notifications | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-web-kit | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| object-deep-merge | 2.0.1 | MIT | no | +| obug | 2.1.4 | MIT | no | +| omegaconf | 2.3.1 | BSD-2-Clause AND BSD-3-Clause | no | +| once_cell | 1.21.4 | MIT OR Apache-2.0 | no | +| open | 10.2.0 | MIT | no | +| openunmix | 1.3.0 | MIT | no | +| option-ext | 0.2.0 | MPL-2.0 | yes | +| optionator | 0.9.4 | MIT | no | +| ossf/scorecard-action | 4eaacf0543bb3f2c246792bd56e8cdeffafb205a | NOASSERTION | yes | +| oxc-parser | 0.127.0 | MIT | no | +| oxc-resolver | 11.24.2 | MIT | no | +| p-limit | 3.1.0 | MIT | no | +| p-locate | 5.0.0 | MIT | no | +| packaging | 26.0 | Apache-2.0 AND BSD-2-Clause | no | +| pango | 0.18.3 | MIT | no | +| pango-sys | 0.18.0 | MIT | no | +| parking_lot | 0.12.5 | MIT OR Apache-2.0 | no | +| parking_lot_core | 0.9.12 | MIT OR Apache-2.0 | no | +| parse-imports-exports | 0.2.4 | MIT | no | +| parse-statements | 1.0.11 | JSON AND MIT | no | +| parse5 | 8.0.1 | MIT | no | +| path-exists | 4.0.0 | MIT | no | +| path-key | 3.1.1 | MIT | no | +| path-parse | 1.0.7 | MIT | no | +| path-scurry | 2.0.2 | BlueOak-1.0.0 | no | +| pathe | 2.0.3 | MIT | no | +| pathspec | 1.0.4 | MPL-2.0 | yes | +| pathval | 2.0.1 | MIT | no | +| pdfjs-dist | 6.2.108 | Apache-2.0 | no | +| percent-encoding | 2.3.2 | MIT OR Apache-2.0 | no | +| phf | 0.13.1 | MIT | no | +| phf_codegen | 0.13.1 | MIT | no | +| phf_generator | 0.13.1 | MIT | no | +| phf_macros | 0.13.1 | MIT | no | +| phf_shared | 0.13.1 | MIT | no | +| picocolors | 1.1.1 | ISC | no | +| picomatch | 4.0.5 | MIT | no | +| pin-project-lite | 0.2.17 | Apache-2.0 OR MIT | no | +| pkg-config | 0.3.33 | MIT OR Apache-2.0 | no | +| platformdirs | 4.9.4 | MIT | no | +| plist | 1.10.0 | MIT | no | +| pluggy | 1.6.0 | MIT | no | +| png | 0.17.16 | MIT OR Apache-2.0 | no | +| png | 0.18.1 | MIT OR Apache-2.0 | no | +| pollster | 0.4.0 | Apache-2.0 OR MIT | no | +| pooch | 1.9.0 | BSD-3-Clause | no | +| portable-atomic | 1.13.1 | Apache-2.0 OR MIT | no | +| portable-atomic-util | 0.2.7 | Apache-2.0 OR MIT | no | +| postcss | 8.5.25 | MIT | no | +| potential_utf | 0.1.5 | Unicode-3.0 | no | +| powerfmt | 0.2.0 | MIT OR Apache-2.0 | no | +| precomputed-hash | 0.1.1 | MIT | no | +| prelude-ls | 1.2.1 | MIT | no | +| pretty-format | 27.5.1 | MIT | no | +| proc-macro-crate | 1.3.1 | MIT OR Apache-2.0 | no | +| proc-macro-crate | 2.0.2 | MIT OR Apache-2.0 | no | +| proc-macro-crate | 3.5.0 | MIT OR Apache-2.0 | no | +| proc-macro-error | 1.0.4 | MIT OR Apache-2.0 | no | +| proc-macro-error-attr | 1.0.4 | MIT OR Apache-2.0 | no | +| proc-macro2 | 1.0.106 | MIT OR Apache-2.0 | no | +| punycode | 2.3.1 | MIT | no | +| pure-rand | 8.4.2 | MIT | no | +| pycparser | 3.0 | BSD-3-Clause | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pyo3 | 0.29.0 | MIT OR Apache-2.0 | no | +| pyo3-build-config | 0.29.0 | MIT OR Apache-2.0 | no | +| pyo3-ffi | 0.29.0 | MIT OR Apache-2.0 | no | +| pyo3-macros | 0.29.0 | MIT OR Apache-2.0 | no | +| pyo3-macros-backend | 0.29.0 | MIT OR Apache-2.0 | no | +| pytest | 9.0.3 | MIT | no | +| pytest-cov | 7.0.0 | MIT | no | +| pyyaml | 6.0.3 | MIT | no | +| quick-xml | 0.39.4 | MIT | no | +| quick-xml | 0.41.0 | MIT | no | +| quote | 1.0.46 | MIT OR Apache-2.0 | no | +| r-efi | 5.3.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | yes | +| r-efi | 6.0.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | yes | +| raw-window-handle | 0.6.2 | MIT OR Apache-2.0 OR Zlib | no | +| rawpointer | 0.2.1 | MIT OR Apache-2.0 | no | +| react | 19.2.8 | MIT | no | +| react | ^19.2.4 | NOASSERTION | yes | +| react-docgen | 8.0.3 | MIT | no | +| react-docgen-typescript | 2.4.0 | MIT | no | +| react-dom | 19.2.8 | MIT | no | +| react-dom | ^19.2.7 | NOASSERTION | yes | +| react-is | 17.0.2 | MIT | no | +| recast | 0.23.19 | MIT | no | +| redent | 3.0.0 | MIT | no | +| redox_syscall | 0.5.18 | MIT | no | +| redox_users | 0.5.2 | MIT | no | +| ref-cast | 1.0.25 | MIT OR Apache-2.0 | no | +| ref-cast-impl | 1.0.25 | MIT OR Apache-2.0 | no | +| regex | 1.13.0 | MIT OR Apache-2.0 | no | +| regex-automata | 0.4.15 | MIT OR Apache-2.0 | no | +| regex-syntax | 0.8.11 | MIT OR Apache-2.0 | no | +| requests | 2.33.0 | Apache-2.0 | no | +| require-from-string | 2.0.2 | MIT | no | +| reqwest | 0.13.4 | MIT OR Apache-2.0 | no | +| reselect | 5.2.0 | MIT | no | +| reserved-identifiers | 1.2.0 | MIT | no | +| resolve | 1.22.12 | MIT | no | +| retrying | 1.4.2 | Apache-2.0 | no | +| rfd | 0.17.2 | MIT | no | +| rich | 15.0.0 | MIT | no | +| rolldown | 1.2.3 | MIT | no | +| ruff | 0.15.5 | MIT | no | +| run-applescript | 7.1.0 | MIT | no | +| rustc-hash | 2.1.3 | Apache-2.0 OR MIT | no | +| rustc_version | 0.4.1 | MIT OR Apache-2.0 | no | +| rustix | 1.1.4 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| rustversion | 1.0.23 | MIT OR Apache-2.0 | no | +| same-file | 1.0.6 | Unlicense OR MIT | no | +| saxes | 6.0.0 | ISC | no | +| scheduler | 0.27.0 | MIT | no | +| schemars | 0.8.22 | MIT | no | +| schemars | 0.9.0 | MIT | no | +| schemars | 1.2.1 | MIT | no | +| schemars_derive | 0.8.22 | MIT | no | +| scikit-learn | 1.8.0 | BSD-3-Clause | no | +| scipy | 1.17.1 | BSD-3-Clause | no | +| scoped-tls | 1.0.1 | MIT OR Apache-2.0 | no | +| scopeguard | 1.2.0 | MIT OR Apache-2.0 | no | +| selectors | 0.36.1 | MPL-2.0 | yes | +| semver | 1.0.28 | MIT OR Apache-2.0 | no | +| semver | 6.3.1 | ISC | no | +| semver | 7.8.5 | ISC | no | +| serde | 1.0.228 | MIT OR Apache-2.0 | no | +| serde | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| serde-untagged | 0.1.9 | MIT OR Apache-2.0 | no | +| serde_core | 1.0.228 | MIT OR Apache-2.0 | no | +| serde_derive | 1.0.228 | MIT OR Apache-2.0 | no | +| serde_derive_internals | 0.29.1 | MIT OR Apache-2.0 | no | +| serde_json | 1.0.150 | MIT OR Apache-2.0 | no | +| serde_json | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| serde_repr | 0.1.20 | MIT OR Apache-2.0 | no | +| serde_spanned | 0.6.9 | MIT OR Apache-2.0 | no | +| serde_spanned | 1.1.1 | MIT OR Apache-2.0 | no | +| serde_with | 3.21.0 | MIT OR Apache-2.0 | no | +| serde_with_macros | 3.21.0 | MIT OR Apache-2.0 | no | +| serialize-to-javascript | 0.1.2 | MIT OR Apache-2.0 | no | +| serialize-to-javascript-impl | 0.1.2 | MIT OR Apache-2.0 | no | +| servo_arc | 0.4.3 | MIT OR Apache-2.0 | no | +| setuptools | 81.0.0 | MIT | no | +| sha2 | 0.10.9 | MIT OR Apache-2.0 | no | +| shebang-command | 2.0.0 | MIT | no | +| shebang-regex | 3.0.0 | MIT | no | +| shlex | 2.0.1 | MIT OR Apache-2.0 | no | +| siginfo | 2.0.0 | ISC | no | +| simd-adler32 | 0.3.9 | MIT | no | +| siphasher | 1.0.3 | MIT OR Apache-2.0 | no | +| slab | 0.4.12 | MIT | no | +| smallvec | 1.15.2 | MIT OR Apache-2.0 | no | +| socket2 | 0.6.4 | MIT OR Apache-2.0 | no | +| softbuffer | 0.4.8 | MIT OR Apache-2.0 | no | +| sonner | 2.0.8 | MIT | no | +| sonner | ^2.0.7 | MIT | no | +| soundfile | 0.13.1 | BSD-3-Clause AND Python-2.0 | no | +| soup3 | 0.5.0 | MIT | no | +| soup3-sys | 0.5.0 | MIT | no | +| source-map | 0.6.1 | BSD-3-Clause | no | +| source-map-js | 1.2.1 | BSD-3-Clause | no | +| soxr | 1.0.0 | Python-2.0 AND LGPL-2.1-or-later | yes | +| spdx-exceptions | 2.5.0 | CC-BY-3.0 | no | +| spdx-expression-parse | 5.0.0 | MIT | no | +| spdx-license-ids | 3.0.23 | CC0-1.0 | no | +| stable_deref_trait | 1.2.1 | MIT OR Apache-2.0 | no | +| stackback | 0.0.2 | MIT | no | +| standard-aifc | 3.13.0 | PSF-2.0 | no | +| standard-chunk | 3.13.0 | PSF-2.0 | no | +| standard-sunau | 3.13.0 | PSF-2.0 | no | +| std-env | 4.2.0 | MIT | no | +| stevedore | 5.7.0 | Apache-2.0 | no | +| storybook | 10.5.7 | MIT | no | +| storybook | ^10.4.6 | NOASSERTION | yes | +| string_cache | 0.9.0 | MIT OR Apache-2.0 | no | +| string_cache_codegen | 0.6.1 | MIT OR Apache-2.0 | no | +| strip-bom | 3.0.0 | MIT | no | +| strip-indent | 3.0.0 | MIT | no | +| strip-indent | 4.1.1 | MIT | no | +| strsim | 0.11.1 | MIT | no | +| submitit | 1.5.4 | MIT | no | +| supports-color | 7.2.0 | MIT | no | +| supports-preserve-symlinks-flag | 1.0.0 | MIT | no | +| swift-rs | 1.0.7 | MIT OR Apache-2.0 | no | +| symbol-tree | 3.2.4 | MIT | no | +| sympy | 1.14.0 | BSD-2-Clause AND BSD-3-Clause AND MIT | no | +| syn | 1.0.109 | MIT OR Apache-2.0 | no | +| syn | 2.0.118 | MIT OR Apache-2.0 | no | +| sync_wrapper | 1.0.2 | Apache-2.0 | no | +| synstructure | 0.13.2 | MIT | no | +| system-deps | 6.2.2 | MIT OR Apache-2.0 | no | +| tailwind-merge | 3.6.0 | MIT | no | +| tailwind-merge | ^3.6.0 | MIT | no | +| tailwindcss | 4.3.3 | MIT | no | +| tailwindcss | ^4.2.4 | MIT | no | +| tao | 0.35.3 | Apache-2.0 | no | +| tao-macros | 0.1.3 | MIT OR Apache-2.0 | no | +| tapable | 2.3.3 | MIT | no | +| target-lexicon | 0.12.16 | Apache-2.0 WITH LLVM-exception | no | +| target-lexicon | 0.13.5 | Apache-2.0 WITH LLVM-exception | no | +| tauri | 2.11.5 | Apache-2.0 OR MIT | no | +| tauri-build | 2.6.3 | Apache-2.0 OR MIT | no | +| tauri-codegen | 2.6.3 | Apache-2.0 OR MIT | no | +| tauri-macros | 2.6.3 | Apache-2.0 OR MIT | no | +| tauri-runtime | 2.11.3 | Apache-2.0 OR MIT | no | +| tauri-runtime-wry | 2.11.4 | Apache-2.0 OR MIT | no | +| tauri-utils | 2.9.3 | Apache-2.0 OR MIT | no | +| tauri-winres | 0.3.6 | MIT | no | +| tendril | 0.5.1 | MIT OR Apache-2.0 | no | +| thiserror | 1.0.69 | MIT OR Apache-2.0 | no | +| thiserror | 2.0.18 | MIT OR Apache-2.0 | no | +| thiserror-impl | 1.0.69 | MIT OR Apache-2.0 | no | +| thiserror-impl | 2.0.18 | MIT OR Apache-2.0 | no | +| threadpoolctl | 3.6.0 | BSD-3-Clause | no | +| time | 0.3.53 | MIT OR Apache-2.0 | no | +| time | >= 0.3.0,< 0.4.0 | NOASSERTION | yes | +| time-core | 0.1.9 | MIT OR Apache-2.0 | no | +| time-macros | 0.2.31 | MIT OR Apache-2.0 | no | +| tiny-invariant | 1.3.3 | MIT | no | +| tinybench | 2.9.0 | MIT | no | +| tinyexec | 1.3.0 | MIT | no | +| tinyglobby | 0.2.17 | MIT | no | +| tinyrainbow | 2.0.0 | MIT | no | +| tinyrainbow | 3.1.1 | MIT | no | +| tinyspy | 4.0.4 | MIT | no | +| tinystr | 0.8.3 | Unicode-3.0 | no | +| tinyvec | 1.12.0 | Zlib OR Apache-2.0 OR MIT | no | +| tinyvec_macros | 0.1.1 | MIT OR Apache-2.0 OR Zlib | no | +| tldts | 7.4.10 | MIT | no | +| tldts-core | 7.4.10 | MIT | no | +| to-valid-identifier | 1.0.0 | MIT | no | +| tokio | 1.52.3 | MIT | no | +| tokio-util | 0.7.18 | MIT | no | +| toml | 0.8.2 | MIT OR Apache-2.0 | no | +| toml | 0.9.12+spec-1.1.0 | MIT OR Apache-2.0 | no | +| toml | 1.1.2+spec-1.1.0 | MIT OR Apache-2.0 | no | +| toml_datetime | 0.6.3 | MIT OR Apache-2.0 | no | +| toml_datetime | 0.7.5+spec-1.1.0 | MIT OR Apache-2.0 | no | +| toml_datetime | 1.1.1+spec-1.1.0 | MIT OR Apache-2.0 | no | +| toml_edit | 0.19.15 | MIT OR Apache-2.0 | no | +| toml_edit | 0.20.2 | MIT OR Apache-2.0 | no | +| toml_edit | 0.25.12+spec-1.1.0 | MIT OR Apache-2.0 | no | +| toml_parser | 1.1.2+spec-1.1.0 | MIT OR Apache-2.0 | no | +| toml_writer | 1.1.1+spec-1.1.0 | MIT OR Apache-2.0 | no | +| torch | 2.12.1 | BSD-2-Clause | no | +| torchaudio | 2.11.0 | BSD-2-Clause | no | +| tough-cookie | 6.0.2 | BSD-3-Clause | no | +| tower | 0.5.3 | MIT | no | +| tower-http | 0.6.11 | MIT | no | +| tower-layer | 0.3.3 | MIT | no | +| tower-service | 0.3.3 | MIT | no | +| tqdm | 4.68.3 | MIT AND MPL-2.0 | yes | +| tr46 | 6.0.0 | MIT | no | +| tracing | 0.1.44 | MIT | no | +| tracing-core | 0.1.36 | MIT | no | +| treetable | 0.2.6 | Unlicense | no | +| triton | 3.7.1 | MIT | no | +| try-lock | 0.2.5 | MIT | no | +| ts-api-utils | 2.5.0 | MIT | no | +| ts-dedent | 2.3.0 | MIT | no | +| tsconfig-paths | 4.2.0 | MIT | no | +| tslib | 2.8.1 | 0BSD | no | +| tw-animate-css | 1.4.0 | MIT | no | +| tw-animate-css | ^1.4.0 | MIT | no | +| type-check | 0.4.0 | MIT | no | +| typeid | 1.0.3 | MIT OR Apache-2.0 | no | +| typenum | 1.20.1 | MIT OR Apache-2.0 | no | +| typescript | 6.0.3 | Apache-2.0 | no | +| typescript | ^6.0.3 | Apache-2.0 | no | +| typescript-eslint | 8.66.0 | MIT | no | +| typescript-eslint | ^8.63.0 | MIT | no | +| typing-extensions | 4.15.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| undici | 7.29.0 | MIT | no | +| undici-types | 8.3.0 | MIT | no | +| unic-char-property | 0.9.0 | MIT OR Apache-2.0 | no | +| unic-char-range | 0.9.0 | MIT OR Apache-2.0 | no | +| unic-common | 0.9.0 | MIT OR Apache-2.0 | no | +| unic-ucd-ident | 0.9.0 | MIT OR Apache-2.0 | no | +| unic-ucd-version | 0.9.0 | MIT OR Apache-2.0 | no | +| unicode-ident | 1.0.24 | (MIT OR Apache-2.0) AND Unicode-3.0 | no | +| unicode-segmentation | 1.13.3 | MIT OR Apache-2.0 | no | +| unplugin | 2.3.11 | MIT | no | +| update-browserslist-db | 1.3.0 | MIT | no | +| uri-js | 4.4.1 | BSD-2-Clause AND BSD-2-Clause-Views | no | +| url | 2.5.8 | MIT OR Apache-2.0 | no | +| url | >= 2.5.8,< 3.0.0 | NOASSERTION | yes | +| urllib3 | 2.7.0 | MIT | no | +| urlpattern | 0.3.0 | MIT | no | +| use-sync-external-store | 1.6.0 | MIT | no | +| utf8_iter | 1.0.4 | Apache-2.0 OR MIT | no | +| uuid | 1.23.4 | Apache-2.0 OR MIT | no | +| uuid | 1.25.0 | Apache-2.0 OR MIT | no | +| uuid | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| version-compare | 0.2.1 | MIT | no | +| version_check | 0.9.5 | MIT OR Apache-2.0 | no | +| vite | 8.2.1 | MIT | no | +| vite | ^8.1.4 | NOASSERTION | yes | +| vitest | 4.1.10 | MIT | no | +| vitest | ^4.1.10 | NOASSERTION | yes | +| vswhom | 0.1.0 | MIT | no | +| vswhom-sys | 0.1.3 | MIT | no | +| w3c-xmlserializer | 5.0.0 | MIT | no | +| walkdir | 2.5.0 | Unlicense OR MIT | no | +| want | 0.3.1 | MIT | no | +| wasi | 0.11.1+wasi-snapshot-preview1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| wasip2 | 1.0.4+wasi-0.2.12 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| wasm-bindgen | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-futures | 0.4.76 | MIT OR Apache-2.0 | no | +| wasm-bindgen-macro | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-macro-support | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-shared | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-streams | 0.5.0 | MIT OR Apache-2.0 | no | +| wayland-backend | 0.3.15 | MIT | no | +| wayland-client | 0.31.14 | MIT | no | +| wayland-protocols | 0.32.13 | MIT | no | +| wayland-scanner | 0.31.10 | MIT | no | +| wayland-sys | 0.31.11 | MIT | no | +| web-sys | 0.3.103 | MIT OR Apache-2.0 | no | +| web_atoms | 0.2.5 | MIT OR Apache-2.0 | no | +| webidl-conversions | 8.0.1 | BSD-2-Clause | no | +| webkit2gtk | 2.0.2 | MIT | no | +| webkit2gtk-sys | 2.0.2 | MIT | no | +| webpack-virtual-modules | 0.6.2 | MIT | no | +| webview2-com | 0.38.2 | MIT | no | +| webview2-com-macros | 0.8.1 | MIT | no | +| webview2-com-sys | 0.38.2 | MIT | no | +| whatwg-mimetype | 5.0.0 | MIT | no | +| whatwg-url | 16.0.1 | MIT | no | +| which | 2.0.2 | ISC | no | +| why-is-node-running | 2.3.0 | MIT | no | +| winapi | 0.3.9 | MIT OR Apache-2.0 | no | +| winapi-i686-pc-windows-gnu | 0.4.0 | MIT OR Apache-2.0 | no | +| winapi-util | 0.1.11 | Unlicense OR MIT | no | +| winapi-x86_64-pc-windows-gnu | 0.4.0 | MIT OR Apache-2.0 | no | +| window-vibrancy | 0.6.0 | Apache-2.0 OR MIT | no | +| windows | 0.61.3 | MIT OR Apache-2.0 | no | +| windows-collections | 0.2.0 | MIT OR Apache-2.0 | no | +| windows-core | 0.61.2 | MIT OR Apache-2.0 | no | +| windows-core | 0.62.2 | MIT OR Apache-2.0 | no | +| windows-future | 0.2.1 | MIT OR Apache-2.0 | no | +| windows-implement | 0.60.2 | MIT OR Apache-2.0 | no | +| windows-interface | 0.59.3 | MIT OR Apache-2.0 | no | +| windows-link | 0.1.3 | MIT OR Apache-2.0 | no | +| windows-link | 0.2.1 | MIT OR Apache-2.0 | no | +| windows-numerics | 0.2.0 | MIT OR Apache-2.0 | no | +| windows-result | 0.3.4 | MIT OR Apache-2.0 | no | +| windows-result | 0.4.1 | MIT OR Apache-2.0 | no | +| windows-strings | 0.4.2 | MIT OR Apache-2.0 | no | +| windows-strings | 0.5.1 | MIT OR Apache-2.0 | no | +| windows-sys | 0.45.0 | MIT OR Apache-2.0 | no | +| windows-sys | 0.59.0 | MIT OR Apache-2.0 | no | +| windows-sys | 0.61.2 | MIT OR Apache-2.0 | no | +| windows-targets | 0.42.2 | MIT OR Apache-2.0 | no | +| windows-targets | 0.52.6 | MIT OR Apache-2.0 | no | +| windows-threading | 0.1.0 | MIT OR Apache-2.0 | no | +| windows-version | 0.1.7 | MIT OR Apache-2.0 | no | +| windows_aarch64_gnullvm | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_aarch64_gnullvm | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_aarch64_msvc | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_aarch64_msvc | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_i686_gnu | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_i686_gnu | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_i686_gnullvm | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_i686_msvc | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_i686_msvc | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnu | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnu | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnullvm | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnullvm | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_x86_64_msvc | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_x86_64_msvc | 0.52.6 | MIT OR Apache-2.0 | no | +| winnow | 0.5.40 | MIT | no | +| winnow | 0.7.15 | MIT | no | +| winnow | 1.0.3 | MIT | no | +| winreg | 0.55.0 | MIT | no | +| wit-bindgen | 0.57.1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| word-wrap | 1.2.5 | MIT | no | +| writeable | 0.6.3 | Unicode-3.0 | no | +| wry | 0.55.1 | Apache-2.0 OR MIT | no | +| ws | 8.21.3 | MIT | no | +| wsl-utils | 0.1.0 | MIT | no | +| xml-name-validator | 5.0.0 | Apache-2.0 | no | +| xmlchars | 2.2.0 | MIT | no | +| yallist | 3.1.1 | ISC | no | +| yocto-queue | 0.1.0 | MIT | no | +| yoke | 0.8.3 | Unicode-3.0 | no | +| yoke-derive | 0.8.2 | Unicode-3.0 | no | +| yt-dlp | 2026.7.4 | Unlicense AND Unlicense AND GPL-3.0-or-later AND MPL-2.0 AND MIT AND BSD-3-Clause AND Apache-2.0 AND MIT AND GPL-2.0-or-later AND BSD-3-Clause AND MIT AND LGPL-2.1-only AND BSD-2-Clause AND GPL-2.0-or-later | yes | +| zerofrom | 0.1.8 | Unicode-3.0 | no | +| zerofrom-derive | 0.1.7 | Unicode-3.0 | no | +| zerotrie | 0.2.4 | Unicode-3.0 | no | +| zerovec | 0.11.6 | Unicode-3.0 | no | +| zerovec-derive | 0.11.3 | Unicode-3.0 | no | +| zmij | 1.0.21 | MIT | no | + +### ContextualWisdomLab/CalendarWeave + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/ccube-jco-potential-customer + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/clearfolio + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/setup-java | b6effb05e454b25005698d916606bdc6ffcbf961 | NOASSERTION | yes | +| actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| com.code-intelligence:jazzer-api | 0.30.0 | Apache-2.0 | no | +| com.code-intelligence:jazzer-junit | 0.30.0 | Apache-2.0 | no | +| com.fasterxml.jackson.core:jackson-databind | 2.22.1 | Apache-2.0 | no | +| com.fasterxml.jackson:jackson-bom | 2.22.1 | Apache-2.0 | no | +| com.github.junrar:junrar | 8.1.0 | LicenseRef-bad-non-standard | no | +| commons-io:commons-io | 2.22.0 | Apache-2.0 | no | +| iniconfig | 2.3.0 | MIT | no | +| org.apache.commons:commons-lang3 | 3.20.0 | Apache-2.0 | no | +| org.apache.maven.plugins:maven-compiler-plugin | — | NOASSERTION | yes | +| org.apache.maven.plugins:maven-javadoc-plugin | 3.12.0 | Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND LicenseRef-scancode-public-domain AND MIT | no | +| org.apache.maven.plugins:maven-surefire-plugin | — | NOASSERTION | yes | +| org.apache.pdfbox:pdfbox | 3.0.8 | Apache-2.0 | no | +| org.bouncycastle:bcpkix-jdk18on | 1.85 | MIT | no | +| org.bouncycastle:bcprov-jdk18on | 1.85.2 | LicenseRef-bad-non-standard | no | +| org.jacoco:jacoco-maven-plugin | 0.8.15 | EPL-2.0 OR (Apache-2.0 AND EPL-2.0) | yes | +| org.springframework.boot:spring-boot-maven-plugin | — | NOASSERTION | yes | +| org.springframework.boot:spring-boot-starter-log4j2 | — | NOASSERTION | yes | +| org.springframework.boot:spring-boot-starter-test | — | NOASSERTION | yes | +| org.springframework.boot:spring-boot-starter-validation | — | NOASSERTION | yes | +| org.springframework.boot:spring-boot-starter-webflux | — | NOASSERTION | yes | +| org.webjars.npm:pdfjs-dist | 6.1.200 | Apache-2.0 AND BSD-2-Clause AND BSD-3-Clause AND CC0-1.0 AND MIT AND OFL-1.1 | no | +| packaging | 26.2 | Apache-2.0 AND BSD-2-Clause | no | +| pluggy | 1.6.0 | MIT | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pytest | 9.1.1 | MIT | no | + +### ContextualWisdomLab/codec-carver + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| aho-corasick | 1.1.4 | Unlicense OR MIT | no | +| aiofiles | — | NOASSERTION | yes | +| aiofiles | 25.1.0 | Apache-2.0 | no | +| aiohappyeyeballs | 2.7.1 | 0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0 | yes | +| aiohttp | 3.14.3 | Apache-2.0 AND MIT | no | +| aiosignal | 1.4.0 | Apache-2.0 | no | +| android_system_properties | 0.1.5 | MIT OR Apache-2.0 | no | +| annotated-doc | 0.0.4 | MIT | no | +| annotated-types | 0.7.0 | MIT | no | +| anstream | 1.0.0 | MIT OR Apache-2.0 | no | +| anstyle | 1.0.14 | MIT OR Apache-2.0 | no | +| anstyle-parse | 1.0.0 | MIT OR Apache-2.0 | no | +| anstyle-query | 1.1.5 | MIT OR Apache-2.0 | no | +| anstyle-wincon | 3.0.11 | MIT OR Apache-2.0 | no | +| anyhow | 1.0.103 | MIT OR Apache-2.0 | no | +| anyio | 4.14.1 | MIT | no | +| anyio | 4.14.2 | MIT | no | +| atheris | 3.0.0 | Apache-2.0 | no | +| attrs | 26.1.0 | MIT | no | +| autocfg | 1.5.1 | Apache-2.0 OR MIT | no | +| bitflags | 2.13.0 | MIT OR Apache-2.0 | no | +| block-buffer | 0.10.4 | MIT OR Apache-2.0 | no | +| block2 | 0.6.2 | MIT | no | +| bumpalo | 3.20.3 | MIT OR Apache-2.0 | no | +| cc | 1.2.67 | MIT OR Apache-2.0 | no | +| certifi | 2026.6.17 | MPL-2.0 | yes | +| cffi | 2.1.0 | MIT-0 | no | +| cfg-if | 1.0.4 | MIT OR Apache-2.0 | no | +| charset-normalizer | 3.4.9 | MIT | no | +| chrono | 0.4.45 | MIT OR Apache-2.0 | no | +| clap | 4.6.1 | MIT OR Apache-2.0 | no | +| clap_builder | 4.6.0 | MIT OR Apache-2.0 | no | +| clap_derive | 4.6.1 | MIT OR Apache-2.0 | no | +| clap_lex | 1.1.0 | MIT OR Apache-2.0 | no | +| click | 8.4.2 | BSD-3-Clause | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| colorchoice | 1.0.5 | MIT OR Apache-2.0 | no | +| core-foundation-sys | 0.8.7 | MIT OR Apache-2.0 | no | +| coverage | — | NOASSERTION | yes | +| coverage | 7.15.3 | Apache-2.0 | no | +| cpufeatures | 0.2.17 | MIT OR Apache-2.0 | no | +| crossbeam-deque | 0.8.7 | MIT OR Apache-2.0 | no | +| crossbeam-epoch | 0.9.20 | MIT OR Apache-2.0 | no | +| crossbeam-utils | 0.8.22 | MIT OR Apache-2.0 | no | +| crypto-common | 0.1.7 | MIT OR Apache-2.0 | no | +| cryptography | 49.0.0 | BSD-3-Clause OR Apache-2.0 | no | +| cryptography | 50.0.0 | Apache-2.0 OR BSD-3-Clause | no | +| datasets | 5.0.0 | Apache-2.0 | no | +| digest | 0.10.7 | MIT OR Apache-2.0 | no | +| dill | 0.4.1 | BSD-3-Clause | no | +| either | 1.16.0 | MIT OR Apache-2.0 | no | +| exceptiongroup | 1.3.1 | MIT AND Python-2.0 | no | +| fastapi | — | NOASSERTION | yes | +| fastapi | 0.139.0 | MIT | no | +| faster-whisper | 1.2.1 | MIT | no | +| filelock | 3.30.2 | MIT | no | +| find-msvc-tools | 0.1.9 | MIT OR Apache-2.0 | no | +| frozenlist | 1.8.0 | Apache-2.0 | no | +| fsspec | 2026.4.0 | BSD-3-Clause | no | +| futures-core | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-task | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-util | 0.3.32 | MIT OR Apache-2.0 | no | +| generic-array | 0.14.7 | MIT | no | +| h11 | 0.16.0 | MIT | no | +| heck | 0.5.0 | MIT OR Apache-2.0 | no | +| hf-xet | 1.5.2 | Apache-2.0 | no | +| httpcore | 1.0.9 | BSD-2-Clause AND BSD-3-Clause | no | +| httpcore2 | 2.5.0 | BSD-2-Clause AND BSD-3-Clause | no | +| httpx | — | NOASSERTION | yes | +| httpx | 0.28.1 | BSD-3-Clause | no | +| httpx-sse | 0.4.3 | MIT | no | +| httpx2 | 2.5.0 | BSD-2-Clause AND BSD-3-Clause | no | +| huggingface-hub | 1.23.0 | Apache-2.0 | no | +| hypothesis | — | NOASSERTION | yes | +| hypothesis | 6.165.0 | MPL-2.0 | yes | +| iana-time-zone | 0.1.65 | MIT OR Apache-2.0 | no | +| iana-time-zone-haiku | 0.1.2 | MIT OR Apache-2.0 | no | +| idna | 3.18 | BSD-3-Clause | no | +| interrogate | 1.7.0 | MIT | no | +| is_terminal_polyfill | 1.70.2 | MIT OR Apache-2.0 | no | +| itoa | 1.0.18 | MIT OR Apache-2.0 | no | +| jinja2 | 3.1.6 | BSD-2-Clause AND BSD-3-Clause | no | +| js-sys | 0.3.103 | MIT OR Apache-2.0 | no | +| jsonschema | 4.26.0 | MIT | no | +| jsonschema-specifications | 2025.9.1 | MIT | no | +| libc | 0.2.186 | MIT OR Apache-2.0 | no | +| llguidance | 1.7.6 | MIT | no | +| llvmlite | 0.48.0 | LicenseRef-bad-bsd-2-clause-and-apache-2.0-and-llvm-exception-and-bsd-2-clause | no | +| log | 0.4.33 | MIT OR Apache-2.0 | no | +| markdown-it-py | 4.2.0 | MIT | no | +| markupsafe | 3.0.3 | BSD-3-Clause | no | +| mcp | — | NOASSERTION | yes | +| mcp | 1.28.1 | MIT AND Python-2.0 | no | +| mdurl | 0.1.2 | MIT | no | +| memchr | 2.8.3 | Unlicense OR MIT | no | +| miniaudio | 1.71 | MIT AND Python-2.0 | no | +| mlx | 0.32.0 | MIT | no | +| mlx-audio | 0.4.5 | MIT | no | +| mlx-lm | 0.31.3 | MIT | no | +| mlx-metal | 0.32.0 | MIT | no | +| mlx-vlm | 0.6.4 | MIT | no | +| mlx-whisper | 0.4.3 | MIT | no | +| more-itertools | 11.1.0 | MIT | no | +| mpmath | 1.3.0 | BSD-3-Clause | no | +| multidict | 6.7.1 | Apache-2.0 | no | +| multiprocess | 0.70.19 | BSD-3-Clause | no | +| networkx | 3.6.1 | BSD-3-Clause | no | +| num-traits | 0.2.19 | MIT OR Apache-2.0 | no | +| numba | 0.66.0 | BSD-2-Clause AND BSD-3-Clause | no | +| numpy | 2.4.6 | BSD-3-Clause | no | +| objc2 | 0.6.4 | MIT | no | +| objc2-encode | 4.1.0 | MIT | no | +| objc2-foundation | 0.3.2 | MIT | no | +| once_cell | 1.21.4 | MIT OR Apache-2.0 | no | +| once_cell_polyfill | 1.70.2 | MIT OR Apache-2.0 | no | +| opencv-python | 5.0.0.93 | Apache-2.0 AND MIT | no | +| packaging | 26.2 | Apache-2.0 AND BSD-2-Clause | no | +| pandas | 3.0.3 | BSD-2-Clause AND BSD-3-Clause | no | +| pillow | 12.3.0 | MIT-CMU | no | +| pin-project-lite | 0.2.17 | Apache-2.0 OR MIT | no | +| proc-macro2 | 1.0.106 | MIT OR Apache-2.0 | no | +| propcache | 0.5.2 | Apache-2.0 | no | +| protobuf | 7.35.1 | BSD-3-Clause AND LicenseRef-scancode-protobuf | no | +| pyarrow | 25.0.0 | Apache-2.0 | no | +| pycparser | 3.0 | BSD-3-Clause | no | +| pydantic | 2.13.4 | MIT | no | +| pydantic-core | 2.46.4 | MIT | no | +| pydantic-settings | 2.14.2 | MIT | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pyjwt | 2.13.0 | MIT | no | +| python-dateutil | 2.9.0.post0 | Apache-2.0 OR BSD-3-Clause | no | +| python-dotenv | 1.2.2 | BSD-3-Clause | no | +| python-multipart | — | NOASSERTION | yes | +| python-multipart | 0.0.32 | Apache-2.0 | no | +| pywin32 | 312 | PSF-2.0 | no | +| pyyaml | 6.0.3 | MIT | no | +| quote | 1.0.46 | MIT OR Apache-2.0 | no | +| rayon | 1.12.0 | MIT OR Apache-2.0 | no | +| rayon-core | 1.13.0 | MIT OR Apache-2.0 | no | +| referencing | 0.37.0 | MIT | no | +| regex | 1.13.0 | MIT OR Apache-2.0 | no | +| regex | 2026.7.10 | CNRI-Python AND Apache-2.0 | no | +| regex-automata | 0.4.15 | MIT OR Apache-2.0 | no | +| regex-syntax | 0.8.11 | MIT OR Apache-2.0 | no | +| requests | 2.34.2 | Apache-2.0 | no | +| rich | 15.0.0 | MIT | no | +| rpds-py | 0.30.0 | MIT | no | +| rpds-py | 2026.6.3 | MIT | no | +| rustversion | 1.0.23 | MIT OR Apache-2.0 | no | +| safetensors | 0.8.0 | Apache-2.0 | no | +| same-file | 1.0.6 | Unlicense OR MIT | no | +| scipy | 1.18.0 | BSD-3-Clause | no | +| sentencepiece | 0.2.2 | Apache-2.0 | no | +| serde | 1.0.228 | MIT OR Apache-2.0 | no | +| serde_core | 1.0.228 | MIT OR Apache-2.0 | no | +| serde_derive | 1.0.228 | MIT OR Apache-2.0 | no | +| serde_json | 1.0.150 | MIT OR Apache-2.0 | no | +| setuptools | 83.0.0 | MIT | no | +| sha2 | 0.10.9 | MIT OR Apache-2.0 | no | +| shellingham | 1.5.4 | ISC | no | +| shlex | 2.0.1 | MIT OR Apache-2.0 | no | +| six | 1.17.0 | MIT | no | +| slab | 0.4.12 | MIT | no | +| sortedcontainers | 2.4.0 | Apache-2.0 | no | +| sounddevice | 0.5.5 | MIT | no | +| sse-starlette | 3.4.5 | BSD-3-Clause | no | +| starlette | 1.3.1 | BSD-3-Clause | no | +| strsim | 0.11.1 | MIT | no | +| sympy | 1.14.0 | BSD-2-Clause AND BSD-3-Clause AND MIT | no | +| syn | 2.0.119 | MIT OR Apache-2.0 | no | +| tiktoken | 0.13.0 | MIT | no | +| tinyvec | 1.12.0 | Zlib OR Apache-2.0 OR MIT | no | +| tinyvec_macros | 0.1.1 | MIT OR Apache-2.0 OR Zlib | no | +| tokenizers | 0.22.2 | Apache-2.0 | no | +| torch | 2.13.0 | BSD-2-Clause | no | +| tqdm | 4.68.4 | MIT AND MPL-2.0 | yes | +| transformers | 5.12.1 | Apache-2.0 | no | +| truststore | 0.10.4 | MIT | no | +| typenum | 1.20.1 | MIT OR Apache-2.0 | no | +| typer | 0.27.0 | MIT | no | +| typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| typing-inspection | 0.4.2 | MIT | no | +| unicode-ident | 1.0.24 | (MIT OR Apache-2.0) AND Unicode-3.0 | no | +| unicode-normalization | 0.1.25 | MIT OR Apache-2.0 | no | +| urllib3 | 2.7.0 | MIT | no | +| utf8parse | 0.2.2 | Apache-2.0 OR MIT | no | +| uvicorn | — | NOASSERTION | yes | +| uvicorn | 0.51.0 | BSD-3-Clause | no | +| version_check | 0.9.5 | MIT OR Apache-2.0 | no | +| walkdir | 2.5.0 | Unlicense OR MIT | no | +| wasm-bindgen | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-macro | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-macro-support | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-shared | 0.2.126 | MIT OR Apache-2.0 | no | +| winapi-util | 0.1.11 | Unlicense OR MIT | no | +| windows-core | 0.62.2 | MIT OR Apache-2.0 | no | +| windows-implement | 0.60.2 | MIT OR Apache-2.0 | no | +| windows-interface | 0.59.3 | MIT OR Apache-2.0 | no | +| windows-link | 0.2.1 | MIT OR Apache-2.0 | no | +| windows-result | 0.4.1 | MIT OR Apache-2.0 | no | +| windows-strings | 0.5.1 | MIT OR Apache-2.0 | no | +| windows-sys | 0.61.2 | MIT OR Apache-2.0 | no | +| xxhash | 3.8.1 | BSD-2-Clause | no | +| yarl | 1.24.2 | Apache-2.0 | no | +| zmij | 1.0.23 | MIT | no | + +### ContextualWisdomLab/ConceptWeave + +No components reported. + +### ContextualWisdomLab/context-graph-contracts + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/contextual-orchestrator + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| @adobe/css-tools | 4.5.0 | MIT | no | +| @babel/code-frame | 7.29.7 | MIT | no | +| @babel/compat-data | 7.29.7 | MIT | no | +| @babel/core | 7.29.7 | MIT | no | +| @babel/generator | 7.29.8 | MIT | no | +| @babel/helper-compilation-targets | 7.29.7 | MIT | no | +| @babel/helper-globals | 7.29.7 | MIT | no | +| @babel/helper-module-imports | 7.29.7 | MIT | no | +| @babel/helper-module-transforms | 7.29.7 | MIT | no | +| @babel/helper-string-parser | 7.29.7 | MIT | no | +| @babel/helper-validator-identifier | 7.29.7 | MIT | no | +| @babel/helper-validator-option | 7.29.7 | MIT | no | +| @babel/helpers | 7.29.7 | MIT | no | +| @babel/parser | 7.29.8 | MIT | no | +| @babel/runtime | 7.29.7 | MIT | no | +| @babel/template | 7.29.7 | MIT | no | +| @babel/traverse | 7.29.8 | MIT | no | +| @babel/types | 7.29.8 | MIT | no | +| @bcoe/v8-coverage | 1.0.2 | ISC AND MIT | no | +| @blazediff/core | 1.9.1 | MIT | no | +| @chromatic-com/storybook | 5.3.0 | MIT | no | +| @chromatic-com/storybook | latest | MIT | no | +| @emnapi/core | 1.11.0 | MIT | no | +| @emnapi/core | 1.9.2 | MIT | no | +| @emnapi/runtime | 1.11.0 | MIT | no | +| @emnapi/runtime | 1.9.2 | MIT | no | +| @emnapi/wasi-threads | 1.2.1 | MIT | no | +| @emnapi/wasi-threads | 1.2.2 | MIT | no | +| @esbuild/aix-ppc64 | 0.28.2 | MIT | no | +| @esbuild/android-arm | 0.28.2 | MIT | no | +| @esbuild/android-arm64 | 0.28.2 | MIT | no | +| @esbuild/android-x64 | 0.28.2 | MIT | no | +| @esbuild/darwin-arm64 | 0.28.2 | MIT | no | +| @esbuild/darwin-x64 | 0.28.2 | MIT | no | +| @esbuild/freebsd-arm64 | 0.28.2 | MIT | no | +| @esbuild/freebsd-x64 | 0.28.2 | MIT | no | +| @esbuild/linux-arm | 0.28.2 | MIT | no | +| @esbuild/linux-arm64 | 0.28.2 | MIT | no | +| @esbuild/linux-ia32 | 0.28.2 | MIT | no | +| @esbuild/linux-loong64 | 0.28.2 | MIT | no | +| @esbuild/linux-mips64el | 0.28.2 | MIT | no | +| @esbuild/linux-ppc64 | 0.28.2 | MIT | no | +| @esbuild/linux-riscv64 | 0.28.2 | MIT | no | +| @esbuild/linux-s390x | 0.28.2 | MIT | no | +| @esbuild/linux-x64 | 0.28.2 | MIT | no | +| @esbuild/netbsd-arm64 | 0.28.2 | MIT | no | +| @esbuild/netbsd-x64 | 0.28.2 | MIT | no | +| @esbuild/openbsd-arm64 | 0.28.2 | MIT | no | +| @esbuild/openbsd-x64 | 0.28.2 | MIT | no | +| @esbuild/openharmony-arm64 | 0.28.2 | MIT | no | +| @esbuild/sunos-x64 | 0.28.2 | MIT | no | +| @esbuild/win32-arm64 | 0.28.2 | MIT | no | +| @esbuild/win32-ia32 | 0.28.2 | MIT | no | +| @esbuild/win32-x64 | 0.28.2 | MIT | no | +| @joshwooding/vite-plugin-react-docgen-typescript | 0.7.0 | MIT | no | +| @jridgewell/gen-mapping | 0.3.13 | MIT | no | +| @jridgewell/remapping | 2.3.5 | MIT | no | +| @jridgewell/resolve-uri | 3.1.2 | MIT | no | +| @jridgewell/sourcemap-codec | 1.5.5 | MIT | no | +| @jridgewell/trace-mapping | 0.3.31 | MIT | no | +| @mdx-js/react | 3.1.1 | MIT | no | +| @napi-rs/wasm-runtime | 1.2.3 | MIT | no | +| @neoconfetti/react | 1.0.0 | MIT | no | +| @oxc-parser/binding-android-arm-eabi | 0.127.0 | MIT | no | +| @oxc-parser/binding-android-arm64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-darwin-arm64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-darwin-x64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-freebsd-x64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-arm-gnueabihf | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-arm-musleabihf | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-arm64-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-arm64-musl | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-ppc64-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-riscv64-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-riscv64-musl | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-s390x-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-x64-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-x64-musl | 0.127.0 | MIT | no | +| @oxc-parser/binding-openharmony-arm64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-wasm32-wasi | 0.127.0 | MIT | no | +| @oxc-parser/binding-win32-arm64-msvc | 0.127.0 | MIT | no | +| @oxc-parser/binding-win32-ia32-msvc | 0.127.0 | MIT | no | +| @oxc-parser/binding-win32-x64-msvc | 0.127.0 | MIT | no | +| @oxc-project/types | 0.127.0 | MIT | no | +| @oxc-project/types | 0.146.0 | MIT | no | +| @oxc-resolver/binding-android-arm-eabi | 11.21.2 | MIT | no | +| @oxc-resolver/binding-android-arm64 | 11.21.2 | MIT | no | +| @oxc-resolver/binding-darwin-arm64 | 11.21.2 | MIT | no | +| @oxc-resolver/binding-darwin-x64 | 11.21.2 | MIT | no | +| @oxc-resolver/binding-freebsd-x64 | 11.21.2 | MIT | no | +| @oxc-resolver/binding-linux-arm-gnueabihf | 11.21.2 | MIT | no | +| @oxc-resolver/binding-linux-arm-musleabihf | 11.21.2 | MIT | no | +| @oxc-resolver/binding-linux-arm64-gnu | 11.21.2 | MIT | no | +| @oxc-resolver/binding-linux-arm64-musl | 11.21.2 | MIT | no | +| @oxc-resolver/binding-linux-ppc64-gnu | 11.21.2 | MIT | no | +| @oxc-resolver/binding-linux-riscv64-gnu | 11.21.2 | MIT | no | +| @oxc-resolver/binding-linux-riscv64-musl | 11.21.2 | MIT | no | +| @oxc-resolver/binding-linux-s390x-gnu | 11.21.2 | MIT | no | +| @oxc-resolver/binding-linux-x64-gnu | 11.21.2 | MIT | no | +| @oxc-resolver/binding-linux-x64-musl | 11.21.2 | MIT | no | +| @oxc-resolver/binding-openharmony-arm64 | 11.21.2 | MIT | no | +| @oxc-resolver/binding-wasm32-wasi | 11.21.2 | MIT | no | +| @oxc-resolver/binding-win32-arm64-msvc | 11.21.2 | MIT | no | +| @oxc-resolver/binding-win32-x64-msvc | 11.21.2 | MIT | no | +| @oxlint/binding-android-arm-eabi | 1.80.0 | MIT | no | +| @oxlint/binding-android-arm64 | 1.80.0 | MIT | no | +| @oxlint/binding-darwin-arm64 | 1.80.0 | MIT | no | +| @oxlint/binding-darwin-x64 | 1.80.0 | MIT | no | +| @oxlint/binding-freebsd-x64 | 1.80.0 | MIT | no | +| @oxlint/binding-linux-arm-gnueabihf | 1.80.0 | MIT | no | +| @oxlint/binding-linux-arm-musleabihf | 1.80.0 | MIT | no | +| @oxlint/binding-linux-arm64-gnu | 1.80.0 | MIT | no | +| @oxlint/binding-linux-arm64-musl | 1.80.0 | MIT | no | +| @oxlint/binding-linux-ppc64-gnu | 1.80.0 | MIT | no | +| @oxlint/binding-linux-riscv64-gnu | 1.80.0 | MIT | no | +| @oxlint/binding-linux-riscv64-musl | 1.80.0 | MIT | no | +| @oxlint/binding-linux-s390x-gnu | 1.80.0 | MIT | no | +| @oxlint/binding-linux-x64-gnu | 1.80.0 | MIT | no | +| @oxlint/binding-linux-x64-musl | 1.80.0 | MIT | no | +| @oxlint/binding-openharmony-arm64 | 1.80.0 | MIT | no | +| @oxlint/binding-win32-arm64-msvc | 1.80.0 | MIT | no | +| @oxlint/binding-win32-ia32-msvc | 1.80.0 | MIT | no | +| @oxlint/binding-win32-x64-msvc | 1.80.0 | MIT | no | +| @polka/url | 1.0.0-next.29 | MIT | no | +| @rolldown/binding-android-arm-eabi | 1.2.5 | MIT | no | +| @rolldown/binding-android-arm64 | 1.2.5 | MIT | no | +| @rolldown/binding-darwin-arm64 | 1.2.5 | MIT | no | +| @rolldown/binding-darwin-x64 | 1.2.5 | MIT | no | +| @rolldown/binding-freebsd-x64 | 1.2.5 | MIT | no | +| @rolldown/binding-linux-arm-gnueabihf | 1.2.5 | MIT | no | +| @rolldown/binding-linux-arm64-gnu | 1.2.5 | MIT | no | +| @rolldown/binding-linux-arm64-musl | 1.2.5 | MIT | no | +| @rolldown/binding-linux-ppc64-gnu | 1.2.5 | MIT | no | +| @rolldown/binding-linux-s390x-gnu | 1.2.5 | MIT | no | +| @rolldown/binding-linux-x64-gnu | 1.2.5 | MIT | no | +| @rolldown/binding-linux-x64-musl | 1.2.5 | MIT | no | +| @rolldown/binding-openharmony-arm64 | 1.2.5 | MIT | no | +| @rolldown/binding-win32-arm64-msvc | 1.2.5 | MIT | no | +| @rolldown/binding-win32-x64-msvc | 1.2.5 | MIT | no | +| @rolldown/pluginutils | 1.0.1 | MIT | no | +| @rollup/pluginutils | 5.4.0 | MIT | no | +| @standard-schema/spec | 1.1.0 | MIT | no | +| @storybook/addon-a11y | 10.5.10 | MIT | no | +| @storybook/addon-a11y | ^10.5.10 | MIT | no | +| @storybook/addon-docs | 10.5.10 | MIT | no | +| @storybook/addon-docs | ^10.5.10 | MIT | no | +| @storybook/addon-vitest | 10.5.10 | MIT | no | +| @storybook/addon-vitest | ^10.5.10 | MIT | no | +| @storybook/builder-vite | 10.5.10 | MIT | no | +| @storybook/csf-plugin | 10.5.10 | MIT | no | +| @storybook/global | 5.0.0 | MIT | no | +| @storybook/icons | 2.1.0 | MIT | no | +| @storybook/react | 10.5.10 | MIT | no | +| @storybook/react-dom-shim | 10.5.10 | MIT | no | +| @storybook/react-vite | 10.5.10 | MIT | no | +| @storybook/react-vite | ^10.5.10 | MIT | no | +| @testing-library/dom | 10.4.1 | MIT | no | +| @testing-library/jest-dom | 6.9.1 | MIT | no | +| @testing-library/user-event | 14.6.6 | MIT | no | +| @tybys/wasm-util | 0.10.3 | MIT | no | +| @types/aria-query | 5.0.4 | MIT | no | +| @types/babel__core | 7.20.5 | MIT | no | +| @types/babel__generator | 7.27.0 | MIT | no | +| @types/babel__template | 7.4.4 | MIT | no | +| @types/babel__traverse | 7.28.0 | MIT | no | +| @types/chai | 5.2.3 | MIT | no | +| @types/deep-eql | 4.0.2 | MIT | no | +| @types/doctrine | 0.0.9 | MIT | no | +| @types/estree | 1.0.9 | MIT | no | +| @types/mdx | 2.0.14 | MIT | no | +| @types/node | 24.13.3 | MIT | no | +| @types/node | ^24.13.3 | MIT | no | +| @types/react | 19.2.18 | MIT | no | +| @types/react | ^19.2.18 | MIT | no | +| @types/react-dom | 19.2.5 | MIT | no | +| @types/react-dom | ^19.2.4 | MIT | no | +| @types/resolve | 1.20.6 | MIT | no | +| @vitejs/plugin-react | 6.1.0 | MIT | no | +| @vitejs/plugin-react | ^6.1.0 | MIT | no | +| @vitest/browser | 4.1.11 | MIT | no | +| @vitest/browser-playwright | 4.1.11 | MIT | no | +| @vitest/browser-playwright | latest | MIT | no | +| @vitest/coverage-v8 | 4.1.11 | MIT | no | +| @vitest/coverage-v8 | latest | MIT | no | +| @vitest/expect | 3.2.4 | MIT | no | +| @vitest/expect | 4.1.11 | MIT | no | +| @vitest/mocker | 4.1.11 | MIT | no | +| @vitest/pretty-format | 3.2.4 | MIT | no | +| @vitest/pretty-format | 4.1.11 | MIT | no | +| @vitest/runner | 4.1.11 | MIT | no | +| @vitest/snapshot | 4.1.11 | MIT | no | +| @vitest/spy | 3.2.4 | MIT | no | +| @vitest/spy | 4.1.11 | MIT | no | +| @vitest/utils | 3.2.4 | MIT | no | +| @vitest/utils | 4.1.11 | MIT | no | +| @webcontainer/env | 1.1.1 | MIT | no | +| acorn | 8.18.0 | MIT | no | +| actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | yes | +| actions/download-artifact | 634f93cb2916e3fdff6788551b99b062d0335ce0 | NOASSERTION | yes | +| actions/setup-node | 820762786026740c76f36085b0efc47a31fe5020 | NOASSERTION | yes | +| actions/setup-python | ece7cb06caefa5fff74198d8649806c4678c61a1 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| actions/upload-artifact | 330a01c490aca151604b8cf639adc76d48f6c5d4 | NOASSERTION | yes | +| aho-corasick | 1.1.5 | Unlicense OR MIT | no | +| alembic | 1.19.1 | MIT | no | +| annotated-doc | 0.0.5 | MIT | no | +| annotated-types | 0.8.0 | MIT | no | +| ansi-regex | 5.0.1 | MIT | no | +| ansi-regex | 6.3.0 | MIT | no | +| ansi-styles | 5.2.0 | MIT | no | +| anyhow | 1.0.104 | MIT OR Apache-2.0 | no | +| anyio | 4.14.2 | MIT | no | +| aria-query | 5.3.0 | Apache-2.0 | no | +| aria-query | 5.3.2 | Apache-2.0 | no | +| arrow | 1.4.0 | Apache-2.0 | no | +| assertion-error | 2.0.1 | MIT | no | +| ast-types | 0.16.1 | MIT | no | +| ast-v8-to-istanbul | 1.0.5 | MIT | no | +| astral-sh/setup-uv | 20cfd1bf945f4377ade1205e4dbc17946fc9a30d | NOASSERTION | yes | +| atheris | 3.1.0 | Apache-2.0 | no | +| attrs | 26.1.0 | MIT | no | +| axe-core | 4.13.0 | MPL-2.0 | yes | +| balanced-match | 4.0.4 | MIT | no | +| base64 | 0.22.1 | MIT OR Apache-2.0 | no | +| baseline-browser-mapping | 2.11.19 | Apache-2.0 | no | +| bit-set | 0.5.3 | MIT OR Apache-2.0 | no | +| bit-vec | 0.6.3 | MIT OR Apache-2.0 | no | +| boolean-py | 5.0 | BSD-2-Clause | no | +| brace-expansion | 5.0.9 | MIT | no | +| browserslist | 4.28.8 | MIT | no | +| bstr | 1.13.1 | MIT OR Apache-2.0 | no | +| bundle-name | 4.1.0 | MIT | no | +| cachecontrol | 0.14.4 | Apache-2.0 | no | +| caniuse-lite | 1.0.30001810 | CC-BY-4.0 | no | +| certifi | 2026.6.17 | MPL-2.0 | yes | +| certifi | 2026.7.22 | MPL-2.0 | yes | +| cffi | 2.1.1 | MIT-0 | no | +| chai | 5.3.3 | MIT | no | +| chai | 6.2.2 | MIT | no | +| chardet | 5.2.0 | LGPL-2.1-or-later | yes | +| charset-normalizer | 3.4.9 | MIT | no | +| charset-normalizer | 3.5.1 | MIT | no | +| check-error | 2.1.3 | MIT | no | +| chromatic | 18.6.0 | MIT | no | +| click | 8.4.2 | BSD-3-Clause | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| contextual-orchestrator | 0.2.0 | NOASSERTION | yes | +| convert-source-map | 2.0.0 | MIT | no | +| coverage | 7.15.4 | Apache-2.0 | no | +| crossbeam-deque | 0.8.7 | MIT OR Apache-2.0 | no | +| crossbeam-epoch | 0.9.20 | MIT OR Apache-2.0 | no | +| crossbeam-utils | 0.8.22 | MIT OR Apache-2.0 | no | +| cryptography | 50.0.1 | Apache-2.0 OR BSD-3-Clause | no | +| css.escape | 1.5.1 | MIT | no | +| csstype | 3.2.3 | MIT | no | +| cyclonedx-bom | 7.3.0 | Apache-2.0 AND Python-2.0 | no | +| cyclonedx-python-lib | 11.11.0 | Apache-2.0 AND Python-2.0 | no | +| debug | 4.4.3 | MIT | no | +| deep-eql | 5.0.2 | MIT | no | +| default-browser | 5.5.1 | MIT | no | +| default-browser-id | 5.0.1 | MIT | no | +| define-lazy-prop | 3.0.0 | MIT | no | +| defusedxml | 0.7.1 | PSF-2.0 | no | +| dequal | 2.0.3 | MIT | no | +| detect-libc | 2.1.2 | Apache-2.0 | no | +| distro | 1.9.0 | Apache-2.0 | no | +| docker/setup-compose-action | 54042514f505b273907334ae2b9cdbb9a0213c1a | NOASSERTION | yes | +| doctrine | 3.0.0 | Apache-2.0 AND BSD-2-Clause | no | +| dom-accessibility-api | 0.5.16 | MIT | no | +| dom-accessibility-api | 0.6.3 | MIT | no | +| dtolnay/rust-toolchain | 6bed0761d98439e5a578e2877258200ad565ba87 | NOASSERTION | yes | +| egressweave | 0.1.0 | NOASSERTION | yes | +| either | 1.18.0 | MIT OR Apache-2.0 | no | +| electron-to-chromium | 1.5.415 | ISC | no | +| empathic | 2.0.1 | MIT | no | +| es-errors | 1.3.0 | MIT | no | +| es-module-lexer | 2.3.2 | MIT | no | +| esbuild | 0.28.2 | MIT | no | +| escalade | 3.2.0 | MIT | no | +| esprima | 4.0.1 | BSD-2-Clause AND BSD-3-Clause | no | +| estree-walker | 2.0.2 | MIT | no | +| estree-walker | 3.0.3 | MIT | no | +| esutils | 2.0.3 | BSD-2-Clause | no | +| expect-type | 1.4.0 | Apache-2.0 | no | +| fancy-regex | 0.13.0 | MIT | no | +| fast-mlsirm | 0.11.4 | MIT | no | +| fastapi | 0.141.1 | MIT | no | +| fdir | 6.5.0 | MIT | no | +| filelock | 3.29.7 | MIT | no | +| fqdn | 1.5.1 | MPL-2.0 | yes | +| fsevents | 2.3.2 | MIT | no | +| fsevents | 2.3.3 | MIT | no | +| function-bind | 1.1.2 | MIT | no | +| gensync | 1.0.0-beta.2 | MIT | no | +| github/codeql-action/analyze | b96794f015dfd88f77b49b1c93e0fa7110f94c63 | NOASSERTION | yes | +| github/codeql-action/init | b96794f015dfd88f77b49b1c93e0fa7110f94c63 | NOASSERTION | yes | +| glob | 13.0.6 | BlueOak-1.0.0 | no | +| googleapis-common-protos | 1.75.1 | Apache-2.0 | no | +| graceful-fs | 4.2.11 | ISC | no | +| greenlet | 3.5.5 | MIT AND PSF-2.0 | no | +| h11 | 0.16.0 | MIT | no | +| has-flag | 4.0.0 | MIT | no | +| hasown | 2.0.4 | MIT | no | +| heck | 0.5.0 | MIT OR Apache-2.0 | no | +| html-escaper | 2.0.2 | MIT | no | +| httpcore | 1.0.9 | BSD-2-Clause AND BSD-3-Clause | no | +| httpcore2 | 2.13.1 | BSD-3-Clause | no | +| httpx | 0.28.1 | BSD-3-Clause | no | +| httpx2 | 2.13.1 | BSD-3-Clause | no | +| httpx2-jsfetch | 1.0 | BSD-3-Clause | no | +| hypothesis | 6.165.10 | MPL-2.0 | yes | +| idna | 3.18 | BSD-3-Clause | no | +| idna | 3.19 | BSD-3-Clause | no | +| indent-string | 4.0.0 | MIT | no | +| iniconfig | 2.3.0 | MIT | no | +| interrogate | 1.7.0 | MIT | no | +| is-core-module | 2.16.2 | MIT | no | +| is-docker | 3.0.0 | MIT | no | +| is-inside-container | 1.0.0 | MIT | no | +| is-wsl | 3.1.1 | MIT | no | +| isoduration | 20.11.0 | ISC | no | +| istanbul-lib-coverage | 3.2.2 | BSD-3-Clause | no | +| istanbul-lib-report | 3.0.1 | BSD-3-Clause | no | +| istanbul-reports | 3.2.0 | BSD-3-Clause | no | +| itoa | 1.0.18 | MIT OR Apache-2.0 | no | +| jiter | 0.16.0 | MIT | no | +| js-tokens | 10.0.0 | MIT | no | +| js-tokens | 4.0.0 | MIT | no | +| jsesc | 3.1.0 | MIT | no | +| json5 | 2.2.3 | MIT | no | +| jsonc-parser | 3.3.1 | MIT | no | +| jsonfile | 6.2.1 | MIT | no | +| jsonpointer | 3.1.1 | BSD-3-Clause | no | +| jsonschema | 4.26.0 | MIT | no | +| jsonschema-specifications | 2025.9.1 | MIT | no | +| lark | 1.3.1 | MIT AND MPL-2.0 | yes | +| lazy_static | 1.5.0 | MIT OR Apache-2.0 | no | +| libc | 0.2.189 | MIT OR Apache-2.0 | no | +| license-expression | 30.4.4 | Apache-2.0 | no | +| lightningcss | 1.33.0 | MPL-2.0 | yes | +| lightningcss-android-arm64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-darwin-arm64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-darwin-x64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-freebsd-x64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm-gnueabihf | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-gnu | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-musl | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-gnu | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-musl | 1.33.0 | MPL-2.0 | yes | +| lightningcss-win32-arm64-msvc | 1.33.0 | MPL-2.0 | yes | +| lightningcss-win32-x64-msvc | 1.33.0 | MPL-2.0 | yes | +| loupe | 3.2.1 | MIT | no | +| lru-cache | 11.5.2 | BlueOak-1.0.0 | no | +| lru-cache | 5.1.1 | ISC | no | +| lxml | 6.1.1 | BSD-3-Clause AND GPL-1.0-or-later | yes | +| lz-string | 1.5.0 | MIT | no | +| magic-string | 0.30.21 | MIT | no | +| magicast | 0.5.4 | MIT | no | +| make-dir | 4.0.0 | MIT | no | +| mako | 1.4.1 | MIT | no | +| markdown-it-py | 4.2.0 | MIT | no | +| markupsafe | 3.0.3 | BSD-3-Clause | no | +| maturin | — | NOASSERTION | yes | +| maturin | 1.15.0 | MIT OR Apache-2.0 | no | +| mcp | 2.2.0 | MIT | no | +| mcp-types | 2.2.0 | MIT | no | +| mdurl | 0.1.2 | MIT | no | +| memchr | 2.8.3 | Unlicense OR MIT | no | +| min-indent | 1.0.1 | MIT | no | +| minimatch | 10.2.6 | BlueOak-1.0.0 | no | +| minimist | 1.2.8 | MIT | no | +| minipass | 7.1.3 | BlueOak-1.0.0 | no | +| mrmime | 2.0.1 | MIT | no | +| ms | 2.1.3 | MIT | no | +| msgpack | 1.2.1 | Apache-2.0 | no | +| nanoid | 3.3.18 | MIT | no | +| node-releases | 2.0.53 | MIT | no | +| numpy | 2.5.2 | BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0 | no | +| obug | 2.1.4 | MIT | no | +| once_cell | 1.21.4 | MIT OR Apache-2.0 | no | +| open | 10.2.0 | MIT | no | +| openai | 2.54.0 | Apache-2.0 | no | +| opencode-ai | 1.18.22 | MIT | no | +| opencode-darwin-arm64 | 1.18.22 | NOASSERTION | yes | +| opencode-darwin-x64 | 1.18.22 | NOASSERTION | yes | +| opencode-darwin-x64-baseline | 1.18.22 | NOASSERTION | yes | +| opencode-linux-arm64 | 1.18.22 | NOASSERTION | yes | +| opencode-linux-arm64-musl | 1.18.22 | NOASSERTION | yes | +| opencode-linux-x64 | 1.18.22 | NOASSERTION | yes | +| opencode-linux-x64-baseline | 1.18.22 | NOASSERTION | yes | +| opencode-linux-x64-baseline-musl | 1.18.22 | NOASSERTION | yes | +| opencode-linux-x64-musl | 1.18.22 | NOASSERTION | yes | +| opencode-windows-arm64 | 1.18.22 | MIT | no | +| opencode-windows-x64 | 1.18.22 | NOASSERTION | yes | +| opencode-windows-x64-baseline | 1.18.22 | NOASSERTION | yes | +| opentelemetry-api | 1.44.0 | Apache-2.0 | no | +| opentelemetry-exporter-otlp-proto-common | 1.44.0 | Apache-2.0 | no | +| opentelemetry-exporter-otlp-proto-http | 1.44.0 | Apache-2.0 | no | +| opentelemetry-proto | 1.44.0 | Apache-2.0 | no | +| opentelemetry-sdk | 1.44.0 | Apache-2.0 | no | +| opentelemetry-semantic-conventions | 0.65b0 | Apache-2.0 | no | +| oxc-parser | 0.127.0 | MIT | no | +| oxc-resolver | 11.21.2 | MIT | no | +| oxlint | 1.80.0 | MIT | no | +| oxlint | ^1.79.0 | MIT | no | +| packageurl-python | 0.17.6 | MIT | no | +| packaging | 26.2 | Apache-2.0 AND BSD-2-Clause | no | +| packaging | 26.3 | Apache-2.0 OR BSD-2-Clause | no | +| path-parse | 1.0.7 | MIT | no | +| path-scurry | 2.0.2 | BlueOak-1.0.0 | no | +| pathe | 2.0.3 | MIT | no | +| pathval | 2.0.1 | MIT | no | +| picocolors | 1.1.1 | ISC | no | +| picomatch | 4.0.7 | MIT | no | +| pip | 26.2.1 | MIT | no | +| pip-api | 0.0.34 | Apache-2.0 | no | +| pip-audit | 2.10.1 | Apache-2.0 AND ISC | no | +| pip-requirements-parser | 32.0.1 | Apache-2.0 AND MIT | no | +| platformdirs | 4.10.0 | MIT | no | +| playwright | 1.62.1 | Apache-2.0 | no | +| playwright | latest | NOASSERTION | yes | +| playwright-core | 1.62.1 | Apache-2.0 | no | +| pluggy | 1.6.0 | MIT | no | +| pngjs | 7.0.0 | MIT | no | +| portable-atomic | 1.15.0 | Apache-2.0 OR MIT | no | +| postcss | 8.5.26 | MIT | no | +| pretty-format | 27.5.1 | MIT | no | +| proc-macro2 | 1.0.107 | MIT OR Apache-2.0 | no | +| protobuf | 7.36.0 | BSD-3-Clause AND LicenseRef-scancode-protobuf | no | +| psycopg | 3.3.4 | LGPL-3.0 AND LGPL-3.0-only | yes | +| psycopg-binary | 3.3.4 | GPL-3.0-or-later | yes | +| py | 1.11.0 | MIT | no | +| py-serializable | 2.1.0 | Apache-2.0 | no | +| pycparser | 3.0 | BSD-3-Clause | no | +| pydantic | 2.13.4 | MIT | no | +| pydantic-core | 2.46.4 | MIT | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pygments | 2.21.0 | BSD-2-Clause | no | +| pyjwt | 2.15.1 | MIT | no | +| pyo3 | 0.29.2 | MIT OR Apache-2.0 | no | +| pyo3 | >= 0.29.0,< 0.30.0 | NOASSERTION | yes | +| pyo3-build-config | 0.29.2 | MIT OR Apache-2.0 | no | +| pyo3-ffi | 0.29.2 | MIT OR Apache-2.0 | no | +| pyo3-macros | 0.29.2 | MIT OR Apache-2.0 | no | +| pyo3-macros-backend | 0.29.2 | MIT OR Apache-2.0 | no | +| pyparsing | 3.3.2 | MIT AND Python-2.0 | no | +| pytest | 9.0.3 | MIT | no | +| pytest | 9.1.1 | MIT | no | +| pytest-cov | 7.1.0 | MIT | no | +| python-dateutil | 2.9.0.post0 | Apache-2.0 OR BSD-3-Clause | no | +| python-multipart | 0.0.32 | Apache-2.0 | no | +| pywin32 | 312 | PSF-2.0 | no | +| quote | 1.0.47 | MIT OR Apache-2.0 | no | +| rayon | 1.12.0 | MIT OR Apache-2.0 | no | +| rayon | >= 1.10.0,< 2.0.0 | NOASSERTION | yes | +| rayon-core | 1.13.0 | MIT OR Apache-2.0 | no | +| react | 19.2.8 | MIT | no | +| react | ^19.2.8 | NOASSERTION | yes | +| react-docgen | 8.0.3 | MIT | no | +| react-docgen-typescript | 2.4.0 | MIT | no | +| react-dom | 19.2.8 | MIT | no | +| react-dom | ^19.2.8 | NOASSERTION | yes | +| react-is | 17.0.2 | MIT | no | +| recast | 0.23.21 | MIT | no | +| redent | 3.0.0 | MIT | no | +| redis | 8.1.0 | MIT | no | +| referencing | 0.37.0 | MIT | no | +| regex | 1.13.1 | MIT OR Apache-2.0 | no | +| regex-automata | 0.4.18 | MIT OR Apache-2.0 | no | +| regex-syntax | 0.8.11 | MIT OR Apache-2.0 | no | +| requests | 2.34.2 | Apache-2.0 | no | +| resolve | 1.22.12 | MIT | no | +| rfc3339-validator | 0.1.4 | MIT | no | +| rfc3986-validator | 0.1.1 | MIT | no | +| rfc3987-syntax | 1.1.0 | Apache-2.0 AND GPL-1.0-or-later AND MIT | yes | +| rich | 15.0.0 | MIT | no | +| rolldown | 1.2.5 | MIT | no | +| rpds-py | 0.30.0 | MIT | no | +| rpds-py | 2026.6.3 | MIT | no | +| run-applescript | 7.1.0 | MIT | no | +| rustc-hash | 1.1.0 | Apache-2.0 OR MIT | no | +| scheduler | 0.27.0 | MIT | no | +| semver | 6.3.1 | ISC | no | +| semver | 7.8.5 | ISC | no | +| serde | 1.0.229 | MIT OR Apache-2.0 | no | +| serde | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| serde_core | 1.0.229 | MIT OR Apache-2.0 | no | +| serde_derive | 1.0.229 | MIT OR Apache-2.0 | no | +| serde_json | 1.0.151 | MIT OR Apache-2.0 | no | +| serde_json | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| setuptools | 84.0.0 | MIT | no | +| siginfo | 2.0.0 | ISC | no | +| sirv | 3.0.2 | MIT | no | +| six | 1.17.0 | MIT | no | +| sniffio | 1.3.1 | Apache-2.0 AND MIT | no | +| sortedcontainers | 2.4.0 | Apache-2.0 | no | +| source-map | 0.6.1 | BSD-3-Clause | no | +| source-map-js | 1.2.1 | BSD-3-Clause | no | +| sqlalchemy | 2.0.52 | MIT | no | +| sse-starlette | 3.5.0 | BSD-3-Clause | no | +| stackback | 0.0.2 | MIT | no | +| starlette | 1.6.0 | BSD-3-Clause | no | +| std-env | 4.2.0 | MIT | no | +| storybook | 10.5.10 | MIT | no | +| storybook | ^10.5.10 | NOASSERTION | yes | +| strip-ansi | 7.2.0 | MIT | no | +| strip-bom | 3.0.0 | MIT | no | +| strip-indent | 3.0.0 | MIT | no | +| strip-indent | 4.1.1 | MIT | no | +| supports-color | 7.2.0 | MIT | no | +| supports-preserve-symlinks-flag | 1.0.0 | MIT | no | +| syn | 2.0.119 | MIT OR Apache-2.0 | no | +| syn | 3.0.4 | MIT OR Apache-2.0 | no | +| tabulate | 0.10.0 | MIT | no | +| target-lexicon | 0.13.5 | Apache-2.0 WITH LLVM-exception | no | +| tiktoken-rs | 0.7.0 | MIT | no | +| tiktoken-rs | >= 0.7.0,< 0.8.0 | NOASSERTION | yes | +| tiny-invariant | 1.3.3 | MIT | no | +| tinybench | 2.9.0 | MIT | no | +| tinyexec | 1.3.0 | MIT | no | +| tinyglobby | 0.2.17 | MIT | no | +| tinyrainbow | 2.0.0 | MIT | no | +| tinyrainbow | 3.1.1 | MIT | no | +| tinyspy | 4.0.4 | MIT | no | +| tomli | 2.4.1 | MIT | no | +| tomli-w | 1.2.0 | MIT | no | +| totalist | 3.0.1 | MIT | no | +| tqdm | 4.70.0 | MIT AND MPL-2.0 | yes | +| truststore | 0.10.4 | MIT | no | +| ts-dedent | 2.3.0 | MIT | no | +| tsconfig-paths | 4.2.0 | MIT | no | +| tslib | 2.8.1 | 0BSD | no | +| typescript | 6.0.3 | Apache-2.0 | no | +| typescript | ~6.0.2 | Apache-2.0 | no | +| typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| typing-inspection | 0.4.4 | MIT | no | +| tzdata | 2026.2 | Apache-2.0 | no | +| tzdata | 2026.3 | Apache-2.0 | no | +| undici-types | 7.18.2 | MIT | no | +| unicode-ident | 1.0.24 | (MIT OR Apache-2.0) AND Unicode-3.0 | no | +| universalify | 2.0.1 | MIT | no | +| unplugin | 2.3.11 | MIT | no | +| update-browserslist-db | 1.3.1 | MIT | no | +| uri-template | 1.3.0 | MIT | no | +| urllib3 | 2.8.0 | MIT | no | +| use-sync-external-store | 1.6.0 | MIT | no | +| uv | 0.12.5 | MIT OR Apache-2.0 | no | +| uvicorn | 0.52.4 | BSD-3-Clause | no | +| vite | 8.2.2 | MIT | no | +| vite | ^8.2.2 | NOASSERTION | yes | +| vitest | 4.1.11 | MIT | no | +| vitest | latest | NOASSERTION | yes | +| webcolors | 25.10.0 | BSD-3-Clause | no | +| webpack-virtual-modules | 0.6.2 | MIT | no | +| why-is-node-running | 2.3.0 | MIT | no | +| ws | 8.21.3 | MIT | no | +| wsl-utils | 0.1.0 | MIT | no | +| yallist | 3.1.1 | ISC | no | +| zmij | 1.0.23 | MIT | no | + +### ContextualWisdomLab/ContextualWisdomLab.github.io + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | yes | +| github/codeql-action/analyze | cdf488f595d80d6e07e03d4674febd5ab45fa938 | NOASSERTION | yes | +| github/codeql-action/init | cdf488f595d80d6e07e03d4674febd5ab45fa938 | NOASSERTION | yes | + +### ContextualWisdomLab/cwl-telemetry + +No components reported. + +### ContextualWisdomLab/DiagramWeave + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/disksage + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/attest | 1e69f48acb82d1966a394da916b4c1698aa569d6 | NOASSERTION | yes | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/download-artifact | 37930b1c2abaa49bbe596cd826c3c89aef350131 | NOASSERTION | yes | +| actions/download-artifact | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c | NOASSERTION | yes | +| actions/setup-node | 820762786026740c76f36085b0efc47a31fe5020 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| adler2 | 2.0.1 | 0BSD OR MIT OR Apache-2.0 | no | +| aes | 0.8.4 | MIT OR Apache-2.0 | no | +| aho-corasick | 1.1.4 | Unlicense OR MIT | no | +| alloc-no-stdlib | 2.0.4 | BSD-3-Clause | no | +| alloc-stdlib | 0.2.4 | BSD-3-Clause | no | +| android_system_properties | 0.1.5 | MIT OR Apache-2.0 | no | +| anyhow | 1.0.103 | MIT OR Apache-2.0 | no | +| apple-native-keyring-store | 1.0.1 | MIT OR Apache-2.0 | no | +| arrayvec | 0.7.8 | MIT OR Apache-2.0 | no | +| async-broadcast | 0.7.2 | MIT OR Apache-2.0 | no | +| async-channel | 2.5.0 | Apache-2.0 OR MIT | no | +| async-executor | 1.14.0 | Apache-2.0 OR MIT | no | +| async-io | 2.6.0 | Apache-2.0 OR MIT | no | +| async-lock | 3.4.2 | Apache-2.0 OR MIT | no | +| async-process | 2.5.0 | Apache-2.0 OR MIT | no | +| async-recursion | 1.1.1 | MIT OR Apache-2.0 | no | +| async-signal | 0.2.14 | Apache-2.0 OR MIT | no | +| async-task | 4.7.1 | Apache-2.0 OR MIT | no | +| async-trait | 0.1.89 | MIT OR Apache-2.0 | no | +| atk | 0.18.2 | MIT | no | +| atk-sys | 0.18.2 | MIT | no | +| atoi_simd | 0.18.1 | MIT OR Apache-2.0 | no | +| atomic-waker | 1.1.2 | Apache-2.0 OR MIT | no | +| autocfg | 1.5.1 | Apache-2.0 OR MIT | no | +| base64 | 0.21.7 | MIT OR Apache-2.0 | no | +| base64 | 0.22.1 | MIT OR Apache-2.0 | no | +| base64 | 0.23.1 | MIT OR Apache-2.0 | no | +| bindgen | 0.72.1 | BSD-3-Clause | no | +| bit-set | 0.8.0 | Apache-2.0 OR MIT | no | +| bit-vec | 0.8.0 | Apache-2.0 OR MIT | no | +| bitflags | 1.3.2 | MIT OR Apache-2.0 | no | +| bitflags | 2.13.0 | MIT OR Apache-2.0 | no | +| blake3 | 1.8.7 | CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception | no | +| block-buffer | 0.10.4 | MIT OR Apache-2.0 | no | +| block-buffer | 0.12.1 | MIT OR Apache-2.0 | no | +| block-padding | 0.3.3 | MIT OR Apache-2.0 | no | +| block2 | 0.6.2 | MIT | no | +| blocking | 1.6.2 | Apache-2.0 OR MIT | no | +| brotli | 8.0.4 | BSD-3-Clause AND MIT | no | +| brotli-decompressor | 5.0.3 | LicenseRef-bad-bsd-3-clausemit | no | +| bs58 | 0.5.1 | MIT OR Apache-2.0 | no | +| bumpalo | 3.20.3 | MIT OR Apache-2.0 | no | +| bytemuck | 1.25.0 | Zlib OR Apache-2.0 OR MIT | no | +| byteorder | 1.5.0 | Unlicense OR MIT | no | +| bytes | 1.12.1 | MIT | no | +| cairo-rs | 0.18.5 | MIT | no | +| cairo-sys-rs | 0.18.2 | MIT | no | +| calamine | 0.36.1 | MIT | no | +| camino | 1.2.4 | MIT OR Apache-2.0 | no | +| cargo-platform | 0.1.9 | MIT OR Apache-2.0 | no | +| cargo_metadata | 0.19.2 | MIT | no | +| cargo_toml | 0.22.3 | Apache-2.0 OR MIT | no | +| cbc | 0.1.2 | MIT OR Apache-2.0 | no | +| cc | 1.2.66 | MIT OR Apache-2.0 | no | +| cesu8 | 1.1.0 | Apache-2.0 OR MIT | no | +| cexpr | 0.6.0 | Apache-2.0 OR MIT | no | +| cfb | 0.14.0 | MIT | no | +| cfb | 0.7.3 | MIT | no | +| cfg-expr | 0.15.8 | MIT OR Apache-2.0 | no | +| cfg-if | 1.0.4 | MIT OR Apache-2.0 | no | +| chrono | 0.4.45 | MIT OR Apache-2.0 | no | +| cipher | 0.4.4 | MIT OR Apache-2.0 | no | +| clang-sys | 1.8.1 | Apache-2.0 | no | +| cmake | 0.1.58 | MIT OR Apache-2.0 | no | +| codepage | 0.1.2 | Apache-2.0 OR MIT | no | +| combine | 4.6.7 | MIT | no | +| concurrent-queue | 2.5.0 | Apache-2.0 OR MIT | no | +| const-oid | 0.10.2 | Apache-2.0 OR MIT | no | +| constant_time_eq | 0.4.2 | CC0-1.0 OR MIT-0 OR Apache-2.0 | no | +| cookie | 0.18.1 | MIT OR Apache-2.0 | no | +| core-foundation | 0.10.1 | MIT OR Apache-2.0 | no | +| core-foundation-sys | 0.8.7 | MIT OR Apache-2.0 | no | +| core-graphics | 0.25.0 | MIT OR Apache-2.0 | no | +| core-graphics-types | 0.2.0 | MIT OR Apache-2.0 | no | +| cpufeatures | 0.2.17 | MIT OR Apache-2.0 | no | +| cpufeatures | 0.3.0 | MIT OR Apache-2.0 | no | +| crc32fast | 1.5.0 | MIT OR Apache-2.0 | no | +| crossbeam-channel | 0.5.16 | MIT OR Apache-2.0 | no | +| crossbeam-utils | 0.8.22 | MIT OR Apache-2.0 | no | +| crypto-common | 0.1.7 | MIT OR Apache-2.0 | no | +| crypto-common | 0.2.2 | MIT OR Apache-2.0 | no | +| cssparser | 0.36.0 | MPL-2.0 | yes | +| cssparser-macros | 0.6.1 | MPL-2.0 | yes | +| csv | 1.4.0 | Unlicense OR MIT | no | +| csv-core | 0.1.13 | Unlicense OR MIT | no | +| ctor | 0.8.0 | Apache-2.0 OR MIT | no | +| ctor-proc-macro | 0.0.7 | Apache-2.0 OR MIT | no | +| darling | 0.23.0 | MIT | no | +| darling_core | 0.23.0 | MIT | no | +| darling_macro | 0.23.0 | MIT | no | +| dbus | 0.9.12 | LicenseRef-bad-apache-2.0mit | no | +| debug_unsafe | 0.1.4 | MIT OR Apache-2.0 | no | +| deranged | 0.5.8 | MIT OR Apache-2.0 | no | +| derive_more | 2.1.1 | MIT | no | +| derive_more-impl | 2.1.1 | MIT | no | +| digest | 0.10.7 | MIT OR Apache-2.0 | no | +| digest | 0.11.3 | MIT OR Apache-2.0 | no | +| dirs | 6.0.0 | MIT OR Apache-2.0 | no | +| dirs-sys | 0.5.0 | MIT OR Apache-2.0 | no | +| dispatch2 | 0.3.1 | Zlib OR Apache-2.0 OR MIT | no | +| displaydoc | 0.2.6 | MIT OR Apache-2.0 | no | +| dlopen2 | 0.8.2 | MIT | no | +| dlopen2_derive | 0.4.3 | MIT | no | +| dom_query | 0.27.0 | MIT | no | +| dpi | 0.1.2 | Apache-2.0 AND MIT | no | +| dtoa | 1.0.11 | MIT OR Apache-2.0 | no | +| dtoa-short | 0.3.5 | MPL-2.0 | yes | +| dtolnay/rust-toolchain | 4be7066ada62dd38de10e7b70166bc74ed198c30 | NOASSERTION | yes | +| dtor | 0.3.0 | Apache-2.0 OR MIT | no | +| dtor-proc-macro | 0.0.6 | Apache-2.0 OR MIT | no | +| dunce | 1.0.5 | CC0-1.0 OR MIT-0 OR Apache-2.0 | no | +| dyn-clone | 1.0.20 | MIT OR Apache-2.0 | no | +| either | 1.16.0 | MIT OR Apache-2.0 | no | +| embed-resource | 3.0.11 | MIT | no | +| embed_plist | 1.2.2 | MIT OR Apache-2.0 | no | +| encoding_rs | 0.8.35 | (Apache-2.0 OR MIT) AND BSD-3-Clause | no | +| endi | 1.1.1 | MIT | no | +| enumflags2 | 0.7.12 | MIT OR Apache-2.0 | no | +| enumflags2_derive | 0.7.12 | MIT OR Apache-2.0 | no | +| equivalent | 1.0.2 | Apache-2.0 OR MIT | no | +| erased-serde | 0.4.10 | MIT OR Apache-2.0 | no | +| errno | 0.3.14 | MIT OR Apache-2.0 | no | +| event-listener | 5.4.1 | Apache-2.0 OR MIT | no | +| event-listener-strategy | 0.5.4 | Apache-2.0 OR MIT | no | +| fast-float2 | 0.2.3 | MIT OR Apache-2.0 | no | +| fastrand | 2.4.1 | Apache-2.0 OR MIT | no | +| fdeflate | 0.3.7 | MIT OR Apache-2.0 | no | +| field-offset | 0.3.6 | MIT OR Apache-2.0 | no | +| find-msvc-tools | 0.1.9 | MIT OR Apache-2.0 | no | +| find_cuda_helper | 0.2.0 | MIT OR Apache-2.0 | no | +| flate2 | 1.1.9 | MIT OR Apache-2.0 | no | +| fnv | 1.0.7 | Apache-2.0 OR MIT | no | +| foldhash | 0.2.0 | Zlib | no | +| foreign-types | 0.5.0 | MIT OR Apache-2.0 | no | +| foreign-types-macros | 0.2.3 | MIT OR Apache-2.0 | no | +| foreign-types-shared | 0.3.1 | MIT OR Apache-2.0 | no | +| form_urlencoded | 1.2.2 | MIT OR Apache-2.0 | no | +| fs4 | 1.1.0 | MIT OR Apache-2.0 | no | +| futures-channel | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-core | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-executor | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-io | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-lite | 2.6.1 | Apache-2.0 OR MIT | no | +| futures-macro | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-sink | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-task | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-util | 0.3.32 | MIT OR Apache-2.0 | no | +| gdk | 0.18.2 | MIT | no | +| gdk-pixbuf | 0.18.5 | MIT | no | +| gdk-pixbuf-sys | 0.18.0 | MIT | no | +| gdk-sys | 0.18.2 | MIT | no | +| gdkwayland-sys | 0.18.2 | MIT | no | +| gdkx11 | 0.18.2 | MIT | no | +| gdkx11-sys | 0.18.2 | MIT | no | +| generic-array | 0.14.7 | MIT | no | +| getrandom | 0.2.17 | MIT OR Apache-2.0 | no | +| getrandom | 0.3.4 | MIT OR Apache-2.0 | no | +| getrandom | 0.4.3 | MIT OR Apache-2.0 | no | +| gio | 0.18.4 | MIT | no | +| gio-sys | 0.18.1 | MIT | no | +| glib | 0.18.5 | MIT | no | +| glib-macros | 0.18.5 | MIT | no | +| glib-sys | 0.18.1 | MIT | no | +| glob | 0.3.3 | MIT OR Apache-2.0 | no | +| gobject-sys | 0.18.0 | MIT | no | +| gtk | 0.18.2 | MIT | no | +| gtk-sys | 0.18.2 | MIT | no | +| gtk3-macros | 0.18.2 | MIT | no | +| hashbrown | 0.12.3 | MIT OR Apache-2.0 | no | +| hashbrown | 0.17.1 | MIT OR Apache-2.0 | no | +| hashify | 0.2.9 | Apache-2.0 OR MIT | no | +| heck | 0.4.1 | MIT OR Apache-2.0 | no | +| heck | 0.5.0 | MIT OR Apache-2.0 | no | +| hermit-abi | 0.5.2 | MIT OR Apache-2.0 | no | +| hex | 0.4.3 | MIT OR Apache-2.0 | no | +| hkdf | 0.12.4 | MIT OR Apache-2.0 | no | +| hmac | 0.12.1 | MIT OR Apache-2.0 | no | +| html5ever | 0.38.0 | MIT OR Apache-2.0 | no | +| http | 1.4.2 | MIT OR Apache-2.0 | no | +| http-body | 1.0.1 | MIT | no | +| http-body-util | 0.1.3 | MIT | no | +| httparse | 1.10.1 | MIT OR Apache-2.0 | no | +| hybrid-array | 0.4.13 | MIT OR Apache-2.0 | no | +| hyper | 1.10.1 | MIT | no | +| hyper-util | 0.1.20 | MIT | no | +| iana-time-zone | 0.1.65 | MIT OR Apache-2.0 | no | +| iana-time-zone-haiku | 0.1.2 | MIT OR Apache-2.0 | no | +| ico | 0.5.0 | MIT | no | +| icu_collections | 2.2.0 | Unicode-3.0 | no | +| icu_locale_core | 2.2.0 | Unicode-3.0 | no | +| icu_normalizer | 2.2.0 | Unicode-3.0 | no | +| icu_normalizer_data | 2.2.0 | Unicode-3.0 | no | +| icu_properties | 2.2.0 | Unicode-3.0 | no | +| icu_properties_data | 2.2.0 | Unicode-3.0 | no | +| icu_provider | 2.2.0 | Unicode-3.0 | no | +| ident_case | 1.0.1 | MIT OR Apache-2.0 | no | +| idna | 1.1.0 | MIT OR Apache-2.0 | no | +| idna_adapter | 1.2.2 | Apache-2.0 OR MIT | no | +| ilammy/msvc-dev-cmd | 0b201ec74fa43914dc39ae48a89fd1d8cb592756 | NOASSERTION | yes | +| indexmap | 1.9.3 | Apache-2.0 OR MIT | no | +| indexmap | 2.14.0 | Apache-2.0 OR MIT | no | +| infer | 0.19.0 | MIT | no | +| infer | 0.22.0 | MIT | no | +| inout | 0.1.4 | MIT OR Apache-2.0 | no | +| ipnet | 2.12.0 | MIT OR Apache-2.0 | no | +| is-docker | 0.2.0 | MIT | no | +| is-wsl | 0.4.0 | MIT | no | +| itertools | 0.13.0 | MIT OR Apache-2.0 | no | +| itoa | 1.0.18 | MIT OR Apache-2.0 | no | +| jakoch/install-vulkan-sdk-action | 37effcfa045411f8bfbbda26df2fd1b3bf3436fa | NOASSERTION | yes | +| javascriptcore-rs | 1.1.2 | MIT | no | +| javascriptcore-rs-sys | 1.1.1 | MIT | no | +| Jimver/cuda-toolkit | b8bf9c6c28f8a92fbb04dcfcaee872e60c57462d | NOASSERTION | yes | +| jni | 0.21.1 | MIT OR Apache-2.0 | no | +| jni-sys | 0.3.1 | MIT OR Apache-2.0 | no | +| jni-sys | 0.4.1 | MIT OR Apache-2.0 | no | +| jni-sys-macros | 0.4.1 | MIT OR Apache-2.0 | no | +| jobserver | 0.1.35 | MIT OR Apache-2.0 | no | +| js-sys | 0.3.103 | MIT OR Apache-2.0 | no | +| json-patch | 3.0.1 | MIT OR Apache-2.0 | no | +| jsonptr | 0.6.3 | MIT OR Apache-2.0 | no | +| keyboard-types | 0.7.0 | MIT OR Apache-2.0 | no | +| keyring | 4.1.6 | MIT OR Apache-2.0 | no | +| keyring-core | 1.0.0 | MIT OR Apache-2.0 | no | +| libappindicator | 0.9.0 | Apache-2.0 OR MIT | no | +| libappindicator-sys | 0.9.0 | Apache-2.0 OR MIT | no | +| libc | 0.2.189 | MIT OR Apache-2.0 | no | +| libdbus-sys | 0.2.7 | LicenseRef-bad-apache-2.0mit | no | +| libloading | 0.7.4 | ISC | no | +| libloading | 0.8.9 | ISC | no | +| libredox | 0.1.18 | MIT | no | +| linux-raw-sys | 0.12.1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| litemap | 0.8.2 | Unicode-3.0 | no | +| llama-cpp-2 | 0.1.154 | MIT OR Apache-2.0 | no | +| llama-cpp-sys-2 | 0.1.154 | MIT OR Apache-2.0 | no | +| lock_api | 0.4.14 | MIT OR Apache-2.0 | no | +| log | 0.4.33 | MIT OR Apache-2.0 | no | +| mail-parser | 0.11.7 | Apache-2.0 OR MIT | no | +| markup5ever | 0.38.0 | MIT OR Apache-2.0 | no | +| md-5 | 0.10.6 | MIT OR Apache-2.0 | no | +| memchr | 2.8.3 | Unlicense OR MIT | no | +| memoffset | 0.9.1 | MIT | no | +| mime | 0.3.17 | MIT OR Apache-2.0 | no | +| minimal-lexical | 0.2.1 | MIT OR Apache-2.0 | no | +| miniz_oxide | 0.8.9 | MIT OR Zlib OR Apache-2.0 | no | +| mio | 1.2.1 | MIT | no | +| mlsirm-core | 0.7.0 | NOASSERTION | yes | +| muda | 0.19.3 | Apache-2.0 OR MIT | no | +| ndk | 0.9.0 | MIT OR Apache-2.0 | no | +| ndk-sys | 0.6.0+11769913 | MIT OR Apache-2.0 | no | +| new_debug_unreachable | 1.0.6 | MIT | no | +| nom | 7.1.3 | MIT | no | +| num | 0.4.3 | MIT OR Apache-2.0 | no | +| num-bigint | 0.4.8 | MIT OR Apache-2.0 | no | +| num-complex | 0.4.6 | MIT OR Apache-2.0 | no | +| num-conv | 0.2.2 | MIT OR Apache-2.0 | no | +| num-integer | 0.1.46 | MIT OR Apache-2.0 | no | +| num-iter | 0.1.46 | MIT OR Apache-2.0 | no | +| num-rational | 0.4.2 | MIT OR Apache-2.0 | no | +| num-traits | 0.2.19 | MIT OR Apache-2.0 | no | +| num_enum | 0.7.6 | BSD-3-Clause OR MIT OR Apache-2.0 | no | +| num_enum_derive | 0.7.6 | BSD-3-Clause OR MIT OR Apache-2.0 | no | +| objc2 | 0.6.4 | MIT | no | +| objc2-app-kit | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-cloud-kit | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-core-data | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-core-foundation | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-core-graphics | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-core-image | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-core-location | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-core-text | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-encode | 4.1.0 | MIT | no | +| objc2-exception-helper | 0.1.1 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-foundation | 0.3.2 | MIT | no | +| objc2-io-surface | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-quartz-core | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-ui-kit | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-user-notifications | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-web-kit | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| once_cell | 1.21.4 | MIT OR Apache-2.0 | no | +| open | 5.3.6 | MIT | no | +| option-ext | 0.2.0 | MPL-2.0 | yes | +| ordered-stream | 0.2.0 | MIT OR Apache-2.0 | no | +| oxilangtag | 0.1.6 | MIT | no | +| oxiri | 0.2.11 | MIT OR Apache-2.0 | no | +| oxrdf | 0.3.3 | MIT OR Apache-2.0 | no | +| oxttl | 0.2.3 | MIT OR Apache-2.0 | no | +| pango | 0.18.3 | MIT | no | +| pango-sys | 0.18.0 | MIT | no | +| parking | 2.2.1 | Apache-2.0 OR MIT | no | +| parking_lot | 0.12.5 | MIT OR Apache-2.0 | no | +| parking_lot_core | 0.9.12 | MIT OR Apache-2.0 | no | +| percent-encoding | 2.3.2 | MIT OR Apache-2.0 | no | +| phf | 0.13.1 | MIT | no | +| phf_codegen | 0.13.1 | MIT | no | +| phf_generator | 0.13.1 | MIT | no | +| phf_macros | 0.13.1 | MIT | no | +| phf_shared | 0.13.1 | MIT | no | +| pin-project-lite | 0.2.17 | Apache-2.0 OR MIT | no | +| piper | 0.2.5 | MIT OR Apache-2.0 | no | +| pkg-config | 0.3.33 | MIT OR Apache-2.0 | no | +| plist | 1.10.0 | MIT | no | +| png | 0.17.16 | MIT OR Apache-2.0 | no | +| png | 0.18.1 | MIT OR Apache-2.0 | no | +| polling | 3.11.0 | Apache-2.0 OR MIT | no | +| potential_utf | 0.1.5 | Unicode-3.0 | no | +| powerfmt | 0.2.0 | MIT OR Apache-2.0 | no | +| ppv-lite86 | 0.2.21 | MIT OR Apache-2.0 | no | +| precomputed-hash | 0.1.1 | MIT | no | +| prettyplease | 0.2.37 | MIT OR Apache-2.0 | no | +| proc-macro-crate | 1.3.1 | MIT OR Apache-2.0 | no | +| proc-macro-crate | 2.0.2 | MIT OR Apache-2.0 | no | +| proc-macro-crate | 3.5.0 | MIT OR Apache-2.0 | no | +| proc-macro-error | 1.0.4 | MIT OR Apache-2.0 | no | +| proc-macro-error-attr | 1.0.4 | MIT OR Apache-2.0 | no | +| proc-macro2 | 1.0.106 | MIT OR Apache-2.0 | no | +| quick-xml | 0.41.0 | MIT | no | +| quote | 1.0.46 | MIT OR Apache-2.0 | no | +| r-efi | 5.3.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | yes | +| r-efi | 6.0.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | yes | +| rand | 0.9.4 | MIT OR Apache-2.0 | no | +| rand_chacha | 0.9.0 | MIT OR Apache-2.0 | no | +| rand_core | 0.9.5 | MIT OR Apache-2.0 | no | +| raw-window-handle | 0.6.2 | MIT OR Apache-2.0 OR Zlib | no | +| redox_syscall | 0.5.18 | MIT | no | +| redox_users | 0.5.2 | MIT | no | +| ref-cast | 1.0.25 | MIT OR Apache-2.0 | no | +| ref-cast-impl | 1.0.25 | MIT OR Apache-2.0 | no | +| regex | 1.13.0 | MIT OR Apache-2.0 | no | +| regex-automata | 0.4.15 | MIT OR Apache-2.0 | no | +| regex-syntax | 0.8.11 | MIT OR Apache-2.0 | no | +| reqwest | 0.13.4 | MIT OR Apache-2.0 | no | +| rfd | 0.16.0 | MIT | no | +| ring | 0.17.14 | Apache-2.0 AND ISC | no | +| rustc-hash | 2.1.3 | Apache-2.0 OR MIT | no | +| rustc_version | 0.4.1 | MIT OR Apache-2.0 | no | +| rustix | 1.1.4 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| rustls | 0.23.41 | Apache-2.0 OR ISC OR MIT | no | +| rustls-pki-types | 1.15.0 | MIT OR Apache-2.0 | no | +| rustls-webpki | 0.103.13 | ISC | no | +| rustversion | 1.0.23 | MIT OR Apache-2.0 | no | +| ryu | 1.0.23 | Apache-2.0 OR BSL-1.0 | no | +| same-file | 1.0.6 | Unlicense OR MIT | no | +| schemars | 0.8.22 | MIT | no | +| schemars | 0.9.0 | MIT | no | +| schemars | 1.2.1 | MIT | no | +| schemars_derive | 0.8.22 | MIT | no | +| scopeguard | 1.2.0 | MIT OR Apache-2.0 | no | +| secret-service | 5.1.0 | MIT OR Apache-2.0 | no | +| security-framework | 3.7.0 | MIT OR Apache-2.0 | no | +| security-framework-sys | 2.17.0 | MIT OR Apache-2.0 | no | +| selectors | 0.36.1 | MPL-2.0 | yes | +| semver | 1.0.28 | MIT OR Apache-2.0 | no | +| serde | 1.0.229 | MIT OR Apache-2.0 | no | +| serde-untagged | 0.1.9 | MIT OR Apache-2.0 | no | +| serde_core | 1.0.229 | MIT OR Apache-2.0 | no | +| serde_derive | 1.0.229 | MIT OR Apache-2.0 | no | +| serde_derive_internals | 0.29.1 | MIT OR Apache-2.0 | no | +| serde_json | 1.0.151 | MIT OR Apache-2.0 | no | +| serde_repr | 0.1.20 | MIT OR Apache-2.0 | no | +| serde_spanned | 0.6.9 | MIT OR Apache-2.0 | no | +| serde_spanned | 1.1.1 | MIT OR Apache-2.0 | no | +| serde_with | 3.21.0 | MIT OR Apache-2.0 | no | +| serde_with_macros | 3.21.0 | MIT OR Apache-2.0 | no | +| serialize-to-javascript | 0.1.2 | MIT OR Apache-2.0 | no | +| serialize-to-javascript-impl | 0.1.2 | MIT OR Apache-2.0 | no | +| servo_arc | 0.4.3 | MIT OR Apache-2.0 | no | +| sha1 | 0.11.0 | MIT OR Apache-2.0 | no | +| sha2 | 0.10.9 | MIT OR Apache-2.0 | no | +| sha2 | 0.11.0 | MIT OR Apache-2.0 | no | +| shlex | 1.3.0 | MIT OR Apache-2.0 | no | +| shlex | 2.0.1 | MIT OR Apache-2.0 | no | +| signal-hook-registry | 1.4.8 | MIT OR Apache-2.0 | no | +| simd-adler32 | 0.3.9 | MIT | no | +| siphasher | 1.0.3 | MIT OR Apache-2.0 | no | +| slab | 0.4.12 | MIT | no | +| smallvec | 1.15.2 | MIT OR Apache-2.0 | no | +| socket2 | 0.6.4 | MIT OR Apache-2.0 | no | +| softbuffer | 0.4.8 | MIT OR Apache-2.0 | no | +| softprops/action-gh-release | 3d0d9888cb7fd7b750713d6e236d1fcb99157228 | NOASSERTION | yes | +| soup3 | 0.5.0 | MIT | no | +| soup3-sys | 0.5.0 | MIT | no | +| stable_deref_trait | 1.2.1 | MIT OR Apache-2.0 | no | +| string_cache | 0.9.0 | MIT OR Apache-2.0 | no | +| string_cache_codegen | 0.6.1 | MIT OR Apache-2.0 | no | +| strsim | 0.11.1 | MIT | no | +| subtle | 2.6.1 | BSD-3-Clause | no | +| Swatinem/rust-cache | 6323deb102c322ba6fcbdcafc7e3dddab59af2b6 | NOASSERTION | yes | +| swift-rs | 1.0.7 | MIT OR Apache-2.0 | no | +| syn | 1.0.109 | MIT OR Apache-2.0 | no | +| syn | 2.0.118 | MIT OR Apache-2.0 | no | +| syn | 3.0.3 | MIT OR Apache-2.0 | no | +| sync_wrapper | 1.0.2 | Apache-2.0 | no | +| synstructure | 0.13.2 | MIT | no | +| system-deps | 6.2.2 | MIT OR Apache-2.0 | no | +| tao | 0.35.3 | Apache-2.0 | no | +| tao-macros | 0.1.3 | MIT OR Apache-2.0 | no | +| target-lexicon | 0.12.16 | Apache-2.0 WITH LLVM-exception | no | +| tauri | 2.11.5 | Apache-2.0 OR MIT | no | +| tauri-build | 2.6.3 | Apache-2.0 OR MIT | no | +| tauri-codegen | 2.6.3 | Apache-2.0 OR MIT | no | +| tauri-macros | 2.6.3 | Apache-2.0 OR MIT | no | +| tauri-plugin | 2.6.3 | Apache-2.0 OR MIT | no | +| tauri-plugin-dialog | 2.7.2 | Apache-2.0 OR MIT | no | +| tauri-plugin-fs | 2.5.1 | Apache-2.0 OR MIT | no | +| tauri-plugin-opener | 2.5.4 | Apache-2.0 OR MIT | no | +| tauri-runtime | 2.11.3 | Apache-2.0 OR MIT | no | +| tauri-runtime-wry | 2.11.4 | Apache-2.0 OR MIT | no | +| tauri-utils | 2.9.3 | Apache-2.0 OR MIT | no | +| tauri-winres | 0.3.6 | MIT | no | +| tempfile | 3.27.0 | MIT OR Apache-2.0 | no | +| tendril | 0.5.1 | MIT OR Apache-2.0 | no | +| thiserror | 1.0.69 | MIT OR Apache-2.0 | no | +| thiserror | 2.0.18 | MIT OR Apache-2.0 | no | +| thiserror-impl | 1.0.69 | MIT OR Apache-2.0 | no | +| thiserror-impl | 2.0.18 | MIT OR Apache-2.0 | no | +| time | 0.3.53 | MIT OR Apache-2.0 | no | +| time-core | 0.1.9 | MIT OR Apache-2.0 | no | +| time-macros | 0.2.31 | MIT OR Apache-2.0 | no | +| tinystr | 0.8.3 | Unicode-3.0 | no | +| tinyvec | 1.11.0 | Zlib OR Apache-2.0 OR MIT | no | +| tinyvec_macros | 0.1.1 | MIT OR Apache-2.0 OR Zlib | no | +| tokio | 1.52.3 | MIT | no | +| tokio-util | 0.7.18 | MIT | no | +| toml | 0.8.2 | MIT OR Apache-2.0 | no | +| toml | 0.9.12+spec-1.1.0 | MIT OR Apache-2.0 | no | +| toml | 1.1.2+spec-1.1.0 | MIT OR Apache-2.0 | no | +| toml_datetime | 0.6.3 | MIT OR Apache-2.0 | no | +| toml_datetime | 0.7.5+spec-1.1.0 | MIT OR Apache-2.0 | no | +| toml_datetime | 1.1.1+spec-1.1.0 | MIT OR Apache-2.0 | no | +| toml_edit | 0.19.15 | MIT OR Apache-2.0 | no | +| toml_edit | 0.20.2 | MIT OR Apache-2.0 | no | +| toml_edit | 0.25.12+spec-1.1.0 | MIT OR Apache-2.0 | no | +| toml_parser | 1.1.2+spec-1.1.0 | MIT OR Apache-2.0 | no | +| toml_writer | 1.1.1+spec-1.1.0 | MIT OR Apache-2.0 | no | +| tower | 0.5.3 | MIT | no | +| tower-http | 0.6.11 | MIT | no | +| tower-layer | 0.3.3 | MIT | no | +| tower-service | 0.3.3 | MIT | no | +| tracing | 0.1.44 | MIT | no | +| tracing-attributes | 0.1.31 | MIT | no | +| tracing-core | 0.1.36 | MIT | no | +| trash | 5.2.6 | MIT | no | +| tray-icon | 0.24.1 | MIT OR Apache-2.0 | no | +| try-lock | 0.2.5 | MIT | no | +| typed-path | 0.12.3 | MIT OR Apache-2.0 | no | +| typeid | 1.0.3 | MIT OR Apache-2.0 | no | +| typenum | 1.20.1 | MIT OR Apache-2.0 | no | +| uds_windows | 1.2.1 | MIT | no | +| unic-char-property | 0.9.0 | MIT OR Apache-2.0 | no | +| unic-char-range | 0.9.0 | MIT OR Apache-2.0 | no | +| unic-common | 0.9.0 | MIT OR Apache-2.0 | no | +| unic-ucd-ident | 0.9.0 | MIT OR Apache-2.0 | no | +| unic-ucd-version | 0.9.0 | MIT OR Apache-2.0 | no | +| unicode-general-category | 1.1.0 | Apache-2.0 | no | +| unicode-ident | 1.0.24 | (MIT OR Apache-2.0) AND Unicode-3.0 | no | +| unicode-normalization | 0.1.25 | MIT OR Apache-2.0 | no | +| unicode-segmentation | 1.13.3 | MIT OR Apache-2.0 | no | +| untrusted | 0.9.0 | ISC | no | +| ureq | 3.4.0 | MIT OR Apache-2.0 | no | +| ureq-proto | 0.6.1 | MIT OR Apache-2.0 | no | +| url | 2.5.8 | MIT OR Apache-2.0 | no | +| urlencoding | 2.1.3 | MIT | no | +| urlpattern | 0.3.0 | MIT | no | +| utf8-zero | 0.8.1 | MIT OR Apache-2.0 | no | +| utf8_iter | 1.0.4 | Apache-2.0 OR MIT | no | +| uuid | 1.23.4 | Apache-2.0 OR MIT | no | +| valuable | 0.1.1 | MIT | no | +| version-compare | 0.2.1 | MIT | no | +| version_check | 0.9.5 | MIT OR Apache-2.0 | no | +| vswhom | 0.1.0 | MIT | no | +| vswhom-sys | 0.1.3 | MIT | no | +| walkdir | 2.5.0 | Unlicense OR MIT | no | +| want | 0.3.1 | MIT | no | +| wasi | 0.11.1+wasi-snapshot-preview1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| wasip2 | 1.0.4+wasi-0.2.12 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| wasm-bindgen | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-futures | 0.4.76 | MIT OR Apache-2.0 | no | +| wasm-bindgen-macro | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-macro-support | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-shared | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-streams | 0.5.0 | MIT OR Apache-2.0 | no | +| web-sys | 0.3.103 | MIT OR Apache-2.0 | no | +| web-time | 1.1.0 | MIT OR Apache-2.0 | no | +| web_atoms | 0.2.5 | MIT OR Apache-2.0 | no | +| webkit2gtk | 2.0.2 | MIT | no | +| webkit2gtk-sys | 2.0.2 | MIT | no | +| webpki-roots | 1.0.8 | CDLA-Permissive-2.0 | no | +| webview2-com | 0.38.2 | MIT | no | +| webview2-com-macros | 0.8.1 | MIT | no | +| webview2-com-sys | 0.38.2 | MIT | no | +| winapi | 0.3.9 | MIT OR Apache-2.0 | no | +| winapi-i686-pc-windows-gnu | 0.4.0 | MIT OR Apache-2.0 | no | +| winapi-util | 0.1.11 | Unlicense OR MIT | no | +| winapi-x86_64-pc-windows-gnu | 0.4.0 | MIT OR Apache-2.0 | no | +| window-vibrancy | 0.6.0 | Apache-2.0 OR MIT | no | +| windows | 0.56.0 | MIT OR Apache-2.0 | no | +| windows | 0.61.3 | MIT OR Apache-2.0 | no | +| windows-collections | 0.2.0 | MIT OR Apache-2.0 | no | +| windows-core | 0.56.0 | MIT OR Apache-2.0 | no | +| windows-core | 0.61.2 | MIT OR Apache-2.0 | no | +| windows-core | 0.62.2 | MIT OR Apache-2.0 | no | +| windows-future | 0.2.1 | MIT OR Apache-2.0 | no | +| windows-implement | 0.56.0 | MIT OR Apache-2.0 | no | +| windows-implement | 0.60.2 | MIT OR Apache-2.0 | no | +| windows-interface | 0.56.0 | MIT OR Apache-2.0 | no | +| windows-interface | 0.59.3 | MIT OR Apache-2.0 | no | +| windows-link | 0.1.3 | MIT OR Apache-2.0 | no | +| windows-link | 0.2.1 | MIT OR Apache-2.0 | no | +| windows-native-keyring-store | 1.1.0 | MIT OR Apache-2.0 | no | +| windows-numerics | 0.2.0 | MIT OR Apache-2.0 | no | +| windows-result | 0.1.2 | MIT OR Apache-2.0 | no | +| windows-result | 0.3.4 | MIT OR Apache-2.0 | no | +| windows-result | 0.4.1 | MIT OR Apache-2.0 | no | +| windows-strings | 0.4.2 | MIT OR Apache-2.0 | no | +| windows-strings | 0.5.1 | MIT OR Apache-2.0 | no | +| windows-sys | 0.45.0 | MIT OR Apache-2.0 | no | +| windows-sys | 0.52.0 | MIT OR Apache-2.0 | no | +| windows-sys | 0.59.0 | MIT OR Apache-2.0 | no | +| windows-sys | 0.60.2 | MIT OR Apache-2.0 | no | +| windows-sys | 0.61.2 | MIT OR Apache-2.0 | no | +| windows-targets | 0.42.2 | MIT OR Apache-2.0 | no | +| windows-targets | 0.52.6 | MIT OR Apache-2.0 | no | +| windows-targets | 0.53.5 | MIT OR Apache-2.0 | no | +| windows-threading | 0.1.0 | MIT OR Apache-2.0 | no | +| windows-version | 0.1.7 | MIT OR Apache-2.0 | no | +| windows_aarch64_gnullvm | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_aarch64_gnullvm | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_aarch64_gnullvm | 0.53.1 | MIT OR Apache-2.0 | no | +| windows_aarch64_msvc | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_aarch64_msvc | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_aarch64_msvc | 0.53.1 | MIT OR Apache-2.0 | no | +| windows_i686_gnu | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_i686_gnu | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_i686_gnu | 0.53.1 | MIT OR Apache-2.0 | no | +| windows_i686_gnullvm | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_i686_gnullvm | 0.53.1 | MIT OR Apache-2.0 | no | +| windows_i686_msvc | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_i686_msvc | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_i686_msvc | 0.53.1 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnu | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnu | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnu | 0.53.1 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnullvm | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnullvm | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnullvm | 0.53.1 | MIT OR Apache-2.0 | no | +| windows_x86_64_msvc | 0.42.2 | MIT OR Apache-2.0 | no | +| windows_x86_64_msvc | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_x86_64_msvc | 0.53.1 | MIT OR Apache-2.0 | no | +| winnow | 0.5.40 | MIT | no | +| winnow | 0.7.15 | MIT | no | +| winnow | 1.0.3 | MIT | no | +| winreg | 0.55.0 | MIT | no | +| wit-bindgen | 0.57.1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| writeable | 0.6.3 | Unicode-3.0 | no | +| wry | 0.55.1 | Apache-2.0 OR MIT | no | +| x11 | 2.21.0 | MIT | no | +| x11-dl | 2.21.0 | MIT | no | +| yoke | 0.8.3 | Unicode-3.0 | no | +| yoke-derive | 0.8.2 | Unicode-3.0 | no | +| zbus | 5.17.0 | MIT | no | +| zbus-secret-service-keyring-store | 1.0.0 | MIT OR Apache-2.0 | no | +| zbus_macros | 5.17.0 | MIT | no | +| zbus_names | 4.3.3 | MIT | no | +| zerocopy | 0.8.54 | BSD-2-Clause OR Apache-2.0 OR MIT | no | +| zerocopy-derive | 0.8.54 | BSD-2-Clause OR Apache-2.0 OR MIT | no | +| zerofrom | 0.1.8 | Unicode-3.0 | no | +| zerofrom-derive | 0.1.7 | Unicode-3.0 | no | +| zeroize | 1.9.0 | Apache-2.0 OR MIT | no | +| zeroize_derive | 1.5.0 | Apache-2.0 OR MIT | no | +| zerotrie | 0.2.4 | Unicode-3.0 | no | +| zerovec | 0.11.6 | Unicode-3.0 | no | +| zerovec-derive | 0.11.3 | Unicode-3.0 | no | +| zip | 8.6.0 | MIT | no | +| zlib-rs | 0.6.6 | Zlib | no | +| zmij | 1.0.21 | MIT | no | +| zopfli | 0.8.3 | Apache-2.0 | no | +| zvariant | 5.13.0 | MIT | no | +| zvariant_derive | 5.13.0 | MIT | no | +| zvariant_utils | 3.5.0 | MIT | no | + +### ContextualWisdomLab/EgressWeave + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/download-artifact | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c | NOASSERTION | yes | +| actions/download-artifact | d3f86a106a0bac45b974a628896c90dbdf5c8093 | NOASSERTION | yes | +| actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | yes | +| anyio | 4.14.2 | MIT | no | +| backports-asyncio-runner | 1.2.0 | NOASSERTION | yes | +| certifi | 2026.7.22 | MPL-2.0 | yes | +| coverage | — | NOASSERTION | yes | +| exceptiongroup | 1.3.1 | MIT AND Python-2.0 | no | +| h11 | 0.16.0 | MIT | no | +| hatchling | — | NOASSERTION | yes | +| httpcore | 1.0.9 | BSD-2-Clause AND BSD-3-Clause | no | +| httpx | 0.28.1 | BSD-3-Clause | no | +| idna | — | NOASSERTION | yes | +| idna | 3.18 | BSD-3-Clause | no | +| iniconfig | 2.3.0 | MIT | no | +| packaging | 26.3 | Apache-2.0 OR BSD-2-Clause | no | +| pathspec | — | NOASSERTION | yes | +| pluggy | 1.6.0 | MIT | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pypa/gh-action-pypi-publish | dc37677b2e1c63e2034f94d8a5b11f265b73ba33 | NOASSERTION | yes | +| pytest | — | NOASSERTION | yes | +| pytest | 9.1.1 | MIT | no | +| pytest-asyncio | — | NOASSERTION | yes | +| pytest-asyncio | 1.4.0 | Apache-2.0 | no | +| ruff | 0.16.1 | MIT | no | +| step-security/harden-runner | bf7454d06d71f1098171f2acdf0cd4708d7b5920 | NOASSERTION | yes | +| tomli | 2.4.1 | MIT | no | +| trove-classifiers | — | NOASSERTION | yes | +| typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | + +### ContextualWisdomLab/ELUNVERA + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/EmbedRelay + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/enterprise-architecture-core + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/fast-mlsirm + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| allocator-api2 | 0.2.21 | MIT OR Apache-2.0 | no | +| android_system_properties | 0.1.5 | MIT OR Apache-2.0 | no | +| android_system_properties | 0.1.6 | MIT OR Apache-2.0 | no | +| arbitrary | 1.4.2 | MIT OR Apache-2.0 | no | +| arrayvec | 0.7.8 | MIT OR Apache-2.0 | no | +| ash | 0.38.0+1.3.281 | MIT OR Apache-2.0 | no | +| atheris | 3.1.0 | Apache-2.0 | no | +| autocfg | 1.5.1 | Apache-2.0 OR MIT | no | +| bit-set | 0.10.0 | Apache-2.0 OR MIT | no | +| bit-set | 0.8.0 | Apache-2.0 OR MIT | no | +| bit-vec | 0.8.0 | Apache-2.0 OR MIT | no | +| bit-vec | 0.9.1 | Apache-2.0 OR MIT | no | +| bitflags | 2.13.0 | MIT OR Apache-2.0 | no | +| bitflags | 2.13.2 | MIT OR Apache-2.0 | no | +| block-buffer | 0.10.4 | MIT OR Apache-2.0 | no | +| block2 | 0.6.2 | MIT | no | +| build | 1.6.0 | MIT | no | +| bumpalo | 3.20.3 | MIT OR Apache-2.0 | no | +| bytemuck | 1.25.2 | Zlib OR Apache-2.0 OR MIT | no | +| bytemuck | >= 1.25.0,< 2.0.0 | NOASSERTION | yes | +| bytemuck_derive | 1.10.2 | Zlib OR Apache-2.0 OR MIT | no | +| bytemuck_derive | 1.12.1 | Zlib OR Apache-2.0 OR MIT | no | +| cc | 1.2.66 | MIT OR Apache-2.0 | no | +| cfg-if | 1.0.4 | MIT OR Apache-2.0 | no | +| cfg-if | 1.0.5 | MIT OR Apache-2.0 | no | +| cfg_aliases | 0.2.1 | MIT | no | +| cfg_aliases | 0.2.2 | MIT | no | +| codespan-reporting | 0.13.1 | Apache-2.0 | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-cflite_pr.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | yes | +| ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-ci.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | yes | +| ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-codeql.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | yes | +| ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-hourly-pr-governance.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | yes | +| ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-publish-pypi.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | yes | +| ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-pypi-gap-guard.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | yes | +| ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-release-tag.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | yes | +| ContextualWisdomLab/.github/.github/workflows/fast-mlsirm-statistical-studies.yml | 4e32b99ec79b6565800d1081f60acfea368adecd | NOASSERTION | yes | +| cpufeatures | 0.2.17 | MIT OR Apache-2.0 | no | +| crunchy | 0.2.4 | MIT | no | +| crypto-common | 0.1.7 | MIT OR Apache-2.0 | no | +| deranged | 0.5.8 | MIT OR Apache-2.0 | no | +| digest | 0.10.7 | MIT OR Apache-2.0 | no | +| dispatch2 | 0.3.1 | Zlib OR Apache-2.0 OR MIT | no | +| dlib | 0.5.3 | MIT | no | +| document-features | 0.2.12 | MIT OR Apache-2.0 | no | +| equivalent | 1.0.2 | Apache-2.0 OR MIT | no | +| errno | 0.3.14 | MIT OR Apache-2.0 | no | +| fast-mlsirm | 0.11.4 | MIT | no | +| fastrand | 2.4.1 | Apache-2.0 OR MIT | no | +| find-msvc-tools | 0.1.9 | MIT OR Apache-2.0 | no | +| fnv | 1.0.7 | Apache-2.0 OR MIT | no | +| foldhash | 0.2.0 | Zlib | no | +| futures-core | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-core | 0.3.34 | MIT OR Apache-2.0 | no | +| futures-task | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-task | 0.3.34 | MIT OR Apache-2.0 | no | +| futures-util | 0.3.32 | MIT OR Apache-2.0 | no | +| futures-util | 0.3.34 | MIT OR Apache-2.0 | no | +| generic-array | 0.14.7 | MIT | no | +| getrandom | 0.3.4 | MIT OR Apache-2.0 | no | +| getrandom | 0.4.3 | MIT OR Apache-2.0 | no | +| gl_generator | 0.14.0 | Apache-2.0 | no | +| glow | 0.17.0 | MIT OR Apache-2.0 OR Zlib | no | +| glutin_wgl_sys | 0.6.1 | Apache-2.0 | no | +| gpu-allocator | 0.28.0 | MIT OR Apache-2.0 | no | +| half | 2.7.1 | MIT OR Apache-2.0 | no | +| hashbrown | 0.16.1 | MIT OR Apache-2.0 | no | +| hashbrown | 0.17.1 | MIT OR Apache-2.0 | no | +| heck | 0.5.0 | MIT OR Apache-2.0 | no | +| hypothesis | 6.156.6 | MPL-2.0 AND MPL-1.1 | yes | +| hypothesis | 6.168.0 | MPL-2.0 | yes | +| indexmap | 2.14.0 | Apache-2.0 OR MIT | no | +| indexmap | 2.14.2 | Apache-2.0 OR MIT | no | +| iniconfig | 2.3.0 | MIT | no | +| itoa | 1.0.18 | MIT OR Apache-2.0 | no | +| jni-sys | 0.3.1 | MIT OR Apache-2.0 | no | +| jni-sys | 0.4.1 | MIT OR Apache-2.0 | no | +| jni-sys-macros | 0.4.1 | MIT OR Apache-2.0 | no | +| jobserver | 0.1.35 | MIT OR Apache-2.0 | no | +| js-sys | 0.3.103 | MIT OR Apache-2.0 | no | +| js-sys | 0.3.105 | MIT OR Apache-2.0 | no | +| khronos-egl | 6.0.0 | MIT OR Apache-2.0 | no | +| khronos_api | 3.1.0 | Apache-2.0 | no | +| libc | 0.2.186 | MIT OR Apache-2.0 | no | +| libc | 0.2.189 | MIT OR Apache-2.0 | no | +| libfuzzer-sys | 0.4.13 | (MIT OR Apache-2.0) AND NCSA | no | +| libloading | 0.8.9 | ISC | no | +| libm | 0.2.16 | MIT | no | +| linux-raw-sys | 0.12.1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| litrs | 1.0.0 | MIT OR Apache-2.0 | no | +| lock_api | 0.4.14 | MIT OR Apache-2.0 | no | +| log | 0.4.33 | MIT OR Apache-2.0 | no | +| log | 0.4.34 | MIT OR Apache-2.0 | no | +| matrixmultiply | 0.3.11 | MIT OR Apache-2.0 | no | +| maturin | — | NOASSERTION | yes | +| maturin | 1.15.0 | MIT OR Apache-2.0 | no | +| memchr | 2.8.3 | Unlicense OR MIT | no | +| naga | 30.0.0 | MIT OR Apache-2.0 | no | +| naga | 30.0.1 | MIT OR Apache-2.0 | no | +| naga-types | 30.0.0 | MIT OR Apache-2.0 | no | +| naga-types | 30.0.1 | MIT OR Apache-2.0 | no | +| ndarray | 0.17.2 | MIT OR Apache-2.0 | no | +| ndk-sys | 0.6.0+11769913 | MIT OR Apache-2.0 | no | +| num-complex | 0.4.6 | MIT OR Apache-2.0 | no | +| num-conv | 0.2.2 | MIT OR Apache-2.0 | no | +| num-integer | 0.1.47 | MIT OR Apache-2.0 | no | +| num-traits | 0.2.19 | MIT OR Apache-2.0 | no | +| numpy | — | NOASSERTION | yes | +| numpy | 0.29.0 | BSD-2-Clause | no | +| numpy | 2.5.1 | BSD-3-Clause | no | +| numpy | 2.5.2 | BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0 | no | +| objc2 | 0.6.4 | MIT | no | +| objc2-core-foundation | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-core-graphics | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-encode | 4.1.0 | MIT | no | +| objc2-foundation | 0.3.2 | MIT | no | +| objc2-io-surface | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-metal | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| objc2-quartz-core | 0.3.2 | Zlib OR Apache-2.0 OR MIT | no | +| once_cell | 1.21.4 | MIT OR Apache-2.0 | no | +| ordered-float | 5.3.0 | MIT | no | +| ordered-float | 5.5.0 | MIT | no | +| packaging | 26.2 | Apache-2.0 AND BSD-2-Clause | no | +| parking_lot | 0.12.5 | MIT OR Apache-2.0 | no | +| parking_lot_core | 0.9.12 | MIT OR Apache-2.0 | no | +| pin-project-lite | 0.2.17 | Apache-2.0 OR MIT | no | +| pkg-config | 0.3.33 | MIT OR Apache-2.0 | no | +| pkg-config | 0.3.34 | MIT OR Apache-2.0 | no | +| pluggy | 1.6.0 | MIT | no | +| pollster | 1.0.1 | LicenseRef-bad-apache-2.0mit | no | +| pollster | >= 1.0.1,< 2.0.0 | NOASSERTION | yes | +| portable-atomic | 1.13.1 | Apache-2.0 OR MIT | no | +| portable-atomic | 1.15.0 | Apache-2.0 OR MIT | no | +| portable-atomic-util | 0.2.7 | Apache-2.0 OR MIT | no | +| portable-atomic-util | 0.2.8 | Apache-2.0 OR MIT | no | +| powerfmt | 0.2.0 | MIT OR Apache-2.0 | no | +| ppv-lite86 | 0.2.21 | MIT OR Apache-2.0 | no | +| presser | 0.3.1 | MIT OR Apache-2.0 | no | +| proc-macro2 | 1.0.106 | MIT OR Apache-2.0 | no | +| proc-macro2 | 1.0.107 | MIT OR Apache-2.0 | no | +| profiling | 1.0.18 | MIT OR Apache-2.0 | no | +| proptest | 1.11.0 | MIT OR Apache-2.0 | no | +| proptest | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pyo3 | 0.29.2 | MIT OR Apache-2.0 | no | +| pyo3-build-config | 0.29.2 | MIT OR Apache-2.0 | no | +| pyo3-ffi | 0.29.2 | MIT OR Apache-2.0 | no | +| pyo3-macros | 0.29.2 | MIT OR Apache-2.0 | no | +| pyo3-macros-backend | 0.29.2 | MIT OR Apache-2.0 | no | +| pyproject-hooks | 1.2.0 | MIT | no | +| pytest | — | NOASSERTION | yes | +| pytest | 9.1.1 | MIT | no | +| quick-error | 1.2.3 | MIT OR Apache-2.0 | no | +| quote | 1.0.46 | MIT OR Apache-2.0 | no | +| quote | 1.0.47 | MIT OR Apache-2.0 | no | +| r-efi | 5.3.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | yes | +| r-efi | 6.0.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | yes | +| rand | 0.9.4 | MIT OR Apache-2.0 | no | +| rand_chacha | 0.9.0 | MIT OR Apache-2.0 | no | +| rand_core | 0.9.5 | MIT OR Apache-2.0 | no | +| rand_xorshift | 0.4.0 | MIT OR Apache-2.0 | no | +| range-alloc | 0.1.5 | MIT OR Apache-2.0 | no | +| raw-window-handle | 0.6.2 | MIT OR Apache-2.0 OR Zlib | no | +| raw-window-metal | 1.1.0 | MIT OR Apache-2.0 | no | +| rawpointer | 0.2.1 | MIT OR Apache-2.0 | no | +| redox_syscall | 0.5.18 | MIT | no | +| regex-syntax | 0.8.11 | MIT OR Apache-2.0 | no | +| renderdoc-sys | 1.1.0 | MIT OR Apache-2.0 | no | +| rustc-hash | 1.1.0 | Apache-2.0 OR MIT | no | +| rustc-hash | 2.1.3 | Apache-2.0 OR MIT | no | +| rustix | 1.1.4 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| rustversion | 1.0.23 | MIT OR Apache-2.0 | no | +| rusty-fork | 0.3.1 | MIT OR Apache-2.0 | no | +| scopeguard | 1.2.0 | MIT OR Apache-2.0 | no | +| serde | 1.0.228 | MIT OR Apache-2.0 | no | +| serde | 1.0.229 | MIT OR Apache-2.0 | no | +| serde | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| serde_core | 1.0.228 | MIT OR Apache-2.0 | no | +| serde_core | 1.0.229 | MIT OR Apache-2.0 | no | +| serde_derive | 1.0.228 | MIT OR Apache-2.0 | no | +| serde_derive | 1.0.229 | MIT OR Apache-2.0 | no | +| serde_json | 1.0.151 | MIT OR Apache-2.0 | no | +| serde_json | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| sha2 | 0.10.9 | MIT OR Apache-2.0 | no | +| sha2 | >= 0.10.9,< 0.11.0 | NOASSERTION | yes | +| shlex | 2.0.1 | MIT OR Apache-2.0 | no | +| slab | 0.4.12 | MIT | no | +| slotmap | 1.1.1 | Zlib | no | +| smallvec | 1.15.2 | MIT OR Apache-2.0 | no | +| smallvec | 1.16.1 | MIT OR Apache-2.0 | no | +| sortedcontainers | 2.4.0 | Apache-2.0 | no | +| spirv | 0.4.0+sdk-1.4.341.0 | Apache-2.0 | no | +| static_assertions | 1.1.0 | MIT OR Apache-2.0 | no | +| syn | 2.0.118 | MIT OR Apache-2.0 | no | +| syn | 2.0.119 | MIT OR Apache-2.0 | no | +| syn | 3.0.6 | MIT OR Apache-2.0 | no | +| target-lexicon | 0.13.5 | Apache-2.0 WITH LLVM-exception | no | +| tempfile | 3.27.0 | MIT OR Apache-2.0 | no | +| termcolor | 1.4.1 | Unlicense OR MIT | no | +| thiserror | 2.0.18 | MIT OR Apache-2.0 | no | +| thiserror | 2.0.20 | MIT OR Apache-2.0 | no | +| thiserror-impl | 2.0.18 | MIT OR Apache-2.0 | no | +| thiserror-impl | 2.0.20 | MIT OR Apache-2.0 | no | +| time | 0.3.55 | MIT OR Apache-2.0 | no | +| time | >= 0.3.0,< 0.4.0 | NOASSERTION | yes | +| time-core | 0.1.9 | MIT OR Apache-2.0 | no | +| time-macros | 0.2.32 | MIT OR Apache-2.0 | no | +| typenum | 1.20.1 | MIT OR Apache-2.0 | no | +| unarray | 0.1.4 | MIT OR Apache-2.0 | no | +| unicode-ident | 1.0.24 | (MIT OR Apache-2.0) AND Unicode-3.0 | no | +| unicode-ident | 1.0.26 | (MIT OR Apache-2.0) AND Unicode-3.0 | no | +| unicode-width | 0.2.2 | MIT OR Apache-2.0 | no | +| uuid | 1.25.0 | Apache-2.0 OR MIT | no | +| uuid | 1.26.1 | Apache-2.0 OR MIT | no | +| uuid | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| version_check | 0.9.5 | MIT OR Apache-2.0 | no | +| wait-timeout | 0.2.1 | MIT OR Apache-2.0 | no | +| wasip2 | 1.0.4+wasi-0.2.12 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| wasm-bindgen | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen | 0.2.128 | MIT OR Apache-2.0 | no | +| wasm-bindgen-futures | 0.4.76 | MIT OR Apache-2.0 | no | +| wasm-bindgen-futures | 0.4.78 | MIT OR Apache-2.0 | no | +| wasm-bindgen-macro | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-macro | 0.2.128 | MIT OR Apache-2.0 | no | +| wasm-bindgen-macro-support | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-macro-support | 0.2.128 | MIT OR Apache-2.0 | no | +| wasm-bindgen-shared | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-shared | 0.2.128 | MIT OR Apache-2.0 | no | +| wayland-sys | 0.31.11 | MIT | no | +| web-sys | 0.3.103 | MIT OR Apache-2.0 | no | +| web-sys | 0.3.105 | MIT OR Apache-2.0 | no | +| wgpu | 30.0.0 | MIT OR Apache-2.0 | no | +| wgpu | 30.0.1 | MIT OR Apache-2.0 | no | +| wgpu | >= 30.0.0,< 31.0.0 | NOASSERTION | yes | +| wgpu-core | 30.0.0 | MIT OR Apache-2.0 | no | +| wgpu-core | 30.0.1 | MIT OR Apache-2.0 | no | +| wgpu-core-deps-apple | 30.0.0 | MIT OR Apache-2.0 | no | +| wgpu-core-deps-apple | 30.0.1 | MIT OR Apache-2.0 | no | +| wgpu-core-deps-emscripten | 30.0.0 | MIT OR Apache-2.0 | no | +| wgpu-core-deps-emscripten | 30.0.1 | MIT OR Apache-2.0 | no | +| wgpu-core-deps-windows-linux-android | 30.0.0 | MIT OR Apache-2.0 | no | +| wgpu-core-deps-windows-linux-android | 30.0.1 | MIT OR Apache-2.0 | no | +| wgpu-hal | 30.0.0 | MIT OR Apache-2.0 | no | +| wgpu-hal | 30.0.1 | MIT OR Apache-2.0 | no | +| wgpu-naga-bridge | 30.0.0 | MIT OR Apache-2.0 | no | +| wgpu-naga-bridge | 30.0.1 | MIT OR Apache-2.0 | no | +| wgpu-types | 30.0.0 | MIT OR Apache-2.0 | no | +| wgpu-types | 30.0.1 | MIT OR Apache-2.0 | no | +| winapi-util | 0.1.11 | Unlicense OR MIT | no | +| windows | 0.62.2 | MIT OR Apache-2.0 | no | +| windows-collections | 0.3.2 | MIT OR Apache-2.0 | no | +| windows-core | 0.62.2 | MIT OR Apache-2.0 | no | +| windows-future | 0.3.2 | MIT OR Apache-2.0 | no | +| windows-implement | 0.60.2 | MIT OR Apache-2.0 | no | +| windows-interface | 0.59.3 | MIT OR Apache-2.0 | no | +| windows-link | 0.2.1 | MIT OR Apache-2.0 | no | +| windows-numerics | 0.3.1 | MIT OR Apache-2.0 | no | +| windows-result | 0.4.1 | MIT OR Apache-2.0 | no | +| windows-strings | 0.5.1 | MIT OR Apache-2.0 | no | +| windows-sys | 0.61.2 | MIT OR Apache-2.0 | no | +| windows-threading | 0.2.1 | MIT OR Apache-2.0 | no | +| wit-bindgen | 0.57.1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| xml-rs | 0.8.28 | MIT | no | +| xml-rs | 0.8.29 | MIT | no | +| zerocopy | 0.8.54 | BSD-2-Clause OR Apache-2.0 OR MIT | no | +| zerocopy | 0.8.57 | BSD-2-Clause OR Apache-2.0 OR MIT | no | +| zerocopy-derive | 0.8.54 | BSD-2-Clause OR Apache-2.0 OR MIT | no | +| zerocopy-derive | 0.8.57 | BSD-2-Clause OR Apache-2.0 OR MIT | no | +| zmij | 1.0.23 | MIT | no | + +### ContextualWisdomLab/feelanet-adfs + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| @bcoe/v8-coverage | 1.0.2 | ISC AND MIT | no | +| @isaacs/cliui | 8.0.2 | ISC | no | +| @istanbuljs/schema | 0.1.6 | MIT | no | +| @jridgewell/resolve-uri | 3.1.2 | MIT | no | +| @jridgewell/sourcemap-codec | 1.5.5 | MIT | no | +| @jridgewell/trace-mapping | 0.3.31 | MIT | no | +| @node-saml/node-saml | 5.1.0 | MIT | no | +| @node-saml/passport-saml | 5.1.0 | MIT | no | +| @pkgjs/parseargs | 0.11.0 | Apache-2.0 AND MIT | no | +| @types/babel-types | 7.0.16 | MIT | no | +| @types/babylon | 6.16.9 | MIT | no | +| @types/body-parser | 1.19.6 | MIT | no | +| @types/connect | 3.4.38 | MIT | no | +| @types/debug | 4.1.13 | MIT | no | +| @types/express | 4.17.25 | MIT | no | +| @types/express-serve-static-core | 4.19.9 | MIT | no | +| @types/http-errors | 2.0.5 | MIT | no | +| @types/istanbul-lib-coverage | 2.0.6 | MIT | no | +| @types/mime | 1.3.5 | MIT | no | +| @types/ms | 2.1.0 | MIT | no | +| @types/node | 26.1.1 | MIT | no | +| @types/passport | 1.0.17 | MIT | no | +| @types/passport-strategy | 0.2.38 | MIT | no | +| @types/qs | 6.15.1 | MIT | no | +| @types/range-parser | 1.2.7 | MIT | no | +| @types/send | 0.17.6 | MIT | no | +| @types/send | 1.2.1 | MIT | no | +| @types/serve-static | 1.15.10 | MIT | no | +| @types/xml-encryption | 1.2.4 | MIT | no | +| @types/xml2js | 0.4.14 | MIT | no | +| @xmldom/is-dom-node | 1.0.1 | MIT | no | +| @xmldom/xmldom | 0.8.13 | MIT | no | +| accepts | 1.3.8 | MIT | no | +| acorn | 1.2.2 | MIT | no | +| acorn | 2.7.0 | MIT | no | +| acorn-globals | 1.0.9 | MIT | no | +| align-text | 0.1.4 | MIT | no | +| ansi-regex | 5.0.1 | MIT | no | +| ansi-regex | 6.2.2 | MIT | no | +| ansi-styles | 4.3.0 | MIT | no | +| ansi-styles | 6.2.3 | MIT | no | +| array-flatten | 1.1.1 | MIT | no | +| asap | 1.0.0 | MIT | no | +| axum | >= 0.7.0,< 0.8.0 | NOASSERTION | yes | +| babel-runtime | 6.26.0 | MIT | no | +| babel-types | 6.26.0 | MIT | no | +| babylon | 6.18.0 | MIT | no | +| balanced-match | 1.0.2 | MIT | no | +| balanced-match | 4.0.4 | MIT | no | +| base64 | >= 0.22.0,< 0.23.0 | NOASSERTION | yes | +| basic-auth | 2.0.1 | MIT | no | +| body-parser | 1.20.5 | MIT | no | +| body-parser | 1.20.6 | MIT | no | +| brace-expansion | 2.1.2 | MIT | no | +| brace-expansion | 5.0.7 | MIT | no | +| buffer-equal-constant-time | 1.0.1 | BSD-3-Clause | no | +| bytes | 3.1.2 | MIT | no | +| c8 | 10.1.3 | ISC | no | +| call-bind-apply-helpers | 1.0.2 | MIT | no | +| call-bound | 1.0.4 | MIT | no | +| camelcase | 1.2.1 | MIT | no | +| center-align | 0.1.3 | MIT | no | +| character-parser | 1.2.1 | MIT | no | +| clean-css | 4.2.4 | MIT | no | +| cliui | 2.1.0 | ISC | no | +| cliui | 8.0.1 | ISC | no | +| color-convert | 2.0.1 | MIT | no | +| color-name | 1.1.4 | MIT | no | +| commander | 2.6.0 | MIT | no | +| constantinople | 3.1.2 | MIT | no | +| content-disposition | 0.5.4 | MIT | no | +| content-type | 1.0.5 | MIT | no | +| convert-source-map | 2.0.0 | MIT | no | +| cookie | 0.7.2 | MIT | no | +| cookie-parser | 1.4.7 | MIT | no | +| cookie-signature | 1.0.6 | MIT | no | +| core-js | 2.6.12 | MIT | no | +| cross-spawn | 7.0.6 | MIT | no | +| cryptography | — | NOASSERTION | yes | +| css | 1.0.8 | MIT | no | +| css-parse | 1.0.4 | MIT | no | +| css-stringify | 1.0.5 | MIT | no | +| debug | 2.6.9 | MIT | no | +| debug | 4.4.3 | MIT | no | +| decamelize | 1.2.0 | MIT | no | +| depd | 2.0.0 | MIT | no | +| destroy | 1.2.0 | MIT | no | +| dunder-proto | 1.0.1 | MIT | no | +| eastasianwidth | 0.2.0 | MIT | no | +| ecdsa-sig-formatter | 1.0.11 | Apache-2.0 | no | +| ee-first | 1.1.1 | MIT | no | +| emoji-regex | 8.0.0 | MIT | no | +| emoji-regex | 9.2.2 | MIT | no | +| encodeurl | 2.0.0 | MIT | no | +| es-define-property | 1.0.1 | MIT | no | +| es-errors | 1.3.0 | MIT | no | +| es-object-atoms | 1.1.2 | MIT | no | +| escalade | 3.2.0 | MIT | no | +| escape-html | 1.0.3 | MIT | no | +| esutils | 2.0.3 | BSD-2-Clause | no | +| etag | 1.8.1 | MIT | no | +| express | 4.22.2 | MIT | no | +| express-rate-limit | 8.5.2 | MIT | no | +| ext-curl | >= 0 | NOASSERTION | yes | +| ext-dom | >= 0 | NOASSERTION | yes | +| ext-mcrypt | >= 0 | NOASSERTION | yes | +| ext-openssl | >= 0 | NOASSERTION | yes | +| fastapi | — | NOASSERTION | yes | +| finalhandler | 1.3.2 | MIT | no | +| find-up | 5.0.0 | MIT | no | +| firebase/php-jwt | 5.0.0 | BSD-3-Clause | no | +| flate2 | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| foreground-child | 3.3.1 | ISC | no | +| form_urlencoded | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| forwarded | 0.2.0 | MIT | no | +| fresh | 0.5.2 | MIT | no | +| function-bind | 1.1.2 | MIT | no | +| get-caller-file | 2.0.5 | ISC | no | +| get-intrinsic | 1.3.0 | MIT | no | +| get-proto | 1.0.1 | MIT | no | +| glob | 10.5.0 | ISC | no | +| gopd | 1.2.0 | MIT | no | +| has-flag | 4.0.0 | MIT | no | +| has-symbols | 1.1.0 | MIT | no | +| hasown | 2.0.4 | MIT | no | +| html-escaper | 2.0.2 | MIT | no | +| http-errors | 2.0.1 | MIT | no | +| httpx | — | NOASSERTION | yes | +| iconv-lite | 0.4.24 | MIT | no | +| inherits | 2.0.4 | ISC | no | +| io.jsonwebtoken:jjwt-api | 0.10.7 | Apache-2.0 | no | +| io.jsonwebtoken:jjwt-impl | 0.10.7 | Apache-2.0 | no | +| io.jsonwebtoken:jjwt-jackson | 0.10.7 | Apache-2.0 | no | +| ip-address | 10.4.0 | MIT | no | +| ipaddr.js | 1.9.1 | MIT | no | +| is-buffer | 1.1.6 | MIT | no | +| is-fullwidth-code-point | 3.0.0 | MIT | no | +| is-promise | 1.0.1 | MIT | no | +| is-promise | 2.2.2 | MIT | no | +| isexe | 2.0.0 | ISC | no | +| istanbul-lib-coverage | 3.2.2 | BSD-3-Clause | no | +| istanbul-lib-report | 3.0.1 | BSD-3-Clause | no | +| istanbul-reports | 3.2.0 | BSD-3-Clause | no | +| itsdangerous | — | NOASSERTION | yes | +| jackspeak | 3.4.3 | BlueOak-1.0.0 | no | +| jade | 1.11.0 | MIT | no | +| jinja2 | — | NOASSERTION | yes | +| jsonwebtoken | 9.0.3 | MIT | no | +| jsonwebtoken | >= 10.3.0,< 11.0.0 | NOASSERTION | yes | +| jstransformer | 0.0.2 | MIT | no | +| jwa | 1.4.2 | MIT | no | +| jws | 3.2.3 | MIT | no | +| kind-of | 3.2.2 | MIT | no | +| lazy-cache | 1.0.4 | MIT | no | +| locate-path | 6.0.0 | MIT | no | +| lodash | 4.18.1 | MIT | no | +| lodash.includes | 4.3.0 | CC0-1.0 AND MIT | no | +| lodash.isboolean | 3.0.3 | MIT | no | +| lodash.isinteger | 4.0.4 | CC0-1.0 AND MIT | no | +| lodash.isnumber | 3.0.3 | MIT | no | +| lodash.isplainobject | 4.0.6 | CC0-1.0 AND MIT | no | +| lodash.isstring | 4.0.1 | MIT | no | +| lodash.once | 4.1.1 | CC0-1.0 AND MIT | no | +| longest | 1.0.1 | MIT | no | +| lru-cache | 10.4.3 | ISC | no | +| lxml | — | NOASSERTION | yes | +| make-dir | 4.0.0 | MIT | no | +| math-intrinsics | 1.1.0 | MIT | no | +| media-typer | 0.3.0 | MIT | no | +| merge-descriptors | 1.0.3 | MIT | no | +| methods | 1.1.2 | MIT | no | +| Microsoft.AspNet.Mvc | 5.2.9 | NOASSERTION | yes | +| Microsoft.AspNet.Mvc.ko | 5.2.9 | NOASSERTION | yes | +| Microsoft.AspNet.Razor | 3.2.9 | LicenseRef-scancode-unknown | no | +| Microsoft.AspNet.Razor.ko | 3.2.9 | LicenseRef-scancode-unknown | no | +| Microsoft.AspNet.WebPages | 3.2.9 | NOASSERTION | yes | +| Microsoft.AspNet.WebPages.ko | 3.2.9 | LicenseRef-scancode-unknown | no | +| Microsoft.CodeDom.Providers.DotNetCompilerPlatform | 2.0.1 | LicenseRef-github-OTHER | no | +| Microsoft.IdentityModel.Clients.ActiveDirectory | 4.5.1 | MIT | no | +| Microsoft.IdentityModel.JsonWebTokens | 5.4.0 | MIT | no | +| Microsoft.IdentityModel.Logging | 5.4.0 | MIT | no | +| Microsoft.IdentityModel.Tokens | 5.4.0 | MIT | no | +| Microsoft.Web.Infrastructure | 2.0.1 | LicenseRef-scancode-unknown | no | +| mime | 1.6.0 | MIT | no | +| mime-db | 1.52.0 | MIT | no | +| mime-types | 2.1.35 | MIT | no | +| minimatch | 10.2.5 | BlueOak-1.0.0 | no | +| minimatch | 9.0.9 | ISC | no | +| minimist | 1.2.8 | MIT | no | +| minipass | 7.1.3 | BlueOak-1.0.0 | no | +| mkdirp | 0.5.6 | MIT | no | +| morgan | 1.11.0 | MIT | no | +| ms | 2.0.0 | MIT | no | +| ms | 2.1.3 | MIT | no | +| negotiator | 0.6.3 | MIT | no | +| Newtonsoft.Json | 12.0.1 | MIT | no | +| node-uuid | 1.4.8 | MIT | no | +| object-inspect | 1.13.4 | MIT | no | +| on-finished | 2.4.1 | MIT | no | +| on-headers | 1.1.0 | MIT | no | +| p-limit | 3.1.0 | MIT | no | +| p-locate | 5.0.0 | MIT | no | +| package-json-from-dist | 1.0.1 | BlueOak-1.0.0 | no | +| parseurl | 1.3.3 | MIT | no | +| passport | 0.7.0 | MIT | no | +| passport-strategy | 1.0.0 | MIT | no | +| path-exists | 4.0.0 | MIT | no | +| path-key | 3.1.1 | MIT | no | +| path-scurry | 1.11.1 | BlueOak-1.0.0 | no | +| path-to-regexp | 0.1.13 | MIT | no | +| pause | 0.0.1 | MIT | no | +| pdepend/pdepend | 1.1.0 | BSD-3-Clause | no | +| pem | >= 3.0.0,< 4.0.0 | NOASSERTION | yes | +| php | >= 5.3.2 | NOASSERTION | yes | +| phploc/phploc | >= 0 | NOASSERTION | yes | +| phpunit/phpunit | 4.8 | BSD-3-Clause | no | +| promise | 2.0.0 | MIT | no | +| promise | 6.1.0 | MIT | no | +| proxy-addr | 2.0.7 | MIT | no | +| pyjwt | — | NOASSERTION | yes | +| pytest | — | NOASSERTION | yes | +| python-multipart | — | NOASSERTION | yes | +| qs | 6.15.3 | BSD-3-Clause | no | +| rand | >= 0.8.0,< 0.9.0 | NOASSERTION | yes | +| range-parser | 1.2.1 | MIT | no | +| raw-body | 2.5.3 | MIT | no | +| regenerator-runtime | 0.11.1 | MIT | no | +| repeat-string | 1.6.1 | MIT | no | +| require-directory | 2.1.1 | MIT | no | +| reqwest | >= 0.12.0,< 0.13.0 | NOASSERTION | yes | +| right-align | 0.1.3 | MIT | no | +| roxmltree | >= 0.20.0,< 0.21.0 | NOASSERTION | yes | +| rsa | >= 0.9.0,< 0.10.0 | NOASSERTION | yes | +| rust-xmlsec | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| safe-buffer | 5.1.2 | MIT | no | +| safe-buffer | 5.2.1 | MIT | no | +| safer-buffer | 2.1.2 | MIT | no | +| satooshi/php-coveralls | 1.0.1 | MIT | no | +| sax | 1.6.0 | BlueOak-1.0.0 | no | +| sebastian/phpcpd | >= 0 | NOASSERTION | yes | +| semver | 7.8.5 | ISC | no | +| send | 0.19.2 | MIT | no | +| serde | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| serde_json | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| serve-static | 1.16.3 | MIT | no | +| setprototypeof | 1.2.0 | ISC | no | +| sha2 | >= 0.10.0,< 0.11.0 | NOASSERTION | yes | +| shebang-command | 2.0.0 | MIT | no | +| shebang-regex | 3.0.0 | MIT | no | +| side-channel | 1.1.1 | MIT | no | +| side-channel-list | 1.0.1 | MIT | no | +| side-channel-map | 1.0.1 | MIT | no | +| side-channel-weakmap | 1.0.2 | MIT | no | +| signal-exit | 4.1.0 | ISC | no | +| signxml | — | NOASSERTION | yes | +| source-map | 0.5.7 | BSD-3-Clause | no | +| source-map | 0.6.1 | BSD-3-Clause | no | +| squizlabs/php_codesniffer | 2.9.0 | BSD-3-Clause | no | +| statuses | 2.0.2 | MIT | no | +| string-width | 4.2.3 | MIT | no | +| string-width | 5.1.2 | MIT | no | +| strip-ansi | 6.0.1 | MIT | no | +| strip-ansi | 7.2.0 | MIT | no | +| supports-color | 7.2.0 | MIT | no | +| System.IdentityModel.Tokens.Jwt | 5.4.0 | NOASSERTION | yes | +| test-exclude | 7.0.2 | ISC | no | +| thiserror | >= 2.0.0,< 3.0.0 | NOASSERTION | yes | +| time | >= 0.3.0,< 0.4.0 | NOASSERTION | yes | +| to-fast-properties | 1.0.3 | MIT | no | +| toidentifier | 1.0.1 | MIT | no | +| tokio | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| tower-sessions | >= 0.13.0,< 0.14.0 | NOASSERTION | yes | +| transformers | 2.1.0 | MIT | no | +| type-is | 1.6.18 | MIT | no | +| uglify-js | 2.8.29 | BSD-2-Clause | no | +| uglify-to-browserify | 1.0.2 | MIT | no | +| undici-types | 8.3.0 | MIT | no | +| unpipe | 1.0.0 | MIT | no | +| utils-merge | 1.0.1 | MIT | no | +| uvicorn | — | NOASSERTION | yes | +| v8-to-istanbul | 9.3.0 | ISC | no | +| vary | 1.1.2 | MIT | no | +| void-elements | 2.0.1 | MIT | no | +| which | 2.0.2 | ISC | no | +| window-size | 0.1.0 | MIT | no | +| with | 4.0.3 | MIT | no | +| wordwrap | 0.0.2 | MIT | no | +| wrap-ansi | 7.0.0 | MIT | no | +| wrap-ansi | 8.1.0 | MIT | no | +| x509-parser | >= 0.16.0,< 0.17.0 | NOASSERTION | yes | +| xml-crypto | 6.1.2 | MIT | no | +| xml-encryption | 3.1.0 | MIT | no | +| xml2js | 0.6.2 | MIT | no | +| xmlbuilder | 11.0.1 | LicenseRef-scancode-unicode AND MIT | no | +| xmlbuilder | 15.1.1 | MIT | no | +| xpath | 0.0.32 | MIT | no | +| xpath | 0.0.33 | MIT | no | +| xpath | 0.0.34 | MIT | no | +| y18n | 5.0.8 | ISC | no | +| yargs | 17.7.3 | MIT | no | +| yargs | 3.10.0 | MIT | no | +| yargs-parser | 21.1.1 | ISC | no | +| yocto-queue | 0.1.0 | MIT | no | + +### ContextualWisdomLab/four-pillars + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 11d5960a326750d5838078e36cf38b85af677262 | NOASSERTION | yes | +| actions/create-github-app-token | bcd2ba49218906704ab6c1aa796996da409d3eb1 | NOASSERTION | yes | +| actions/download-artifact | d3f86a106a0bac45b974a628896c90dbdf5c8093 | NOASSERTION | yes | +| actions/setup-python | a26af69be951a213d495a4c3e4e4022e16d87065 | NOASSERTION | yes | +| actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | yes | +| annotated-doc | 0.0.5 | MIT | no | +| annotated-types | 0.8.0 | MIT | no | +| anyio | 4.14.2 | MIT | no | +| build | 1.5.0 | MIT | no | +| certifi | 2026.7.22 | MPL-2.0 | yes | +| charset-normalizer | 3.4.9 | MIT | no | +| click | 8.4.2 | BSD-3-Clause | no | +| coverage | 7.15.3 | Apache-2.0 | no | +| fastapi | 0.141.1 | MIT | no | +| fastapi | >= 0.115,< 1 | NOASSERTION | yes | +| h11 | 0.16.0 | MIT | no | +| hatchling | 1.31.0 | MIT | no | +| httpcore | 1.0.9 | BSD-2-Clause AND BSD-3-Clause | no | +| httpcore2 | 2.9.1 | BSD-2-Clause AND BSD-3-Clause | no | +| httptools | 0.8.0 | MIT | no | +| httpx | 0.28.1 | BSD-3-Clause | no | +| httpx | >= 0.27,< 1 | NOASSERTION | yes | +| httpx2 | 2.9.1 | BSD-2-Clause AND BSD-3-Clause | no | +| idna | 3.18 | BSD-3-Clause | no | +| iniconfig | 2.3.0 | MIT | no | +| jinja2 | 3.1.6 | BSD-2-Clause AND BSD-3-Clause | no | +| jinja2 | >= 3.1,< 4 | NOASSERTION | yes | +| korean-lunar-calendar | 0.4.0 | MIT | no | +| korean-lunar-calendar | >= 0.3.1,< 1 | NOASSERTION | yes | +| librt | 0.13.0 | MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause | no | +| markdown-it-py | 4.2.0 | MIT | no | +| markupsafe | 3.0.3 | BSD-3-Clause | no | +| mdurl | 0.1.2 | MIT | no | +| mypy | 1.20.2 | MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause AND MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause | no | +| mypy-extensions | 1.1.0 | MIT | no | +| packaging | 26.2 | Apache-2.0 AND BSD-2-Clause | no | +| pathspec | 1.1.1 | MPL-2.0 | yes | +| pillow | 12.3.0 | MIT-CMU | no | +| pluggy | 1.6.0 | MIT | no | +| pydantic | 2.13.4 | MIT | no | +| pydantic | >= 2.10,< 3 | NOASSERTION | yes | +| pydantic-core | 2.46.4 | MIT | no | +| pydantic-settings | 2.14.2 | MIT | no | +| pydantic-settings | >= 2.7,< 3 | NOASSERTION | yes | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pyproject-hooks | 1.2.0 | MIT | no | +| pytest | 8.4.2 | MIT | no | +| pytest-asyncio | 0.26.0 | Apache-2.0 | no | +| pytest-cov | 6.3.0 | MIT | no | +| python-dotenv | 1.2.2 | BSD-3-Clause | no | +| python-multipart | 0.0.32 | Apache-2.0 | no | +| python-multipart | >= 0.0.20,< 1 | NOASSERTION | yes | +| pyyaml | 6.0.3 | MIT | no | +| reportlab | 4.5.1 | BSD-2-Clause AND BSD-3-Clause | no | +| reportlab | >= 4.2,< 5 | NOASSERTION | yes | +| rich | 15.0.0 | MIT | no | +| ruff | 0.16.1 | MIT | no | +| shellingham | 1.5.4 | ISC | no | +| starlette | 1.3.1 | BSD-3-Clause | no | +| trove-classifiers | 2026.6.1.19 | Apache-2.0 | no | +| truststore | 0.10.4 | MIT | no | +| typer | 0.27.1 | MIT | no | +| typer | >= 0.15,< 1 | NOASSERTION | yes | +| typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| typing-inspection | 0.4.2 | MIT | no | +| uvicorn | — | NOASSERTION | yes | +| uvicorn | 0.52.1 | BSD-3-Clause | no | +| uvloop | 0.22.1 | Apache-2.0 AND MIT | no | +| watchfiles | 1.2.0 | MIT | no | +| websockets | 17.0.1 | BSD-3-Clause | no | + +### ContextualWisdomLab/global-hs-trade + +No components reported. + +### ContextualWisdomLab/governance-risk-compliance + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/gyeot + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| @babel/code-frame | 7.29.7 | MIT | no | +| @babel/compat-data | 7.29.7 | MIT | no | +| @babel/core | 7.29.7 | MIT | no | +| @babel/generator | 7.29.8 | MIT | no | +| @babel/helper-annotate-as-pure | 7.29.7 | MIT | no | +| @babel/helper-compilation-targets | 7.29.7 | MIT | no | +| @babel/helper-create-class-features-plugin | 7.29.7 | MIT | no | +| @babel/helper-create-regexp-features-plugin | 7.29.7 | MIT | no | +| @babel/helper-define-polyfill-provider | 0.6.8 | MIT | no | +| @babel/helper-globals | 7.29.7 | MIT | no | +| @babel/helper-member-expression-to-functions | 7.29.7 | MIT | no | +| @babel/helper-module-imports | 7.29.7 | MIT | no | +| @babel/helper-module-transforms | 7.29.7 | MIT | no | +| @babel/helper-optimise-call-expression | 7.29.7 | MIT | no | +| @babel/helper-plugin-utils | 7.29.7 | MIT | no | +| @babel/helper-remap-async-to-generator | 7.29.7 | MIT | no | +| @babel/helper-replace-supers | 7.29.7 | MIT | no | +| @babel/helper-skip-transparent-expression-wrappers | 7.29.7 | MIT | no | +| @babel/helper-string-parser | 7.29.7 | MIT | no | +| @babel/helper-validator-identifier | 7.29.7 | MIT | no | +| @babel/helper-validator-option | 7.29.7 | MIT | no | +| @babel/helper-wrap-function | 7.29.7 | MIT | no | +| @babel/helpers | 7.29.7 | MIT | no | +| @babel/parser | 7.29.8 | MIT | no | +| @babel/plugin-proposal-decorators | 7.29.7 | MIT | no | +| @babel/plugin-proposal-export-default-from | 7.29.7 | MIT | no | +| @babel/plugin-syntax-async-generators | 7.8.4 | MIT | no | +| @babel/plugin-syntax-bigint | 7.8.3 | MIT | no | +| @babel/plugin-syntax-class-properties | 7.12.13 | MIT | no | +| @babel/plugin-syntax-class-static-block | 7.14.5 | MIT | no | +| @babel/plugin-syntax-decorators | 7.29.7 | MIT | no | +| @babel/plugin-syntax-dynamic-import | 7.8.3 | MIT | no | +| @babel/plugin-syntax-export-default-from | 7.29.7 | MIT | no | +| @babel/plugin-syntax-flow | 7.29.7 | MIT | no | +| @babel/plugin-syntax-import-attributes | 7.29.7 | MIT | no | +| @babel/plugin-syntax-import-meta | 7.10.4 | MIT | no | +| @babel/plugin-syntax-json-strings | 7.8.3 | MIT | no | +| @babel/plugin-syntax-jsx | 7.29.7 | MIT | no | +| @babel/plugin-syntax-logical-assignment-operators | 7.10.4 | MIT | no | +| @babel/plugin-syntax-nullish-coalescing-operator | 7.8.3 | MIT | no | +| @babel/plugin-syntax-numeric-separator | 7.10.4 | MIT | no | +| @babel/plugin-syntax-object-rest-spread | 7.8.3 | MIT | no | +| @babel/plugin-syntax-optional-catch-binding | 7.8.3 | MIT | no | +| @babel/plugin-syntax-optional-chaining | 7.8.3 | MIT | no | +| @babel/plugin-syntax-private-property-in-object | 7.14.5 | MIT | no | +| @babel/plugin-syntax-top-level-await | 7.14.5 | MIT | no | +| @babel/plugin-syntax-typescript | 7.29.7 | MIT | no | +| @babel/plugin-transform-async-generator-functions | 7.29.7 | MIT | no | +| @babel/plugin-transform-async-to-generator | 7.29.7 | MIT | no | +| @babel/plugin-transform-block-scoping | 7.29.7 | MIT | no | +| @babel/plugin-transform-class-properties | 7.29.7 | MIT | no | +| @babel/plugin-transform-class-static-block | 7.29.7 | MIT | no | +| @babel/plugin-transform-classes | 7.29.7 | MIT | no | +| @babel/plugin-transform-destructuring | 7.29.7 | MIT | no | +| @babel/plugin-transform-export-namespace-from | 7.29.7 | MIT | no | +| @babel/plugin-transform-flow-strip-types | 7.29.7 | MIT | no | +| @babel/plugin-transform-for-of | 7.29.7 | MIT | no | +| @babel/plugin-transform-logical-assignment-operators | 7.29.7 | MIT | no | +| @babel/plugin-transform-modules-commonjs | 7.29.7 | MIT | no | +| @babel/plugin-transform-named-capturing-groups-regex | 7.29.7 | MIT | no | +| @babel/plugin-transform-nullish-coalescing-operator | 7.29.7 | MIT | no | +| @babel/plugin-transform-object-rest-spread | 7.29.7 | MIT | no | +| @babel/plugin-transform-optional-catch-binding | 7.29.7 | MIT | no | +| @babel/plugin-transform-optional-chaining | 7.29.7 | MIT | no | +| @babel/plugin-transform-parameters | 7.29.7 | MIT | no | +| @babel/plugin-transform-private-methods | 7.29.7 | MIT | no | +| @babel/plugin-transform-private-property-in-object | 7.29.7 | MIT | no | +| @babel/plugin-transform-react-display-name | 7.29.7 | MIT | no | +| @babel/plugin-transform-react-jsx | 7.29.7 | MIT | no | +| @babel/plugin-transform-react-jsx-development | 7.29.7 | MIT | no | +| @babel/plugin-transform-react-pure-annotations | 7.29.7 | MIT | no | +| @babel/plugin-transform-runtime | 7.29.7 | MIT | no | +| @babel/plugin-transform-typescript | 7.29.7 | MIT | no | +| @babel/plugin-transform-unicode-regex | 7.29.7 | MIT | no | +| @babel/preset-typescript | 7.29.7 | MIT | no | +| @babel/runtime | 7.29.7 | MIT | no | +| @babel/template | 7.29.7 | MIT | no | +| @babel/traverse | 7.29.8 | MIT | no | +| @babel/types | 7.29.8 | MIT | no | +| @bcoe/v8-coverage | 0.2.3 | ISC AND MIT | no | +| @egjs/hammerjs | 2.0.17 | MIT | no | +| @emnapi/core | 1.10.0 | MIT | no | +| @emnapi/runtime | 1.10.0 | MIT | no | +| @emnapi/wasi-threads | 1.2.1 | MIT | no | +| @esbuild/aix-ppc64 | 0.28.1 | MIT | no | +| @esbuild/android-arm | 0.28.1 | MIT | no | +| @esbuild/android-arm64 | 0.28.1 | MIT | no | +| @esbuild/android-x64 | 0.28.1 | MIT | no | +| @esbuild/darwin-arm64 | 0.28.1 | MIT | no | +| @esbuild/darwin-x64 | 0.28.1 | MIT | no | +| @esbuild/freebsd-arm64 | 0.28.1 | MIT | no | +| @esbuild/freebsd-x64 | 0.28.1 | MIT | no | +| @esbuild/linux-arm | 0.28.1 | MIT | no | +| @esbuild/linux-arm64 | 0.28.1 | MIT | no | +| @esbuild/linux-ia32 | 0.28.1 | MIT | no | +| @esbuild/linux-loong64 | 0.28.1 | MIT | no | +| @esbuild/linux-mips64el | 0.28.1 | MIT | no | +| @esbuild/linux-ppc64 | 0.28.1 | MIT | no | +| @esbuild/linux-riscv64 | 0.28.1 | MIT | no | +| @esbuild/linux-s390x | 0.28.1 | MIT | no | +| @esbuild/linux-x64 | 0.28.1 | MIT | no | +| @esbuild/netbsd-arm64 | 0.28.1 | MIT | no | +| @esbuild/netbsd-x64 | 0.28.1 | MIT | no | +| @esbuild/openbsd-arm64 | 0.28.1 | MIT | no | +| @esbuild/openbsd-x64 | 0.28.1 | MIT | no | +| @esbuild/openharmony-arm64 | 0.28.1 | MIT | no | +| @esbuild/sunos-x64 | 0.28.1 | MIT | no | +| @esbuild/win32-arm64 | 0.28.1 | MIT | no | +| @esbuild/win32-ia32 | 0.28.1 | MIT | no | +| @esbuild/win32-x64 | 0.28.1 | MIT | no | +| @expo-google-fonts/material-symbols | 0.4.44 | MIT AND Apache-2.0 | no | +| @expo/cli | 57.0.17 | MIT | no | +| @expo/code-signing-certificates | 0.0.6 | MIT | no | +| @expo/config | 57.0.8 | MIT | no | +| @expo/config-plugins | 57.0.8 | MIT | no | +| @expo/config-types | 57.0.2 | MIT | no | +| @expo/devcert | 1.2.1 | MIT | no | +| @expo/devtools | 57.0.1 | MIT | no | +| @expo/dom-webview | 57.0.1 | MIT | no | +| @expo/env | 2.4.2 | MIT | no | +| @expo/expo-modules-macros-plugin | 0.6.1 | MIT | no | +| @expo/fingerprint | 0.20.9 | MIT | no | +| @expo/image-utils | 0.11.4 | MIT | no | +| @expo/inline-modules | 0.1.6 | MIT | no | +| @expo/json-file | 11.0.1 | MIT | no | +| @expo/local-build-cache-provider | 57.0.7 | MIT | no | +| @expo/log-box | 57.0.3 | MIT | no | +| @expo/metro | 56.0.2 | MIT | no | +| @expo/metro-config | 57.0.9 | MIT | no | +| @expo/metro-file-map | 57.0.1 | MIT | no | +| @expo/metro-runtime | 57.0.12 | MIT | no | +| @expo/osascript | 2.7.1 | MIT | no | +| @expo/package-manager | 1.13.1 | MIT | no | +| @expo/plist | 0.8.1 | MIT | no | +| @expo/prebuild-config | 57.0.13 | MIT | no | +| @expo/require-utils | 57.0.4 | MIT | no | +| @expo/router-server | 57.0.7 | MIT | no | +| @expo/schema-utils | 57.0.2 | MIT | no | +| @expo/sdk-runtime-versions | 1.0.0 | MIT | no | +| @expo/spawn-async | 1.8.0 | MIT | no | +| @expo/sudo-prompt | 9.3.2 | MIT | no | +| @expo/ui | 57.0.12 | MIT | no | +| @expo/ws-tunnel | 2.0.0 | MIT | no | +| @expo/xcpretty | 4.4.4 | BSD-3-Clause | no | +| @isaacs/cliui | 8.0.2 | ISC | no | +| @isaacs/ttlcache | 1.4.1 | ISC | no | +| @istanbuljs/load-nyc-config | 1.1.0 | ISC | no | +| @istanbuljs/schema | 0.1.6 | MIT | no | +| @jest/console | 30.4.1 | MIT | no | +| @jest/core | 30.4.2 | MIT | no | +| @jest/diff-sequences | 30.4.0 | MIT | no | +| @jest/environment | 30.4.1 | MIT | no | +| @jest/expect | 30.4.1 | MIT | no | +| @jest/expect-utils | 30.4.1 | MIT | no | +| @jest/fake-timers | 30.4.1 | MIT | no | +| @jest/get-type | 30.1.0 | MIT | no | +| @jest/globals | 30.4.1 | MIT | no | +| @jest/pattern | 30.4.0 | MIT | no | +| @jest/reporters | 30.4.1 | MIT | no | +| @jest/schemas | 29.6.3 | MIT | no | +| @jest/schemas | 30.4.1 | MIT | no | +| @jest/snapshot-utils | 30.4.1 | MIT | no | +| @jest/source-map | 30.0.1 | MIT | no | +| @jest/test-result | 30.4.1 | MIT | no | +| @jest/test-sequencer | 30.4.1 | MIT | no | +| @jest/transform | 30.4.1 | MIT | no | +| @jest/types | 29.6.3 | MIT | no | +| @jest/types | 30.4.1 | MIT | no | +| @jridgewell/gen-mapping | 0.3.13 | MIT | no | +| @jridgewell/remapping | 2.3.5 | MIT | no | +| @jridgewell/resolve-uri | 3.1.2 | MIT | no | +| @jridgewell/source-map | 0.3.11 | MIT | no | +| @jridgewell/sourcemap-codec | 1.5.5 | MIT | no | +| @jridgewell/trace-mapping | 0.3.31 | MIT | no | +| @napi-rs/wasm-runtime | 1.2.3 | MIT | no | +| @pkgjs/parseargs | 0.11.0 | Apache-2.0 AND MIT | no | +| @pkgr/core | 0.3.6 | MIT | no | +| @radix-ui/primitive | 1.1.7 | MIT | no | +| @radix-ui/react-collection | 1.1.15 | MIT | no | +| @radix-ui/react-compose-refs | 1.1.5 | MIT | no | +| @radix-ui/react-context | 1.2.2 | MIT | no | +| @radix-ui/react-dialog | 1.1.23 | MIT | no | +| @radix-ui/react-direction | 1.1.4 | MIT | no | +| @radix-ui/react-dismissable-layer | 1.1.19 | MIT | no | +| @radix-ui/react-focus-guards | 1.1.6 | MIT | no | +| @radix-ui/react-focus-scope | 1.1.16 | MIT | no | +| @radix-ui/react-id | 1.1.4 | MIT | no | +| @radix-ui/react-portal | 1.1.17 | MIT | no | +| @radix-ui/react-presence | 1.1.10 | MIT | no | +| @radix-ui/react-primitive | 2.1.10 | MIT | no | +| @radix-ui/react-roving-focus | 1.1.19 | MIT | no | +| @radix-ui/react-slot | 1.3.3 | MIT | no | +| @radix-ui/react-tabs | 1.1.21 | MIT | no | +| @radix-ui/react-use-callback-ref | 1.1.4 | MIT | no | +| @radix-ui/react-use-controllable-state | 1.2.6 | MIT | no | +| @radix-ui/react-use-effect-event | 0.0.5 | MIT | no | +| @radix-ui/react-use-is-hydrated | 0.1.3 | MIT | no | +| @radix-ui/react-use-layout-effect | 1.1.4 | MIT | no | +| @react-native-async-storage/async-storage | 2.2.0 | MIT | no | +| @react-native-masked-view/masked-view | 0.3.2 | MIT | no | +| @react-native/asset-utils | 0.87.0 | MIT | no | +| @react-native/babel-plugin-codegen | 0.86.2 | MIT | no | +| @react-native/codegen | 0.86.2 | MIT | no | +| @react-native/codegen | 0.87.0 | MIT | no | +| @react-native/community-cli-plugin | 0.87.0 | MIT | no | +| @react-native/debugger-frontend | 0.86.2 | BSD-3-Clause | no | +| @react-native/debugger-frontend | 0.87.0 | BSD-3-Clause | no | +| @react-native/debugger-shell | 0.86.2 | MIT | no | +| @react-native/debugger-shell | 0.87.0 | MIT | no | +| @react-native/dev-middleware | 0.86.2 | MIT | no | +| @react-native/dev-middleware | 0.87.0 | MIT | no | +| @react-native/gradle-plugin | 0.87.0 | MIT | no | +| @react-native/normalize-colors | 0.86.2 | MIT | no | +| @react-native/normalize-colors | 0.87.0 | MIT | no | +| @react-native/virtualized-lists | 0.87.0 | MIT | no | +| @sinclair/typebox | 0.27.12 | MIT | no | +| @sinclair/typebox | 0.34.52 | MIT | no | +| @sinonjs/commons | 3.0.1 | BSD-3-Clause | no | +| @sinonjs/fake-timers | 15.4.0 | BSD-3-Clause | no | +| @tybys/wasm-util | 0.10.3 | MIT | no | +| @types/babel__core | 7.20.5 | MIT | no | +| @types/babel__generator | 7.27.0 | MIT | no | +| @types/babel__template | 7.4.4 | MIT | no | +| @types/babel__traverse | 7.28.0 | MIT | no | +| @types/emscripten | 1.41.5 | MIT | no | +| @types/hammerjs | 2.0.46 | MIT | no | +| @types/istanbul-lib-coverage | 2.0.6 | MIT | no | +| @types/istanbul-lib-report | 3.0.3 | MIT | no | +| @types/istanbul-reports | 3.0.4 | MIT | no | +| @types/jest | 30.0.0 | MIT | no | +| @types/node | 24.13.3 | MIT | no | +| @types/node | 26.2.0 | MIT | no | +| @types/pg | 8.20.0 | MIT | no | +| @types/react | 19.2.18 | MIT | no | +| @types/react-test-renderer | 19.1.0 | MIT | no | +| @types/stack-utils | 2.0.3 | MIT | no | +| @types/yargs | 17.0.35 | MIT | no | +| @types/yargs-parser | 21.0.3 | MIT | no | +| @ungap/structured-clone | 1.3.3 | ISC | no | +| @unrs/resolver-binding-android-arm-eabi | 1.12.2 | MIT | no | +| @unrs/resolver-binding-android-arm64 | 1.12.2 | MIT | no | +| @unrs/resolver-binding-darwin-arm64 | 1.12.2 | MIT | no | +| @unrs/resolver-binding-darwin-x64 | 1.12.2 | MIT | no | +| @unrs/resolver-binding-freebsd-x64 | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-arm-gnueabihf | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-arm-musleabihf | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-arm64-gnu | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-arm64-musl | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-loong64-gnu | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-loong64-musl | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-ppc64-gnu | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-riscv64-gnu | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-riscv64-musl | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-s390x-gnu | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-x64-gnu | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-x64-musl | 1.12.2 | MIT | no | +| @unrs/resolver-binding-openharmony-arm64 | 1.12.2 | MIT | no | +| @unrs/resolver-binding-wasm32-wasi | 1.12.2 | MIT | no | +| @unrs/resolver-binding-win32-arm64-msvc | 1.12.2 | MIT | no | +| @unrs/resolver-binding-win32-ia32-msvc | 1.12.2 | MIT | no | +| @unrs/resolver-binding-win32-x64-msvc | 1.12.2 | MIT | no | +| @xmldom/xmldom | 0.8.15 | MIT | no | +| @xmldom/xmldom | 0.9.12 | MIT | no | +| abort-controller | 3.0.0 | MIT | no | +| accepts | 1.3.8 | MIT | no | +| accepts | 2.0.0 | MIT | no | +| acorn | 8.18.0 | MIT | no | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/setup-node | 249970729cb0ef3589644e2896645e5dc5ba9c38 | NOASSERTION | yes | +| actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | yes | +| agent-base | 7.1.4 | MIT | no | +| agent-cli-detector | 0.1.6 | MIT | no | +| anser | 1.4.10 | MIT | no | +| ansi-escapes | 4.3.2 | MIT | no | +| ansi-regex | 4.1.1 | MIT | no | +| ansi-regex | 5.0.1 | MIT | no | +| ansi-regex | 6.2.2 | MIT | no | +| ansi-styles | 3.2.1 | MIT | no | +| ansi-styles | 4.3.0 | MIT | no | +| ansi-styles | 5.2.0 | MIT | no | +| ansi-styles | 6.2.3 | MIT | no | +| anymatch | 3.1.3 | ISC | no | +| arg | 5.0.2 | MIT | no | +| argparse | 1.0.10 | MIT | no | +| argparse | 2.0.1 | Python-2.0 | no | +| aria-hidden | 1.2.6 | MIT | no | +| asap | 2.0.6 | MIT | no | +| await-lock | 2.2.2 | MIT | no | +| babel-jest | 30.4.1 | MIT | no | +| babel-plugin-istanbul | 7.0.1 | BSD-3-Clause | no | +| babel-plugin-jest-hoist | 30.4.0 | MIT | no | +| babel-plugin-polyfill-corejs2 | 0.4.17 | MIT | no | +| babel-plugin-polyfill-corejs3 | 0.13.0 | MIT | no | +| babel-plugin-polyfill-regenerator | 0.6.8 | MIT | no | +| babel-plugin-react-compiler | 1.0.0 | MIT | no | +| babel-plugin-react-native-web | 0.21.2 | MIT | no | +| babel-plugin-syntax-hermes-parser | 0.36.1 | MIT | no | +| babel-plugin-transform-flow-enums | 0.0.2 | MIT | no | +| babel-preset-current-node-syntax | 1.2.0 | MIT | no | +| babel-preset-expo | 57.0.7 | MIT | no | +| babel-preset-jest | 30.4.0 | MIT | no | +| badgin | 1.2.3 | MIT | no | +| balanced-match | 4.0.4 | MIT | no | +| barcode-detector | 3.2.2 | MIT | no | +| base64-js | 1.5.1 | MIT | no | +| baseline-browser-mapping | 2.11.18 | Apache-2.0 | no | +| big-integer | 1.6.52 | LicenseRef-scancode-public-domain AND Unlicense | no | +| boolbase | 1.0.0 | ISC | no | +| bplist-creator | 0.1.0 | MIT | no | +| bplist-parser | 0.3.1 | MIT | no | +| bplist-parser | 0.3.2 | MIT | no | +| brace-expansion | 5.0.9 | MIT | no | +| braces | 3.0.3 | MIT | no | +| browserslist | 4.28.8 | MIT | no | +| bs-logger | 0.2.6 | MIT | no | +| bser | 2.1.1 | Apache-2.0 | no | +| buffer-from | 1.1.2 | MIT | no | +| bytes | 3.1.2 | MIT | no | +| callsites | 3.1.0 | MIT | no | +| camelcase | 5.3.1 | MIT | no | +| camelcase | 6.3.0 | MIT | no | +| caniuse-lite | 1.0.30001809 | CC-BY-4.0 | no | +| chalk | 2.4.2 | MIT | no | +| chalk | 4.1.2 | MIT | no | +| char-regex | 1.0.2 | MIT | no | +| chrome-launcher | 0.15.2 | Apache-2.0 | no | +| chromium-edge-launcher | 0.3.0 | Apache-2.0 | no | +| ci-info | 2.0.0 | MIT | no | +| ci-info | 3.9.0 | MIT | no | +| ci-info | 4.4.0 | MIT | no | +| cjs-module-lexer | 2.2.1 | MIT | no | +| cli-cursor | 2.1.0 | MIT | no | +| cli-spinners | 2.9.2 | MIT | no | +| client-only | 0.0.1 | MIT | no | +| cliui | 8.0.1 | ISC | no | +| clone | 1.0.4 | MIT | no | +| co | 4.6.0 | MIT | no | +| collect-v8-coverage | 1.0.3 | MIT | no | +| color | 4.2.3 | MIT | no | +| color-convert | 1.9.3 | MIT | no | +| color-convert | 2.0.1 | MIT | no | +| color-name | 1.1.3 | MIT | no | +| color-name | 1.1.4 | MIT | no | +| color-string | 1.9.1 | MIT | no | +| commander | 12.1.0 | MIT | no | +| commander | 2.20.3 | MIT | no | +| commander | 7.2.0 | MIT | no | +| compressible | 2.0.18 | MIT | no | +| compression | 1.8.1 | MIT | no | +| connect | 3.7.0 | MIT | no | +| convert-source-map | 2.0.0 | MIT | no | +| core-js-compat | 3.50.0 | MIT | no | +| cross-spawn | 7.0.6 | MIT | no | +| css-select | 5.2.2 | BSD-2-Clause | no | +| css-tree | 1.1.3 | MIT | no | +| css-what | 6.2.2 | BSD-2-Clause | no | +| csstype | 3.2.3 | MIT | no | +| debug | 2.6.9 | MIT | no | +| debug | 3.2.7 | MIT | no | +| debug | 4.4.3 | MIT | no | +| decode-uri-component | 0.2.2 | MIT | no | +| dedent | 1.7.2 | MIT | no | +| deepmerge | 4.3.1 | MIT | no | +| defaults | 1.0.4 | MIT | no | +| depd | 2.0.0 | MIT | no | +| destroy | 1.2.0 | MIT | no | +| detect-libc | 2.1.2 | Apache-2.0 | no | +| detect-newline | 3.1.0 | MIT | no | +| detect-node-es | 1.1.0 | MIT | no | +| dnssd-advertise | 1.1.6 | MIT | no | +| dom-serializer | 2.0.0 | MIT | no | +| domelementtype | 2.3.0 | BSD-2-Clause | no | +| domhandler | 5.0.3 | BSD-2-Clause | no | +| domutils | 3.2.2 | BSD-2-Clause | no | +| eastasianwidth | 0.2.0 | MIT | no | +| ee-first | 1.1.1 | MIT | no | +| electron-to-chromium | 1.5.412 | ISC | no | +| emittery | 0.13.1 | MIT | no | +| emoji-regex | 8.0.0 | MIT | no | +| emoji-regex | 9.2.2 | MIT | no | +| encodeurl | 1.0.2 | MIT | no | +| encodeurl | 2.0.0 | MIT | no | +| entities | 4.5.0 | BSD-2-Clause | no | +| error-ex | 1.3.4 | MIT | no | +| error-stack-parser | 2.1.4 | MIT | no | +| es-errors | 1.3.0 | MIT | no | +| esbuild | 0.28.1 | MIT | no | +| escalade | 3.2.0 | MIT | no | +| escape-html | 1.0.3 | MIT | no | +| escape-string-regexp | 1.0.5 | MIT | no | +| escape-string-regexp | 2.0.0 | MIT | no | +| escape-string-regexp | 4.0.0 | MIT | no | +| esprima | 4.0.1 | BSD-2-Clause AND BSD-3-Clause | no | +| etag | 1.8.1 | MIT | no | +| event-target-shim | 5.0.1 | MIT | no | +| execa | 5.1.1 | MIT | no | +| exit-x | 0.2.2 | MIT | no | +| expect | 30.4.1 | MIT | no | +| expo | 57.0.15 | MIT | no | +| expo-application | 57.0.2 | MIT | no | +| expo-asset | 57.0.13 | MIT | no | +| expo-audio | 57.0.4 | MIT | no | +| expo-camera | 57.0.4 | MIT | no | +| expo-constants | 57.0.13 | MIT | no | +| expo-crypto | 57.0.1 | MIT | no | +| expo-file-system | 57.0.5 | MIT | no | +| expo-font | 57.0.1 | MIT | no | +| expo-glass-effect | 57.0.1 | MIT | no | +| expo-haptics | 57.0.1 | MIT | no | +| expo-image-loader | 57.0.1 | MIT | no | +| expo-image-manipulator | 57.0.12 | MIT | no | +| expo-image-picker | 57.0.12 | MIT | no | +| expo-keep-awake | 57.0.1 | MIT | no | +| expo-linking | 57.0.7 | MIT | no | +| expo-media-library | 57.0.4 | MIT | no | +| expo-modules-autolinking | 57.0.10 | MIT | no | +| expo-modules-core | 57.0.12 | MIT | no | +| expo-modules-jsi | 57.0.5 | MIT | no | +| expo-notifications | 57.0.13 | MIT | no | +| expo-router | 57.0.15 | MIT | no | +| expo-secure-store | 57.0.1 | MIT | no | +| expo-server | 57.0.3 | MIT | no | +| expo-sqlite | 57.0.1 | MIT | no | +| expo-status-bar | 57.0.1 | MIT | no | +| expo-symbols | 57.0.2 | MIT | no | +| expo-video | 57.0.2 | MIT | no | +| exponential-backoff | 3.1.3 | Apache-2.0 | no | +| fast-deep-equal | 3.1.3 | MIT | no | +| fast-json-stable-stringify | 2.1.0 | MIT | no | +| fb-dotslash | 0.5.8 | Apache-2.0 AND MIT | no | +| fb-watchman | 2.0.2 | Apache-2.0 | no | +| fdir | 6.5.0 | MIT | no | +| fetch-nodeshim | 0.4.10 | MIT | no | +| fill-range | 7.1.1 | MIT | no | +| filter-obj | 1.1.0 | MIT | no | +| finalhandler | 1.1.2 | MIT | no | +| find-up | 4.1.0 | MIT | no | +| flow-enums-runtime | 0.0.6 | MIT | no | +| fontfaceobserver | 2.3.0 | BSD-2-Clause | no | +| foreground-child | 3.3.1 | ISC | no | +| fresh | 0.5.2 | MIT | no | +| fs.realpath | 1.0.0 | ISC AND MIT | no | +| fsevents | 2.3.3 | MIT | no | +| function-bind | 1.1.2 | MIT | no | +| gensync | 1.0.0-beta.2 | MIT | no | +| get-caller-file | 2.0.5 | ISC | no | +| get-nonce | 1.0.1 | MIT | no | +| get-package-type | 0.1.0 | MIT | no | +| get-stream | 6.0.1 | MIT | no | +| getenv | 2.0.0 | MIT | no | +| glob | 10.5.0 | ISC | no | +| glob | 13.0.6 | BlueOak-1.0.0 | no | +| glob | 7.2.3 | ISC | no | +| graceful-fs | 4.2.11 | ISC | no | +| handlebars | 4.7.9 | MIT | no | +| has-flag | 3.0.0 | MIT | no | +| has-flag | 4.0.0 | MIT | no | +| hasown | 2.0.4 | MIT | no | +| hermes-compiler | 250829098.0.16 | MIT | no | +| hermes-estree | 0.35.0 | MIT | no | +| hermes-estree | 0.36.0 | MIT | no | +| hermes-estree | 0.36.1 | MIT | no | +| hermes-parser | 0.35.0 | MIT | no | +| hermes-parser | 0.36.0 | MIT | no | +| hermes-parser | 0.36.1 | MIT | no | +| hoist-non-react-statics | 3.3.2 | BSD-3-Clause | no | +| hosted-git-info | 7.0.2 | ISC | no | +| html-escaper | 2.0.2 | MIT | no | +| http-errors | 2.0.1 | MIT | no | +| https-proxy-agent | 7.0.6 | MIT | no | +| human-signals | 2.1.0 | Apache-2.0 | no | +| ignore | 5.3.2 | MIT | no | +| image-size | 1.2.1 | MIT | no | +| import-local | 3.2.0 | MIT | no | +| imurmurhash | 0.1.4 | MIT | no | +| inflight | 1.0.6 | ISC | no | +| inherits | 2.0.4 | ISC | no | +| invariant | 2.2.4 | MIT | no | +| is-arrayish | 0.2.1 | MIT | no | +| is-arrayish | 0.3.4 | MIT | no | +| is-core-module | 2.16.2 | MIT | no | +| is-docker | 2.2.1 | MIT | no | +| is-fullwidth-code-point | 3.0.0 | MIT | no | +| is-generator-fn | 2.1.0 | MIT | no | +| is-number | 7.0.0 | MIT | no | +| is-plain-obj | 2.1.0 | MIT | no | +| is-stream | 2.0.1 | MIT | no | +| is-wsl | 2.2.0 | MIT | no | +| isexe | 2.0.0 | ISC | no | +| istanbul-lib-coverage | 3.2.2 | BSD-3-Clause | no | +| istanbul-lib-instrument | 6.0.3 | BSD-3-Clause | no | +| istanbul-lib-report | 3.0.1 | BSD-3-Clause | no | +| istanbul-lib-source-maps | 5.0.6 | BSD-3-Clause | no | +| istanbul-reports | 3.2.0 | BSD-3-Clause | no | +| jackspeak | 3.4.3 | BlueOak-1.0.0 | no | +| jest | 30.4.2 | MIT | no | +| jest-changed-files | 30.4.1 | MIT | no | +| jest-circus | 30.4.2 | MIT | no | +| jest-cli | 30.4.2 | MIT | no | +| jest-config | 30.4.2 | MIT | no | +| jest-diff | 30.4.1 | MIT | no | +| jest-docblock | 30.4.0 | MIT | no | +| jest-each | 30.4.1 | MIT | no | +| jest-environment-node | 30.4.1 | MIT | no | +| jest-get-type | 29.6.3 | MIT | no | +| jest-haste-map | 30.4.1 | MIT | no | +| jest-leak-detector | 30.4.1 | MIT | no | +| jest-matcher-utils | 30.4.1 | MIT | no | +| jest-message-util | 30.4.1 | MIT | no | +| jest-mock | 30.4.1 | MIT | no | +| jest-pnp-resolver | 1.2.3 | MIT | no | +| jest-regex-util | 30.4.0 | MIT | no | +| jest-resolve | 30.4.1 | MIT | no | +| jest-resolve-dependencies | 30.4.2 | MIT | no | +| jest-runner | 30.4.2 | MIT | no | +| jest-runtime | 30.4.2 | MIT | no | +| jest-snapshot | 30.4.1 | MIT | no | +| jest-util | 29.7.0 | MIT | no | +| jest-util | 30.4.1 | MIT | no | +| jest-validate | 29.7.0 | MIT | no | +| jest-validate | 30.4.1 | MIT | no | +| jest-watcher | 30.4.1 | MIT | no | +| jest-worker | 29.7.0 | MIT | no | +| jest-worker | 30.4.1 | MIT | no | +| jimp-compact | 0.16.1 | MIT | no | +| js-tokens | 4.0.0 | MIT | no | +| js-yaml | 3.15.1 | MIT | no | +| js-yaml | 4.3.1 | MIT | no | +| jsc-safe-url | 0.2.4 | 0BSD | no | +| jsesc | 3.1.0 | MIT | no | +| json-parse-even-better-errors | 2.3.1 | MIT | no | +| json5 | 2.2.3 | MIT | no | +| kleur | 3.0.3 | MIT | no | +| lan-network | 0.2.1 | MIT | no | +| leven | 3.1.0 | MIT | no | +| lighthouse-logger | 1.4.2 | Apache-2.0 | no | +| lightningcss | 1.33.0 | MPL-2.0 | yes | +| lightningcss-android-arm64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-darwin-arm64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-darwin-x64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-freebsd-x64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm-gnueabihf | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-gnu | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-musl | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-gnu | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-musl | 1.33.0 | MPL-2.0 | yes | +| lightningcss-win32-arm64-msvc | 1.33.0 | MPL-2.0 | yes | +| lightningcss-win32-x64-msvc | 1.33.0 | MPL-2.0 | yes | +| lines-and-columns | 1.2.4 | MIT | no | +| locate-path | 5.0.0 | MIT | no | +| lodash.debounce | 4.0.8 | MIT | no | +| lodash.memoize | 4.1.2 | MIT | no | +| lodash.throttle | 4.1.1 | MIT | no | +| log-symbols | 2.2.0 | MIT | no | +| loose-envify | 1.4.0 | MIT | no | +| lru-cache | 10.4.3 | ISC | no | +| lru-cache | 11.5.2 | BlueOak-1.0.0 | no | +| lru-cache | 5.1.1 | ISC | no | +| make-dir | 4.0.0 | MIT | no | +| make-error | 1.3.6 | ISC | no | +| makeerror | 1.0.12 | BSD-3-Clause | no | +| marky | 1.3.0 | Apache-2.0 | no | +| mdn-data | 2.0.14 | CC0-1.0 | no | +| memoize-one | 5.2.1 | MIT | no | +| merge-options | 3.0.4 | MIT | no | +| merge-stream | 2.0.0 | MIT | no | +| metro | 0.84.5 | MIT | no | +| metro | 0.87.0 | MIT | no | +| metro-babel-transformer | 0.84.5 | MIT | no | +| metro-babel-transformer | 0.87.0 | MIT | no | +| metro-cache | 0.84.5 | MIT | no | +| metro-cache | 0.87.0 | MIT | no | +| metro-cache-key | 0.84.5 | MIT | no | +| metro-cache-key | 0.87.0 | MIT | no | +| metro-config | 0.84.5 | MIT | no | +| metro-config | 0.87.0 | MIT | no | +| metro-core | 0.84.5 | MIT | no | +| metro-core | 0.87.0 | MIT | no | +| metro-file-map | 0.84.5 | MIT | no | +| metro-file-map | 0.87.0 | MIT | no | +| metro-minify-terser | 0.84.5 | MIT | no | +| metro-minify-terser | 0.87.0 | MIT | no | +| metro-resolver | 0.84.5 | MIT | no | +| metro-resolver | 0.87.0 | MIT | no | +| metro-runtime | 0.84.5 | MIT | no | +| metro-runtime | 0.87.0 | MIT | no | +| metro-source-map | 0.84.5 | MIT | no | +| metro-source-map | 0.87.0 | MIT | no | +| metro-symbolicate | 0.84.5 | MIT | no | +| metro-symbolicate | 0.87.0 | MIT | no | +| metro-transform-plugins | 0.84.5 | MIT | no | +| metro-transform-plugins | 0.87.0 | MIT | no | +| metro-transform-worker | 0.84.5 | MIT | no | +| metro-transform-worker | 0.87.0 | MIT | no | +| micromatch | 4.0.8 | MIT | no | +| mime | 1.6.0 | MIT | no | +| mime-db | 1.52.0 | MIT | no | +| mime-db | 1.54.0 | MIT | no | +| mime-types | 2.1.35 | MIT | no | +| mime-types | 3.0.2 | MIT | no | +| mimic-fn | 1.2.0 | MIT | no | +| mimic-fn | 2.1.0 | MIT | no | +| minimatch | 10.2.6 | BlueOak-1.0.0 | no | +| minimatch | 3.1.5 | ISC | no | +| minimatch | 9.0.9 | ISC | no | +| minimist | 1.2.8 | MIT | no | +| minipass | 7.1.3 | BlueOak-1.0.0 | no | +| mkdirp | 1.0.4 | MIT | no | +| ms | 2.0.0 | MIT | no | +| ms | 2.1.3 | MIT | no | +| multitars | 1.0.2 | MIT | no | +| nanoid | 3.3.18 | MIT | no | +| napi-postinstall | 0.3.4 | MIT | no | +| natural-compare | 1.4.0 | MIT | no | +| negotiator | 0.6.3 | MIT | no | +| negotiator | 0.6.4 | MIT | no | +| negotiator | 1.0.0 | MIT | no | +| neo-async | 2.6.2 | MIT | no | +| node-forge | 1.4.0 | BSD-3-Clause OR GPL-2.0-only | yes | +| node-int64 | 0.4.0 | MIT | no | +| node-releases | 2.0.53 | MIT | no | +| normalize-path | 3.0.0 | MIT | no | +| npm-package-arg | 11.0.3 | ISC | no | +| npm-run-path | 4.0.1 | MIT | no | +| nth-check | 2.1.1 | BSD-2-Clause | no | +| nullthrows | 1.1.1 | MIT | no | +| ob1 | 0.84.5 | MIT | no | +| ob1 | 0.87.0 | MIT | no | +| on-finished | 2.3.0 | MIT | no | +| on-finished | 2.4.1 | MIT | no | +| on-headers | 1.1.0 | MIT | no | +| once | 1.4.0 | ISC | no | +| onetime | 2.0.1 | MIT | no | +| onetime | 5.1.2 | MIT | no | +| open | 7.4.2 | MIT | no | +| ora | 3.4.0 | MIT | no | +| p-limit | 2.3.0 | MIT | no | +| p-limit | 3.1.0 | MIT | no | +| p-locate | 4.1.0 | MIT | no | +| p-try | 2.2.0 | MIT | no | +| package-json-from-dist | 1.0.1 | BlueOak-1.0.0 | no | +| parse-json | 5.2.0 | MIT | no | +| parse-png | 2.1.0 | MIT | no | +| parseurl | 1.3.3 | MIT | no | +| path-exists | 4.0.0 | MIT | no | +| path-is-absolute | 1.0.1 | MIT | no | +| path-key | 3.1.1 | MIT | no | +| path-parse | 1.0.7 | MIT | no | +| path-scurry | 1.11.1 | BlueOak-1.0.0 | no | +| path-scurry | 2.0.2 | BlueOak-1.0.0 | no | +| pg | 8.22.0 | MIT | no | +| pg-cloudflare | 1.4.0 | MIT | no | +| pg-connection-string | 2.14.0 | MIT | no | +| pg-int8 | 1.0.1 | ISC | no | +| pg-pool | 3.14.0 | MIT | no | +| pg-protocol | 1.15.0 | MIT | no | +| pg-types | 2.2.0 | MIT | no | +| pgpass | 1.0.5 | MIT | no | +| picocolors | 1.1.1 | ISC | no | +| picomatch | 2.3.2 | MIT | no | +| picomatch | 4.0.5 | MIT | no | +| pirates | 4.0.7 | MIT | no | +| pkg-dir | 4.2.0 | MIT | no | +| plist | 3.1.1 | MIT | no | +| pngjs | 3.4.0 | MIT | no | +| postcss | 8.5.26 | MIT | no | +| postgres-array | 2.0.0 | MIT | no | +| postgres-bytea | 1.0.1 | MIT | no | +| postgres-date | 1.0.7 | MIT | no | +| postgres-interval | 1.2.0 | MIT | no | +| pretty-format | 29.7.0 | MIT | no | +| pretty-format | 30.4.1 | MIT | no | +| proc-log | 4.2.0 | ISC | no | +| progress | 2.0.3 | MIT | no | +| promise | 8.3.0 | MIT | no | +| prompts | 2.4.2 | MIT | no | +| pure-rand | 7.0.1 | MIT | no | +| query-string | 7.1.3 | MIT | no | +| queue | 6.0.2 | MIT | no | +| range-parser | 1.2.1 | MIT | no | +| react | 19.2.8 | MIT | no | +| react-devtools-core | 6.1.5 | MIT | no | +| react-dom | 19.2.8 | MIT | no | +| react-fast-compare | 3.2.2 | MIT | no | +| react-freeze | 1.0.4 | MIT | no | +| react-is | 16.13.1 | MIT | no | +| react-is | 18.3.1 | MIT | no | +| react-is | 19.2.7 | MIT | no | +| react-native | 0.87.0 | MIT | no | +| react-native-drawer-layout | 4.2.10 | MIT | no | +| react-native-gesture-handler | 2.32.0 | MIT | no | +| react-native-safe-area-context | 5.9.1 | MIT | no | +| react-native-screens | 4.27.0 | MIT | no | +| react-native-svg | 15.15.5 | MIT | no | +| react-refresh | 0.14.2 | MIT | no | +| react-remove-scroll | 2.7.2 | MIT | no | +| react-remove-scroll-bar | 2.3.8 | MIT | no | +| react-style-singleton | 2.2.3 | MIT | no | +| regenerate | 1.4.2 | MIT | no | +| regenerate-unicode-properties | 10.2.2 | MIT | no | +| regenerator-runtime | 0.13.11 | MIT | no | +| regexpu-core | 6.4.0 | MIT | no | +| regjsgen | 0.8.0 | MIT | no | +| regjsparser | 0.13.2 | BSD-2-Clause | no | +| require-directory | 2.1.1 | MIT | no | +| resolve | 1.22.12 | MIT | no | +| resolve-cwd | 3.0.0 | MIT | no | +| resolve-from | 5.0.0 | MIT | no | +| resolve-workspace-root | 2.0.1 | MIT | no | +| restore-cursor | 2.0.0 | MIT | no | +| safe-buffer | 5.2.1 | MIT | no | +| sandbox-cli-detector | 0.2.0 | MIT | no | +| sax | 1.6.1 | BlueOak-1.0.0 | no | +| scheduler | 0.27.0 | MIT | no | +| semver | 6.3.1 | ISC | no | +| semver | 7.8.5 | ISC | no | +| send | 0.19.2 | MIT | no | +| serialize-error | 2.1.0 | MIT | no | +| serve-static | 1.16.3 | MIT | no | +| server-only | 0.0.1 | MIT | no | +| setprototypeof | 1.2.0 | ISC | no | +| sf-symbols-typescript | 2.2.0 | MIT | no | +| shallowequal | 1.1.0 | MIT | no | +| shebang-command | 2.0.0 | MIT | no | +| shebang-regex | 3.0.0 | MIT | no | +| shell-quote | 1.10.0 | MIT | no | +| signal-exit | 3.0.7 | ISC | no | +| signal-exit | 4.1.0 | ISC | no | +| simple-plist | 1.3.1 | MIT | no | +| simple-swizzle | 0.2.4 | MIT | no | +| sisteransi | 1.0.5 | MIT | no | +| slash | 3.0.0 | MIT | no | +| slugify | 1.6.9 | MIT | no | +| source-map | 0.5.7 | BSD-3-Clause | no | +| source-map | 0.6.1 | BSD-3-Clause | no | +| source-map-js | 1.2.1 | BSD-3-Clause | no | +| source-map-support | 0.5.13 | MIT | no | +| source-map-support | 0.5.21 | MIT | no | +| split-on-first | 1.1.0 | MIT | no | +| split2 | 4.2.0 | ISC | no | +| sprintf-js | 1.0.3 | BSD-3-Clause | no | +| stack-utils | 2.0.6 | MIT | no | +| stackframe | 1.3.4 | MIT | no | +| stacktrace-parser | 0.1.11 | MIT | no | +| standard-navigation | 0.0.5 | MIT | no | +| statuses | 1.5.0 | MIT | no | +| statuses | 2.0.2 | MIT | no | +| stream-buffers | 2.2.0 | Unlicense | no | +| strict-uri-encode | 2.0.0 | MIT | no | +| string-length | 4.0.2 | MIT | no | +| string-width | 4.2.3 | MIT | no | +| string-width | 5.1.2 | MIT | no | +| strip-ansi | 5.2.0 | MIT | no | +| strip-ansi | 6.0.1 | MIT | no | +| strip-ansi | 7.2.0 | MIT | no | +| strip-bom | 4.0.0 | MIT | no | +| strip-final-newline | 2.0.0 | MIT | no | +| strip-json-comments | 3.1.1 | MIT | no | +| structured-headers | 0.4.1 | MIT | no | +| supports-color | 5.5.0 | MIT | no | +| supports-color | 7.2.0 | MIT | no | +| supports-color | 8.1.1 | MIT | no | +| supports-hyperlinks | 2.3.0 | MIT | no | +| supports-preserve-symlinks-flag | 1.0.0 | MIT | no | +| synckit | 0.11.13 | MIT | no | +| tagged-tag | 1.0.0 | MIT | no | +| terminal-link | 2.1.1 | MIT | no | +| terser | 5.50.0 | BSD-2-Clause | no | +| test-exclude | 6.0.0 | ISC | no | +| throat | 5.0.0 | MIT | no | +| tinyglobby | 0.2.17 | MIT | no | +| tmpl | 1.0.5 | BSD-3-Clause | no | +| to-regex-range | 5.0.1 | MIT | no | +| toidentifier | 1.0.1 | MIT | no | +| toqr | 0.1.1 | MIT | no | +| ts-jest | 29.4.12 | MIT | no | +| tslib | 2.8.1 | 0BSD | no | +| tsx | 4.23.1 | MIT | no | +| type-detect | 4.0.8 | MIT | no | +| type-fest | 0.21.3 | CC0-1.0 AND MIT | no | +| type-fest | 0.7.1 | MIT OR (CC0-1.0 AND MIT) | no | +| type-fest | 4.41.0 | CC0-1.0 AND MIT | no | +| type-fest | 5.8.0 | (MIT OR CC0-1.0) | no | +| typescript | 5.9.3 | Apache-2.0 | no | +| typescript | 6.0.3 | Apache-2.0 | no | +| uglify-js | 3.19.3 | BSD-2-Clause AND BSD-3-Clause | no | +| undici-types | 7.18.2 | MIT | no | +| undici-types | 8.3.0 | MIT | no | +| unicode-canonical-property-names-ecmascript | 2.0.1 | MIT | no | +| unicode-match-property-ecmascript | 2.0.0 | MIT | no | +| unicode-match-property-value-ecmascript | 2.2.1 | LicenseRef-scancode-unicode AND MIT | no | +| unicode-property-aliases-ecmascript | 2.2.0 | MIT | no | +| unpipe | 1.0.0 | MIT | no | +| unrs-resolver | 1.12.2 | Apache-2.0 AND MIT | no | +| update-browserslist-db | 1.3.1 | MIT | no | +| use-callback-ref | 1.3.3 | MIT | no | +| use-latest-callback | 0.2.6 | MIT | no | +| use-sidecar | 1.1.3 | MIT | no | +| utils-merge | 1.0.1 | MIT | no | +| uuid | 11.1.1 | MIT | no | +| v8-to-istanbul | 9.3.0 | ISC | no | +| validate-npm-package-name | 5.0.1 | ISC | no | +| vary | 1.1.2 | MIT | no | +| vaul | 1.1.2 | MIT | no | +| vlq | 1.0.1 | MIT | no | +| walker | 1.0.8 | Apache-2.0 | no | +| warn-once | 0.1.1 | MIT | no | +| wcwidth | 1.0.1 | MIT | no | +| whatwg-fetch | 3.6.20 | MIT | no | +| whatwg-url-minimum | 0.1.2 | MIT | no | +| which | 2.0.2 | ISC | no | +| wordwrap | 1.0.0 | MIT | no | +| wrap-ansi | 7.0.0 | MIT | no | +| wrap-ansi | 8.1.0 | MIT | no | +| wrappy | 1.0.2 | ISC | no | +| write-file-atomic | 5.0.1 | ISC | no | +| ws | 7.5.13 | MIT | no | +| ws | 8.21.3 | MIT | no | +| xcode | 3.0.1 | Apache-2.0 | no | +| xml2js | 0.6.0 | MIT | no | +| xmlbuilder | 11.0.1 | LicenseRef-scancode-unicode AND MIT | no | +| xmlbuilder | 15.1.1 | MIT | no | +| xtend | 4.0.2 | MIT | no | +| y18n | 5.0.8 | ISC | no | +| yallist | 3.1.1 | ISC | no | +| yaml | 2.9.0 | ISC | no | +| yargs | 17.7.3 | MIT | no | +| yargs-parser | 21.1.1 | ISC | no | +| yocto-queue | 0.1.0 | MIT | no | +| zod | 3.25.76 | MIT | no | +| zxing-wasm | 3.1.3 | MIT | no | + +### ContextualWisdomLab/health-evidence + +No components reported. + +### ContextualWisdomLab/hyosung-itx-slogan-brief + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | yes | +| actions/setup-node | 49933ea5288caeca8642d1e84afbd3f7d6820020 | NOASSERTION | yes | + +### ContextualWisdomLab/inkspan + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| @adobe/css-tools | 4.5.0 | MIT | no | +| @ampproject/remapping | 2.3.0 | Apache-2.0 | no | +| @asamuzakjp/css-color | 3.2.0 | MIT | no | +| @babel/code-frame | 7.29.7 | MIT | no | +| @babel/compat-data | 7.29.7 | MIT | no | +| @babel/core | 7.29.7 | MIT | no | +| @babel/generator | 7.29.7 | MIT | no | +| @babel/helper-compilation-targets | 7.29.7 | MIT | no | +| @babel/helper-globals | 7.29.7 | MIT | no | +| @babel/helper-module-imports | 7.29.7 | MIT | no | +| @babel/helper-module-transforms | 7.29.7 | MIT | no | +| @babel/helper-plugin-utils | 7.29.7 | MIT | no | +| @babel/helper-string-parser | 7.29.7 | MIT | no | +| @babel/helper-validator-identifier | 7.29.7 | MIT | no | +| @babel/helper-validator-option | 7.29.7 | MIT | no | +| @babel/helpers | 7.29.7 | MIT | no | +| @babel/parser | 7.29.7 | MIT | no | +| @babel/plugin-transform-react-jsx-self | 7.29.7 | MIT | no | +| @babel/plugin-transform-react-jsx-source | 7.29.7 | MIT | no | +| @babel/runtime | 7.29.7 | MIT | no | +| @babel/template | 7.29.7 | MIT | no | +| @babel/traverse | 7.29.7 | MIT | no | +| @babel/types | 7.29.7 | MIT | no | +| @bcoe/v8-coverage | 1.0.2 | ISC AND MIT | no | +| @csstools/color-helpers | 5.1.0 | MIT-0 | no | +| @csstools/css-calc | 2.1.4 | MIT | no | +| @csstools/css-color-parser | 3.1.0 | MIT | no | +| @csstools/css-parser-algorithms | 3.0.5 | MIT | no | +| @csstools/css-tokenizer | 3.0.4 | MIT | no | +| @emnapi/core | 1.11.2 | MIT | no | +| @emnapi/core | 1.9.2 | MIT | no | +| @emnapi/runtime | 1.11.2 | MIT | no | +| @emnapi/runtime | 1.9.2 | MIT | no | +| @emnapi/wasi-threads | 1.2.1 | MIT | no | +| @emnapi/wasi-threads | 1.2.2 | MIT | no | +| @esbuild/aix-ppc64 | 0.25.12 | MIT | no | +| @esbuild/android-arm | 0.25.12 | MIT | no | +| @esbuild/android-arm64 | 0.25.12 | MIT | no | +| @esbuild/android-x64 | 0.25.12 | MIT | no | +| @esbuild/darwin-arm64 | 0.25.12 | MIT | no | +| @esbuild/darwin-x64 | 0.25.12 | MIT | no | +| @esbuild/freebsd-arm64 | 0.25.12 | MIT | no | +| @esbuild/freebsd-x64 | 0.25.12 | MIT | no | +| @esbuild/linux-arm | 0.25.12 | MIT | no | +| @esbuild/linux-arm64 | 0.25.12 | MIT | no | +| @esbuild/linux-ia32 | 0.25.12 | MIT | no | +| @esbuild/linux-loong64 | 0.25.12 | MIT | no | +| @esbuild/linux-mips64el | 0.25.12 | MIT | no | +| @esbuild/linux-ppc64 | 0.25.12 | MIT | no | +| @esbuild/linux-riscv64 | 0.25.12 | MIT | no | +| @esbuild/linux-s390x | 0.25.12 | MIT | no | +| @esbuild/linux-x64 | 0.25.12 | MIT | no | +| @esbuild/netbsd-arm64 | 0.25.12 | MIT | no | +| @esbuild/netbsd-x64 | 0.25.12 | MIT | no | +| @esbuild/openbsd-arm64 | 0.25.12 | MIT | no | +| @esbuild/openbsd-x64 | 0.25.12 | MIT | no | +| @esbuild/openharmony-arm64 | 0.25.12 | MIT | no | +| @esbuild/sunos-x64 | 0.25.12 | MIT | no | +| @esbuild/win32-arm64 | 0.25.12 | MIT | no | +| @esbuild/win32-ia32 | 0.25.12 | MIT | no | +| @esbuild/win32-x64 | 0.25.12 | MIT | no | +| @isaacs/cliui | 8.0.2 | ISC | no | +| @istanbuljs/schema | 0.1.6 | MIT | no | +| @joshwooding/vite-plugin-react-docgen-typescript | 0.7.0 | MIT | no | +| @jridgewell/gen-mapping | 0.3.13 | MIT | no | +| @jridgewell/remapping | 2.3.5 | MIT | no | +| @jridgewell/resolve-uri | 3.1.2 | MIT | no | +| @jridgewell/sourcemap-codec | 1.5.5 | MIT | no | +| @jridgewell/trace-mapping | 0.3.31 | MIT | no | +| @mdx-js/react | 3.1.1 | MIT | no | +| @microsoft/api-extractor | 7.58.9 | MIT | no | +| @microsoft/api-extractor-model | 7.33.8 | MIT | no | +| @microsoft/tsdoc | 0.16.0 | MIT | no | +| @microsoft/tsdoc-config | 0.18.1 | MIT | no | +| @mixmark-io/domino | 2.2.0 | BSD-2-Clause | no | +| @napi-rs/wasm-runtime | 1.2.3 | MIT | no | +| @oxc-parser/binding-android-arm-eabi | 0.127.0 | MIT | no | +| @oxc-parser/binding-android-arm64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-darwin-arm64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-darwin-x64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-freebsd-x64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-arm-gnueabihf | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-arm-musleabihf | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-arm64-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-arm64-musl | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-ppc64-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-riscv64-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-riscv64-musl | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-s390x-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-x64-gnu | 0.127.0 | MIT | no | +| @oxc-parser/binding-linux-x64-musl | 0.127.0 | MIT | no | +| @oxc-parser/binding-openharmony-arm64 | 0.127.0 | MIT | no | +| @oxc-parser/binding-wasm32-wasi | 0.127.0 | MIT | no | +| @oxc-parser/binding-win32-arm64-msvc | 0.127.0 | MIT | no | +| @oxc-parser/binding-win32-ia32-msvc | 0.127.0 | MIT | no | +| @oxc-parser/binding-win32-x64-msvc | 0.127.0 | MIT | no | +| @oxc-project/types | 0.127.0 | MIT | no | +| @oxc-resolver/binding-android-arm-eabi | 11.24.2 | MIT | no | +| @oxc-resolver/binding-android-arm64 | 11.24.2 | MIT | no | +| @oxc-resolver/binding-darwin-arm64 | 11.24.2 | MIT | no | +| @oxc-resolver/binding-darwin-x64 | 11.24.2 | MIT | no | +| @oxc-resolver/binding-freebsd-x64 | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-arm-gnueabihf | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-arm-musleabihf | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-arm64-gnu | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-arm64-musl | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-ppc64-gnu | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-riscv64-gnu | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-riscv64-musl | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-s390x-gnu | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-x64-gnu | 11.24.2 | MIT | no | +| @oxc-resolver/binding-linux-x64-musl | 11.24.2 | MIT | no | +| @oxc-resolver/binding-openharmony-arm64 | 11.24.2 | MIT | no | +| @oxc-resolver/binding-wasm32-wasi | 11.24.2 | MIT | no | +| @oxc-resolver/binding-win32-arm64-msvc | 11.24.2 | MIT | no | +| @oxc-resolver/binding-win32-x64-msvc | 11.24.2 | MIT | no | +| @pkgjs/parseargs | 0.11.0 | Apache-2.0 AND MIT | no | +| @playwright/test | 1.62.0 | Apache-2.0 | no | +| @popperjs/core | 2.11.8 | MIT | no | +| @remirror/core-constants | 3.0.0 | MIT | no | +| @rolldown/pluginutils | 1.0.0-beta.27 | MIT | no | +| @rollup/pluginutils | 5.4.0 | MIT | no | +| @rollup/rollup-android-arm-eabi | 4.62.2 | MIT | no | +| @rollup/rollup-android-arm64 | 4.62.2 | MIT | no | +| @rollup/rollup-darwin-arm64 | 4.62.2 | MIT | no | +| @rollup/rollup-darwin-x64 | 4.62.2 | MIT | no | +| @rollup/rollup-freebsd-arm64 | 4.62.2 | MIT | no | +| @rollup/rollup-freebsd-x64 | 4.62.2 | MIT | no | +| @rollup/rollup-linux-arm-gnueabihf | 4.62.2 | MIT | no | +| @rollup/rollup-linux-arm-musleabihf | 4.62.2 | MIT | no | +| @rollup/rollup-linux-arm64-gnu | 4.62.2 | MIT | no | +| @rollup/rollup-linux-arm64-musl | 4.62.2 | MIT | no | +| @rollup/rollup-linux-loong64-gnu | 4.62.2 | MIT | no | +| @rollup/rollup-linux-loong64-musl | 4.62.2 | MIT | no | +| @rollup/rollup-linux-ppc64-gnu | 4.62.2 | MIT | no | +| @rollup/rollup-linux-ppc64-musl | 4.62.2 | MIT | no | +| @rollup/rollup-linux-riscv64-gnu | 4.62.2 | MIT | no | +| @rollup/rollup-linux-riscv64-musl | 4.62.2 | MIT | no | +| @rollup/rollup-linux-s390x-gnu | 4.62.2 | MIT | no | +| @rollup/rollup-linux-x64-gnu | 4.62.2 | MIT | no | +| @rollup/rollup-linux-x64-musl | 4.62.2 | MIT | no | +| @rollup/rollup-openbsd-x64 | 4.62.2 | MIT | no | +| @rollup/rollup-openharmony-arm64 | 4.62.2 | MIT | no | +| @rollup/rollup-win32-arm64-msvc | 4.62.2 | MIT | no | +| @rollup/rollup-win32-ia32-msvc | 4.62.2 | MIT | no | +| @rollup/rollup-win32-x64-gnu | 4.62.2 | MIT | no | +| @rollup/rollup-win32-x64-msvc | 4.62.2 | MIT | no | +| @rushstack/node-core-library | 5.23.1 | MIT | no | +| @rushstack/problem-matcher | 0.2.1 | MIT | no | +| @rushstack/rig-package | 0.7.3 | MIT | no | +| @rushstack/terminal | 0.24.0 | MIT | no | +| @rushstack/ts-command-line | 5.3.10 | MIT | no | +| @storybook/addon-docs | 10.5.8 | MIT | no | +| @storybook/builder-vite | 10.5.8 | MIT | no | +| @storybook/csf-plugin | 10.5.8 | MIT | no | +| @storybook/global | 5.0.0 | MIT | no | +| @storybook/icons | 2.1.0 | MIT | no | +| @storybook/react | 10.5.8 | MIT | no | +| @storybook/react-dom-shim | 10.5.8 | MIT | no | +| @storybook/react-vite | 10.5.8 | MIT | no | +| @testing-library/dom | 10.4.1 | MIT | no | +| @testing-library/jest-dom | 6.9.1 | MIT | no | +| @testing-library/react | 16.3.2 | MIT | no | +| @testing-library/user-event | 14.6.1 | MIT | no | +| @tiptap/core | 2.27.2 | MIT | no | +| @tiptap/extension-blockquote | 2.27.2 | MIT | no | +| @tiptap/extension-bold | 2.27.2 | MIT | no | +| @tiptap/extension-bubble-menu | 2.27.2 | MIT | no | +| @tiptap/extension-bullet-list | 2.27.2 | MIT | no | +| @tiptap/extension-code | 2.27.2 | MIT | no | +| @tiptap/extension-code-block | 2.27.2 | MIT | no | +| @tiptap/extension-collaboration | 2.27.2 | MIT | no | +| @tiptap/extension-collaboration-cursor | 2.27.2 | MIT | no | +| @tiptap/extension-document | 2.27.2 | MIT | no | +| @tiptap/extension-dropcursor | 2.27.2 | MIT | no | +| @tiptap/extension-floating-menu | 2.27.2 | MIT | no | +| @tiptap/extension-gapcursor | 2.27.2 | MIT | no | +| @tiptap/extension-hard-break | 2.27.2 | MIT | no | +| @tiptap/extension-heading | 2.27.2 | MIT | no | +| @tiptap/extension-history | 2.27.2 | MIT | no | +| @tiptap/extension-horizontal-rule | 2.27.2 | MIT | no | +| @tiptap/extension-image | 2.27.2 | MIT | no | +| @tiptap/extension-italic | 2.27.2 | MIT | no | +| @tiptap/extension-link | 2.27.2 | MIT | no | +| @tiptap/extension-list-item | 2.27.2 | MIT | no | +| @tiptap/extension-ordered-list | 2.27.2 | MIT | no | +| @tiptap/extension-paragraph | 2.27.2 | MIT | no | +| @tiptap/extension-placeholder | 2.27.2 | MIT | no | +| @tiptap/extension-strike | 2.27.2 | MIT | no | +| @tiptap/extension-table | 2.27.2 | MIT | no | +| @tiptap/extension-table-cell | 2.27.2 | MIT | no | +| @tiptap/extension-table-header | 2.27.2 | MIT | no | +| @tiptap/extension-table-row | 2.27.2 | MIT | no | +| @tiptap/extension-text | 2.27.2 | MIT | no | +| @tiptap/extension-text-style | 2.27.2 | MIT | no | +| @tiptap/pm | 2.27.2 | MIT | no | +| @tiptap/react | 2.27.2 | MIT | no | +| @tiptap/starter-kit | 2.27.2 | MIT | no | +| @tybys/wasm-util | 0.10.3 | MIT | no | +| @types/argparse | 1.0.38 | MIT | no | +| @types/aria-query | 5.0.4 | MIT | no | +| @types/babel__core | 7.20.5 | MIT | no | +| @types/babel__generator | 7.27.0 | MIT | no | +| @types/babel__template | 7.4.4 | MIT | no | +| @types/babel__traverse | 7.28.0 | MIT | no | +| @types/chai | 5.2.3 | MIT | no | +| @types/deep-eql | 4.0.2 | MIT | no | +| @types/doctrine | 0.0.9 | MIT | no | +| @types/estree | 1.0.9 | MIT | no | +| @types/linkify-it | 5.0.0 | MIT | no | +| @types/markdown-it | 14.1.2 | MIT | no | +| @types/mdurl | 2.0.0 | MIT | no | +| @types/mdx | 2.0.14 | MIT | no | +| @types/node | 22.20.1 | MIT | no | +| @types/prop-types | 15.7.15 | MIT | no | +| @types/react | 18.3.31 | MIT | no | +| @types/react-dom | 18.3.7 | MIT | no | +| @types/resolve | 1.20.6 | MIT | no | +| @types/turndown | 5.0.6 | MIT | no | +| @types/use-sync-external-store | 0.0.6 | MIT | no | +| @vitejs/plugin-react | 4.7.0 | MIT | no | +| @vitest/coverage-v8 | 3.2.7 | MIT | no | +| @vitest/expect | 3.2.4 | MIT | no | +| @vitest/expect | 3.2.7 | MIT | no | +| @vitest/mocker | 3.2.7 | MIT | no | +| @vitest/pretty-format | 3.2.4 | MIT | no | +| @vitest/pretty-format | 3.2.7 | MIT | no | +| @vitest/runner | 3.2.7 | MIT | no | +| @vitest/snapshot | 3.2.7 | MIT | no | +| @vitest/spy | 3.2.4 | MIT | no | +| @vitest/spy | 3.2.7 | MIT | no | +| @vitest/utils | 3.2.4 | MIT | no | +| @vitest/utils | 3.2.7 | MIT | no | +| @volar/language-core | 2.4.28 | MIT | no | +| @volar/source-map | 2.4.28 | MIT | no | +| @volar/typescript | 2.4.28 | MIT | no | +| @vue/compiler-core | 3.5.39 | MIT | no | +| @vue/compiler-dom | 3.5.39 | MIT | no | +| @vue/compiler-vue2 | 2.7.16 | MIT | no | +| @vue/language-core | 2.2.0 | MIT | no | +| @vue/shared | 3.5.39 | MIT | no | +| @webcontainer/env | 1.1.1 | MIT | no | +| acorn | 8.17.0 | MIT | no | +| actions/attest | 59d89421af93a897026c735860bf21b6eb4f7b26 | NOASSERTION | yes | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/download-artifact | 37930b1c2abaa49bbe596cd826c3c89aef350131 | NOASSERTION | yes | +| actions/setup-node | 820762786026740c76f36085b0efc47a31fe5020 | NOASSERTION | yes | +| actions/setup-python | a26af69be951a213d495a4c3e4e4022e16d87065 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| agent-base | 7.1.4 | MIT | no | +| ajv | 8.18.0 | MIT | no | +| ajv-draft-04 | 1.0.0 | MIT | no | +| ajv-formats | 3.0.1 | MIT | no | +| alien-signals | 0.4.14 | MIT | no | +| ansi-regex | 5.0.1 | MIT | no | +| ansi-regex | 6.2.2 | MIT | no | +| ansi-styles | 4.3.0 | MIT | no | +| ansi-styles | 5.2.0 | MIT | no | +| ansi-styles | 6.2.3 | MIT | no | +| argparse | 1.0.10 | MIT | no | +| argparse | 2.0.1 | Python-2.0 | no | +| aria-query | 5.3.0 | Apache-2.0 | no | +| aria-query | 5.3.2 | Apache-2.0 | no | +| assertion-error | 2.0.1 | MIT | no | +| ast-types | 0.16.1 | MIT | no | +| ast-v8-to-istanbul | 0.3.12 | MIT | no | +| asynckit | 0.4.0 | MIT | no | +| balanced-match | 4.0.4 | MIT | no | +| baseline-browser-mapping | 2.10.43 | Apache-2.0 | no | +| brace-expansion | 5.0.9 | MIT | no | +| browserslist | 4.28.5 | MIT | no | +| bundle-name | 4.1.0 | MIT | no | +| cac | 6.7.14 | MIT | no | +| call-bind-apply-helpers | 1.0.2 | MIT | no | +| caniuse-lite | 1.0.30001803 | CC-BY-4.0 | no | +| chai | 5.3.3 | MIT | no | +| check-error | 2.1.3 | MIT | no | +| color-convert | 2.0.1 | MIT | no | +| color-name | 1.1.4 | MIT | no | +| combined-stream | 1.0.8 | MIT | no | +| compare-versions | 6.1.1 | MIT | no | +| confbox | 0.1.8 | BSD-3-Clause AND MIT | no | +| confbox | 0.2.4 | MIT | no | +| convert-source-map | 2.0.0 | MIT | no | +| coverage | 7.15.3 | Apache-2.0 | no | +| crelt | 1.0.7 | MIT | no | +| cross-spawn | 7.0.6 | MIT | no | +| css.escape | 1.5.1 | MIT | no | +| cssstyle | 4.6.0 | MIT | no | +| csstype | 3.2.3 | MIT | no | +| data-urls | 5.0.0 | MIT | no | +| de-indent | 1.0.2 | MIT | no | +| debug | 4.4.3 | MIT | no | +| decimal.js | 10.6.0 | MIT | no | +| deep-eql | 5.0.2 | MIT | no | +| default-browser | 5.5.0 | MIT | no | +| default-browser-id | 5.0.1 | MIT | no | +| define-lazy-prop | 3.0.0 | MIT | no | +| delayed-stream | 1.0.0 | MIT | no | +| dequal | 2.0.3 | MIT | no | +| diff | 8.0.4 | BSD-3-Clause | no | +| doctrine | 3.0.0 | Apache-2.0 AND BSD-2-Clause | no | +| dom-accessibility-api | 0.5.16 | MIT | no | +| dom-accessibility-api | 0.6.3 | MIT | no | +| dunder-proto | 1.0.1 | MIT | no | +| eastasianwidth | 0.2.0 | MIT | no | +| electron-to-chromium | 1.5.389 | ISC | no | +| emoji-regex | 8.0.0 | MIT | no | +| emoji-regex | 9.2.2 | MIT | no | +| empathic | 2.0.1 | MIT | no | +| entities | 4.5.0 | BSD-2-Clause | no | +| entities | 6.0.1 | BSD-2-Clause | no | +| entities | 7.0.1 | BSD-2-Clause | no | +| es-define-property | 1.0.1 | MIT | no | +| es-errors | 1.3.0 | MIT | no | +| es-module-lexer | 1.7.0 | MIT | no | +| es-object-atoms | 1.1.2 | MIT | no | +| es-set-tostringtag | 2.1.0 | MIT | no | +| esbuild | 0.25.12 | MIT | no | +| escalade | 3.2.0 | MIT | no | +| escape-string-regexp | 4.0.0 | MIT | no | +| esprima | 4.0.1 | BSD-2-Clause AND BSD-3-Clause | no | +| estree-walker | 2.0.2 | MIT | no | +| estree-walker | 3.0.3 | MIT | no | +| esutils | 2.0.3 | BSD-2-Clause | no | +| et-xmlfile | 2.0.0 | 0BSD AND BSD-3-Clause AND MIT AND Python-2.0 | no | +| expect-type | 1.4.0 | Apache-2.0 | no | +| exsolve | 1.1.0 | MIT | no | +| fast-deep-equal | 3.1.3 | MIT | no | +| fast-uri | 3.1.5 | BSD-3-Clause | no | +| fdir | 6.5.0 | MIT | no | +| foreground-child | 3.3.1 | ISC | no | +| form-data | 4.0.6 | MIT | no | +| fs-extra | 11.3.6 | MIT | no | +| fsevents | 2.3.2 | MIT | no | +| fsevents | 2.3.3 | MIT | no | +| function-bind | 1.1.2 | MIT | no | +| gensync | 1.0.0-beta.2 | MIT | no | +| get-intrinsic | 1.3.0 | MIT | no | +| get-proto | 1.0.1 | MIT | no | +| glob | 10.5.0 | ISC | no | +| glob | 13.0.6 | BlueOak-1.0.0 | no | +| gopd | 1.2.0 | MIT | no | +| graceful-fs | 4.2.11 | ISC | no | +| has-flag | 4.0.0 | MIT | no | +| has-symbols | 1.1.0 | MIT | no | +| has-tostringtag | 1.0.2 | MIT | no | +| hasown | 2.0.4 | MIT | no | +| he | 1.2.0 | MIT | no | +| html-encoding-sniffer | 4.0.0 | MIT | no | +| html-escaper | 2.0.2 | MIT | no | +| http-proxy-agent | 7.0.2 | MIT | no | +| https-proxy-agent | 7.0.6 | MIT | no | +| iconv-lite | 0.6.3 | MIT | no | +| import-lazy | 4.0.0 | MIT | no | +| indent-string | 4.0.0 | MIT | no | +| iniconfig | 2.3.0 | MIT | no | +| is-core-module | 2.16.2 | MIT | no | +| is-docker | 3.0.0 | MIT | no | +| is-fullwidth-code-point | 3.0.0 | MIT | no | +| is-inside-container | 1.0.0 | MIT | no | +| is-potential-custom-element-name | 1.0.1 | MIT | no | +| is-wsl | 3.1.1 | MIT | no | +| isexe | 2.0.0 | ISC | no | +| isomorphic.js | 0.2.5 | MIT | no | +| istanbul-lib-coverage | 3.2.2 | BSD-3-Clause | no | +| istanbul-lib-report | 3.0.1 | BSD-3-Clause | no | +| istanbul-lib-source-maps | 5.0.6 | BSD-3-Clause | no | +| istanbul-reports | 3.2.0 | BSD-3-Clause | no | +| jackspeak | 3.4.3 | BlueOak-1.0.0 | no | +| jju | 1.4.0 | MIT | no | +| js-tokens | 10.0.0 | MIT | no | +| js-tokens | 4.0.0 | MIT | no | +| js-tokens | 9.0.1 | MIT | no | +| jsdom | 25.0.1 | MIT | no | +| jsesc | 3.1.0 | MIT | no | +| json-schema-traverse | 1.0.0 | MIT | no | +| json5 | 2.2.3 | MIT | no | +| jsonc-parser | 3.3.1 | MIT | no | +| jsonfile | 6.2.1 | MIT | no | +| kolorist | 1.8.0 | MIT | no | +| lib0 | 0.2.117 | MIT | no | +| linkify-it | 5.0.2 | MIT | no | +| linkifyjs | 4.3.3 | MIT | no | +| local-pkg | 1.2.1 | MIT | no | +| loose-envify | 1.4.0 | MIT | no | +| loupe | 3.2.1 | MIT | no | +| lru-cache | 10.4.3 | ISC | no | +| lru-cache | 11.5.2 | BlueOak-1.0.0 | no | +| lru-cache | 5.1.1 | ISC | no | +| lxml | 6.1.0 | BSD-3-Clause AND GPL-1.0-or-later | yes | +| lz-string | 1.5.0 | MIT | no | +| magic-string | 0.30.21 | MIT | no | +| magicast | 0.3.5 | MIT | no | +| make-dir | 4.0.0 | MIT | no | +| markdown-it | 14.3.0 | MIT | no | +| marked | 15.0.12 | BSD-3-Clause AND MIT | no | +| math-intrinsics | 1.1.0 | MIT | no | +| mdurl | 2.0.0 | MIT | no | +| mime-db | 1.52.0 | MIT | no | +| mime-types | 2.1.35 | MIT | no | +| min-indent | 1.0.1 | MIT | no | +| minimatch | 10.2.3 | BlueOak-1.0.0 | no | +| minimatch | 10.2.5 | BlueOak-1.0.0 | no | +| minimist | 1.2.8 | MIT | no | +| minipass | 7.1.3 | BlueOak-1.0.0 | no | +| mlly | 1.8.2 | MIT | no | +| ms | 2.1.3 | MIT | no | +| muggle-string | 0.4.1 | MIT | no | +| nanoid | 3.3.18 | MIT | no | +| node-releases | 2.0.51 | MIT | no | +| nwsapi | 2.2.24 | MIT | no | +| open | 10.2.0 | MIT | no | +| openpyxl | 3.1.5 | MIT | no | +| openpyxl | >= 3.1.5,< 4 | NOASSERTION | yes | +| orderedmap | 2.1.1 | MIT | no | +| oxc-parser | 0.127.0 | MIT | no | +| oxc-resolver | 11.24.2 | MIT | no | +| package-json-from-dist | 1.0.1 | BlueOak-1.0.0 | no | +| packaging | 25.0 | Apache-2.0 AND BSD-2-Clause | no | +| parse5 | 7.3.0 | MIT | no | +| path-browserify | 1.0.1 | MIT | no | +| path-key | 3.1.1 | MIT | no | +| path-parse | 1.0.7 | MIT | no | +| path-scurry | 1.11.1 | BlueOak-1.0.0 | no | +| path-scurry | 2.0.2 | BlueOak-1.0.0 | no | +| pathe | 2.0.3 | MIT | no | +| pathval | 2.0.1 | MIT | no | +| picocolors | 1.1.1 | ISC | no | +| picomatch | 4.0.5 | MIT | no | +| pillow | 12.3.0 | MIT-CMU | no | +| pkg-types | 1.3.1 | MIT | no | +| pkg-types | 2.3.1 | MIT | no | +| playwright | 1.62.0 | Apache-2.0 | no | +| playwright-core | 1.62.0 | Apache-2.0 | no | +| pluggy | 1.6.0 | MIT | no | +| pnpm/action-setup | 0977fd99725f1db4007ccb2928dbb4e90d06cc86 | NOASSERTION | yes | +| postcss | 8.5.25 | MIT | no | +| pretty-format | 27.5.1 | MIT | no | +| prosemirror-changeset | 2.4.1 | MIT | no | +| prosemirror-collab | 1.3.1 | MIT | no | +| prosemirror-commands | 1.7.1 | MIT | no | +| prosemirror-dropcursor | 1.8.3 | MIT | no | +| prosemirror-gapcursor | 1.4.1 | MIT | no | +| prosemirror-history | 1.5.0 | MIT | no | +| prosemirror-inputrules | 1.5.1 | MIT | no | +| prosemirror-keymap | 1.2.3 | MIT | no | +| prosemirror-markdown | 1.13.5 | MIT | no | +| prosemirror-menu | 1.3.2 | MIT | no | +| prosemirror-model | 1.25.11 | MIT | no | +| prosemirror-schema-basic | 1.2.4 | MIT | no | +| prosemirror-schema-list | 1.5.1 | MIT | no | +| prosemirror-state | 1.4.4 | MIT | no | +| prosemirror-tables | 1.8.5 | MIT | no | +| prosemirror-trailing-node | 3.0.0 | MIT | no | +| prosemirror-transform | 1.12.0 | MIT | no | +| prosemirror-view | 1.42.1 | MIT | no | +| punycode | 2.3.1 | MIT | no | +| punycode.js | 2.3.1 | MIT | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pypa/gh-action-pypi-publish | dc37677b2e1c63e2034f94d8a5b11f265b73ba33 | NOASSERTION | yes | +| pytest | 9.0.3 | MIT | no | +| python-docx | 1.2.0 | MIT | no | +| python-docx | >= 1.2.0,< 2 | NOASSERTION | yes | +| python-pptx | 1.0.2 | MIT | no | +| python-pptx | >= 1.0.2,< 2 | NOASSERTION | yes | +| quansync | 0.2.11 | MIT | no | +| react | 18.3.1 | MIT | no | +| react-docgen | 8.0.3 | MIT | no | +| react-docgen-typescript | 2.4.0 | MIT | no | +| react-dom | 18.3.1 | MIT | no | +| react-is | 17.0.2 | MIT | no | +| react-refresh | 0.17.0 | MIT | no | +| recast | 0.23.21 | MIT | no | +| redent | 3.0.0 | MIT | no | +| require-from-string | 2.0.2 | MIT | no | +| resolve | 1.22.12 | MIT | no | +| rollup | 4.62.2 | MIT | no | +| rope-sequence | 1.3.4 | MIT | no | +| rrweb-cssom | 0.7.1 | MIT | no | +| rrweb-cssom | 0.8.0 | MIT | no | +| run-applescript | 7.1.0 | MIT | no | +| safer-buffer | 2.1.2 | MIT | no | +| saxes | 6.0.0 | ISC | no | +| scheduler | 0.23.2 | MIT | no | +| semver | 6.3.1 | ISC | no | +| semver | 7.7.4 | ISC | no | +| semver | 7.8.5 | ISC | no | +| setuptools | 83.0.0 | MIT | no | +| shebang-command | 2.0.0 | MIT | no | +| shebang-regex | 3.0.0 | MIT | no | +| siginfo | 2.0.0 | ISC | no | +| signal-exit | 4.1.0 | ISC | no | +| sigstore/cosign-installer | 6f9f17788090df1f26f669e9d70d6ae9567deba6 | NOASSERTION | yes | +| source-map | 0.6.1 | BSD-3-Clause | no | +| source-map-js | 1.2.1 | BSD-3-Clause | no | +| sprintf-js | 1.0.3 | BSD-3-Clause | no | +| stackback | 0.0.2 | MIT | no | +| std-env | 3.10.0 | MIT | no | +| storybook | 10.5.8 | MIT | no | +| string-argv | 0.3.2 | MIT | no | +| string-width | 4.2.3 | MIT | no | +| string-width | 5.1.2 | MIT | no | +| strip-ansi | 6.0.1 | MIT | no | +| strip-ansi | 7.2.0 | MIT | no | +| strip-bom | 3.0.0 | MIT | no | +| strip-indent | 3.0.0 | MIT | no | +| strip-indent | 4.1.1 | MIT | no | +| strip-literal | 3.1.0 | MIT | no | +| supports-color | 7.2.0 | MIT | no | +| supports-color | 8.1.1 | MIT | no | +| supports-preserve-symlinks-flag | 1.0.0 | MIT | no | +| symbol-tree | 3.2.4 | MIT | no | +| test-exclude | 7.0.2 | ISC | no | +| tiny-invariant | 1.3.3 | MIT | no | +| tinybench | 2.9.0 | MIT | no | +| tinyexec | 0.3.2 | MIT | no | +| tinyglobby | 0.2.17 | MIT | no | +| tinypool | 1.1.1 | MIT | no | +| tinyrainbow | 2.0.0 | MIT | no | +| tinyspy | 4.0.4 | MIT | no | +| tippy.js | 6.3.7 | MIT | no | +| tldts | 6.1.86 | MIT | no | +| tldts-core | 6.1.86 | MIT | no | +| tough-cookie | 5.1.2 | BSD-3-Clause | no | +| tr46 | 5.1.1 | MIT | no | +| ts-dedent | 2.3.0 | MIT | no | +| tsconfig-paths | 4.2.0 | MIT | no | +| tslib | 2.8.1 | 0BSD | no | +| turndown | 7.2.4 | MIT | no | +| turndown-plugin-gfm | 1.0.2 | MIT | no | +| typescript | 5.9.3 | Apache-2.0 | no | +| typing-extensions | 4.15.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| uc.micro | 2.1.0 | MIT | no | +| ufo | 1.6.4 | MIT | no | +| undici-types | 6.21.0 | MIT | no | +| universalify | 2.0.1 | MIT | no | +| unplugin | 2.3.11 | MIT | no | +| update-browserslist-db | 1.2.3 | MIT | no | +| use-sync-external-store | 1.6.0 | MIT | no | +| vite | 6.4.3 | MIT | no | +| vite-node | 3.2.4 | MIT | no | +| vite-plugin-dts | 4.5.4 | MIT | no | +| vitest | 3.2.7 | MIT | no | +| vscode-uri | 3.1.0 | MIT | no | +| w3c-keyname | 2.2.8 | MIT | no | +| w3c-xmlserializer | 5.0.0 | MIT | no | +| webidl-conversions | 7.0.0 | BSD-2-Clause | no | +| webpack-virtual-modules | 0.6.2 | MIT | no | +| whatwg-encoding | 3.1.1 | MIT | no | +| whatwg-mimetype | 4.0.0 | MIT | no | +| whatwg-url | 14.2.0 | MIT | no | +| wheel | 0.47.0 | MIT | no | +| which | 2.0.2 | ISC | no | +| why-is-node-running | 2.3.0 | MIT | no | +| wrap-ansi | 7.0.0 | MIT | no | +| wrap-ansi | 8.1.0 | MIT | no | +| ws | 8.21.0 | MIT | no | +| ws | 8.21.3 | MIT | no | +| wsl-utils | 0.1.0 | MIT | no | +| xlsxwriter | 3.2.9 | BSD-2-Clause | no | +| xml-name-validator | 5.0.0 | Apache-2.0 | no | +| xmlchars | 2.2.0 | MIT | no | +| y-prosemirror | 1.3.7 | MIT | no | +| y-protocols | 1.0.7 | MIT | no | +| yallist | 3.1.1 | ISC | no | +| yjs | 13.6.31 | MIT | no | + +### ContextualWisdomLab/IRT-bibliography-set + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/j-planner + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/k-csap-skills + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 4.*.* | NOASSERTION | yes | +| astral-sh/setup-uv | 6.*.* | NOASSERTION | yes | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| iniconfig | 2.3.0 | MIT | no | +| packaging | 26.3 | Apache-2.0 OR BSD-2-Clause | no | +| pluggy | 1.6.0 | MIT | no | +| pygments | 2.21.0 | BSD-2-Clause | no | +| pytest | 9.1.1 | MIT | no | +| pyyaml | 6.0.3 | MIT | no | +| ruff | 0.16.10 | MIT | no | + +### ContextualWisdomLab/kaefa + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| r-lib/actions/check-r-package | 6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 | NOASSERTION | yes | +| r-lib/actions/setup-pandoc | d3c5be51b12e724e68f33216ca3c148b66d5f0b6 | NOASSERTION | yes | +| r-lib/actions/setup-r | 6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 | NOASSERTION | yes | +| r-lib/actions/setup-r-dependencies | 6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 | NOASSERTION | yes | + +### ContextualWisdomLab/keyverse + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/download-artifact | 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c | NOASSERTION | yes | +| actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| annotated-doc | 0.0.5 | MIT | no | +| annotated-types | 0.8.0 | MIT | no | +| anyio | 4.14.2 | MIT | no | +| astral-sh/setup-uv | 20cfd1bf945f4377ade1205e4dbc17946fc9a30d | NOASSERTION | yes | +| atheris | 3.1.0 | Apache-2.0 | no | +| attrs | 26.1.0 | MIT | no | +| certifi | 2026.7.22 | MPL-2.0 | yes | +| cffi | 2.1.1 | MIT-0 | no | +| click | 8.4.2 | BSD-3-Clause | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| coverage | 7.15.4 | Apache-2.0 | no | +| cryptography | 50.0.0 | Apache-2.0 OR BSD-3-Clause | no | +| cwl-idp-account-unification | 0.1.0 | NOASSERTION | yes | +| fastapi | 0.141.1 | MIT | no | +| h11 | 0.16.0 | MIT | no | +| httpcore | 1.0.9 | BSD-2-Clause AND BSD-3-Clause | no | +| httpcore2 | 2.12.0 | BSD-3-Clause | no | +| httpx | 0.28.1 | BSD-3-Clause | no | +| httpx2 | 2.12.0 | BSD-3-Clause | no | +| httpx2-jsfetch | 1.0 | BSD-3-Clause | no | +| idna | 3.18 | BSD-3-Clause | no | +| iniconfig | 2.3.0 | MIT | no | +| interrogate | 1.7.0 | MIT | no | +| packaging | 26.2 | Apache-2.0 AND BSD-2-Clause | no | +| pluggy | 1.6.0 | MIT | no | +| py | 1.11.0 | MIT | no | +| pycparser | 3.0 | BSD-3-Clause | no | +| pydantic | 2.13.4 | MIT | no | +| pydantic-core | 2.46.4 | MIT | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pytest | 9.1.1 | MIT | no | +| pyyaml | 6.0.3 | MIT | no | +| ruff | 0.16.3 | MIT | no | +| setuptools | 84.0.0 | MIT | no | +| starlette | 1.3.1 | BSD-3-Clause | no | +| step-security/harden-runner | 05e31511f85b41b11d1cf0ef85d0992719546e2c | NOASSERTION | yes | +| tabulate | 0.10.0 | MIT | no | +| truststore | 0.10.4 | MIT | no | +| typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| typing-inspection | 0.4.4 | MIT | no | +| uvicorn | 0.52.3 | BSD-3-Clause | no | + +### ContextualWisdomLab/korean-writing-skills + +No components reported. + +### ContextualWisdomLab/late-life-anxiety-reanalysis + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| fast-mlsirm | — | NOASSERTION | yes | +| numpy | 2.5.3 | BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0 | no | + +### ContextualWisdomLab/law-ai-agent + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| annotated-types | 0.8.0 | MIT | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| ContextualWisdomLab/.github/.github/workflows/law-ai-agent-ci.yml | 8dff0538c55b99df3f6f19b25a06633fc72b38ef | NOASSERTION | yes | +| coverage | 7.16.2 | Apache-2.0 | no | +| iniconfig | 2.3.0 | MIT | no | +| librt | 0.16.0 | MIT | no | +| mypy | 1.20.2 | MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause AND MIT AND Python-2.0 AND Python-2.0.1 AND BSD-2-Clause | no | +| mypy-extensions | 1.1.0 | MIT | no | +| packaging | 26.3 | Apache-2.0 OR BSD-2-Clause | no | +| pathspec | 1.1.1 | MPL-2.0 | yes | +| pluggy | 1.6.0 | MIT | no | +| psycopg | 3.3.6 | LGPL-3.0-only | yes | +| psycopg-binary | 3.3.6 | LGPL-3.0-only | yes | +| pydantic | 2.13.5 | MIT | no | +| pydantic-core | 2.46.5 | MIT | no | +| pygments | 2.21.0 | BSD-2-Clause | no | +| pytest | 9.1.1 | MIT | no | +| pytest-cov | 7.1.0 | MIT | no | +| ruff | 0.16.10 | MIT | no | +| typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| typing-inspection | 0.4.4 | MIT | no | +| tzdata | 2026.5 | Apache-2.0 | no | + +### ContextualWisdomLab/learning-content-studio + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/learning-interoperability-contracts + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/learning-management-platform + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/learning-record-store + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/life-os + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/LineageWeave + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/linux-cluster-ops + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/attest-build-provenance | a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 | NOASSERTION | yes | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/setup-python | ece7cb06caefa5fff74198d8649806c4678c61a1 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| atheris | 3.0.0 | Apache-2.0 | no | + +### ContextualWisdomLab/litellm-patched-proxy + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/litellm-poinnetworks-ops + +No components reported. + +### ContextualWisdomLab/macos_utility_packs + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/mcp-shared-gateway + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 11bd71901bbe5b1630ceea73d27597364c9af683 | NOASSERTION | yes | +| actions/setup-node | 49933ea5288caeca8642d1e84afbd3f7d6820020 | NOASSERTION | yes | +| actions/setup-python | a26af69be951a213d495a4c3e4e4022e16d87065 | NOASSERTION | yes | + +### ContextualWisdomLab/metering-billing-platform + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/mhtml-etl-gateway + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/mightyETL + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | yes | +| actions/dependency-review-action | a1d282b36b6f3519aa1f3fc636f609c47dddb294 | NOASSERTION | yes | +| actions/setup-java | 1bcf9fb12cf4aa7d266a90ae39939e61372fe520 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| com.fasterxml.jackson.core:jackson-databind | — | NOASSERTION | yes | +| com.fasterxml.jackson:jackson-bom | 2.21.5 | Apache-2.0 | no | +| com.h2database:h2 | — | NOASSERTION | yes | +| github/codeql-action/analyze | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | yes | +| github/codeql-action/autobuild | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | yes | +| github/codeql-action/init | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | yes | +| github/codeql-action/upload-sarif | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | yes | +| io.debezium:debezium-api | 3.4.0 | Apache-2.0 | no | +| io.debezium:debezium-connector-postgres | 3.4.0 | Apache-2.0 | no | +| io.debezium:debezium-embedded | 3.4.0 | Apache-2.0 | no | +| io.jsonwebtoken:jjwt | 0.13.0 | Apache-2.0 | no | +| io.micrometer:micrometer-tracing-bridge-brave | — | NOASSERTION | yes | +| io.zipkin.reporter2:zipkin-reporter-brave | — | NOASSERTION | yes | +| org.apache.maven.plugins:maven-compiler-plugin | 3.13.0 | Apache-2.0 | no | +| org.apache.maven.plugins:maven-dependency-plugin | — | NOASSERTION | yes | +| org.apache.maven.plugins:maven-dependency-plugin | 3.9.0 | Apache-2.0 | no | +| org.apache.maven.plugins:maven-surefire-plugin | 3.5.4 | Apache-2.0 | no | +| org.flywaydb:flyway-core | — | NOASSERTION | yes | +| org.flywaydb:flyway-database-postgresql | — | NOASSERTION | yes | +| org.jacoco:jacoco-maven-plugin | 0.8.15 | EPL-2.0 OR (Apache-2.0 AND EPL-2.0) | yes | +| org.junit.jupiter:junit-jupiter-api | 5.12.2 | EPL-2.0 | yes | +| org.junit.jupiter:junit-jupiter-engine | 5.12.2 | EPL-2.0 | yes | +| org.junit.platform:junit-platform-commons | 1.12.2 | EPL-2.0 | yes | +| org.junit.platform:junit-platform-engine | 1.12.2 | EPL-2.0 | yes | +| org.junit.platform:junit-platform-launcher | 1.12.2 | EPL-2.0 | yes | +| org.mockito:mockito-core | 5.21.0 | MIT | no | +| org.mockito:mockito-junit-jupiter | 5.21.0 | MIT | no | +| org.postgresql:postgresql | — | NOASSERTION | yes | +| org.postgresql:postgresql | 42.7.12 | BSD-2-Clause | no | +| org.springframework.boot:spring-boot-configuration-processor | — | NOASSERTION | yes | +| org.springframework.boot:spring-boot-dependencies | 3.5.16 | Apache-2.0 | no | +| org.springframework.boot:spring-boot-maven-plugin | — | NOASSERTION | yes | +| org.springframework.boot:spring-boot-maven-plugin | 3.5.16 | Apache-2.0 | no | +| org.springframework.boot:spring-boot-starter-actuator | — | NOASSERTION | yes | +| org.springframework.boot:spring-boot-starter-aop | — | NOASSERTION | yes | +| org.springframework.boot:spring-boot-starter-data-jpa | — | NOASSERTION | yes | +| org.springframework.boot:spring-boot-starter-security | — | NOASSERTION | yes | +| org.springframework.boot:spring-boot-starter-test | — | NOASSERTION | yes | +| org.springframework.boot:spring-boot-starter-web | — | NOASSERTION | yes | +| org.springframework.cloud:spring-cloud-config-server | — | NOASSERTION | yes | +| org.springframework.cloud:spring-cloud-dependencies | 2025.0.3 | Apache-2.0 | no | +| org.springframework.cloud:spring-cloud-starter-gateway | — | NOASSERTION | yes | +| org.springframework.cloud:spring-cloud-starter-netflix-eureka-client | — | NOASSERTION | yes | +| org.springframework.cloud:spring-cloud-starter-netflix-eureka-server | — | NOASSERTION | yes | +| org.springframework.kafka:spring-kafka | — | NOASSERTION | yes | +| org.springframework.kafka:spring-kafka | 3.3.16 | Apache-2.0 | no | +| org.springframework.retry:spring-retry | — | NOASSERTION | yes | +| org.springframework.retry:spring-retry | 2.0.13 | Apache-2.0 | no | +| org.springframework.security:spring-security-test | — | NOASSERTION | yes | +| ossf/scorecard-action | 4eaacf0543bb3f2c246792bd56e8cdeffafb205a | NOASSERTION | yes | + +### ContextualWisdomLab/naruon + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| @alloc/quick-lru | 5.2.0 | MIT | no | +| @asamuzakjp/css-color | 6.0.5 | MIT | no | +| @asamuzakjp/dom-selector | 8.3.0 | MIT | no | +| @babel/code-frame | 7.29.7 | MIT | no | +| @babel/compat-data | 7.29.7 | MIT | no | +| @babel/core | 7.29.7 | MIT | no | +| @babel/generator | 7.29.7 | MIT | no | +| @babel/helper-compilation-targets | 7.29.7 | MIT | no | +| @babel/helper-globals | 7.29.7 | MIT | no | +| @babel/helper-module-imports | 7.29.7 | MIT | no | +| @babel/helper-module-transforms | 7.29.7 | MIT | no | +| @babel/helper-string-parser | 7.29.7 | MIT | no | +| @babel/helper-validator-identifier | 7.29.7 | MIT | no | +| @babel/helper-validator-option | 7.29.7 | MIT | no | +| @babel/helpers | 7.29.7 | MIT | no | +| @babel/parser | 7.29.7 | MIT | no | +| @babel/runtime | 7.29.7 | MIT | no | +| @babel/template | 7.29.7 | MIT | no | +| @babel/traverse | 7.29.7 | MIT | no | +| @babel/types | 7.29.7 | MIT | no | +| @base-ui/react | 1.6.0 | MIT | no | +| @base-ui/utils | 0.3.1 | MIT | no | +| @bcoe/v8-coverage | 1.0.2 | ISC AND MIT | no | +| @bramus/specificity | 2.4.2 | MIT | no | +| @csstools/color-helpers | 6.1.0 | MIT-0 | no | +| @csstools/css-calc | 3.3.0 | MIT | no | +| @csstools/css-color-parser | 4.1.10 | MIT | no | +| @csstools/css-parser-algorithms | 4.0.0 | MIT | no | +| @csstools/css-syntax-patches-for-csstree | 1.1.7 | MIT-0 | no | +| @csstools/css-tokenizer | 4.0.0 | MIT | no | +| @egjs/hammerjs | 2.0.17 | MIT | no | +| @emnapi/core | 1.10.0 | MIT | no | +| @emnapi/core | 1.11.1 | MIT | no | +| @emnapi/runtime | 1.10.0 | MIT | no | +| @emnapi/runtime | 1.11.1 | MIT | no | +| @emnapi/runtime | 1.11.3 | MIT | no | +| @emnapi/wasi-threads | 1.2.1 | MIT | no | +| @emnapi/wasi-threads | 1.2.2 | MIT | no | +| @eslint-community/eslint-utils | 4.10.1 | MIT | no | +| @eslint-community/regexpp | 4.12.2 | MIT | no | +| @eslint/config-array | 0.21.2 | Apache-2.0 | no | +| @eslint/config-helpers | 0.4.2 | Apache-2.0 | no | +| @eslint/core | 0.17.0 | Apache-2.0 | no | +| @eslint/eslintrc | 3.3.6 | MIT | no | +| @eslint/js | 9.39.5 | MIT | no | +| @eslint/object-schema | 2.1.7 | Apache-2.0 | no | +| @eslint/plugin-kit | 0.4.1 | Apache-2.0 | no | +| @exodus/bytes | 1.15.1 | MIT | no | +| @floating-ui/core | 1.8.0 | MIT | no | +| @floating-ui/dom | 1.8.0 | MIT | no | +| @floating-ui/react-dom | 2.1.9 | MIT | no | +| @floating-ui/utils | 0.2.12 | MIT | no | +| @humanfs/core | 0.19.2 | Apache-2.0 | no | +| @humanfs/node | 0.16.8 | Apache-2.0 | no | +| @humanfs/types | 0.15.0 | Apache-2.0 | no | +| @humanwhocodes/module-importer | 1.0.1 | Apache-2.0 | no | +| @humanwhocodes/retry | 0.4.3 | Apache-2.0 | no | +| @img/colour | 1.1.0 | MIT | no | +| @img/sharp-darwin-arm64 | 0.35.0 | Apache-2.0 | no | +| @img/sharp-darwin-x64 | 0.35.0 | Apache-2.0 | no | +| @img/sharp-freebsd-wasm32 | 0.35.0 | Apache-2.0 | no | +| @img/sharp-libvips-darwin-arm64 | 1.3.0 | LGPL-3.0-or-later | yes | +| @img/sharp-libvips-darwin-x64 | 1.3.0 | LGPL-3.0-or-later | yes | +| @img/sharp-libvips-linux-arm | 1.3.0 | LGPL-3.0-or-later | yes | +| @img/sharp-libvips-linux-arm64 | 1.3.0 | LGPL-3.0-or-later | yes | +| @img/sharp-libvips-linux-ppc64 | 1.3.0 | LGPL-3.0-or-later | yes | +| @img/sharp-libvips-linux-riscv64 | 1.3.0 | LGPL-3.0-or-later | yes | +| @img/sharp-libvips-linux-s390x | 1.3.0 | LGPL-3.0-or-later | yes | +| @img/sharp-libvips-linux-x64 | 1.3.0 | LGPL-3.0-or-later | yes | +| @img/sharp-libvips-linuxmusl-arm64 | 1.3.0 | LGPL-3.0-or-later | yes | +| @img/sharp-libvips-linuxmusl-x64 | 1.3.0 | LGPL-3.0-or-later | yes | +| @img/sharp-linux-arm | 0.35.0 | Apache-2.0 | no | +| @img/sharp-linux-arm64 | 0.35.0 | Apache-2.0 | no | +| @img/sharp-linux-ppc64 | 0.35.0 | Apache-2.0 | no | +| @img/sharp-linux-riscv64 | 0.35.0 | Apache-2.0 | no | +| @img/sharp-linux-s390x | 0.35.0 | Apache-2.0 | no | +| @img/sharp-linux-x64 | 0.35.0 | Apache-2.0 | no | +| @img/sharp-linuxmusl-arm64 | 0.35.0 | Apache-2.0 | no | +| @img/sharp-linuxmusl-x64 | 0.35.0 | Apache-2.0 | no | +| @img/sharp-wasm32 | 0.35.0 | Apache-2.0 AND LGPL-3.0-or-later AND MIT | yes | +| @img/sharp-webcontainers-wasm32 | 0.35.0 | Apache-2.0 | no | +| @img/sharp-win32-arm64 | 0.35.0 | Apache-2.0 AND LGPL-3.0-or-later | yes | +| @img/sharp-win32-ia32 | 0.35.0 | Apache-2.0 AND LGPL-3.0-or-later | yes | +| @img/sharp-win32-x64 | 0.35.0 | Apache-2.0 AND LGPL-3.0-or-later | yes | +| @jridgewell/gen-mapping | 0.3.13 | MIT | no | +| @jridgewell/remapping | 2.3.5 | MIT | no | +| @jridgewell/resolve-uri | 3.1.2 | MIT | no | +| @jridgewell/sourcemap-codec | 1.5.5 | MIT | no | +| @jridgewell/trace-mapping | 0.3.31 | MIT | no | +| @napi-rs/wasm-runtime | 1.1.6 | MIT | no | +| @next/env | 16.2.12 | MIT | no | +| @next/eslint-plugin-next | 16.2.12 | MIT | no | +| @next/swc-darwin-arm64 | 16.2.12 | MIT | no | +| @next/swc-darwin-x64 | 16.2.12 | MIT | no | +| @next/swc-linux-arm64-gnu | 16.2.12 | MIT | no | +| @next/swc-linux-arm64-musl | 16.2.12 | MIT | no | +| @next/swc-linux-x64-gnu | 16.2.12 | MIT | no | +| @next/swc-linux-x64-musl | 16.2.12 | MIT | no | +| @next/swc-win32-arm64-msvc | 16.2.12 | MIT | no | +| @next/swc-win32-x64-msvc | 16.2.12 | MIT | no | +| @nodelib/fs.scandir | 2.1.5 | MIT | no | +| @nodelib/fs.stat | 2.0.5 | MIT | no | +| @nodelib/fs.walk | 1.2.8 | MIT | no | +| @nolyfill/is-core-module | 1.0.39 | MIT | no | +| @oxc-project/types | 0.139.0 | MIT | no | +| @playwright/test | 1.62.0 | Apache-2.0 | no | +| @radix-ui/primitive | 1.1.7 | MIT | no | +| @radix-ui/react-collection | 1.1.15 | MIT | no | +| @radix-ui/react-compose-refs | 1.1.5 | MIT | no | +| @radix-ui/react-context | 1.2.2 | MIT | no | +| @radix-ui/react-direction | 1.1.4 | MIT | no | +| @radix-ui/react-id | 1.1.4 | MIT | no | +| @radix-ui/react-presence | 1.1.10 | MIT | no | +| @radix-ui/react-primitive | 2.1.10 | MIT | no | +| @radix-ui/react-roving-focus | 1.1.19 | MIT | no | +| @radix-ui/react-slot | 1.3.3 | MIT | no | +| @radix-ui/react-tabs | 1.1.21 | MIT | no | +| @radix-ui/react-use-callback-ref | 1.1.4 | MIT | no | +| @radix-ui/react-use-controllable-state | 1.2.6 | MIT | no | +| @radix-ui/react-use-effect-event | 0.0.5 | MIT | no | +| @radix-ui/react-use-is-hydrated | 0.1.3 | MIT | no | +| @radix-ui/react-use-layout-effect | 1.1.4 | MIT | no | +| @rolldown/binding-android-arm64 | 1.1.5 | MIT | no | +| @rolldown/binding-darwin-arm64 | 1.1.5 | MIT | no | +| @rolldown/binding-darwin-x64 | 1.1.5 | MIT | no | +| @rolldown/binding-freebsd-x64 | 1.1.5 | MIT | no | +| @rolldown/binding-linux-arm-gnueabihf | 1.1.5 | MIT | no | +| @rolldown/binding-linux-arm64-gnu | 1.1.5 | MIT | no | +| @rolldown/binding-linux-arm64-musl | 1.1.5 | MIT | no | +| @rolldown/binding-linux-ppc64-gnu | 1.1.5 | MIT | no | +| @rolldown/binding-linux-s390x-gnu | 1.1.5 | MIT | no | +| @rolldown/binding-linux-x64-gnu | 1.1.5 | MIT | no | +| @rolldown/binding-linux-x64-musl | 1.1.5 | MIT | no | +| @rolldown/binding-openharmony-arm64 | 1.1.5 | MIT | no | +| @rolldown/binding-wasm32-wasi | 1.1.5 | MIT | no | +| @rolldown/binding-win32-arm64-msvc | 1.1.5 | MIT | no | +| @rolldown/binding-win32-x64-msvc | 1.1.5 | MIT | no | +| @rolldown/pluginutils | 1.0.1 | MIT | no | +| @rtsao/scc | 1.1.0 | MIT | no | +| @standard-schema/spec | 1.1.0 | MIT | no | +| @swc/helpers | 0.5.15 | Apache-2.0 | no | +| @tailwindcss/node | 4.3.3 | MIT | no | +| @tailwindcss/oxide | 4.3.3 | MIT | no | +| @tailwindcss/oxide-android-arm64 | 4.3.3 | MIT | no | +| @tailwindcss/oxide-darwin-arm64 | 4.3.3 | MIT | no | +| @tailwindcss/oxide-darwin-x64 | 4.3.3 | MIT | no | +| @tailwindcss/oxide-freebsd-x64 | 4.3.3 | MIT | no | +| @tailwindcss/oxide-linux-arm-gnueabihf | 4.3.3 | MIT | no | +| @tailwindcss/oxide-linux-arm64-gnu | 4.3.3 | MIT | no | +| @tailwindcss/oxide-linux-arm64-musl | 4.3.3 | MIT | no | +| @tailwindcss/oxide-linux-x64-gnu | 4.3.3 | MIT | no | +| @tailwindcss/oxide-linux-x64-musl | 4.3.3 | MIT | no | +| @tailwindcss/oxide-wasm32-wasi | 4.3.3 | MIT | no | +| @tailwindcss/oxide-win32-arm64-msvc | 4.3.3 | MIT | no | +| @tailwindcss/oxide-win32-x64-msvc | 4.3.3 | MIT | no | +| @tailwindcss/postcss | 4.3.3 | MIT | no | +| @tybys/wasm-util | 0.10.3 | MIT | no | +| @types/chai | 5.2.3 | MIT | no | +| @types/deep-eql | 4.0.2 | MIT | no | +| @types/estree | 1.0.9 | MIT | no | +| @types/hammerjs | 2.0.46 | MIT | no | +| @types/json-schema | 7.0.15 | MIT | no | +| @types/json5 | 0.0.29 | MIT | no | +| @types/node | 26.1.2 | MIT | no | +| @types/react | 19.2.17 | MIT | no | +| @types/react-dom | 19.2.3 | MIT | no | +| @typescript-eslint/eslint-plugin | 8.65.0 | MIT | no | +| @typescript-eslint/parser | 8.65.0 | MIT | no | +| @typescript-eslint/project-service | 8.65.0 | MIT | no | +| @typescript-eslint/scope-manager | 8.65.0 | MIT | no | +| @typescript-eslint/tsconfig-utils | 8.65.0 | MIT | no | +| @typescript-eslint/type-utils | 8.65.0 | MIT | no | +| @typescript-eslint/types | 8.65.0 | MIT | no | +| @typescript-eslint/typescript-estree | 8.65.0 | MIT | no | +| @typescript-eslint/utils | 8.65.0 | MIT | no | +| @typescript-eslint/visitor-keys | 8.65.0 | MIT | no | +| @unrs/resolver-binding-android-arm-eabi | 1.12.2 | MIT | no | +| @unrs/resolver-binding-android-arm64 | 1.12.2 | MIT | no | +| @unrs/resolver-binding-darwin-arm64 | 1.12.2 | MIT | no | +| @unrs/resolver-binding-darwin-x64 | 1.12.2 | MIT | no | +| @unrs/resolver-binding-freebsd-x64 | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-arm-gnueabihf | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-arm-musleabihf | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-arm64-gnu | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-arm64-musl | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-loong64-gnu | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-loong64-musl | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-ppc64-gnu | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-riscv64-gnu | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-riscv64-musl | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-s390x-gnu | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-x64-gnu | 1.12.2 | MIT | no | +| @unrs/resolver-binding-linux-x64-musl | 1.12.2 | MIT | no | +| @unrs/resolver-binding-openharmony-arm64 | 1.12.2 | MIT | no | +| @unrs/resolver-binding-wasm32-wasi | 1.12.2 | MIT | no | +| @unrs/resolver-binding-win32-arm64-msvc | 1.12.2 | MIT | no | +| @unrs/resolver-binding-win32-ia32-msvc | 1.12.2 | MIT | no | +| @unrs/resolver-binding-win32-x64-msvc | 1.12.2 | MIT | no | +| @vitest/coverage-v8 | 4.1.10 | MIT | no | +| @vitest/expect | 4.1.10 | MIT | no | +| @vitest/mocker | 4.1.10 | MIT | no | +| @vitest/pretty-format | 4.1.10 | MIT | no | +| @vitest/runner | 4.1.10 | MIT | no | +| @vitest/snapshot | 4.1.10 | MIT | no | +| @vitest/spy | 4.1.10 | MIT | no | +| @vitest/utils | 4.1.10 | MIT | no | +| acorn | 8.17.0 | MIT | no | +| acorn-jsx | 5.3.2 | MIT | no | +| actions/cache | 55cc8345863c7cc4c66a329aec7e433d2d1c52a9 | NOASSERTION | yes | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/dependency-review-action | a1d282b36b6f3519aa1f3fc636f609c47dddb294 | NOASSERTION | yes | +| actions/setup-node | 820762786026740c76f36085b0efc47a31fe5020 | NOASSERTION | yes | +| actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | yes | +| aiohappyeyeballs | 2.7.1 | 0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0 | yes | +| aiohttp | 3.14.1 | Apache-2.0 AND MIT | no | +| aioimaplib | 2.0.1 | GPL-3.0 AND GPL-3.0-only AND GPL-3.0-or-later | yes | +| aiosignal | 1.4.0 | Apache-2.0 | no | +| aiosmtplib | 5.1.2 | MIT | no | +| ajv | 6.15.0 | MIT | no | +| alembic | 1.18.5 | MIT | no | +| annotated-doc | 0.0.4 | MIT | no | +| annotated-types | 0.7.0 | MIT | no | +| ansi-styles | 4.3.0 | MIT | no | +| anyio | 4.14.1 | MIT | no | +| anyio | 4.14.2 | MIT | no | +| argparse | 2.0.1 | Python-2.0 | no | +| aria-query | 5.3.2 | Apache-2.0 | no | +| array-buffer-byte-length | 1.0.2 | MIT | no | +| array-includes | 3.1.9 | MIT | no | +| array.prototype.findlast | 1.2.5 | MIT | no | +| array.prototype.findlastindex | 1.2.6 | MIT | no | +| array.prototype.flat | 1.3.3 | MIT | no | +| array.prototype.flatmap | 1.3.3 | MIT | no | +| array.prototype.tosorted | 1.1.4 | MIT | no | +| arraybuffer.prototype.slice | 1.0.4 | MIT | no | +| asgiref | 3.11.1 | BSD-3-Clause | no | +| assertion-error | 2.0.1 | MIT | no | +| ast-types-flow | 0.0.8 | MIT | no | +| ast-v8-to-istanbul | 1.0.4 | MIT | no | +| async-function | 1.0.0 | MIT | no | +| asyncpg | 0.31.0 | Apache-2.0 | no | +| attrs | 26.1.0 | MIT | no | +| available-typed-arrays | 1.0.7 | MIT | no | +| axe-core | 4.12.1 | MPL-2.0 | yes | +| axobject-query | 4.1.0 | Apache-2.0 | no | +| backoff | 2.2.1 | MIT | no | +| balanced-match | 4.0.4 | MIT | no | +| bandit | 1.9.4 | Apache-2.0 | no | +| baseline-browser-mapping | 2.11.5 | Apache-2.0 | no | +| bidi-js | 1.0.3 | MIT | no | +| brace-expansion | 5.0.9 | MIT | no | +| braces | 3.0.3 | MIT | no | +| browserslist | 4.28.7 | MIT | no | +| caido-sdk-client | 0.2.0 | MIT | no | +| caido-server-auth | 0.1.2 | NOASSERTION | yes | +| call-bind | 1.0.9 | MIT | no | +| call-bind-apply-helpers | 1.0.2 | MIT | no | +| call-bound | 1.0.4 | MIT | no | +| callsites | 3.1.0 | MIT | no | +| caniuse-lite | 1.0.30001806 | CC-BY-4.0 | no | +| certifi | 2026.6.17 | MPL-2.0 | yes | +| cffi | 2.0.0 | MIT-0 | no | +| cffi | 2.1.0 | MIT-0 | no | +| chai | 6.2.2 | MIT | no | +| chalk | 4.1.2 | MIT | no | +| charset-normalizer | 3.4.7 | MIT | no | +| charset-normalizer | 3.4.9 | MIT | no | +| class-variance-authority | 0.7.1 | Apache-2.0 | no | +| click | 8.4.2 | BSD-3-Clause | no | +| client-only | 0.0.1 | MIT | no | +| clsx | 2.1.1 | MIT | no | +| color-convert | 2.0.1 | MIT | no | +| color-name | 1.1.4 | MIT | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| component-emitter | 2.0.0 | MIT | no | +| convert-source-map | 2.0.0 | MIT | no | +| coverage | 7.15.1 | Apache-2.0 | no | +| cross-spawn | 7.0.6 | MIT | no | +| cryptography | 49.0.0 | BSD-3-Clause OR Apache-2.0 | no | +| cryptography | 50.0.0 | Apache-2.0 OR BSD-3-Clause | no | +| css-tree | 3.2.1 | MIT | no | +| csstype | 3.2.3 | MIT | no | +| cvss | 3.6 | LGPL-3.0 AND LGPL-3.0-only AND LGPL-3.0-or-later | yes | +| damerau-levenshtein | 1.0.8 | BSD-2-Clause | no | +| data-urls | 7.0.0 | MIT | no | +| data-view-buffer | 1.0.2 | MIT | no | +| data-view-byte-length | 1.0.2 | MIT | no | +| data-view-byte-offset | 1.0.1 | MIT | no | +| debug | 3.2.7 | MIT | no | +| debug | 4.4.3 | MIT | no | +| decimal.js | 10.6.0 | MIT | no | +| deep-is | 0.1.4 | MIT | no | +| define-data-property | 1.1.4 | MIT | no | +| define-properties | 1.2.1 | MIT | no | +| defusedxml | 0.7.1 | PSF-2.0 | no | +| detect-libc | 2.1.2 | Apache-2.0 | no | +| distro | 1.9.0 | Apache-2.0 | no | +| dnspython | 2.8.0 | ISC AND MPL-2.0 | yes | +| docker | 7.2.0 | Apache-2.0 | no | +| docker/build-push-action | 53b7df96c91f9c12dcc8a07bcb9ccacbed38856a | NOASSERTION | yes | +| docker/login-action | dbcb813823bdd20940b903addbd779551569679f | NOASSERTION | yes | +| docker/metadata-action | dc802804100637a589fabce1cb79ff13a1411302 | NOASSERTION | yes | +| docker/setup-buildx-action | bb05f3f5519dd87d3ba754cc423b652a5edd6d2c | NOASSERTION | yes | +| docker/setup-qemu-action | 96fe6ef7f33517b61c61be40b68a1882f3264fb8 | NOASSERTION | yes | +| docstring-parser | 0.18.0 | MIT | no | +| doctrine | 2.1.0 | Apache-2.0 AND BSD-2-Clause | no | +| dunder-proto | 1.0.1 | MIT | no | +| electron-to-chromium | 1.5.396 | ISC | no | +| email-validator | 2.3.0 | CC0-1.0 AND Unlicense | no | +| emoji-regex | 9.2.2 | MIT | no | +| enhanced-resolve | 5.24.3 | MIT | no | +| entities | 8.0.0 | BSD-2-Clause | no | +| es-abstract | 1.24.2 | MIT | no | +| es-abstract-get | 1.0.0 | MIT | no | +| es-define-property | 1.0.1 | MIT | no | +| es-errors | 1.3.0 | MIT | no | +| es-iterator-helpers | 1.4.0 | MIT | no | +| es-module-lexer | 2.3.0 | MIT | no | +| es-object-atoms | 1.1.2 | MIT | no | +| es-set-tostringtag | 2.1.0 | MIT | no | +| es-shim-unscopables | 1.1.0 | MIT | no | +| es-to-primitive | 1.3.4 | MIT | no | +| escalade | 3.2.0 | MIT | no | +| escape-string-regexp | 4.0.0 | MIT | no | +| eslint | 9.39.5 | MIT | no | +| eslint-config-next | 16.2.12 | MIT | no | +| eslint-import-resolver-node | 0.3.10 | MIT | no | +| eslint-import-resolver-typescript | 3.10.1 | ISC | no | +| eslint-module-utils | 2.14.0 | MIT | no | +| eslint-plugin-import | 2.32.0 | MIT | no | +| eslint-plugin-jsx-a11y | 6.10.2 | MIT | no | +| eslint-plugin-react | 7.37.5 | MIT | no | +| eslint-plugin-react-hooks | 7.1.1 | MIT | no | +| eslint-scope | 8.4.0 | BSD-2-Clause | no | +| eslint-visitor-keys | 3.4.3 | Apache-2.0 | no | +| eslint-visitor-keys | 4.2.1 | Apache-2.0 | no | +| eslint-visitor-keys | 5.0.1 | Apache-2.0 | no | +| espree | 10.4.0 | BSD-2-Clause | no | +| esquery | 1.7.0 | BSD-3-Clause | no | +| esrecurse | 4.3.0 | BSD-2-Clause | no | +| estraverse | 5.3.0 | BSD-2-Clause | no | +| estree-walker | 3.0.3 | MIT | no | +| esutils | 2.0.3 | BSD-2-Clause | no | +| expect-type | 1.4.0 | Apache-2.0 | no | +| fast-check | 4.9.0 | MIT | no | +| fast-deep-equal | 3.1.3 | MIT | no | +| fast-glob | 3.3.1 | MIT | no | +| fast-json-stable-stringify | 2.1.0 | MIT | no | +| fast-levenshtein | 2.0.6 | MIT | no | +| fastapi | 0.138.2 | MIT | no | +| fastapi | 0.139.0 | MIT | no | +| fastq | 1.20.1 | ISC | no | +| fastuuid | 0.14.0 | BSD-2-Clause AND BSD-3-Clause | no | +| fdir | 6.5.0 | MIT | no | +| file-entry-cache | 8.0.0 | MIT | no | +| filelock | 3.29.7 | MIT | no | +| fill-range | 7.1.1 | MIT | no | +| find-up | 5.0.0 | MIT | no | +| flat-cache | 4.0.1 | MIT | no | +| flatted | 3.4.3 | ISC | no | +| for-each | 0.3.5 | MIT | no | +| frozenlist | 1.8.0 | Apache-2.0 | no | +| fsevents | 2.3.2 | MIT | no | +| fsevents | 2.3.3 | MIT | no | +| fsspec | 2026.6.0 | BSD-3-Clause | no | +| function-bind | 1.1.2 | MIT | no | +| function.prototype.name | 1.2.0 | MIT | no | +| functions-have-names | 1.2.3 | MIT | no | +| genai-prices | 0.0.71 | MIT | no | +| generator-function | 2.0.1 | MIT | no | +| gensync | 1.0.0-beta.2 | MIT | no | +| get-intrinsic | 1.3.0 | MIT | no | +| get-proto | 1.0.1 | MIT | no | +| get-symbol-description | 1.1.0 | MIT | no | +| get-tsconfig | 4.14.0 | MIT | no | +| github/codeql-action/upload-sarif | f205ea1c3313d32999d8d6a48b4f6530d4437b38 | NOASSERTION | yes | +| glob-parent | 5.1.2 | ISC | no | +| glob-parent | 6.0.2 | ISC | no | +| globals | 14.0.0 | MIT | no | +| globals | 16.4.0 | MIT | no | +| globalthis | 1.0.4 | MIT | no | +| google-api-core | 2.31.0 | Apache-2.0 | no | +| google-api-python-client | 2.198.0 | Apache-2.0 | no | +| google-auth | 2.55.1 | Apache-2.0 | no | +| google-auth | 2.55.2 | Apache-2.0 | no | +| google-auth-httplib2 | 0.4.0 | Apache-2.0 | no | +| google-auth-oauthlib | 1.4.0 | Apache-2.0 | no | +| google-cloud-aiplatform | 1.160.0 | Apache-2.0 | no | +| google-cloud-bigquery | 3.42.2 | Apache-2.0 | no | +| google-cloud-core | 2.6.0 | Apache-2.0 | no | +| google-cloud-resource-manager | 1.18.0 | Apache-2.0 | no | +| google-cloud-storage | 3.12.1 | Apache-2.0 | no | +| google-crc32c | 1.8.0 | Apache-2.0 | no | +| google-genai | 2.11.0 | Apache-2.0 | no | +| google-resumable-media | 2.10.0 | Apache-2.0 | no | +| googleapis-common-protos | 1.75.0 | Apache-2.0 | no | +| gopd | 1.2.0 | MIT | no | +| gql | 4.0.0 | MIT | no | +| graceful-fs | 4.2.11 | ISC | no | +| graphql-core | 3.2.11 | MIT | no | +| greenlet | 3.5.3 | MIT AND PSF-2.0 | no | +| griffelib | 2.1.0 | ISC | no | +| grpc-google-iam-v1 | 0.14.4 | Apache-2.0 | no | +| grpcio | 1.81.1 | Apache-2.0 AND BSD-3-Clause AND MPL-2.0 | yes | +| grpcio | 1.82.1 | Apache-2.0 AND BSD-3-Clause AND MPL-2.0 | yes | +| grpcio-status | 1.81.1 | Apache-2.0 AND BSD-3-Clause AND MPL-2.0 | yes | +| h11 | 0.16.0 | MIT | no | +| has-bigints | 1.1.0 | MIT | no | +| has-flag | 4.0.0 | MIT | no | +| has-property-descriptors | 1.0.2 | MIT | no | +| has-proto | 1.2.0 | MIT | no | +| has-symbols | 1.1.0 | MIT | no | +| has-tostringtag | 1.0.2 | MIT | no | +| hasown | 2.0.4 | MIT | no | +| hermes-estree | 0.25.1 | MIT | no | +| hermes-parser | 0.25.1 | MIT | no | +| hf-xet | 1.5.1 | Apache-2.0 | no | +| html-encoding-sniffer | 6.0.0 | MIT | no | +| html-escaper | 2.0.2 | MIT | no | +| httpcore | 1.0.9 | BSD-2-Clause AND BSD-3-Clause | no | +| httpcore2 | 2.5.0 | BSD-2-Clause AND BSD-3-Clause | no | +| httplib2 | 0.32.0 | MIT | no | +| httpx | 0.28.1 | BSD-3-Clause | no | +| httpx-sse | 0.4.3 | MIT | no | +| httpx2 | 2.5.0 | BSD-2-Clause AND BSD-3-Clause | no | +| huggingface-hub | 1.23.0 | Apache-2.0 | no | +| icalendar | 7.2.0 | BSD-2-Clause AND BSD-3-Clause | no | +| idna | 3.18 | BSD-3-Clause | no | +| ignore | 5.3.2 | MIT | no | +| ignore | 7.0.6 | MIT | no | +| import-fresh | 3.3.1 | MIT | no | +| importlib-metadata | 8.9.0 | Apache-2.0 | no | +| imurmurhash | 0.1.4 | MIT | no | +| iniconfig | 2.3.0 | MIT | no | +| internal-slot | 1.1.0 | MIT | no | +| is-array-buffer | 3.0.5 | MIT | no | +| is-async-function | 2.1.1 | MIT | no | +| is-bigint | 1.1.0 | MIT | no | +| is-boolean-object | 1.2.2 | MIT | no | +| is-bun-module | 2.0.0 | MIT | no | +| is-callable | 1.2.7 | MIT | no | +| is-core-module | 2.16.2 | MIT | no | +| is-data-view | 1.0.2 | MIT | no | +| is-date-object | 1.1.0 | MIT | no | +| is-document.all | 1.0.0 | MIT | no | +| is-extglob | 2.1.1 | MIT | no | +| is-finalizationregistry | 1.1.1 | MIT | no | +| is-generator-function | 1.1.2 | MIT | no | +| is-glob | 4.0.3 | MIT | no | +| is-map | 2.0.3 | MIT | no | +| is-negative-zero | 2.0.3 | MIT | no | +| is-number | 7.0.0 | MIT | no | +| is-number-object | 1.1.1 | MIT | no | +| is-potential-custom-element-name | 1.0.1 | MIT | no | +| is-regex | 1.2.1 | MIT | no | +| is-set | 2.0.3 | MIT | no | +| is-shared-array-buffer | 1.0.4 | MIT | no | +| is-string | 1.1.1 | MIT | no | +| is-symbol | 1.1.1 | MIT | no | +| is-typed-array | 1.1.15 | MIT | no | +| is-weakmap | 2.0.2 | MIT | no | +| is-weakref | 1.1.1 | MIT | no | +| is-weakset | 2.0.4 | MIT | no | +| isarray | 2.0.5 | MIT | no | +| isexe | 2.0.0 | ISC | no | +| istanbul-lib-coverage | 3.2.2 | BSD-3-Clause | no | +| istanbul-lib-report | 3.0.1 | BSD-3-Clause | no | +| istanbul-reports | 3.2.0 | BSD-3-Clause | no | +| iterator.prototype | 1.1.5 | MIT | no | +| jinja2 | 3.1.6 | BSD-2-Clause AND BSD-3-Clause | no | +| jiter | 0.16.0 | MIT | no | +| jiti | 2.7.0 | MIT | no | +| js-tokens | 10.0.0 | MIT | no | +| js-tokens | 4.0.0 | MIT | no | +| js-yaml | 4.3.0 | MIT | no | +| jschema-to-python | 1.2.3 | MIT | no | +| jsdom | 30.0.1 | MIT | no | +| jsesc | 3.1.0 | MIT | no | +| json-buffer | 3.0.1 | MIT | no | +| json-schema-traverse | 0.4.1 | MIT | no | +| json-stable-stringify-without-jsonify | 1.0.1 | MIT | no | +| json5 | 1.0.2 | MIT | no | +| json5 | 2.2.3 | MIT | no | +| jsonpatch | 1.33 | BSD-3-Clause | no | +| jsonpickle | 4.1.2 | BSD-3-Clause | no | +| jsonpointer | 3.1.1 | BSD-3-Clause | no | +| jsonschema | 4.26.0 | MIT | no | +| jsonschema-specifications | 2025.9.1 | MIT | no | +| jsx-ast-utils | 3.3.5 | MIT | no | +| keycharm | 0.4.0 | Apache-2.0 AND MIT | no | +| keyv | 4.5.4 | MIT | no | +| langchain-core | 1.4.8 | MIT | no | +| langchain-core | 1.4.9 | MIT | no | +| langchain-protocol | 0.0.18 | MIT | no | +| langchain-text-splitters | 1.1.2 | MIT | no | +| langsmith | 0.10.1 | MIT | no | +| langsmith | 0.10.2 | MIT | no | +| langsmith | 0.9.4 | MIT | no | +| language-subtag-registry | 0.3.23 | CC0-1.0 | no | +| language-tags | 1.0.9 | MIT | no | +| levn | 0.4.1 | MIT | no | +| lightningcss | 1.32.0 | MPL-2.0 | yes | +| lightningcss | 1.33.0 | MPL-2.0 | yes | +| lightningcss-android-arm64 | 1.32.0 | MPL-2.0 | yes | +| lightningcss-android-arm64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-darwin-arm64 | 1.32.0 | MPL-2.0 | yes | +| lightningcss-darwin-arm64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-darwin-x64 | 1.32.0 | MPL-2.0 | yes | +| lightningcss-darwin-x64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-freebsd-x64 | 1.32.0 | MPL-2.0 | yes | +| lightningcss-freebsd-x64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm-gnueabihf | 1.32.0 | MPL-2.0 | yes | +| lightningcss-linux-arm-gnueabihf | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-gnu | 1.32.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-gnu | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-musl | 1.32.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-musl | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-gnu | 1.32.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-gnu | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-musl | 1.32.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-musl | 1.33.0 | MPL-2.0 | yes | +| lightningcss-win32-arm64-msvc | 1.32.0 | MPL-2.0 | yes | +| lightningcss-win32-arm64-msvc | 1.33.0 | MPL-2.0 | yes | +| lightningcss-win32-x64-msvc | 1.32.0 | MPL-2.0 | yes | +| lightningcss-win32-x64-msvc | 1.33.0 | MPL-2.0 | yes | +| linkify-it-py | 2.1.0 | MIT | no | +| litellm | 1.89.2 | MIT | no | +| locate-path | 6.0.0 | MIT | no | +| lodash.merge | 4.6.2 | MIT | no | +| logfire-api | 4.37.0 | MIT | no | +| loose-envify | 1.4.0 | MIT | no | +| lru-cache | 11.5.2 | BlueOak-1.0.0 | no | +| lru-cache | 5.1.1 | ISC | no | +| lucide-react | 1.27.0 | ISC | no | +| magic-string | 0.30.21 | MIT | no | +| magicast | 0.5.3 | MIT | no | +| make-dir | 4.0.0 | MIT | no | +| mako | 1.3.12 | MIT | no | +| markdown-it-py | 4.2.0 | MIT | no | +| markupsafe | 3.0.3 | BSD-3-Clause | no | +| math-intrinsics | 1.1.0 | MIT | no | +| mcp | 1.28.1 | MIT AND Python-2.0 | no | +| mdit-py-plugins | 0.6.1 | MIT | no | +| mdn-data | 2.27.1 | CC0-1.0 | no | +| mdurl | 0.1.2 | MIT | no | +| merge2 | 1.4.1 | MIT | no | +| micromatch | 4.0.8 | MIT | no | +| minimatch | 10.2.6 | BlueOak-1.0.0 | no | +| minimatch | 3.1.5 | ISC | no | +| minimist | 1.2.8 | MIT | no | +| ms | 2.1.3 | MIT | no | +| multidict | 6.7.1 | Apache-2.0 | no | +| nanoid | 3.3.18 | MIT | no | +| napi-postinstall | 0.3.4 | MIT | no | +| naruon-backend | 0.14.4 | NOASSERTION | yes | +| natural-compare | 1.4.0 | MIT | no | +| next | 16.2.12 | MIT | no | +| node-exports-info | 1.6.2 | MIT | no | +| node-releases | 2.0.51 | MIT | no | +| numpy | 2.5.0 | BSD-3-Clause | no | +| oauthlib | 3.3.1 | BSD-3-Clause | no | +| object-assign | 4.1.1 | MIT | no | +| object-inspect | 1.13.4 | MIT | no | +| object-keys | 1.1.1 | MIT | no | +| object.assign | 4.1.7 | MIT | no | +| object.entries | 1.1.9 | MIT | no | +| object.fromentries | 2.0.8 | MIT | no | +| object.groupby | 1.0.3 | MIT | no | +| object.values | 1.2.1 | MIT | no | +| obug | 2.1.3 | MIT | no | +| openai | 2.44.0 | Apache-2.0 | no | +| openai | 2.45.0 | Apache-2.0 | no | +| openai-agents | 0.14.6 | MIT | no | +| opentelemetry-api | 1.43.0 | Apache-2.0 | no | +| opentelemetry-exporter-otlp | 1.43.0 | Apache-2.0 | no | +| opentelemetry-exporter-otlp-proto-common | 1.43.0 | Apache-2.0 | no | +| opentelemetry-exporter-otlp-proto-grpc | 1.43.0 | Apache-2.0 | no | +| opentelemetry-exporter-otlp-proto-http | 1.43.0 | Apache-2.0 | no | +| opentelemetry-instrumentation | 0.64b0 | Apache-2.0 | no | +| opentelemetry-instrumentation-asgi | 0.64b0 | Apache-2.0 | no | +| opentelemetry-instrumentation-fastapi | 0.64b0 | Apache-2.0 | no | +| opentelemetry-proto | 1.43.0 | Apache-2.0 | no | +| opentelemetry-sdk | 1.43.0 | Apache-2.0 | no | +| opentelemetry-semantic-conventions | 0.64b0 | Apache-2.0 | no | +| opentelemetry-util-http | 0.64b0 | Apache-2.0 | no | +| optionator | 0.9.4 | MIT | no | +| orjson | 3.11.9 | Apache-2.0 AND MIT AND MPL-2.0 | yes | +| own-keys | 1.0.2 | MIT | no | +| p-limit | 3.1.0 | MIT | no | +| p-locate | 5.0.0 | MIT | no | +| packaging | 26.2 | Apache-2.0 AND BSD-2-Clause | no | +| parent-module | 1.0.1 | MIT | no | +| parse5 | 8.0.1 | MIT | no | +| path-exists | 4.0.0 | MIT | no | +| path-key | 3.1.1 | MIT | no | +| path-parse | 1.0.7 | MIT | no | +| pathe | 2.0.3 | MIT | no | +| pbr | 7.0.3 | Apache-2.0 | no | +| pgvector | 0.4.2 | MIT | no | +| pgvector | 0.5.0 | MIT | no | +| picocolors | 1.1.1 | ISC | no | +| picomatch | 2.3.2 | MIT | no | +| picomatch | 4.0.5 | MIT | no | +| platformdirs | 4.10.0 | MIT | no | +| playwright | 1.62.0 | Apache-2.0 | no | +| playwright-core | 1.62.0 | Apache-2.0 | no | +| pluggy | 1.6.0 | MIT | no | +| possible-typed-array-names | 1.1.0 | MIT | no | +| postcss | 8.5.24 | MIT | no | +| prelude-ls | 1.2.1 | MIT | no | +| prometheus-client | 0.25.0 | Apache-2.0 AND BSD-2-Clause | no | +| prometheus-fastapi-instrumentator | 8.0.2 | BSD-2-Clause AND BSD-3-Clause AND ISC AND MIT | no | +| prop-types | 15.8.1 | MIT | no | +| propcache | 0.5.2 | Apache-2.0 | no | +| proto-plus | 1.28.0 | Apache-2.0 | no | +| proto-plus | 1.28.1 | Apache-2.0 | no | +| protobuf | 6.33.6 | BSD-3-Clause AND LicenseRef-scancode-protobuf | no | +| protobuf | 7.35.1 | BSD-3-Clause AND LicenseRef-scancode-protobuf | no | +| punycode | 2.3.1 | MIT | no | +| pure-rand | 8.4.2 | MIT | no | +| pyasn1 | 0.6.3 | BSD-2-Clause AND BSD-3-Clause AND MIT | no | +| pyasn1 | 0.6.4 | BSD-2-Clause AND BSD-3-Clause AND MIT | no | +| pyasn1-modules | 0.4.2 | BSD-2-Clause AND BSD-3-Clause | no | +| pycparser | 3.0 | BSD-3-Clause | no | +| pydantic | 2.13.4 | MIT | no | +| pydantic-ai-slim | 2.9.0 | MIT | no | +| pydantic-core | 2.46.4 | MIT | no | +| pydantic-graph | 2.9.0 | MIT | no | +| pydantic-settings | 2.14.2 | MIT | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pyjwt | 2.13.0 | MIT | no | +| pyparsing | 3.3.2 | MIT AND Python-2.0 | no | +| pytest | 9.1.1 | MIT | no | +| pytest-asyncio | 1.4.0 | Apache-2.0 | no | +| python-dateutil | 2.9.0.post0 | Apache-2.0 OR BSD-3-Clause | no | +| python-dotenv | 1.2.2 | BSD-3-Clause | no | +| python-multipart | 0.0.32 | Apache-2.0 | no | +| pyyaml | 6.0.3 | MIT | no | +| queue-microtask | 1.2.3 | MIT | no | +| rankweave | 0.1.0 | NOASSERTION | yes | +| react | 19.2.8 | MIT | no | +| react-dom | 19.2.8 | MIT | no | +| react-is | 16.13.1 | MIT | no | +| react-resizable-panels | 4.12.2 | MIT | no | +| referencing | 0.37.0 | MIT | no | +| reflect.getprototypeof | 1.0.10 | MIT | no | +| regex | 2026.6.28 | CNRI-Python AND Apache-2.0 | no | +| regex | 2026.7.10 | CNRI-Python AND Apache-2.0 | no | +| regexp.prototype.flags | 1.5.4 | MIT | no | +| requests | 2.34.2 | Apache-2.0 | no | +| requests-oauthlib | 2.0.0 | ISC | no | +| requests-toolbelt | 1.0.0 | Apache-2.0 | no | +| require-from-string | 2.0.2 | MIT | no | +| reselect | 5.2.0 | MIT | no | +| resolve | 2.0.0-next.7 | MIT | no | +| resolve-from | 4.0.0 | MIT | no | +| resolve-pkg-maps | 1.0.0 | MIT | no | +| reusify | 1.1.0 | MIT | no | +| rich | 15.0.0 | MIT | no | +| rolldown | 1.1.5 | MIT | no | +| rpds-py | 2026.6.3 | MIT | no | +| ruff | 0.15.20 | MIT | no | +| ruff | 0.15.21 | MIT | no | +| run-parallel | 1.2.0 | MIT | no | +| safe-array-concat | 1.1.4 | MIT | no | +| safe-push-apply | 1.0.0 | MIT | no | +| safe-regex-test | 1.1.0 | MIT | no | +| sarif-om | 1.0.4 | MIT | no | +| saxes | 6.0.0 | ISC | no | +| scheduler | 0.27.0 | MIT | no | +| semver | 6.3.1 | ISC | no | +| semver | 7.8.5 | ISC | no | +| set-function-length | 1.2.2 | MIT | no | +| set-function-name | 2.0.2 | MIT | no | +| set-proto | 1.0.0 | MIT | no | +| setuptools | 82.0.1 | MIT | no | +| setuptools | 83.0.0 | MIT | no | +| sharp | 0.35.0 | Apache-2.0 | no | +| shebang-command | 2.0.0 | MIT | no | +| shebang-regex | 3.0.0 | MIT | no | +| side-channel | 1.1.1 | MIT | no | +| side-channel-list | 1.0.1 | MIT | no | +| side-channel-map | 1.0.1 | MIT | no | +| side-channel-weakmap | 1.0.2 | MIT | no | +| siginfo | 2.0.0 | ISC | no | +| six | 1.17.0 | MIT | no | +| sniffio | 1.3.1 | Apache-2.0 AND MIT | no | +| source-map-js | 1.2.1 | BSD-3-Clause | no | +| sqlalchemy | 2.0.51 | MIT | no | +| sse-starlette | 3.4.5 | BSD-3-Clause | no | +| stable-hash | 0.0.5 | MIT | no | +| stackback | 0.0.2 | MIT | no | +| starlette | 1.3.1 | BSD-3-Clause | no | +| std-env | 4.2.0 | MIT | no | +| step-security/harden-runner | bf7454d06d71f1098171f2acdf0cd4708d7b5920 | NOASSERTION | yes | +| stevedore | 5.9.0 | Apache-2.0 | no | +| stop-iteration-iterator | 1.1.0 | MIT | no | +| string.prototype.includes | 2.0.1 | MIT | no | +| string.prototype.matchall | 4.0.12 | MIT | no | +| string.prototype.repeat | 1.0.0 | MIT | no | +| string.prototype.trim | 1.2.11 | MIT | no | +| string.prototype.trimend | 1.0.10 | MIT | no | +| string.prototype.trimstart | 1.0.8 | MIT | no | +| strip-bom | 3.0.0 | MIT | no | +| strip-json-comments | 3.1.1 | MIT | no | +| strix-agent | 1.0.4 | Apache-2.0 | no | +| styled-jsx | 5.1.6 | MIT | no | +| supports-color | 7.2.0 | MIT | no | +| supports-preserve-symlinks-flag | 1.0.0 | MIT | no | +| symbol-tree | 3.2.4 | MIT | no | +| tailwind-merge | 3.6.0 | MIT | no | +| tailwindcss | 4.3.3 | MIT | no | +| tapable | 2.3.3 | MIT | no | +| tenacity | 9.1.4 | Apache-2.0 | no | +| textual | 8.2.8 | MIT | no | +| tiktoken | 0.13.0 | MIT | no | +| tinybench | 2.9.0 | MIT | no | +| tinyexec | 1.2.4 | MIT | no | +| tinyglobby | 0.2.17 | MIT | no | +| tinyrainbow | 3.1.0 | MIT | no | +| tldts | 7.4.9 | MIT | no | +| tldts-core | 7.4.9 | MIT | no | +| to-regex-range | 5.0.1 | MIT | no | +| tokenizers | 0.23.1 | Apache-2.0 | no | +| tough-cookie | 6.0.2 | BSD-3-Clause | no | +| tqdm | 4.68.3 | MIT AND MPL-2.0 | yes | +| tqdm | 4.68.4 | MIT AND MPL-2.0 | yes | +| tr46 | 6.0.0 | MIT | no | +| truststore | 0.10.4 | MIT | no | +| ts-api-utils | 2.5.0 | MIT | no | +| tsconfig-paths | 3.15.0 | MIT | no | +| tslib | 2.8.1 | 0BSD | no | +| tw-animate-css | 1.4.0 | MIT | no | +| type-check | 0.4.0 | MIT | no | +| typed-array-buffer | 1.0.3 | MIT | no | +| typed-array-byte-length | 1.0.3 | MIT | no | +| typed-array-byte-offset | 1.0.4 | MIT | no | +| typed-array-length | 1.0.8 | MIT | no | +| types-requests | 2.33.0.20260712 | Apache-2.0 AND MIT | no | +| typescript | 6.0.3 | Apache-2.0 | no | +| typescript-eslint | 8.65.0 | MIT | no | +| typing-extensions | 4.15.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| typing-inspection | 0.4.2 | MIT | no | +| tzdata | 2026.2 | Apache-2.0 | no | +| tzdata | 2026.3 | Apache-2.0 | no | +| uc-micro-py | 2.0.0 | MIT | no | +| unbox-primitive | 1.1.0 | MIT | no | +| undici | 8.9.0 | MIT | no | +| undici-types | 8.3.0 | MIT | no | +| unrs-resolver | 1.12.2 | Apache-2.0 AND MIT | no | +| update-browserslist-db | 1.2.3 | MIT | no | +| uri-js | 4.4.1 | BSD-2-Clause AND BSD-2-Clause-Views | no | +| uritemplate | 4.2.0 | Apache-2.0 OR BSD-3-Clause OR (Apache-2.0 AND BSD-3-Clause) | no | +| urllib3 | 2.7.0 | MIT | no | +| use-sync-external-store | 1.6.0 | MIT | no | +| uuid | 14.0.1 | MIT | no | +| uuid-utils | 0.16.2 | BSD-3-Clause | no | +| uuid-utils | 0.17.0 | BSD-3-Clause | no | +| uvicorn | 0.49.0 | BSD-3-Clause | no | +| uvicorn | 0.51.0 | BSD-3-Clause | no | +| vis-data | 8.0.4 | (Apache-2.0 OR MIT) | no | +| vis-network | 10.1.0 | MIT OR (Apache-2.0 AND MIT) | no | +| vis-util | 6.0.0 | (Apache-2.0 OR MIT) | no | +| vite | 8.1.4 | MIT | no | +| vitest | 4.1.10 | MIT | no | +| w3c-xmlserializer | 5.0.0 | MIT | no | +| webidl-conversions | 8.0.1 | BSD-2-Clause | no | +| websockets | 15.0.1 | BSD-3-Clause | no | +| websockets | 16.0 | BSD-3-Clause | no | +| websockets | 16.1 | BSD-3-Clause | no | +| whatwg-mimetype | 5.0.0 | MIT | no | +| whatwg-url | 16.0.1 | MIT | no | +| whatwg-url | 17.1.0 | MIT | no | +| wheel | 0.47.0 | MIT | no | +| which | 2.0.2 | ISC | no | +| which-boxed-primitive | 1.1.1 | MIT | no | +| which-builtin-type | 1.2.1 | MIT | no | +| which-collection | 1.0.2 | MIT | no | +| which-typed-array | 1.1.22 | MIT | no | +| why-is-node-running | 2.3.0 | MIT | no | +| word-wrap | 1.2.5 | MIT | no | +| wrapt | 2.2.2 | BSD-2-Clause AND BSD-3-Clause AND Python-2.0 AND Ruby | no | +| xml-name-validator | 5.0.0 | Apache-2.0 | no | +| xmlchars | 2.2.0 | MIT | no | +| xxhash | 3.8.0 | BSD-2-Clause | no | +| xxhash | 3.8.1 | BSD-2-Clause | no | +| yallist | 3.1.1 | ISC | no | +| yarl | 1.24.2 | Apache-2.0 | no | +| yocto-queue | 0.1.0 | MIT | no | +| zipp | 4.1.0 | MIT | no | +| zod | 4.4.3 | MIT | no | +| zod-validation-error | 4.0.2 | MIT | no | +| zstandard | 0.25.0 | BSD-3-Clause | no | + +### ContextualWisdomLab/newsdom-api + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/attest-build-provenance | 0f67c3f4856b2e3261c31976d6725780e5e4c373 | NOASSERTION | yes | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/dependency-review-action | a1d282b36b6f3519aa1f3fc636f609c47dddb294 | NOASSERTION | yes | +| actions/deploy-pages | cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 | NOASSERTION | yes | +| actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| altgraph | 0.17.5 | MIT AND MIT-0 | no | +| annotated-doc | 0.0.4 | MIT | no | +| annotated-types | 0.7.0 | MIT | no | +| anyio | 4.13.0 | MIT | no | +| astral-sh/setup-uv | c771a70e6277c0a99b617c7a806ffedaca235ff9 | NOASSERTION | yes | +| atheris | 3.0.0 | Apache-2.0 | no | +| atheris | 3.1.0 | Apache-2.0 | no | +| babel | 2.18.0 | BSD-3-Clause | no | +| backports-asyncio-runner | 1.2.0 | NOASSERTION | yes | +| backrefs | 5.9 | MIT | no | +| certifi | 2026.2.25 | MPL-2.0 | yes | +| charset-normalizer | 3.4.7 | MIT | no | +| click | 8.4.2 | BSD-3-Clause | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| coverage | 7.13.5 | Apache-2.0 | no | +| docker/build-push-action | 53b7df96c91f9c12dcc8a07bcb9ccacbed38856a | NOASSERTION | yes | +| docker/login-action | dbcb813823bdd20940b903addbd779551569679f | NOASSERTION | yes | +| docker/metadata-action | dc802804100637a589fabce1cb79ff13a1411302 | NOASSERTION | yes | +| docker/setup-buildx-action | bb05f3f5519dd87d3ba754cc423b652a5edd6d2c | NOASSERTION | yes | +| docker/setup-qemu-action | 96fe6ef7f33517b61c61be40b68a1882f3264fb8 | NOASSERTION | yes | +| exceptiongroup | 1.3.1 | MIT AND Python-2.0 | no | +| fastapi | 0.135.3 | MIT | no | +| ghp-import | 2.1.0 | Apache-2.0 | no | +| github/codeql-action/analyze | f205ea1c3313d32999d8d6a48b4f6530d4437b38 | NOASSERTION | yes | +| github/codeql-action/autobuild | f205ea1c3313d32999d8d6a48b4f6530d4437b38 | NOASSERTION | yes | +| github/codeql-action/init | f205ea1c3313d32999d8d6a48b4f6530d4437b38 | NOASSERTION | yes | +| github/codeql-action/upload-sarif | f205ea1c3313d32999d8d6a48b4f6530d4437b38 | NOASSERTION | yes | +| google/clusterfuzzlite/actions/build_fuzzers | 52ecc61cb587ee99c26825a112a21abf19c7448c | NOASSERTION | yes | +| google/clusterfuzzlite/actions/run_fuzzers | 52ecc61cb587ee99c26825a112a21abf19c7448c | NOASSERTION | yes | +| h11 | 0.16.0 | MIT | no | +| httpcore | 1.0.9 | BSD-2-Clause AND BSD-3-Clause | no | +| httpcore2 | 2.4.0 | BSD-2-Clause AND BSD-3-Clause | no | +| httpx | 0.28.1 | BSD-3-Clause | no | +| httpx2 | 2.4.0 | BSD-2-Clause AND BSD-3-Clause | no | +| idna | 3.18 | BSD-3-Clause | no | +| iniconfig | 2.3.0 | MIT | no | +| jinja2 | 3.1.6 | BSD-2-Clause AND BSD-3-Clause | no | +| macholib | 1.16.4 | MIT | no | +| markdown | 3.10.2 | BSD-3-Clause | no | +| markupsafe | 3.0.3 | BSD-3-Clause | no | +| mergedeep | 1.3.4 | MIT | no | +| mkdocs | 1.6.1 | BSD-2-Clause AND BSD-3-Clause | no | +| mkdocs-get-deps | 0.2.2 | MIT | no | +| mkdocs-material | 9.7.7 | MIT | no | +| mkdocs-material-extensions | 1.3.1 | MIT | no | +| newsdom-api | 0.2.0 | NOASSERTION | yes | +| ossf/scorecard-action | 2d1146689b8cda280b9bc96326124645441f03bc | NOASSERTION | yes | +| packaging | 26.0 | Apache-2.0 AND BSD-2-Clause | no | +| paginate | 0.5.7 | MIT | no | +| pathspec | 1.0.4 | MPL-2.0 | yes | +| pefile | 2023.2.7 | MIT | no | +| pillow | 12.3.0 | MIT-CMU | no | +| platformdirs | 4.9.6 | MIT | no | +| pluggy | 1.6.0 | MIT | no | +| pydantic | 2.12.5 | MIT | no | +| pydantic-core | 2.41.5 | MIT | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pyinstaller | 6.21.0 | GPL-2.0-only AND GPL-2.0-or-later | yes | +| pyinstaller-hooks-contrib | 2026.6 | Apache-2.0 AND GPL-1.0-or-later AND GPL-2.0-only AND GPL-2.0-or-later | yes | +| pymdown-extensions | 11.0.1 | BSD-3-Clause AND MIT | no | +| pypdf | 6.15.0 | BSD-3-Clause | no | +| pytest | 9.0.3 | MIT | no | +| pytest-asyncio | 1.3.0 | Apache-2.0 | no | +| pytest-cov | 7.1.0 | MIT | no | +| python-dateutil | 2.9.0.post0 | Apache-2.0 OR BSD-3-Clause | no | +| python-multipart | 0.0.31 | Apache-2.0 | no | +| pywin32-ctypes | 0.2.3 | BSD-3-Clause | no | +| pyyaml | 6.0.3 | MIT | no | +| pyyaml-env-tag | 1.1 | MIT | no | +| reportlab | 4.4.10 | BSD-2-Clause AND BSD-3-Clause | no | +| requests | 2.33.1 | Apache-2.0 | no | +| setuptools | 83.0.0 | MIT | no | +| six | 1.17.0 | MIT | no | +| starlette | 1.3.1 | BSD-3-Clause | no | +| tomli | 2.4.1 | MIT | no | +| truststore | 0.10.4 | MIT | no | +| typing-extensions | 4.15.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| typing-inspection | 0.4.2 | MIT | no | +| urllib3 | 2.7.0 | MIT | no | +| uvicorn | 0.44.0 | BSD-3-Clause | no | +| watchdog | 6.0.0 | Apache-2.0 AND Python-2.0 | no | + +### ContextualWisdomLab/noema + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| @babel/helper-string-parser | 7.29.7 | MIT | no | +| @babel/helper-validator-identifier | 7.29.7 | MIT | no | +| @babel/parser | 7.29.7 | MIT | no | +| @babel/types | 7.29.7 | MIT | no | +| @bcoe/v8-coverage | 1.0.2 | ISC AND MIT | no | +| @cloudflare/workerd-darwin-64 | 1.20260625.1 | Apache-2.0 | no | +| @cloudflare/workerd-darwin-arm64 | 1.20260625.1 | Apache-2.0 | no | +| @cloudflare/workerd-linux-64 | 1.20260625.1 | Apache-2.0 | no | +| @cloudflare/workerd-linux-arm64 | 1.20260625.1 | Apache-2.0 | no | +| @cloudflare/workerd-windows-64 | 1.20260625.1 | Apache-2.0 | no | +| @cloudflare/workers-types | 4.20260630.1 | MIT OR Apache-2.0 | no | +| @colbymchenry/codegraph | 1.4.1 | MIT | no | +| @colbymchenry/codegraph-darwin-arm64 | 1.4.1 | MIT | no | +| @colbymchenry/codegraph-darwin-x64 | 1.4.1 | MIT | no | +| @colbymchenry/codegraph-linux-arm64 | 1.4.1 | MIT | no | +| @colbymchenry/codegraph-linux-x64 | 1.4.1 | MIT | no | +| @colbymchenry/codegraph-win32-arm64 | 1.4.1 | MIT | no | +| @colbymchenry/codegraph-win32-x64 | 1.4.1 | MIT | no | +| @emnapi/core | 2.0.0-alpha.4 | MIT | no | +| @emnapi/runtime | 2.0.0-alpha.4 | MIT | no | +| @emnapi/wasi-threads | 2.0.1 | MIT | no | +| @esbuild/aix-ppc64 | 0.28.1 | MIT | no | +| @esbuild/android-arm | 0.28.1 | MIT | no | +| @esbuild/android-arm64 | 0.28.1 | MIT | no | +| @esbuild/android-x64 | 0.28.1 | MIT | no | +| @esbuild/darwin-arm64 | 0.28.1 | MIT | no | +| @esbuild/darwin-x64 | 0.28.1 | MIT | no | +| @esbuild/freebsd-arm64 | 0.28.1 | MIT | no | +| @esbuild/freebsd-x64 | 0.28.1 | MIT | no | +| @esbuild/linux-arm | 0.28.1 | MIT | no | +| @esbuild/linux-arm64 | 0.28.1 | MIT | no | +| @esbuild/linux-ia32 | 0.28.1 | MIT | no | +| @esbuild/linux-loong64 | 0.28.1 | MIT | no | +| @esbuild/linux-mips64el | 0.28.1 | MIT | no | +| @esbuild/linux-ppc64 | 0.28.1 | MIT | no | +| @esbuild/linux-riscv64 | 0.28.1 | MIT | no | +| @esbuild/linux-s390x | 0.28.1 | MIT | no | +| @esbuild/linux-x64 | 0.28.1 | MIT | no | +| @esbuild/netbsd-arm64 | 0.28.1 | MIT | no | +| @esbuild/netbsd-x64 | 0.28.1 | MIT | no | +| @esbuild/openbsd-arm64 | 0.28.1 | MIT | no | +| @esbuild/openbsd-x64 | 0.28.1 | MIT | no | +| @esbuild/openharmony-arm64 | 0.28.1 | MIT | no | +| @esbuild/sunos-x64 | 0.28.1 | MIT | no | +| @esbuild/win32-arm64 | 0.28.1 | MIT | no | +| @esbuild/win32-ia32 | 0.28.1 | MIT | no | +| @esbuild/win32-x64 | 0.28.1 | MIT | no | +| @jridgewell/resolve-uri | 3.1.2 | MIT | no | +| @jridgewell/sourcemap-codec | 1.5.5 | MIT | no | +| @jridgewell/trace-mapping | 0.3.31 | MIT | no | +| @napi-rs/wasm-runtime | 1.2.3 | MIT | no | +| @oxc-project/types | 0.148.0 | MIT | no | +| @rolldown/binding-android-arm-eabi | 1.2.7 | MIT | no | +| @rolldown/binding-android-arm64 | 1.2.7 | MIT | no | +| @rolldown/binding-darwin-arm64 | 1.2.7 | MIT | no | +| @rolldown/binding-darwin-x64 | 1.2.7 | MIT | no | +| @rolldown/binding-freebsd-x64 | 1.2.7 | MIT | no | +| @rolldown/binding-linux-arm-gnueabihf | 1.2.7 | MIT | no | +| @rolldown/binding-linux-arm64-gnu | 1.2.7 | MIT | no | +| @rolldown/binding-linux-arm64-musl | 1.2.7 | MIT | no | +| @rolldown/binding-linux-ppc64-gnu | 1.2.7 | MIT | no | +| @rolldown/binding-linux-s390x-gnu | 1.2.7 | MIT | no | +| @rolldown/binding-linux-x64-gnu | 1.2.7 | MIT | no | +| @rolldown/binding-linux-x64-musl | 1.2.7 | MIT | no | +| @rolldown/binding-openharmony-arm64 | 1.2.7 | MIT | no | +| @rolldown/binding-wasm32-wasi | 1.2.7 | MIT | no | +| @rolldown/binding-win32-arm64-msvc | 1.2.7 | MIT | no | +| @rolldown/binding-win32-x64-msvc | 1.2.7 | MIT | no | +| @rolldown/pluginutils | 1.0.1 | MIT | no | +| @standard-schema/spec | 1.1.0 | MIT | no | +| @tybys/wasm-util | 0.10.3 | MIT | no | +| @types/chai | 5.2.3 | MIT | no | +| @types/deep-eql | 4.0.2 | MIT | no | +| @types/estree | 1.0.9 | MIT | no | +| @vitest/coverage-v8 | 4.1.11 | MIT | no | +| @vitest/expect | 4.1.11 | MIT | no | +| @vitest/mocker | 4.1.11 | MIT | no | +| @vitest/pretty-format | 4.1.11 | MIT | no | +| @vitest/runner | 4.1.11 | MIT | no | +| @vitest/snapshot | 4.1.11 | MIT | no | +| @vitest/spy | 4.1.11 | MIT | no | +| @vitest/utils | 4.1.11 | MIT | no | +| actions/attest | 59d89421af93a897026c735860bf21b6eb4f7b26 | NOASSERTION | yes | +| actions/checkout | 11bd71901bbe5b1630ceea73d27597364c9af683 | NOASSERTION | yes | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/checkout | de0fac2e4500dabe0009e67214ff5f5447ce83dd | NOASSERTION | yes | +| actions/create-github-app-token | bcd2ba49218906704ab6c1aa796996da409d3eb1 | NOASSERTION | yes | +| actions/download-artifact | d3f86a106a0bac45b974a628896c90dbdf5c8093 | NOASSERTION | yes | +| actions/setup-node | 48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e | NOASSERTION | yes | +| actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | yes | +| actions/setup-python | a26af69be951a213d495a4c3e4e4022e16d87065 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| annotated-types | 0.7.0 | MIT | no | +| anyio | 4.14.2 | MIT | no | +| aquasecurity/setup-trivy | 81e514348e19b6112ce2a7e3ecbafe19c1e1f567 | NOASSERTION | yes | +| assertion-error | 2.0.1 | MIT | no | +| ast-v8-to-istanbul | 1.0.4 | MIT | no | +| attrs | 26.1.0 | MIT | no | +| certifi | 2026.6.17 | MPL-2.0 | yes | +| chai | 6.2.2 | MIT | no | +| charset-normalizer | 3.4.9 | MIT | no | +| click | 8.4.2 | BSD-3-Clause | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| convert-source-map | 2.0.0 | MIT | no | +| coverage | 7.15.0 | Apache-2.0 | no | +| detect-libc | 2.1.2 | Apache-2.0 | no | +| distro | 1.9.0 | Apache-2.0 | no | +| docker/build-push-action | d08e5c354a6adb9ed34480a06d141179aa583294 | NOASSERTION | yes | +| docker/setup-buildx-action | 37fe631027851001ddb9b187196cc803df7f5f0e | NOASSERTION | yes | +| es-module-lexer | 2.3.2 | MIT | no | +| esbuild | 0.28.1 | MIT | no | +| estree-walker | 3.0.3 | MIT | no | +| expect-type | 1.4.0 | Apache-2.0 | no | +| fdir | 6.5.0 | MIT | no | +| fsevents | 2.3.3 | MIT | no | +| genai-prices | 0.0.71 | MIT | no | +| griffelib | 2.1.0 | ISC | no | +| h11 | 0.16.0 | MIT | no | +| has-flag | 4.0.0 | MIT | no | +| html-escaper | 2.0.2 | MIT | no | +| httpcore | 1.0.9 | BSD-2-Clause AND BSD-3-Clause | no | +| httpcore2 | 2.12.0 | BSD-3-Clause | no | +| httpx | 0.28.1 | BSD-3-Clause | no | +| httpx2 | 2.12.0 | BSD-3-Clause | no | +| idna | 3.18 | BSD-3-Clause | no | +| iniconfig | 2.3.0 | MIT | no | +| interrogate | — | NOASSERTION | yes | +| interrogate | 1.7.0 | MIT | no | +| istanbul-lib-coverage | 3.2.2 | BSD-3-Clause | no | +| istanbul-lib-report | 3.0.1 | BSD-3-Clause | no | +| istanbul-reports | 3.2.0 | BSD-3-Clause | no | +| jiter | 0.16.0 | MIT | no | +| js-tokens | 10.0.0 | MIT | no | +| lightningcss | 1.33.0 | MPL-2.0 | yes | +| lightningcss-android-arm64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-darwin-arm64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-darwin-x64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-freebsd-x64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm-gnueabihf | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-gnu | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-musl | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-gnu | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-musl | 1.33.0 | MPL-2.0 | yes | +| lightningcss-win32-arm64-msvc | 1.33.0 | MPL-2.0 | yes | +| lightningcss-win32-x64-msvc | 1.33.0 | MPL-2.0 | yes | +| logfire-api | 4.37.0 | MIT | no | +| magic-string | 0.30.21 | MIT | no | +| magicast | 0.5.3 | MIT | no | +| make-dir | 4.0.0 | MIT | no | +| nanoid | 3.3.18 | MIT | no | +| obug | 2.1.4 | MIT | no | +| openai | 2.45.0 | Apache-2.0 | no | +| opentelemetry-api | 1.43.0 | Apache-2.0 | no | +| packaging | 26.2 | Apache-2.0 AND BSD-2-Clause | no | +| pathe | 2.0.3 | MIT | no | +| picocolors | 1.1.1 | ISC | no | +| picomatch | 4.0.4 | MIT | no | +| picomatch | 4.0.7 | MIT | no | +| pluggy | 1.6.0 | MIT | no | +| postcss | 8.5.28 | MIT | no | +| py | 1.11.0 | MIT | no | +| pydantic | — | NOASSERTION | yes | +| pydantic | 2.13.4 | MIT | no | +| pydantic-ai-slim | — | NOASSERTION | yes | +| pydantic-ai-slim | 2.9.0 | MIT | no | +| pydantic-core | 2.46.4 | MIT | no | +| pydantic-graph | 2.9.0 | MIT | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pytest | — | NOASSERTION | yes | +| pytest | 9.1.1 | MIT | no | +| pytest-cov | — | NOASSERTION | yes | +| pytest-cov | 7.1.0 | MIT | no | +| regex | 2026.7.10 | CNRI-Python AND Apache-2.0 | no | +| requests | 2.34.2 | Apache-2.0 | no | +| rolldown | 1.2.7 | MIT | no | +| semver | 7.8.5 | ISC | no | +| setuptools | — | NOASSERTION | yes | +| siginfo | 2.0.0 | ISC | no | +| sigstore/cosign-installer | 6f9f17788090df1f26f669e9d70d6ae9567deba6 | NOASSERTION | yes | +| sniffio | 1.3.1 | Apache-2.0 AND MIT | no | +| source-map-js | 1.2.1 | BSD-3-Clause | no | +| stackback | 0.0.2 | MIT | no | +| std-env | 4.2.0 | MIT | no | +| supports-color | 7.2.0 | MIT | no | +| tabulate | 0.10.0 | MIT | no | +| tiktoken | 0.13.0 | MIT | no | +| tinybench | 2.9.0 | MIT | no | +| tinyexec | 1.3.1 | MIT | no | +| tinyglobby | 0.2.17 | MIT | no | +| tinyrainbow | 3.1.1 | MIT | no | +| tomli | 2.4.1 | MIT | no | +| tqdm | 4.68.4 | MIT AND MPL-2.0 | yes | +| truststore | 0.10.4 | MIT | no | +| tslib | 2.8.1 | 0BSD | no | +| typescript | 5.9.3 | Apache-2.0 | no | +| typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| typing-inspection | 0.4.2 | MIT | no | +| urllib3 | 2.7.0 | MIT | no | +| vite | 8.2.2 | MIT | no | +| vitest | 4.1.11 | MIT | no | +| why-is-node-running | 2.3.0 | MIT | no | +| workerd | 1.20260625.1 | Apache-2.0 | no | + +### ContextualWisdomLab/Orgmetra + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/OriginWeave + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | yes | +| asn1-rs | 0.7.2 | MIT OR Apache-2.0 | no | +| asn1-rs-derive | 0.6.0 | MIT OR Apache-2.0 | no | +| asn1-rs-impl | 0.2.0 | MIT OR Apache-2.0 | no | +| autocfg | 1.5.1 | Apache-2.0 OR MIT | no | +| base64 | 0.22.1 | MIT OR Apache-2.0 | no | +| bit-vec | 0.9.1 | Apache-2.0 OR MIT | no | +| block-buffer | 0.10.4 | MIT OR Apache-2.0 | no | +| cc | 1.4.0 | MIT OR Apache-2.0 | no | +| cfg-if | 1.0.4 | MIT OR Apache-2.0 | no | +| cpufeatures | 0.2.17 | MIT OR Apache-2.0 | no | +| crypto-common | 0.1.7 | MIT OR Apache-2.0 | no | +| data-encoding | 2.11.1 | MIT | no | +| der-parser | 10.0.0 | MIT OR Apache-2.0 | no | +| deranged | 0.5.8 | MIT OR Apache-2.0 | no | +| digest | 0.10.7 | MIT OR Apache-2.0 | no | +| displaydoc | 0.2.7 | MIT OR Apache-2.0 | no | +| dtolnay/rust-toolchain | 4be7066ada62dd38de10e7b70166bc74ed198c30 | NOASSERTION | yes | +| find-msvc-tools | 0.1.9 | MIT OR Apache-2.0 | no | +| generic-array | 0.14.7 | MIT | no | +| getrandom | 0.2.17 | MIT OR Apache-2.0 | no | +| lazy_static | 1.5.0 | MIT OR Apache-2.0 | no | +| libc | 0.2.189 | MIT OR Apache-2.0 | no | +| memchr | 2.8.3 | Unlicense OR MIT | no | +| minimal-lexical | 0.2.1 | MIT OR Apache-2.0 | no | +| nom | 7.1.3 | MIT | no | +| num-bigint | 0.4.8 | MIT OR Apache-2.0 | no | +| num-conv | 0.2.2 | MIT OR Apache-2.0 | no | +| num-integer | 0.1.46 | MIT OR Apache-2.0 | no | +| num-traits | 0.2.19 | MIT OR Apache-2.0 | no | +| oid-registry | 0.8.1 | MIT OR Apache-2.0 | no | +| once_cell | 1.21.4 | MIT OR Apache-2.0 | no | +| pem | 3.0.6 | MIT | no | +| powerfmt | 0.2.0 | MIT OR Apache-2.0 | no | +| proc-macro2 | 1.0.107 | MIT OR Apache-2.0 | no | +| quote | 1.0.47 | MIT OR Apache-2.0 | no | +| rcgen | 0.14.8 | MIT OR Apache-2.0 | no | +| ring | 0.17.14 | Apache-2.0 AND ISC | no | +| rusticata-macros | 4.1.0 | MIT OR Apache-2.0 | no | +| rustls | 0.23.42 | Apache-2.0 OR ISC OR MIT | no | +| rustls-pki-types | 1.15.1 | MIT OR Apache-2.0 | no | +| rustls-webpki | 0.103.13 | ISC | no | +| serde | 1.0.229 | MIT OR Apache-2.0 | no | +| serde_core | 1.0.229 | MIT OR Apache-2.0 | no | +| serde_derive | 1.0.229 | MIT OR Apache-2.0 | no | +| sha2 | 0.10.9 | MIT OR Apache-2.0 | no | +| shlex | 2.0.1 | MIT OR Apache-2.0 | no | +| step-security/harden-runner | bf7454d06d71f1098171f2acdf0cd4708d7b5920 | NOASSERTION | yes | +| subtle | 2.6.1 | BSD-3-Clause | no | +| syn | 2.0.119 | MIT OR Apache-2.0 | no | +| syn | 3.0.3 | MIT OR Apache-2.0 | no | +| synstructure | 0.13.2 | MIT | no | +| thiserror | 2.0.19 | MIT OR Apache-2.0 | no | +| thiserror-impl | 2.0.19 | MIT OR Apache-2.0 | no | +| time | 0.3.55 | MIT OR Apache-2.0 | no | +| time-core | 0.1.9 | MIT OR Apache-2.0 | no | +| time-macros | 0.2.32 | MIT OR Apache-2.0 | no | +| tinyvec | 1.10.0 | Zlib OR Apache-2.0 OR MIT | no | +| tinyvec_macros | 0.1.1 | MIT OR Apache-2.0 OR Zlib | no | +| typenum | 1.20.1 | MIT OR Apache-2.0 | no | +| unicode-ident | 1.0.24 | (MIT OR Apache-2.0) AND Unicode-3.0 | no | +| unicode-normalization | 0.1.25 | MIT OR Apache-2.0 | no | +| untrusted | 0.9.0 | ISC | no | +| version_check | 0.9.5 | MIT OR Apache-2.0 | no | +| wasi | 0.11.1+wasi-snapshot-preview1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| windows-sys | 0.52.0 | MIT OR Apache-2.0 | no | +| windows-targets | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_aarch64_gnullvm | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_aarch64_msvc | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_i686_gnu | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_i686_gnullvm | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_i686_msvc | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnu | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnullvm | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_x86_64_msvc | 0.52.6 | MIT OR Apache-2.0 | no | +| x509-parser | 0.18.1 | MIT OR Apache-2.0 | no | +| yasna | 0.6.0 | MIT OR Apache-2.0 | no | +| zeroize | 1.9.0 | Apache-2.0 OR MIT | no | + +### ContextualWisdomLab/pg-erd-cloud + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| @adobe/css-tools | 4.5.0 | MIT | no | +| @asamuzakjp/css-color | 6.0.5 | MIT | no | +| @asamuzakjp/dom-selector | 8.3.2 | MIT | no | +| @babel/code-frame | 7.29.7 | MIT | no | +| @babel/helper-string-parser | 7.29.7 | MIT | no | +| @babel/helper-validator-identifier | 7.29.7 | MIT | no | +| @babel/parser | 7.29.7 | MIT | no | +| @babel/runtime | 7.29.7 | MIT | no | +| @babel/types | 7.29.7 | MIT | no | +| @bcoe/v8-coverage | 1.0.2 | ISC AND MIT | no | +| @bramus/specificity | 2.4.2 | MIT | no | +| @csstools/color-helpers | 6.1.0 | MIT-0 | no | +| @csstools/css-calc | 3.3.0 | MIT | no | +| @csstools/css-color-parser | 4.1.10 | MIT | no | +| @csstools/css-parser-algorithms | 4.0.0 | MIT | no | +| @csstools/css-syntax-patches-for-csstree | 1.1.7 | MIT-0 | no | +| @csstools/css-tokenizer | 4.0.0 | MIT | no | +| @emnapi/core | 2.0.0-alpha.3 | MIT | no | +| @emnapi/runtime | 2.0.0-alpha.3 | MIT | no | +| @emnapi/wasi-threads | 2.0.1 | MIT | no | +| @exodus/bytes | 1.15.1 | MIT | no | +| @jridgewell/resolve-uri | 3.1.2 | MIT | no | +| @jridgewell/sourcemap-codec | 1.5.5 | MIT | no | +| @jridgewell/trace-mapping | 0.3.31 | MIT | no | +| @napi-rs/wasm-runtime | 1.2.2 | MIT | no | +| @oxc-project/types | 0.142.0 | MIT | no | +| @rolldown/binding-android-arm64 | 1.2.1 | MIT | no | +| @rolldown/binding-darwin-arm64 | 1.2.1 | MIT | no | +| @rolldown/binding-darwin-x64 | 1.2.1 | MIT | no | +| @rolldown/binding-freebsd-x64 | 1.2.1 | MIT | no | +| @rolldown/binding-linux-arm-gnueabihf | 1.2.1 | MIT | no | +| @rolldown/binding-linux-arm64-gnu | 1.2.1 | MIT | no | +| @rolldown/binding-linux-arm64-musl | 1.2.1 | MIT | no | +| @rolldown/binding-linux-ppc64-gnu | 1.2.1 | MIT | no | +| @rolldown/binding-linux-s390x-gnu | 1.2.1 | MIT | no | +| @rolldown/binding-linux-x64-gnu | 1.2.1 | MIT | no | +| @rolldown/binding-linux-x64-musl | 1.2.1 | MIT | no | +| @rolldown/binding-openharmony-arm64 | 1.2.1 | MIT | no | +| @rolldown/binding-wasm32-wasi | 1.2.1 | MIT | no | +| @rolldown/binding-win32-arm64-msvc | 1.2.1 | MIT | no | +| @rolldown/binding-win32-x64-msvc | 1.2.1 | MIT | no | +| @rolldown/pluginutils | 1.0.1 | MIT | no | +| @standard-schema/spec | 1.1.0 | MIT | no | +| @testing-library/dom | 10.4.1 | MIT | no | +| @testing-library/jest-dom | 6.9.1 | MIT | no | +| @testing-library/react | 16.3.2 | MIT | no | +| @testing-library/user-event | 14.6.4 | MIT | no | +| @tybys/wasm-util | 0.10.3 | MIT | no | +| @types/aria-query | 5.0.4 | MIT | no | +| @types/chai | 5.2.3 | MIT | no | +| @types/d3-color | 3.1.3 | MIT | no | +| @types/d3-drag | 3.0.7 | MIT | no | +| @types/d3-interpolate | 3.0.4 | MIT | no | +| @types/d3-selection | 3.0.11 | MIT | no | +| @types/d3-transition | 3.0.9 | MIT | no | +| @types/d3-zoom | 3.0.8 | MIT | no | +| @types/deep-eql | 4.0.2 | MIT | no | +| @types/estree | 1.0.9 | MIT | no | +| @types/react | 19.2.18 | MIT | no | +| @types/react-dom | 19.2.4 | MIT | no | +| @vitest/coverage-v8 | 4.1.10 | MIT | no | +| @vitest/expect | 4.1.10 | MIT | no | +| @vitest/mocker | 4.1.10 | MIT | no | +| @vitest/pretty-format | 4.1.10 | MIT | no | +| @vitest/runner | 4.1.10 | MIT | no | +| @vitest/snapshot | 4.1.10 | MIT | no | +| @vitest/spy | 4.1.10 | MIT | no | +| @vitest/utils | 4.1.10 | MIT | no | +| @xyflow/react | 12.11.3 | MIT | no | +| @xyflow/system | 0.0.80 | MIT | no | +| actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | yes | +| actions/setup-node | 48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e | NOASSERTION | yes | +| actions/setup-python | ece7cb06caefa5fff74198d8649806c4678c61a1 | NOASSERTION | yes | +| aiohttp | — | NOASSERTION | yes | +| alembic | — | NOASSERTION | yes | +| ansi-regex | 5.0.1 | MIT | no | +| ansi-styles | 5.2.0 | MIT | no | +| aria-query | 5.3.0 | Apache-2.0 | no | +| assertion-error | 2.0.1 | MIT | no | +| ast-v8-to-istanbul | 1.0.4 | MIT | no | +| asyncpg | — | NOASSERTION | yes | +| asyncpg-stubs | — | NOASSERTION | yes | +| bidi-js | 1.0.3 | MIT | no | +| chai | 6.2.2 | MIT | no | +| classcat | 5.0.5 | MIT | no | +| convert-source-map | 2.0.0 | MIT | no | +| cryptography | — | NOASSERTION | yes | +| css-tree | 3.2.1 | MIT | no | +| css.escape | 1.5.1 | MIT | no | +| csstype | 3.2.3 | MIT | no | +| d3-color | 3.1.0 | ISC | no | +| d3-dispatch | 3.0.1 | ISC | no | +| d3-drag | 3.0.0 | ISC | no | +| d3-ease | 3.0.1 | BSD-3-Clause | no | +| d3-interpolate | 3.0.1 | ISC | no | +| d3-selection | 3.0.0 | ISC | no | +| d3-timer | 3.0.1 | ISC | no | +| d3-transition | 3.0.1 | ISC | no | +| d3-zoom | 3.0.0 | ISC | no | +| data-urls | 7.0.0 | MIT | no | +| decimal.js | 10.6.0 | MIT | no | +| dequal | 2.0.3 | MIT | no | +| detect-libc | 2.1.2 | Apache-2.0 | no | +| dom-accessibility-api | 0.5.16 | MIT | no | +| dom-accessibility-api | 0.6.3 | MIT | no | +| entities | 8.0.0 | BSD-2-Clause | no | +| es-module-lexer | 2.1.0 | MIT | no | +| estree-walker | 3.0.3 | MIT | no | +| expect-type | 1.3.0 | Apache-2.0 | no | +| fast-check | 4.8.0 | MIT | no | +| fastapi | — | NOASSERTION | yes | +| fdir | 6.5.0 | MIT | no | +| fsevents | 2.3.3 | MIT | no | +| github/codeql-action/analyze | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | yes | +| github/codeql-action/autobuild | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | yes | +| github/codeql-action/init | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | yes | +| has-flag | 4.0.0 | MIT | no | +| html-encoding-sniffer | 6.0.0 | MIT | no | +| html-escaper | 2.0.2 | MIT | no | +| httpx | — | NOASSERTION | yes | +| hypercorn | — | NOASSERTION | yes | +| indent-string | 4.0.0 | MIT | no | +| is-potential-custom-element-name | 1.0.1 | MIT | no | +| istanbul-lib-coverage | 3.2.2 | BSD-3-Clause | no | +| istanbul-lib-report | 3.0.1 | BSD-3-Clause | no | +| istanbul-reports | 3.2.0 | BSD-3-Clause | no | +| js-tokens | 10.0.0 | MIT | no | +| js-tokens | 4.0.0 | MIT | no | +| jsdom | 30.0.1 | MIT | no | +| lightningcss | 1.33.0 | MPL-2.0 | yes | +| lightningcss-android-arm64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-darwin-arm64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-darwin-x64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-freebsd-x64 | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm-gnueabihf | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-gnu | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-arm64-musl | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-gnu | 1.33.0 | MPL-2.0 | yes | +| lightningcss-linux-x64-musl | 1.33.0 | MPL-2.0 | yes | +| lightningcss-win32-arm64-msvc | 1.33.0 | MPL-2.0 | yes | +| lightningcss-win32-x64-msvc | 1.33.0 | MPL-2.0 | yes | +| lru-cache | 11.5.2 | BlueOak-1.0.0 | no | +| lz-string | 1.5.0 | MIT | no | +| magic-string | 0.30.21 | MIT | no | +| magicast | 0.5.3 | MIT | no | +| make-dir | 4.0.0 | MIT | no | +| mdn-data | 2.27.1 | CC0-1.0 | no | +| min-indent | 1.0.1 | MIT | no | +| mypy | — | NOASSERTION | yes | +| nanoid | 3.3.16 | MIT | no | +| obug | 2.1.3 | MIT | no | +| parse5 | 8.0.1 | MIT | no | +| pathe | 2.0.3 | MIT | no | +| picocolors | 1.1.1 | ISC | no | +| picomatch | 4.0.5 | MIT | no | +| postcss | 8.5.25 | MIT | no | +| pretty-format | 27.5.1 | MIT | no | +| prometheus-client | — | NOASSERTION | yes | +| psycopg | — | NOASSERTION | yes | +| punycode | 2.3.1 | MIT | no | +| pure-rand | 8.4.1 | MIT | no | +| pydantic | — | NOASSERTION | yes | +| pydantic-settings | — | NOASSERTION | yes | +| pyjwt | — | NOASSERTION | yes | +| pytest | — | NOASSERTION | yes | +| pytest-asyncio | — | NOASSERTION | yes | +| pytest-cov | — | NOASSERTION | yes | +| python-jose | — | NOASSERTION | yes | +| python-multipart | — | NOASSERTION | yes | +| react | 19.2.8 | MIT | no | +| react-dom | 19.2.8 | MIT | no | +| react-is | 17.0.2 | MIT | no | +| redent | 3.0.0 | MIT | no | +| redis | — | NOASSERTION | yes | +| requests | — | NOASSERTION | yes | +| require-from-string | 2.0.2 | MIT | no | +| rolldown | 1.2.1 | MIT | no | +| saxes | 6.0.0 | ISC | no | +| scheduler | 0.27.0 | MIT | no | +| semver | 7.8.5 | ISC | no | +| setuptools | — | NOASSERTION | yes | +| siginfo | 2.0.0 | ISC | no | +| snowflake-connector-python | — | NOASSERTION | yes | +| source-map-js | 1.2.1 | BSD-3-Clause | no | +| sqlalchemy | — | NOASSERTION | yes | +| stackback | 0.0.2 | MIT | no | +| starlette | — | NOASSERTION | yes | +| std-env | 4.1.0 | MIT | no | +| step-security/harden-runner | b09bb98e06d4d774595224525879c09bc6e98c40 | NOASSERTION | yes | +| strip-indent | 3.0.0 | MIT | no | +| supports-color | 7.2.0 | MIT | no | +| symbol-tree | 3.2.4 | MIT | no | +| tinybench | 2.9.0 | MIT | no | +| tinyexec | 1.2.4 | MIT | no | +| tinyglobby | 0.2.17 | MIT | no | +| tinyrainbow | 3.1.0 | MIT | no | +| tldts | 7.4.10 | MIT | no | +| tldts-core | 7.4.10 | MIT | no | +| tough-cookie | 6.0.2 | BSD-3-Clause | no | +| tr46 | 6.0.0 | MIT | no | +| tslib | 2.8.1 | 0BSD | no | +| types-python-jose | — | NOASSERTION | yes | +| typescript | 6.0.3 | Apache-2.0 | no | +| undici | 8.10.0 | MIT | no | +| urllib3 | — | NOASSERTION | yes | +| use-sync-external-store | 1.6.0 | MIT | no | +| vite | 8.2.1 | MIT | no | +| vitest | 4.1.10 | MIT | no | +| w3c-xmlserializer | 5.0.0 | MIT | no | +| webidl-conversions | 8.0.1 | BSD-2-Clause | no | +| whatwg-mimetype | 5.0.0 | MIT | no | +| whatwg-url | 16.0.1 | MIT | no | +| whatwg-url | 17.1.0 | MIT | no | +| why-is-node-running | 2.3.0 | MIT | no | +| xml-name-validator | 5.0.0 | Apache-2.0 | no | +| xmlchars | 2.2.0 | MIT | no | +| zustand | 4.5.7 | MIT | no | + +### ContextualWisdomLab/pg-llm-batch + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/setup-python | 5fda3b95a4ea91299a34e894583c3862153e4b97 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| aiohappyeyeballs | 2.7.1 | 0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0 | yes | +| aiohttp | — | NOASSERTION | yes | +| aiohttp | 3.14.3 | Apache-2.0 AND MIT | no | +| aiosignal | 1.4.0 | Apache-2.0 | no | +| astral-sh/setup-uv | c771a70e6277c0a99b617c7a806ffedaca235ff9 | NOASSERTION | yes | +| async-timeout | 5.0.1 | Apache-2.0 | no | +| attrs | 26.1.0 | MIT | no | +| backports-asyncio-runner | 1.2.0 | NOASSERTION | yes | +| cffi | 2.1.0 | MIT-0 | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| cryptography | — | NOASSERTION | yes | +| cryptography | 50.0.0 | Apache-2.0 OR BSD-3-Clause | no | +| exceptiongroup | 1.3.1 | MIT AND Python-2.0 | no | +| frozenlist | 1.8.0 | Apache-2.0 | no | +| idna | 3.18 | BSD-3-Clause | no | +| iniconfig | 2.3.0 | MIT | no | +| multidict | 6.7.1 | Apache-2.0 | no | +| opentelemetry-api | 1.44.0 | Apache-2.0 | no | +| packaging | 26.2 | Apache-2.0 AND BSD-2-Clause | no | +| pg-llm-batch | 0.1.0 | NOASSERTION | yes | +| pluggy | 1.6.0 | MIT | no | +| propcache | 0.5.2 | Apache-2.0 | no | +| psycopg | — | NOASSERTION | yes | +| psycopg | 3.3.4 | LGPL-3.0 AND LGPL-3.0-only | yes | +| psycopg-binary | 3.3.4 | GPL-3.0-or-later | yes | +| pycparser | 3.0 | BSD-3-Clause | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pytest | — | NOASSERTION | yes | +| pytest | 9.1.1 | MIT | no | +| pytest-asyncio | — | NOASSERTION | yes | +| pytest-asyncio | 1.4.0 | Apache-2.0 | no | +| ruff | 0.16.1 | MIT | no | +| setuptools | — | NOASSERTION | yes | +| step-security/harden-runner | b09bb98e06d4d774595224525879c09bc6e98c40 | NOASSERTION | yes | +| tomli | 2.4.1 | MIT | no | +| typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| tzdata | 2026.2 | Apache-2.0 | no | +| yarl | 1.24.2 | Apache-2.0 | no | + +### ContextualWisdomLab/pingora-gateway + +No components reported. + +### ContextualWisdomLab/PolicyWeave + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/procurement-workbench + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| annotated-doc | 0.0.5 | MIT | no | +| annotated-types | 0.8.0 | MIT | no | +| anyio | 4.15.1 | MIT | no | +| attrs | 26.1.0 | MIT | no | +| beautifulsoup4 | 4.15.0 | MIT | no | +| certifi | 2026.7.22 | MPL-2.0 | yes | +| cffi | 2.1.1 | MIT-0 | no | +| click | 8.5.0 | BSD-3-Clause | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| cryptography | 50.0.2 | Apache-2.0 OR BSD-3-Clause | no | +| defusedxml | 0.7.1 | PSF-2.0 | no | +| fastapi | 0.142.2 | MIT | no | +| greenlet | 3.5.6 | MIT AND PSF-2.0 | no | +| h11 | 0.16.0 | MIT | no | +| httpcore | 1.0.9 | BSD-2-Clause AND BSD-3-Clause | no | +| httpx | 0.28.1 | BSD-3-Clause | no | +| httpx-sse | 0.4.3 | MIT | no | +| idna | 3.20 | BSD-3-Clause | no | +| iniconfig | 2.3.0 | MIT | no | +| jsonschema | 4.26.0 | MIT | no | +| jsonschema-specifications | 2025.9.1 | MIT | no | +| mcp | 1.30.0 | MIT | no | +| opentelemetry-api | 1.45.0 | Apache-2.0 | no | +| packaging | 26.3 | Apache-2.0 OR BSD-2-Clause | no | +| playwright | 1.63.0 | Apache-2.0 | no | +| pluggy | 1.6.0 | MIT | no | +| pycparser | 3.0 | BSD-3-Clause | no | +| pydantic | 2.13.5 | MIT | no | +| pydantic-core | 2.46.5 | MIT | no | +| pydantic-settings | 2.15.0 | MIT | no | +| pyee | 13.0.1 | MIT | no | +| pygments | 2.21.0 | BSD-2-Clause | no | +| pyjwt | 2.15.1 | MIT | no | +| pytest | 9.1.1 | MIT | no | +| python-dotenv | 1.2.4 | BSD-3-Clause | no | +| python-multipart | 0.0.32 | Apache-2.0 | no | +| pywin32 | 312 | PSF-2.0 | no | +| referencing | 0.37.0 | MIT | no | +| rpds-py | 2026.6.3 | MIT | no | +| ruff | 0.16.10 | MIT | no | +| soupsieve | 2.10 | MIT | no | +| sse-starlette | 3.5.0 | BSD-3-Clause | no | +| starlette | 1.7.0 | BSD-3-Clause | no | +| typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| typing-inspection | 0.4.4 | MIT | no | +| uvicorn | 0.54.0 | BSD-3-Clause | no | + +### ContextualWisdomLab/psychometrics-commons + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/quarantine-sandbox-runtime + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/RankWeave + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/saju-caldav + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/scopeweave + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| @bcoe/v8-coverage | 1.0.2 | ISC AND MIT | no | +| @hono/node-server | 2.1.1 | MIT | no | +| @istanbuljs/schema | 0.1.6 | MIT | no | +| @jridgewell/resolve-uri | 3.1.2 | MIT | no | +| @jridgewell/sourcemap-codec | 1.5.5 | MIT | no | +| @jridgewell/trace-mapping | 0.3.31 | MIT | no | +| @playwright/test | 1.62.1 | Apache-2.0 | no | +| @types/istanbul-lib-coverage | 2.0.6 | MIT | no | +| actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | yes | +| actions/configure-pages | 45bfe0192ca1faeb007ade9deae92b16b8254a0d | NOASSERTION | yes | +| actions/dependency-review-action | a1d282b36b6f3519aa1f3fc636f609c47dddb294 | NOASSERTION | yes | +| actions/deploy-pages | cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 | NOASSERTION | yes | +| actions/setup-node | 2028fbc5c25fe9cf00d9f06a71cc4710d4507903 | NOASSERTION | yes | +| actions/setup-node | 39370e3970a6d050c480ffad4ff0ed4d3fdee5af | NOASSERTION | yes | +| actions/upload-pages-artifact | fc324d3547104276b827a68afc52ff2a11cc49c9 | NOASSERTION | yes | +| ansi-regex | 6.2.2 | MIT | no | +| ansi-styles | 6.2.3 | MIT | no | +| balanced-match | 4.0.4 | MIT | no | +| brace-expansion | 5.0.9 | MIT | no | +| c8 | 12.0.0 | ISC | no | +| cliui | 9.0.1 | ISC | no | +| convert-source-map | 2.0.0 | MIT | no | +| cross-spawn | 7.0.6 | MIT | no | +| emoji-regex | 10.6.0 | MIT | no | +| escalade | 3.2.0 | MIT | no | +| fast-check | 4.9.0 | MIT | no | +| find-up | 5.0.0 | MIT | no | +| foreground-child | 3.3.1 | ISC | no | +| fsevents | 2.3.2 | MIT | no | +| get-caller-file | 2.0.5 | ISC | no | +| get-east-asian-width | 1.6.0 | MIT | no | +| github/codeql-action/analyze | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | yes | +| github/codeql-action/init | 8aad20d150bbac5944a9f9d289da16a4b0d87c1e | NOASSERTION | yes | +| glob | 13.0.6 | BlueOak-1.0.0 | no | +| google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml | 3a7550f43ba5b58905a821ce3a0ed24c4858b3f4 | NOASSERTION | yes | +| has-flag | 4.0.0 | MIT | no | +| hono | 4.13.0 | MIT | no | +| html-escaper | 2.0.2 | MIT | no | +| isexe | 2.0.0 | ISC | no | +| istanbul-lib-coverage | 3.2.2 | BSD-3-Clause | no | +| istanbul-lib-report | 3.0.1 | BSD-3-Clause | no | +| istanbul-reports | 3.2.0 | BSD-3-Clause | no | +| locate-path | 6.0.0 | MIT | no | +| lru-cache | 11.5.2 | BlueOak-1.0.0 | no | +| make-dir | 4.0.0 | MIT | no | +| minimatch | 10.2.6 | BlueOak-1.0.0 | no | +| minipass | 7.1.3 | BlueOak-1.0.0 | no | +| p-limit | 3.1.0 | MIT | no | +| p-locate | 5.0.0 | MIT | no | +| path-exists | 4.0.0 | MIT | no | +| path-key | 3.1.1 | MIT | no | +| path-scurry | 2.0.2 | BlueOak-1.0.0 | no | +| playwright | 1.62.1 | Apache-2.0 | no | +| playwright-core | 1.62.1 | Apache-2.0 | no | +| pure-rand | 8.4.1 | MIT | no | +| semver | 7.8.5 | ISC | no | +| shebang-command | 2.0.0 | MIT | no | +| shebang-regex | 3.0.0 | MIT | no | +| signal-exit | 4.1.0 | ISC | no | +| string-width | 7.2.0 | MIT | no | +| string-width | 8.2.2 | MIT | no | +| strip-ansi | 7.2.0 | MIT | no | +| supports-color | 7.2.0 | MIT | no | +| test-exclude | 8.0.0 | ISC | no | +| v8-to-istanbul | 9.3.0 | ISC | no | +| which | 2.0.2 | ISC | no | +| wrap-ansi | 9.0.2 | MIT | no | +| y18n | 5.0.8 | ISC | no | +| yargs | 18.1.0 | MIT | no | +| yargs-parser | 21.1.1 | ISC | no | +| yargs-parser | 22.0.0 | ISC | no | +| yocto-queue | 0.1.0 | MIT | no | + +### ContextualWisdomLab/semantic-data-portal + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/checkout | 9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 | NOASSERTION | yes | +| actions/setup-python | ece7cb06caefa5fff74198d8649806c4678c61a1 | NOASSERTION | yes | +| actions/upload-artifact | 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | NOASSERTION | yes | +| annotated-doc | 0.0.4 | MIT | no | +| annotated-types | 0.7.0 | MIT | no | +| anyio | 4.14.1 | MIT | no | +| anyio | 4.14.2 | MIT | no | +| atheris | 3.0.0 | Apache-2.0 | no | +| certifi | 2026.6.17 | MPL-2.0 | yes | +| cffi | 2.1.0 | MIT-0 | no | +| click | 8.4.2 | BSD-3-Clause | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| cryptography | 49.0.0 | BSD-3-Clause OR Apache-2.0 | no | +| fastapi | 0.139.0 | MIT | no | +| github/codeql-action/upload-sarif | 54f647b7e1bb85c95cddabcd46b0c578ec92bc1a | NOASSERTION | yes | +| greenlet | 3.2.5 | MIT AND PSF-2.0 | no | +| greenlet | 3.5.3 | MIT AND PSF-2.0 | no | +| h11 | 0.16.0 | MIT | no | +| httpcore | 1.0.9 | BSD-2-Clause AND BSD-3-Clause | no | +| httpx | 0.28.1 | BSD-3-Clause | no | +| hypothesis | — | NOASSERTION | yes | +| hypothesis | 6.156.6 | MPL-2.0 AND MPL-1.1 | yes | +| idna | 3.18 | BSD-3-Clause | no | +| iniconfig | 2.3.0 | MIT | no | +| ossf/scorecard-action | 4eaacf0543bb3f2c246792bd56e8cdeffafb205a | NOASSERTION | yes | +| packaging | 26.2 | Apache-2.0 AND BSD-2-Clause | no | +| pluggy | 1.6.0 | MIT | no | +| psycopg | 3.3.4 | LGPL-3.0 AND LGPL-3.0-only | yes | +| psycopg-binary | 3.3.4 | GPL-3.0-or-later | yes | +| pycparser | 3.0 | BSD-3-Clause | no | +| pydantic | 2.13.4 | MIT | no | +| pydantic-core | 2.46.4 | MIT | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pyjwt | 2.13.0 | MIT | no | +| pytest | 9.1.1 | MIT | no | +| setuptools | 80.9.0 | MIT | no | +| sortedcontainers | 2.4.0 | Apache-2.0 | no | +| sqlalchemy | — | NOASSERTION | yes | +| sqlalchemy | 2.0.51 | MIT | no | +| starlette | 1.3.1 | BSD-3-Clause | no | +| typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| typing-inspection | 0.4.2 | MIT | no | +| tzdata | 2026.2 | Apache-2.0 | no | +| tzdata | 2026.3 | Apache-2.0 | no | +| uvicorn | 0.51.0 | BSD-3-Clause | no | + +### ContextualWisdomLab/supply-chain-control-plane + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/TEPP + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/ThreadWeave + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/tokyo-travel-2026 + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| fonttools | 4.66.1 | MIT | no | +| pillow | 12.3.0 | MIT-CMU | no | +| pymupdf | 1.28.2 | AGPL-3.0 AND AGPL-3.0-only AND AGPL-3.0-or-later | yes | + +### ContextualWisdomLab/traveling-agent + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| ContextualWisdomLab/.github/.github/workflows/traveling-agent-poc-quality.yml | f52a0d71b3ce2b60d738f4326f0f9906af9ea1b9 | NOASSERTION | yes | + +### ContextualWisdomLab/Veilpick + +No components reported. + +### ContextualWisdomLab/wardnet + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| actions/cache | 0057852bfaa89a56745cba8c7296529d2fc39830 | NOASSERTION | yes | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | yes | +| arbitrary | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| atomic-waker | 1.1.2 | Apache-2.0 OR MIT | no | +| autocfg | 1.5.1 | Apache-2.0 OR MIT | no | +| axum | 0.8.9 | MIT | no | +| axum-core | 0.5.6 | MIT | no | +| base64 | 0.22.1 | MIT OR Apache-2.0 | no | +| bit-set | 0.8.0 | Apache-2.0 OR MIT | no | +| bit-vec | 0.8.0 | Apache-2.0 OR MIT | no | +| bitflags | 2.13.1 | MIT OR Apache-2.0 | no | +| bumpalo | 3.20.3 | MIT OR Apache-2.0 | no | +| bytes | 1.12.1 | MIT | no | +| cc | 1.4.0 | MIT OR Apache-2.0 | no | +| cfg-if | 1.0.4 | MIT OR Apache-2.0 | no | +| cfg_aliases | 0.2.2 | MIT | no | +| chacha20 | 0.10.1 | MIT OR Apache-2.0 | no | +| cpufeatures | 0.3.0 | MIT OR Apache-2.0 | no | +| displaydoc | 0.2.7 | MIT OR Apache-2.0 | no | +| dtolnay/rust-toolchain | 4be7066ada62dd38de10e7b70166bc74ed198c30 | NOASSERTION | yes | +| dtolnay/rust-toolchain | efcb852328a9f50117170cc43094fb6f09eaf1ae | NOASSERTION | yes | +| errno | 0.3.14 | MIT OR Apache-2.0 | no | +| fastrand | 2.5.0 | Apache-2.0 OR MIT | no | +| find-msvc-tools | 0.1.9 | MIT OR Apache-2.0 | no | +| fnv | 1.0.7 | Apache-2.0 OR MIT | no | +| form_urlencoded | 1.2.2 | MIT OR Apache-2.0 | no | +| futures-channel | 0.3.33 | MIT OR Apache-2.0 | no | +| futures-core | 0.3.34 | MIT OR Apache-2.0 | no | +| futures-io | 0.3.34 | MIT OR Apache-2.0 | no | +| futures-macro | 0.3.34 | MIT OR Apache-2.0 | no | +| futures-sink | 0.3.34 | MIT OR Apache-2.0 | no | +| futures-task | 0.3.34 | MIT OR Apache-2.0 | no | +| futures-util | 0.3.34 | MIT OR Apache-2.0 | no | +| getrandom | 0.2.17 | MIT OR Apache-2.0 | no | +| getrandom | 0.3.4 | MIT OR Apache-2.0 | no | +| getrandom | 0.4.3 | MIT OR Apache-2.0 | no | +| github/codeql-action/upload-sarif | ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd | NOASSERTION | yes | +| http | 1.5.0 | MIT OR Apache-2.0 | no | +| http-body | 1.1.0 | MIT | no | +| http-body-util | 0.1.4 | MIT | no | +| httparse | 1.10.1 | MIT OR Apache-2.0 | no | +| httpdate | 1.0.3 | MIT OR Apache-2.0 | no | +| hyper | 1.11.0 | MIT | no | +| hyper-rustls | 0.27.9 | Apache-2.0 OR ISC OR MIT | no | +| hyper-util | 0.1.20 | MIT | no | +| icu_collections | 2.2.0 | Unicode-3.0 | no | +| icu_locale_core | 2.2.0 | Unicode-3.0 | no | +| icu_normalizer | 2.2.0 | Unicode-3.0 | no | +| icu_normalizer_data | 2.2.0 | Unicode-3.0 | no | +| icu_properties | 2.2.0 | Unicode-3.0 | no | +| icu_properties_data | 2.2.0 | Unicode-3.0 | no | +| icu_provider | 2.2.0 | Unicode-3.0 | no | +| idna | 1.1.0 | MIT OR Apache-2.0 | no | +| idna_adapter | 1.2.2 | Apache-2.0 OR MIT | no | +| ipnet | 2.12.0 | MIT OR Apache-2.0 | no | +| itoa | 1.0.18 | MIT OR Apache-2.0 | no | +| js-sys | 0.3.103 | MIT OR Apache-2.0 | no | +| libc | 0.2.189 | MIT OR Apache-2.0 | no | +| libfuzzer-sys | >= 0.4.0,< 0.5.0 | NOASSERTION | yes | +| linux-raw-sys | 0.12.1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| litemap | 0.8.2 | Unicode-3.0 | no | +| log | 0.4.33 | MIT OR Apache-2.0 | no | +| lru-slab | 0.1.2 | MIT OR Apache-2.0 OR Zlib | no | +| matchit | 0.8.4 | MIT AND BSD-3-Clause | no | +| memchr | 2.8.3 | Unlicense OR MIT | no | +| mime | 0.3.17 | MIT OR Apache-2.0 | no | +| mime_guess | 2.0.5 | MIT | no | +| mio | 1.2.2 | MIT | no | +| num-traits | 0.2.19 | MIT OR Apache-2.0 | no | +| once_cell | 1.21.4 | MIT OR Apache-2.0 | no | +| ossf/scorecard-action | 2d1146689b8cda280b9bc96326124645441f03bc | NOASSERTION | yes | +| percent-encoding | 2.3.2 | MIT OR Apache-2.0 | no | +| percent-encoding | >= 2.0.0,< 3.0.0 | NOASSERTION | yes | +| pin-project-lite | 0.2.17 | Apache-2.0 OR MIT | no | +| potential_utf | 0.1.5 | Unicode-3.0 | no | +| ppv-lite86 | 0.2.21 | MIT OR Apache-2.0 | no | +| proc-macro2 | 1.0.107 | MIT OR Apache-2.0 | no | +| proptest | 1.11.0 | MIT OR Apache-2.0 | no | +| proptest | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| quick-error | 1.2.3 | MIT OR Apache-2.0 | no | +| quinn | 0.11.11 | MIT OR Apache-2.0 | no | +| quinn-proto | 0.11.16 | MIT OR Apache-2.0 | no | +| quinn-udp | 0.5.15 | MIT OR Apache-2.0 | no | +| quote | 1.0.47 | MIT OR Apache-2.0 | no | +| r-efi | 5.3.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | yes | +| r-efi | 6.0.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later | yes | +| rand | 0.10.2 | MIT OR Apache-2.0 | no | +| rand | 0.9.5 | MIT OR Apache-2.0 | no | +| rand_chacha | 0.9.0 | MIT OR Apache-2.0 | no | +| rand_core | 0.10.1 | MIT OR Apache-2.0 | no | +| rand_core | 0.9.5 | MIT OR Apache-2.0 | no | +| rand_pcg | 0.10.2 | MIT OR Apache-2.0 | no | +| rand_xorshift | 0.4.0 | MIT OR Apache-2.0 | no | +| regex-syntax | 0.8.11 | MIT OR Apache-2.0 | no | +| reqwest | 0.12.28 | MIT OR Apache-2.0 | no | +| ring | 0.17.14 | Apache-2.0 AND ISC | no | +| rustc-hash | 2.1.3 | Apache-2.0 OR MIT | no | +| rustix | 1.1.4 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| rustls | 0.23.43 | Apache-2.0 OR ISC OR MIT | no | +| rustls-pki-types | 1.15.1 | MIT OR Apache-2.0 | no | +| rustls-webpki | 0.103.13 | ISC | no | +| rustversion | 1.0.23 | MIT OR Apache-2.0 | no | +| rusty-fork | 0.3.1 | MIT OR Apache-2.0 | no | +| ryu | 1.0.23 | Apache-2.0 OR BSL-1.0 | no | +| serde | 1.0.229 | MIT OR Apache-2.0 | no | +| serde | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| serde_core | 1.0.229 | MIT OR Apache-2.0 | no | +| serde_derive | 1.0.229 | MIT OR Apache-2.0 | no | +| serde_json | 1.0.151 | MIT OR Apache-2.0 | no | +| serde_json | >= 1.0.0,< 2.0.0 | NOASSERTION | yes | +| serde_path_to_error | 0.1.20 | MIT OR Apache-2.0 | no | +| serde_urlencoded | 0.7.1 | MIT OR Apache-2.0 | no | +| shlex | 2.0.1 | MIT OR Apache-2.0 | no | +| signal-hook-registry | 1.4.8 | MIT OR Apache-2.0 | no | +| slab | 0.4.12 | MIT | no | +| smallvec | 1.15.2 | MIT OR Apache-2.0 | no | +| socket2 | 0.6.5 | MIT OR Apache-2.0 | no | +| stable_deref_trait | 1.2.1 | MIT OR Apache-2.0 | no | +| subtle | 2.6.1 | BSD-3-Clause | no | +| syn | 2.0.119 | MIT OR Apache-2.0 | no | +| syn | 3.0.3 | MIT OR Apache-2.0 | no | +| sync_wrapper | 1.0.2 | Apache-2.0 | no | +| synstructure | 0.13.2 | MIT | no | +| tempfile | 3.27.0 | MIT OR Apache-2.0 | no | +| thiserror | 2.0.19 | MIT OR Apache-2.0 | no | +| thiserror-impl | 2.0.19 | MIT OR Apache-2.0 | no | +| tinystr | 0.8.3 | Unicode-3.0 | no | +| tinyvec | 1.12.0 | Zlib OR Apache-2.0 OR MIT | no | +| tinyvec_macros | 0.1.1 | MIT OR Apache-2.0 OR Zlib | no | +| tokio | 1.53.1 | MIT | no | +| tokio-macros | 2.7.2 | MIT | no | +| tokio-rustls | 0.26.4 | MIT OR Apache-2.0 | no | +| tokio-util | 0.7.19 | MIT | no | +| tower | 0.5.3 | MIT | no | +| tower-http | 0.6.11 | MIT | no | +| tower-layer | 0.3.3 | MIT | no | +| tower-service | 0.3.3 | MIT | no | +| tracing | 0.1.44 | MIT | no | +| tracing-core | 0.1.36 | MIT | no | +| try-lock | 0.2.5 | MIT | no | +| unarray | 0.1.4 | MIT OR Apache-2.0 | no | +| unicase | 2.9.0 | MIT OR Apache-2.0 | no | +| unicode-ident | 1.0.24 | (MIT OR Apache-2.0) AND Unicode-3.0 | no | +| untrusted | 0.9.0 | ISC | no | +| url | 2.5.8 | MIT OR Apache-2.0 | no | +| utf8_iter | 1.0.4 | Apache-2.0 OR MIT | no | +| wait-timeout | 0.2.1 | MIT OR Apache-2.0 | no | +| want | 0.3.1 | MIT | no | +| wasi | 0.11.1+wasi-snapshot-preview1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| wasip2 | 1.0.4+wasi-0.2.12 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| wasm-bindgen | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-futures | 0.4.76 | MIT OR Apache-2.0 | no | +| wasm-bindgen-macro | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-macro-support | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-bindgen-shared | 0.2.126 | MIT OR Apache-2.0 | no | +| wasm-streams | 0.4.2 | MIT OR Apache-2.0 | no | +| web-sys | 0.3.103 | MIT OR Apache-2.0 | no | +| web-time | 1.1.0 | MIT OR Apache-2.0 | no | +| webpki-roots | 1.0.9 | CDLA-Permissive-2.0 | no | +| windows-link | 0.2.1 | MIT OR Apache-2.0 | no | +| windows-sys | 0.52.0 | MIT OR Apache-2.0 | no | +| windows-sys | 0.61.2 | MIT OR Apache-2.0 | no | +| windows-targets | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_aarch64_gnullvm | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_aarch64_msvc | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_i686_gnu | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_i686_gnullvm | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_i686_msvc | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnu | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_x86_64_gnullvm | 0.52.6 | MIT OR Apache-2.0 | no | +| windows_x86_64_msvc | 0.52.6 | MIT OR Apache-2.0 | no | +| wit-bindgen | 0.57.1 | Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT | no | +| writeable | 0.6.3 | Unicode-3.0 | no | +| yoke | 0.8.3 | Unicode-3.0 | no | +| yoke-derive | 0.8.2 | Unicode-3.0 | no | +| zerocopy | 0.8.55 | BSD-2-Clause OR Apache-2.0 OR MIT | no | +| zerocopy-derive | 0.8.55 | BSD-2-Clause OR Apache-2.0 OR MIT | no | +| zerofrom | 0.1.8 | Unicode-3.0 | no | +| zerofrom-derive | 0.1.7 | Unicode-3.0 | no | +| zeroize | 1.9.0 | Apache-2.0 OR MIT | no | +| zerotrie | 0.2.4 | Unicode-3.0 | no | +| zerovec | 0.11.6 | Unicode-3.0 | no | +| zerovec-derive | 0.11.3 | Unicode-3.0 | no | +| zmij | 1.0.23 | MIT | no | + +### ContextualWisdomLab/xtrm-lead-pi-outbound + +SBOM unavailable: gh: Not Found (HTTP 404) + +### ContextualWisdomLab/xtrmLLMBatchPython + +| Component | Version | License | Flagged | +| --- | --- | --- | --- | +| @types/express | ^4.17.0 | MIT | no | +| @types/inquirer | ^8.2.12 | MIT | no | +| @types/node | ^24.0.1 | MIT | no | +| @vitest/coverage-v8 | ^3.2.6 | MIT | no | +| @vitest/ui | ^3.2.6 | MIT | no | +| actions/checkout | 3d3c42e5aac5ba805825da76410c181273ba90b1 | NOASSERTION | yes | +| actions/setup-python | a26af69be951a213d495a4c3e4e4022e16d87065 | NOASSERTION | yes | +| actions/upload-artifact | ea165f8d65b6e75b540449e92b4886f43607fa02 | NOASSERTION | yes | +| aiofiles | 25.1.0 | Apache-2.0 | no | +| aiohappyeyeballs | 2.7.1 | 0BSD AND BSD-3-Clause AND GPL-1.0-or-later AND PSF-2.0 AND Python-2.0 | yes | +| aiohttp | 3.14.1 | Apache-2.0 AND MIT | no | +| aioresponses | 0.7.9 | MIT | no | +| aiosignal | 1.4.0 | Apache-2.0 | no | +| aiosqlite | 0.22.1 | MIT | no | +| annotated-types | 0.7.0 | MIT | no | +| anyio | 4.14.1 | MIT | no | +| astral-sh/setup-uv | c771a70e6277c0a99b617c7a806ffedaca235ff9 | NOASSERTION | yes | +| async-timeout | 5.0.1 | Apache-2.0 | no | +| attrs | 26.1.0 | MIT | no | +| axios | ^1.9.0 | MIT | no | +| backports-asyncio-runner | 1.2.0 | NOASSERTION | yes | +| certifi | 2026.6.17 | MPL-2.0 | yes | +| cffi | 2.0.0 | MIT-0 | no | +| charset-normalizer | 3.4.7 | MIT | no | +| click | 8.4.2 | BSD-3-Clause | no | +| colorama | 0.4.6 | BSD-2-Clause AND BSD-3-Clause | no | +| commander | ^14.0.0 | NOASSERTION | yes | +| cryptography | 49.0.0 | BSD-3-Clause OR Apache-2.0 | no | +| eslint | ^9.27.0 | NOASSERTION | yes | +| et-xmlfile | 2.0.0 | 0BSD AND BSD-3-Clause AND MIT AND Python-2.0 | no | +| exceptiongroup | 1.3.1 | MIT AND Python-2.0 | no | +| express | ^4.18.0 | NOASSERTION | yes | +| extract-zip | ^2.0.1 | NOASSERTION | yes | +| frozenlist | 1.8.0 | Apache-2.0 | no | +| github.com/lib/pq | v1.12.3 | MIT | no | +| h11 | 0.16.0 | MIT | no | +| httpcore | 1.0.9 | BSD-2-Clause AND BSD-3-Clause | no | +| httpx | 0.28.1 | BSD-3-Clause | no | +| idna | 3.18 | BSD-3-Clause | no | +| iniconfig | 2.3.0 | MIT | no | +| inquirer | ^8.2.6 | MIT | no | +| markdown-it-py | 4.2.0 | MIT | no | +| mdurl | 0.1.2 | MIT | no | +| multidict | 6.7.1 | Apache-2.0 | no | +| numpy | 2.2.6 | Apache-2.0 AND BSD-3-Clause AND MIT AND Zlib | no | +| numpy | 2.4.6 | BSD-3-Clause | no | +| numpy | 2.5.0 | BSD-3-Clause | no | +| open | ^10.0.0 | MIT | no | +| openpyxl | 3.1.5 | MIT | no | +| packaging | 26.2 | Apache-2.0 AND BSD-2-Clause | no | +| pandas | 2.3.3 | BSD-2-Clause AND BSD-3-Clause | no | +| pandas | 3.0.3 | BSD-2-Clause AND BSD-3-Clause | no | +| pino | ^9.7.0 | NOASSERTION | yes | +| pluggy | 1.6.0 | MIT | no | +| propcache | 0.5.2 | Apache-2.0 | no | +| psycopg | 3.3.4 | LGPL-3.0 AND LGPL-3.0-only | yes | +| psycopg-binary | 3.3.4 | GPL-3.0-or-later | yes | +| pycparser | 3.0 | BSD-3-Clause | no | +| pydantic | 2.13.4 | MIT | no | +| pydantic-core | 2.46.4 | MIT | no | +| pygments | 2.20.0 | BSD-2-Clause | no | +| pypa/gh-action-pip-audit | 1220774d901786e6f652ae159f7b6bc8fea6d266 | NOASSERTION | yes | +| pytest | 9.1.1 | MIT | no | +| pytest-asyncio | 1.4.0 | Apache-2.0 | no | +| python-dateutil | 2.9.0.post0 | Apache-2.0 OR BSD-3-Clause | no | +| python-dotenv | 1.2.2 | BSD-3-Clause | no | +| pytz | 2026.2 | MIT AND ZPL-2.1 | no | +| regex | 2026.6.28 | CNRI-Python AND Apache-2.0 | no | +| requests | 2.34.2 | Apache-2.0 | no | +| rich | 15.0.0 | MIT | no | +| six | 1.17.0 | MIT | no | +| tiktoken | 0.13.0 | MIT | no | +| tomli | 2.4.1 | MIT | no | +| typescript | ^5.8.3 | Apache-2.0 | no | +| typing-extensions | 4.16.0 | Python-2.0 AND GPL-1.0-or-later AND Python-2.0 AND BSD-3-Clause AND Python-2.0 AND BSD-3-Clause AND 0BSD | yes | +| typing-inspection | 0.4.2 | MIT | no | +| tzdata | 2026.2 | Apache-2.0 | no | +| urllib3 | 2.7.0 | MIT | no | +| vitest | ^3.2.6 | NOASSERTION | yes | +| xtrmllmbatch | 0.1.0 | NOASSERTION | yes | +| yarl | 1.24.2 | Apache-2.0 | no | +| zod | ^3.25.67 | NOASSERTION | yes | diff --git a/docs/templates/repository-readme-template.md b/docs/templates/repository-readme-template.md new file mode 100644 index 0000000000..88be951603 --- /dev/null +++ b/docs/templates/repository-readme-template.md @@ -0,0 +1,129 @@ +# Repository README authoring template + +Use this scaffold with the [README quality standard](../repository-readme-quality-standard.md). +It is an authoring aid, not finished customer copy or an organization-wide generator. +Keep each product's language, ownership, supported workflow, and license evidence. +Remove irrelevant sections rather than publishing empty headings. + +## Before adapting + +Choose one truthful starting path: a verified released package, an evaluated source +worktree, or a design/contract foundation. Do not give all three equal prominence +when only one exists. For a source path, state the working directory, actual +runtime prerequisite, lock/install command, and expected next action. For a +foundation, replace install commands with a real contract/validation entry point. + +Replace every `{{...}}` field from current evidence. Do not publish the scaffold, +these instructions, placeholder links, or guessed output. The text-fenced fields +below are intentionally not executable. Promote them to a language-tagged code +example only after the repository's actual command has been checked and, where +possible, run safely. Record an unexecuted example as unverified in the PR rather +than inventing a successful run. No badge, license, benchmark, or support channel +is selected by this template. + +## Copyable scaffold + + +````markdown +# {{product_name}} + +**{{one_sentence_user_outcome}}** + +{{who_this_helps_and_the_problem_it_solves}} + +[Get started](#get-started) · [Documentation](#documentation) · [Support](#support-and-contributing) + +## What you can do + +{{two_or_three_current_user_jobs_with_concrete_results}} + +{{important_limitation_that_changes_a_users_next_action}} + +## Get started + +{{release_or_source_or_foundation_status_and_prerequisites}} + +```text +{{verified_working_directory_and_setup_commands}} +``` + +{{observed_result_and_next_action}} + +{{network_data_permission_cost_and_stop_or_cleanup_notes}} + +## Example + +{{one_representative_task_and_its_input_requirements}} + +```text +{{verified_public_api_or_cli_example}} +``` + +{{bounded_expected_result_and_failure_recovery}} + +## How it fits + +{{short_data_flow_and_optional_integrations_in_user_language}} + +{{what_this_product_owns_and_what_remains_with_the_host_or_source_system}} + +## Status and verification + +{{current_maturity_and_link_to_actual_release_or_verification_evidence}} + +```text +{{repository_supported_verification_commands}} +``` + +{{tested_scope_and_remaining_limits_without_global_quality_claims}} + +## Documentation + +| I need to... | Start here | +| --- | --- | +| {{first_reader_job}} | [{{document_title}}]({{existing_document_path}}) | +| {{second_reader_job}} | [{{reference_title}}]({{existing_reference_path}}) | + +## Support and contributing + +{{existing_support_and_private_security_reporting_routes_or_their_explicit_limits}} + +{{smallest_contributor_verification_contract_and_link_to_details}} + +## License + +{{verified_license_statement}} + +{{third_party_notice_links_and_relevant_distribution_limits}} +```` + + +## Presentation choices + +Use a clear title, one strong opening sentence, short paragraphs, and a compact +navigation line. Keep badges few and relevant, and link each to real evidence. +Avoid walls of logos, decorative shields, HTML layout tables, unsupported +superlatives, and embedded internal incident dashboards. Long tables and exhaustive +CLI flags belong in linked references. Check heading navigation and code wrapping +at a narrow viewport as well as on desktop. + +A screenshot can establish what a real interface looks like, not that every +feature works. Include only an actual, reviewed product view with useful alt text, +no personal data or secrets, and an identifiable source revision in its retained +evidence. Omit screenshots when the product has no visual interface. + +## Adaptation and handoff record + +Keep the record in the PR or doctoring, not in the customer README. Bind each +material claim to its owning source and exact revision. For quick start, record +working directory, runtime/lock identity, command, observed result, side effects, +and stop/recovery. For a claim you could not execute, say **Not executed** and +state the missing capability. For licensing, separate first-party grant from +inherited source, dependencies, assets and service terms; metadata is inventory, +not rights approval. Retain required attribution rather than hiding an intake +conflict. + +Validate relative links against the proposed tree and the actual publishing root. +Generated READMEs require updating their authoring source and checking generated +output. Keep the existing authoritative PR and prove every valid delta survives +any consolidation. A queued check, external blocker, or saved draft is not delivery. diff --git a/docs/traveling-agent-poc-quality.md b/docs/traveling-agent-poc-quality.md new file mode 100644 index 0000000000..892afbed73 --- /dev/null +++ b/docs/traveling-agent-poc-quality.md @@ -0,0 +1,46 @@ +# Traveling Agent PoC quality workflow + +This checkout contains the central reusable workflow candidate for the Traveling Agent PoC quality contract: + +- `.github/workflows/traveling-agent-poc-quality.yml` +- callable only through `workflow_call`, with `contents: read` +- workflow-scope, trigger-aware concurrency: pull requests coalesce by repository and PR number; push/dispatch use repository, event, and ref fallback without head-SHA grouping +- rejects fork pull requests and non-`main` push/dispatch runs before runner admission; the shell admission repeats the same repository, event, base, and exact-head checks +- preserves the original 10-minute timeout, Python unittest discovery, Node syntax/renderer checks, whitespace check, and pinned checkout action +- checks out the PR head SHA (or event SHA for main push/dispatch) with `persist-credentials: false` +- requests only `CWL CI isolated` plus `self-hosted/Linux/X64/cwlab-ci-isolated` + +The original caller's `pull_request`, `push` on `main`, and `workflow_dispatch` events, security permissions, and concurrency intent are represented by the dedicated contract tests. This source-only integration checkout does not alter runner-group settings, publish the supplier, or execute hosted Actions. + +The leaf owner must retain caller workflow-scope cancellation before runner admission. Do not copy the earlier report-only caller that omitted concurrency. The intended caller is: + +```yaml +name: PoC quality +on: + pull_request: + push: + branches: [main] + workflow_dispatch: +permissions: + contents: read +concurrency: + group: poc-quality-${{ github.repository }}-${{ github.event.pull_request.number || format('{0}-{1}', github.event_name, github.ref) }} + cancel-in-progress: true +jobs: + offline-contracts: + uses: ContextualWisdomLab/.github/.github/workflows/traveling-agent-poc-quality.yml@ +``` + +The placeholder is intentionally unpublishable: the workflow does not exist at the inspected central base `4e32b99ec79b6565800d1081f60acfea368adecd`. Replace it only after reviewed supplier publication. No caller inputs or inherited secrets are needed. Supplier concurrency has a distinct literal namespace from `poc-quality-`; caller and called workflow must not cancel one another. Supplier PR keys omit the head SHA; non-PR fallback separates push from dispatch while retaining per-ref coalescing. + +Validation is source/runtime-local only: the focused Python contract executes admission against synthetic events and the actual whitespace shell against disposable real Git repositories; `actionlint` validates workflow syntax. Runner access, clean-per-job capacity, GitHub exact-head execution, and formal approval remain external gates. + +## Committed whitespace policy + +Checkout requests full history (`fetch-depth: 0`); the final check refuses a shallow repository, malformed or absent commit objects, and a checkout that differs from the expected head. Missing event-range objects (including a force-push predecessor no longer fetched) fail closed rather than silently checking only the last commit. There is no authenticated follow-up fetch or caller-supplied shell input. + +- PR: compare the exact event base SHA directly to the exact PR head SHA (`git diff --check BASE HEAD --`), not the working tree or a synthetic merge checkout. +- Push: compare event `before` to event `after`, requiring `after` to match the checked-out head. An all-zero `before` (initial branch push) compares the entire head tree against the empty tree; missing/nonzero invalid predecessors are rejected. +- Manual dispatch on main: check only the dispatched commit against its first parent (including merge commits). A root commit is checked against the empty tree. This is a latest-commit recheck, not a whole-history audit. + +All event values enter the check through `env`, are validated as exact hexadecimal SHA values before Git use, and are passed as subprocess arguments rather than interpolated into shell commands. Caller PR cancellation uses repository and PR number, without a head SHA; push and manual dispatch retain separate repository/event/ref keys. The supplier keeps its distinct `traveling-agent-poc-quality-` namespace. diff --git a/opencode.jsonc b/opencode.jsonc index 8946175a13..2663186084 100644 --- a/opencode.jsonc +++ b/opencode.jsonc @@ -1,11 +1,11 @@ { "$schema": "https://opencode.ai/config.json", // Default routed through the vendored contextual-orchestrator gateway - // (orchestrator/free: fail-closed zero-cost pool, ZDR-first). The gateway + // (orchestrator/auto: fail-closed zero-cost pool, ZDR-first). The gateway // must be provisioned before a review starts; there is no direct-provider // fallback for review content. - "model": "contextual-orchestrator/orchestrator/free", - "small_model": "contextual-orchestrator/orchestrator/free", + "model": "contextual-orchestrator/orchestrator/auto", + "small_model": "contextual-orchestrator/orchestrator/auto", "enabled_providers": ["contextual-orchestrator"], "lsp": false, "mcp": {}, @@ -290,21 +290,24 @@ // vendored contextual-orchestrator LLM gateway. It auto-discovers models // across Bytez/NVIDIA NIM (x2 keys)/OpenRouter/OpenAI from KV-registered // credentials and serves the fail-closed zero-cost pool under the virtual - // model id `orchestrator/free`, with ZDR-compliant (zero-data-retention) + // model id `orchestrator/auto`, with ZDR-compliant (zero-data-retention) // routes prioritized by scripts/ci/zdr_policy.py. Requires // CONTEXTUAL_ORCHESTRATOR_BASE_URL and CONTEXTUAL_ORCHESTRATOR_TOKEN, // which scripts/ci/contextual_orchestrator_review_sidecar.sh provisions on - // each runner before OpenCode starts. + // each runner before OpenCode starts. The sidecar exports a bare + // scheme://host:port, while the OpenAI-compatible provider appends only + // `/chat/completions`, so the `/v1` prefix belongs here. Without it the + // gateway answers route_not_found and OpenCode prints `Error: not found`. "contextual-orchestrator": { "npm": "@ai-sdk/openai-compatible", "name": "Contextual Orchestrator", "options": { - "baseURL": "{env:CONTEXTUAL_ORCHESTRATOR_BASE_URL}", + "baseURL": "{env:CONTEXTUAL_ORCHESTRATOR_BASE_URL}/v1", "apiKey": "{env:CONTEXTUAL_ORCHESTRATOR_TOKEN}" }, "models": { - "orchestrator/free": { - "name": "Orchestrator Free (ZDR-first zero-cost pool)", + "orchestrator/auto": { + "name": "Orchestrator Auto (cost-fenced dynamic selection)", "tool_call": true, "reasoning": true, "options": { diff --git a/pyproject.toml b/pyproject.toml index ff6353c164..cebf24066b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,16 +19,8 @@ pythonpath = ["."] [tool.coverage.run] branch = true source = ["scripts/ci"] -# contextual_orchestrator_review_launcher.py imports the vendored -# contextual-orchestrator library, which is installed only inside the review -# sidecar's runtime (scripts/ci/contextual_orchestrator_review_sidecar.sh). -# It cannot be imported by this repository's test suite, so it is omitted from -# coverage exactly as contextual-orchestrator omits its own runtime-only -# __main__.py and server.py. The policy and ZDR decision logic it consumes are -# fully tested offline. omit = [ "tests/*", - "scripts/ci/contextual_orchestrator_review_launcher.py", ] [tool.coverage.report] diff --git a/requirements-k-csap-quality-tests-hashes.txt b/requirements-k-csap-quality-tests-hashes.txt new file mode 100644 index 0000000000..d04a9d179c --- /dev/null +++ b/requirements-k-csap-quality-tests-hashes.txt @@ -0,0 +1,89 @@ +iniconfig==2.3.1 \ + --hash=sha256:67f4b9c50da0dedf52af349e7749a80a9057a5031199791b906c3bb3ae878960 \ + --hash=sha256:9121e2c1fdb355232495be3194c8dfe87ccc2d5dee45947b78e68f499790d7a7 +packaging==26.3 \ + --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ + --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c +pluggy==1.6.0 \ + --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ + --hash=sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746 +pygments==2.21.0 \ + --hash=sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9 \ + --hash=sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c +pytest==9.1.1 \ + --hash=sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313 \ + --hash=sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 diff --git a/requirements-k-csap-quality-tests.in b/requirements-k-csap-quality-tests.in new file mode 100644 index 0000000000..4cc8ae66e5 --- /dev/null +++ b/requirements-k-csap-quality-tests.in @@ -0,0 +1,3 @@ +# 중앙 K-CSAP workflow 계약 테스트 전용. 런타임 제품 의존성 아님. +pytest==9.1.1 +PyYAML==6.0.3 diff --git a/requirements-noema-document-ci-hashes.txt b/requirements-noema-document-ci-hashes.txt new file mode 100644 index 0000000000..0fd5dd54d7 --- /dev/null +++ b/requirements-noema-document-ci-hashes.txt @@ -0,0 +1,5 @@ +# Generated with uv pip compile --generate-hashes --python-version 3.12 +# requirements-noema-document-ci.txt +defusedxml==0.7.1 \ + --hash=sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69 \ + --hash=sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61 diff --git a/requirements-noema-document-ci.txt b/requirements-noema-document-ci.txt new file mode 100644 index 0000000000..09dd20d248 --- /dev/null +++ b/requirements-noema-document-ci.txt @@ -0,0 +1 @@ +defusedxml==0.7.1 diff --git a/requirements-opencode-review-ci-hashes.txt b/requirements-opencode-review-ci-hashes.txt index d8aaca3ad8..f58dc7f4d3 100644 --- a/requirements-opencode-review-ci-hashes.txt +++ b/requirements-opencode-review-ci-hashes.txt @@ -4,9 +4,9 @@ attrs==26.1.0 \ --hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \ --hash=sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32 # via interrogate -click==8.4.2 \ - --hash=sha256:9a6cea6e60b17ebe0a44c5cc636d94f09bd66142c1cd7d8b4cd731c4917a15f6 \ - --hash=sha256:e6f9f66136c816745b9d65817da91d61d957fb16e02e4dcd0552553c5a197b76 +click==8.5.0 \ + --hash=sha256:255bc9599cf7748b4b1a446ccc735421bd08a2ae529a8b88597d3de5664ee360 \ + --hash=sha256:ba0d2089de75ea0310e2dde03160e6ca10009947fb95a182f9b54021bb272e34 # via interrogate colorama==0.4.6 \ --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ @@ -137,69 +137,96 @@ coverage==7.15.4 \ # via # -r requirements-opencode-review-ci.txt # pytest-cov -hypothesis==6.163.0 \ - --hash=sha256:002a9709345892279fb0e81b5a05b72d08cfe81f937339827be0d588607ca9b0 \ - --hash=sha256:00d3091b28de83c5116e0ccd9a4bcb28ef61d2aace5df91093bb22434fd2350c \ - --hash=sha256:0a0c396244c13805edcb73ff467c4c8178ccefc41c4ef5ed00a68e612fd773e9 \ - --hash=sha256:0a933aca9ebf9daf951d07cf01200c94c321b6ee0b42cc7b67675c9686d914c2 \ - --hash=sha256:0cba5202f74e7e4cdb676d86f26e8cc1b4fdc88f7f58ba73c8ac45b6b22f3070 \ - --hash=sha256:213527755f0fc2b1f3721e73fd60023e2752a48f914e3e2df8d35111956ae5c8 \ - --hash=sha256:21e72e8d5818e5ef8cd6a2191c386e3fd1a6d9e3739cf97289b4d9b5dbc8e38d \ - --hash=sha256:2849c23b2e0fe2eef4c1ec336b01eac7ad7397c49fca43c264f59ec1e6046eac \ - --hash=sha256:28a6cc1c25a6cc9b6ec079eaabd32ac769994831ecddd57123ce43c9056dcf34 \ - --hash=sha256:31dc46c48aa53c3ec92d03120978ca7f19b9cf96d195ed3fc93503f1433c94a6 \ - --hash=sha256:320b076bf6436f971f1c73ee651e60001226d1b4e341f2c4a1ca87248261ca03 \ - --hash=sha256:331906cb029b6b360b8ebac3ec00c3cfa720037fe2efb294a503a1979c9a9a8f \ - --hash=sha256:34fc895691a2420595506eb17f3a104f2fa9039f013c0770a6cc2743ccaf6fed \ - --hash=sha256:3b6cee2afe6c67b31a4a64b63a876e0b020befdc61daabea80f7a0e14f19203a \ - --hash=sha256:3f3cceb4720a39127622fbf3bcebe1775b894372c53b5edddfdef10bbdeef9ec \ - --hash=sha256:40dfab6fe6a02a80abef81aebf88e53cd529e3f2f6ba3486b674a67b1f4a3512 \ - --hash=sha256:4159a1c2560e10de51b1c14956e277eb1b37526c9abef9e87c1e531760486448 \ - --hash=sha256:487ab8ec2f01a225d6a1e2ceadc5290cde2c691952bd2e7f76199cf82e06fb25 \ - --hash=sha256:4ab0dadc09c537d4ac57e564039dfe7daf09c98375306d54bfc0fd6c218efcca \ - --hash=sha256:50073f8e63c1e7d3403899755657a990d8bba7b5b5bff66b1c56796d4969bb28 \ - --hash=sha256:520480d4bd3a17557616c25923640953e360332c89d012fffcebd69857e674a9 \ - --hash=sha256:52f16840add2eb02c2416f3b83cec4f527b6c19699f2d31eff4859233c715526 \ - --hash=sha256:56ed585baab75cb98462c57ca88bbdc6a9d935a14118dd572fb476c3ecec2a06 \ - --hash=sha256:58be45d1737bf8c2e10cf29505c0f10f8a23d61bc82e4339182a6c8251cbc2d9 \ - --hash=sha256:59f5fdb8addb44c17520a60d50542d9db6ceba577bbf54efefa9c10ee20be140 \ - --hash=sha256:5a3ac6c62d49f7fe518dfe7fa924fa03aac839993702207802b0e45f9e1b0dab \ - --hash=sha256:67d1593941ede41052b4a35ec25b50d0e280358c7674ef7812d520010e7e8bdf \ - --hash=sha256:6ae63dec6d1d467b7f4737455f81a7a82f14a41c14510937fcfbc726a085b5f8 \ - --hash=sha256:7a3db868a943c814cc557104712d43bf609adfe5ea9f708f38377d366b4855f8 \ - --hash=sha256:7ca7b20bf38d51e15f7808b0239791c4792b1709ce0c63093acaff56a09c31e6 \ - --hash=sha256:7cb3d927360fe73f9a06d646e6082237142ee39c24679c7133d22bf06dd03b45 \ - --hash=sha256:7ef8954e37c80e0c46e6161eef1c72c71059b95250e620a77bd646f6c7a52a2d \ - --hash=sha256:8aac96db8a6c7ee43aba2ee0d3c43893da1fb7c38ed54790c1be2b6d8fd87b96 \ - --hash=sha256:8c5d1e6bad47edf6fb1d7406cf6d67314ac08325c63a49550d782a4596ea302b \ - --hash=sha256:9105c66ea8dbc108adc42058bb7b65bd953f53ee178bf63bf9ebb0cded6c8c96 \ - --hash=sha256:9be37b7ddf0af9e3f9112cd133afc34e78a56da1f96db5f2b4fc289fe1c4d1c3 \ - --hash=sha256:9c084749c115ea7918cf7efa144682783da17eec70d1276689182b871126e715 \ - --hash=sha256:9d23f0f3a14bb6e6f99c793d340196dba4af95ba25bfcab624d1794f540f5e27 \ - --hash=sha256:a16ebce774755a7a652bd44c62101dc914372ed1a98935969624848c9627b4a4 \ - --hash=sha256:a2a20e9835d3c4b293a709ee6ef769bcb18c6ed4ef337a9e251c1a9496d5e8be \ - --hash=sha256:a57352efa938889ea9992667a5014c0fc870d03945de71918574d1cf28276378 \ - --hash=sha256:ab34c61d9249f1a8129cb4276062c04e3e47b5be8de6446e7c7fe11362d6fe43 \ - --hash=sha256:b123b4995a7612f1130e2b2362c9a5d0568df887bf7e7bdb45c23af8cd5423c9 \ - --hash=sha256:b268211e625cd550e361fc387bf1db5deb1e9cae0ce4041116f0a0aafeef7c06 \ - --hash=sha256:b2ddcdaf6691101e06dc4a5add7b8c8fdf1e68daba599255a281f3f3550d3331 \ - --hash=sha256:b4ad2134405d5345434c22dea96bbc12c85abcfc3c253a8063dbc9ff01164555 \ - --hash=sha256:b839dfd1342bb50570cb0c66b80322307cdb468abf14faf5df4dab022bc1b9ce \ - --hash=sha256:b8f22fb8218ba6a452bf9000fc656e1ed57625d17cc8a3871a0fcea3b1b69ebf \ - --hash=sha256:bd312b15044b1c1a0920a5827a830559b2d1fa380851cedf509f8b835309c5b9 \ - --hash=sha256:c0ec3b709508ccd835d8ded1db025b7800618f2289a22a6bfd4927da5f4eb33c \ - --hash=sha256:c4f5be1482189c7b0a1dcac269fffe97a7d18cc04ac9a9a4d6613212dd87f38b \ - --hash=sha256:ca1b48bde68c528a79dec2a2859e05035802e5b1c9c3579f388c9de6ed6d0148 \ - --hash=sha256:d0838a28e9943d5b834ebae59b02adda76e2cd1e65caa808104c72102052057d \ - --hash=sha256:e165f6cc2075059b7c95dac1612bfb25494f72d90f56880e84c288b089f8a896 \ - --hash=sha256:e568a3d766b7ba8df00e0c33efc4c6530cde14fbc72daabe4824eed211ed7596 \ - --hash=sha256:ee47c2cb1be03a052ebd3549dad07f636a98b3ccfd7acbe5e17b3b7da0ab9e37 \ - --hash=sha256:f1fe222f50a1898e87a1e7323ab35f9e956278efabe4dd55a1342808206d05ad \ - --hash=sha256:f28ad27193c1fbcfb52ef2ee63d2b721563525089e80962b4268b306dac45507 \ - --hash=sha256:f2f1b67a48da86d3e41c9445367b49a49f7efdb60fc8b5e3593f05e6afb2efbe \ - --hash=sha256:f7f706df6839dcc53f20833f2933cbcd126fd2fdee7c312e053de49df4b64e44 \ - --hash=sha256:fae7305ae20fddeea09df317b920c45d3e20bfedbdb041f4db6ca5267c458189 \ - --hash=sha256:ffdda3006a383a48f71a23b4f2b3fae3fe1b09af67925d885985f7ec34d66bcb +defusedxml==0.7.1 \ + --hash=sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69 \ + --hash=sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61 + # via -r requirements-noema-document-ci.txt +hypothesis==6.168.3 \ + --hash=sha256:01768a03a30dc54df7fe457c0b34016c84d598fa00d5965ededab93ba4eb3408 \ + --hash=sha256:0369f5df055f96e117ab12e5f249668ff144731ab5280bc7a205fdf81f990b89 \ + --hash=sha256:03b131043608f94a2578a2a896a1a72092acb7079815eef5b8513b08447e0e62 \ + --hash=sha256:0608c610fc002978fc5de0f471770d8817e9455cb8649a47983c9f8bccfc1897 \ + --hash=sha256:0bdfc53c041b61c854fa3761735736b991bc2bddafee45a361cfe4d43027c1ef \ + --hash=sha256:0df00cbe8133aa220308d11fa28e3add996cf5102a39b279c1d711015fd9114b \ + --hash=sha256:0e1d91530cefdb9e0b6467c203eb509c61a11d70480a37db59cb53a2f9913102 \ + --hash=sha256:101531b4ccf7fa12965a6b295d10b64c2f8a8fab61e1f9b18c55335d8fb02575 \ + --hash=sha256:140fe3cc7ba98a4ffeeb6492c318b43c326110a4d7b4f56bb759d5c264e2dc1c \ + --hash=sha256:1a8a4ffc6c6e6e577f2bfbcfebf7cffbb310283ba2532c729570a0a752cebaaa \ + --hash=sha256:1b230a850de63334c16654a34a2d547e0179d36b9071d4439b3e7237f6d077e7 \ + --hash=sha256:209dc54cdb1b4d6d7020ad8d09e44c49756361b7183adacea4d6f5705085b595 \ + --hash=sha256:26084ef31653108da9e5eb171278a1d930e90d8631604391f828219f5c1f6cb2 \ + --hash=sha256:26928956c54748e4dfa587333741ab750246123b93484955646ff316cca27eef \ + --hash=sha256:28d88fa174ecbd4ecbd7bb290f06d0db084a3971c3f511ae2830b65b5e25500f \ + --hash=sha256:29dc56e6dc6eeb0aeb08ac463f847279bde2336c4786faf7ee0af4b93cd031a7 \ + --hash=sha256:2b52ac363096232bebc2add117e9178d91f4248f4cdb919fd1026b5f86a4bb16 \ + --hash=sha256:320920b1e3dae8611eee8a03d063cf2187446f2a17c38cfb8a7fc1466f71eee2 \ + --hash=sha256:32d0699566aaa93f9e97a44705d7164386f1e91de78d277f53bada35e78bcc96 \ + --hash=sha256:35380baa981108a7f60c4eab71e46acd8d8f58440520346a1a6aba06dca7e074 \ + --hash=sha256:377438de53afb94347d9845b7d90db5905c6d2a8d612108e9938e0f80503bb90 \ + --hash=sha256:3bc85014577982ec6d2e266edc5cd6e7a7d3674c648791ca983c67a52f89a0c4 \ + --hash=sha256:3cf6f1eeaf41cd8d60cf1f88fde905ca1dd77c906929a507d6ac7f66f2ccba2a \ + --hash=sha256:3ef7d26f5789e691401d5f87eafed9bd2763f0dbe47af6d6b66012a509404766 \ + --hash=sha256:3f1122223759acc0fe5301c1ae505d762ee61af91db7b191a791ccdbcd965462 \ + --hash=sha256:41e0120814de5c3a6b58d8cb27cb11b8873ab33fba130cd09855a27f5a12a1ca \ + --hash=sha256:44ace770bda3a0301739fc5a413c764de790c739df1f1a7218dd049f8594d9f5 \ + --hash=sha256:4bedbb379eab34f792af7ee9a05aae04e9c08bbb52e0f90f5a2110d4fe4b2fbd \ + --hash=sha256:558625ff28e415f2f770cad618c2c9e05a045a6586949397f178d537bec2f083 \ + --hash=sha256:5a953115b9f5c95133ab2d04efffeec96e5658c3207923dca7285f7db3e6bbef \ + --hash=sha256:6042b8707a4b25bbbfe20b110258fb7549a68951e5525608a8ce06091039e9fc \ + --hash=sha256:6173558e676ad25ed1e20507fa4024a0d816dd90f715f77006e5a28f19109026 \ + --hash=sha256:61f5782d807b1e6aa5c9beef1054cb2037e7d1add48a64972cd6ee447778781f \ + --hash=sha256:650528e2b1e2a45e95c2df624d4b9364b8ac5a027ba84e1eea2bc5398dd01dcd \ + --hash=sha256:65d78e4357ec48ed2c67825f06740ee3599be4cfe770a6092bed07108d679ac5 \ + --hash=sha256:71ab606c472449cb872ef2a7acaec679ee4f651b7f0a477a04ebc85d8933ef8c \ + --hash=sha256:7311d8ff296806ea41513d52edc832fcf43a1dc8d5c5f308cc386fc31b253a05 \ + --hash=sha256:73960a58f6efc8cbc57d8ad0647955f7c5b25f3d562e3eaa57afa1c69894f7aa \ + --hash=sha256:73c5627497968cc62d140e9fde1ea21e12cac7b64dc419fea8286cd34ff1ad4e \ + --hash=sha256:753bb501f8560d2e321ed3b62596b4496c56f15668b0a0669231ccc3e6c4e80d \ + --hash=sha256:785e2c45f8c08e274b4bf1ccae97f1a4e09407e9a68e80790cfab1d17a4a45fa \ + --hash=sha256:7aacf3cf40c7ce8f9e4924d5b57bc0b068beafdfed2347bcf160a28b4cfbba7b \ + --hash=sha256:7b9638789548361a57d984f56619ac694a914c328181d911271618409261ff4a \ + --hash=sha256:818b3d09bba60ef90463e47a4bc87275dba1e8aeb184d3102888440ca7c7837d \ + --hash=sha256:8367f623a98cbf90f33fd2a43b100671b152b5975bc343a578290945f52f7018 \ + --hash=sha256:836eda971f25ddfce274107ed113297e8acede8d9b267775742b8ba8a404d0f0 \ + --hash=sha256:9029775dc2e25e3e8e596c4306926f0079158353631c7c52c285bbc5c8073c2b \ + --hash=sha256:92325b276360fe86c5bf71a568c0d53a6d140b0de36dcf029f9164a17803bb24 \ + --hash=sha256:9b2d47f5d9060b049039bef0b267e88480f6468f95ee394e514e84a81ccdc5f5 \ + --hash=sha256:9d120009d145697909f2f0c532f2d568558ddd6990707f8cba6576bfb9770696 \ + --hash=sha256:9dcf6448b1ecc37f2b23f2d1b3ddfc9ff6b6910614c15a642dc82f419b96037b \ + --hash=sha256:9e3c36eba80e089ecfa28ec08049723223cb41d3cfe4d9988169cf98440e402b \ + --hash=sha256:a43388f9067678fef6e13bdff325b6cfa6961a590498bb37f7ff31589c83bc75 \ + --hash=sha256:aac5889ea7b0b86cf2bf5709250b7576c44859dd249d1be23c009a7d6fb9f742 \ + --hash=sha256:ae4f9f094041dcce5119ebd7bab71062743ab02b6e654d056b370beda78c19e2 \ + --hash=sha256:af8ca98cd11dc7f9427bb90483abcd9688d4df2e8e63893a30a2423b027ebb11 \ + --hash=sha256:b1872d2dc3f3758dfbf17cc39838b8e6b6a5c39a37811c067245e87b12d50f74 \ + --hash=sha256:b345f818083ec99966a43ca4f7b38feb62c6920ce28572bc2bde4948a8b7eaba \ + --hash=sha256:b823ba1fcec8da730f29316d010b06d3f7e0c3828dcf91020e24c55e7d24652a \ + --hash=sha256:b987d73eba95183a7e59cca6d1925c588aa4307922d9852cc2b4d282a2ae4128 \ + --hash=sha256:bb1063cb794765097b8c0add598bd34a904f44dc15db81275dd1f0ed1ac567ed \ + --hash=sha256:bc2b1c37631f2231dd0d077225aa5908fc2bd34a4c3faf475053b0bc2eb24c95 \ + --hash=sha256:bff12e036b67abc5ded682f724f4d7afaff5223190270a0763695f218c079068 \ + --hash=sha256:c73c6188056e6dc110260e39d141c69c3d02f23e3ba1d8610c7a8b6f2510ec96 \ + --hash=sha256:ccfc29505aa1cdcc254cb9cd701fd0811fef5480addd97d4127a00df023410f7 \ + --hash=sha256:d20972ca134e652e9928ecd200966a8a2857adc2812c1d74b12a872e5cd503eb \ + --hash=sha256:d28e3a6b511a74ce37df5274b51f36c2b274fea7365e00b16f0c81e22acd5957 \ + --hash=sha256:d4569c39bd97d9573e946429ed676f3b55a7c8ed80920addd67d384a47d59b38 \ + --hash=sha256:d479985fe73af97badfb72dc6d20c6a353e486a36f6d065f600026e0cf954a86 \ + --hash=sha256:d63b0226cd3e0d8bdd97c3384b22a21934ed4d53246c1c93575dada616672499 \ + --hash=sha256:dc66390fb12d80585aa9222bf538ce8b7aa22cf5d118250647355a1c9e8f62f4 \ + --hash=sha256:dd2849c269d674e4618590f3b48d443bd4c06b5aef3d8d869086c2e6d213d248 \ + --hash=sha256:e2d4c68729a13df9af4998d2652cfb5d541c5609b88c306880a5dfb284938ec2 \ + --hash=sha256:e9784aca26eddfe99b03a0292320db742cc8a74200ef864fdce949f526f973cd \ + --hash=sha256:eafbec09d3e87d13d8242411d1f5868b5e879f1bd6d95e233e9ff80c527bea1c \ + --hash=sha256:f071737e4e775bebba07e319eb645a880d1e0186b4d24bad23255e849d86d483 \ + --hash=sha256:f076bcd0f77fdcdb7797826c879099573d03a02e228ebe649ea917081b962ac0 \ + --hash=sha256:f09c05a23a8025dd5cad07c2ed299a46778e72d49ff0dd5bf353bcc0f7dc1580 \ + --hash=sha256:f0aaaed00438fa6673d856aaee12a4a8afbde82a9f3c5ff487cacfb064239afe \ + --hash=sha256:f27e6df1576bf838e7f484d4ae5cba92114497ca30afb917056bf1ea33e95675 \ + --hash=sha256:f413629de94d38a7a2ad259697a6752526143cb49c2e2d7bdba19381c80693f6 \ + --hash=sha256:f59a3912858d0609c26054aa1c474847c797937f5e0e1c77f0d3f08032e50f09 \ + --hash=sha256:f8122cfdd0bc0ba843063effa22ac310bdebdb3a1319bf1e63f14baa119c72ab \ + --hash=sha256:fd7f75a2e23288ee82ee965a952473d9c5447c2cbc1afc94be09d2402201774e \ + --hash=sha256:fd81241f4cd76fb18d481e87b9688b30a0cb5c30841730513323ba1c7aba1837 # via -r requirements-opencode-review-ci.txt iniconfig==2.3.0 \ --hash=sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730 \ @@ -209,9 +236,25 @@ interrogate==1.7.0 \ --hash=sha256:a320d6ec644dfd887cc58247a345054fc4d9f981100c45184470068f4b3719b0 \ --hash=sha256:b13ff4dd8403369670e2efe684066de9fcb868ad9d7f2b4095d8112142dc9d12 # via -r requirements-opencode-review-ci.txt -packaging==26.2 \ - --hash=sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e \ - --hash=sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661 +maturin==1.15.0 \ + --hash=sha256:0ebf9767892725083138e671c34482c660317a2f3d6a29fc0e0f34e9d8c99136 \ + --hash=sha256:126e12e618b4db42f68c779a56d41f82a390145ba36ac3f621d057eb34f5ad9d \ + --hash=sha256:4f9d33e6c3f9615c8caceecbbbd440f8eb25a3ddeb687077682cd5eca2e9ae15 \ + --hash=sha256:552c2be4afd43fe8d5c9f3ec8d4c4756d973b8dcbe94c14084390301f50243e1 \ + --hash=sha256:653020a63525bb224e5ab0adf02e17a2e08bc86dbea7fc1399c9a56d7529b99e \ + --hash=sha256:6bf6dc62e22d4dcfd5a51244ff0d58975fa4979c48209fe84159617648956d82 \ + --hash=sha256:7ab7eebffd7b8debca2265985de4eaeb332141276d24b9560b5ad484d4b3add1 \ + --hash=sha256:7eb066372f541f8eb4909c79c5d9bd0b9e8125980bdf1ec9e8aba23c6c8d6c55 \ + --hash=sha256:94b26cc8e8aba61a5f2099715fe640e18c5f678e9a500408b38761263954228a \ + --hash=sha256:bf29beddd0c6708f112db51d5275fc28b28b9e9c9c5faae387eaef662918b176 \ + --hash=sha256:c40b4eae7bf5ef1f4b1af8d623fe4105016f93578fb15b764e741d08ec3b92dd \ + --hash=sha256:c7dc0c66c78d3debdd9c5aa807e861fbcbf07f3505d34b125df74c03986b0f48 \ + --hash=sha256:cd35772633f489841132bc8e71d6fc7f842df30b9c05cd5cdf1ee1ddcb744cc7 \ + --hash=sha256:da649988be98e87e009e51b1bf0d301b6a301bc0cecbdd60d40d8ba60748d1ca + # via -r requirements-opencode-review-ci.txt +packaging==26.3 \ + --hash=sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79 \ + --hash=sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c # via pytest pluggy==1.6.0 \ --hash=sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3 \ @@ -223,9 +266,9 @@ py==1.11.0 \ --hash=sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719 \ --hash=sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378 # via interrogate -pygments==2.20.0 \ - --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \ - --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 +pygments==2.21.0 \ + --hash=sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9 \ + --hash=sha256:610ca751c9bc2492b38eb9a38a7fbc93edbbb2d7182edaf34e66ae493dee5c8c # via pytest pytest==9.1.1 \ --hash=sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313 \ @@ -237,6 +280,81 @@ pytest-cov==7.1.0 \ --hash=sha256:30674f2b5f6351aa09702a9c8c364f6a01c27aae0c1366ae8016160d1efc56b2 \ --hash=sha256:a0461110b7865f9a271aa1b51e516c9a95de9d696734a2f71e3e78f46e1d4678 # via -r requirements-opencode-review-ci.txt +pyyaml==6.0.3 \ + --hash=sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c \ + --hash=sha256:0150219816b6a1fa26fb4699fb7daa9caf09eb1999f3b70fb6e786805e80375a \ + --hash=sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3 \ + --hash=sha256:02ea2dfa234451bbb8772601d7b8e426c2bfa197136796224e50e35a78777956 \ + --hash=sha256:0f29edc409a6392443abf94b9cf89ce99889a1dd5376d94316ae5145dfedd5d6 \ + --hash=sha256:10892704fc220243f5305762e276552a0395f7beb4dbf9b14ec8fd43b57f126c \ + --hash=sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65 \ + --hash=sha256:1d37d57ad971609cf3c53ba6a7e365e40660e3be0e5175fa9f2365a379d6095a \ + --hash=sha256:1ebe39cb5fc479422b83de611d14e2c0d3bb2a18bbcb01f229ab3cfbd8fee7a0 \ + --hash=sha256:214ed4befebe12df36bcc8bc2b64b396ca31be9304b8f59e25c11cf94a4c033b \ + --hash=sha256:2283a07e2c21a2aa78d9c4442724ec1eb15f5e42a723b99cb3d822d48f5f7ad1 \ + --hash=sha256:22ba7cfcad58ef3ecddc7ed1db3409af68d023b7f940da23c6c2a1890976eda6 \ + --hash=sha256:27c0abcb4a5dac13684a37f76e701e054692a9b2d3064b70f5e4eb54810553d7 \ + --hash=sha256:28c8d926f98f432f88adc23edf2e6d4921ac26fb084b028c733d01868d19007e \ + --hash=sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007 \ + --hash=sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310 \ + --hash=sha256:37503bfbfc9d2c40b344d06b2199cf0e96e97957ab1c1b546fd4f87e53e5d3e4 \ + --hash=sha256:3c5677e12444c15717b902a5798264fa7909e41153cdf9ef7ad571b704a63dd9 \ + --hash=sha256:3ff07ec89bae51176c0549bc4c63aa6202991da2d9a6129d7aef7f1407d3f295 \ + --hash=sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea \ + --hash=sha256:418cf3f2111bc80e0933b2cd8cd04f286338bb88bdc7bc8e6dd775ebde60b5e0 \ + --hash=sha256:44edc647873928551a01e7a563d7452ccdebee747728c1080d881d68af7b997e \ + --hash=sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac \ + --hash=sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9 \ + --hash=sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7 \ + --hash=sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35 \ + --hash=sha256:5498cd1645aa724a7c71c8f378eb29ebe23da2fc0d7a08071d89469bf1d2defb \ + --hash=sha256:5cf4e27da7e3fbed4d6c3d8e797387aaad68102272f8f9752883bc32d61cb87b \ + --hash=sha256:5e0b74767e5f8c593e8c9b5912019159ed0533c70051e9cce3e8b6aa699fcd69 \ + --hash=sha256:5ed875a24292240029e4483f9d4a4b8a1ae08843b9c54f43fcc11e404532a8a5 \ + --hash=sha256:5fcd34e47f6e0b794d17de1b4ff496c00986e1c83f7ab2fb8fcfe9616ff7477b \ + --hash=sha256:5fdec68f91a0c6739b380c83b951e2c72ac0197ace422360e6d5a959d8d97b2c \ + --hash=sha256:6344df0d5755a2c9a276d4473ae6b90647e216ab4757f8426893b5dd2ac3f369 \ + --hash=sha256:64386e5e707d03a7e172c0701abfb7e10f0fb753ee1d773128192742712a98fd \ + --hash=sha256:652cb6edd41e718550aad172851962662ff2681490a8a711af6a4d288dd96824 \ + --hash=sha256:66291b10affd76d76f54fad28e22e51719ef9ba22b29e1d7d03d6777a9174198 \ + --hash=sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065 \ + --hash=sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c \ + --hash=sha256:79005a0d97d5ddabfeeea4cf676af11e647e41d81c9a7722a193022accdb6b7c \ + --hash=sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764 \ + --hash=sha256:7f047e29dcae44602496db43be01ad42fc6f1cc0d8cd6c83d342306c32270196 \ + --hash=sha256:8098f252adfa6c80ab48096053f512f2321f0b998f98150cea9bd23d83e1467b \ + --hash=sha256:850774a7879607d3a6f50d36d04f00ee69e7fc816450e5f7e58d7f17f1ae5c00 \ + --hash=sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac \ + --hash=sha256:8da9669d359f02c0b91ccc01cac4a67f16afec0dac22c2ad09f46bee0697eba8 \ + --hash=sha256:8dc52c23056b9ddd46818a57b78404882310fb473d63f17b07d5c40421e47f8e \ + --hash=sha256:9149cad251584d5fb4981be1ecde53a1ca46c891a79788c0df828d2f166bda28 \ + --hash=sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3 \ + --hash=sha256:96b533f0e99f6579b3d4d4995707cf36df9100d67e0c8303a0c55b27b5f99bc5 \ + --hash=sha256:9c57bb8c96f6d1808c030b1687b9b5fb476abaa47f0db9c0101f5e9f394e97f4 \ + --hash=sha256:9c7708761fccb9397fe64bbc0395abcae8c4bf7b0eac081e12b809bf47700d0b \ + --hash=sha256:9f3bfb4965eb874431221a3ff3fdcddc7e74e3b07799e0e84ca4a0f867d449bf \ + --hash=sha256:a33284e20b78bd4a18c8c2282d549d10bc8408a2a7ff57653c0cf0b9be0afce5 \ + --hash=sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702 \ + --hash=sha256:b30236e45cf30d2b8e7b3e85881719e98507abed1011bf463a8fa23e9c3e98a8 \ + --hash=sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788 \ + --hash=sha256:b865addae83924361678b652338317d1bd7e79b1f4596f96b96c77a5a34b34da \ + --hash=sha256:b8bb0864c5a28024fac8a632c443c87c5aa6f215c0b126c449ae1a150412f31d \ + --hash=sha256:ba1cc08a7ccde2d2ec775841541641e4548226580ab850948cbfda66a1befcdc \ + --hash=sha256:bdb2c67c6c1390b63c6ff89f210c8fd09d9a1217a465701eac7316313c915e4c \ + --hash=sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba \ + --hash=sha256:c2514fceb77bc5e7a2f7adfaa1feb2fb311607c9cb518dbc378688ec73d8292f \ + --hash=sha256:c3355370a2c156cffb25e876646f149d5d68f5e0a3ce86a5084dd0b64a994917 \ + --hash=sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5 \ + --hash=sha256:d0eae10f8159e8fdad514efdc92d74fd8d682c933a6dd088030f3834bc8e6b26 \ + --hash=sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f \ + --hash=sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b \ + --hash=sha256:eda16858a3cab07b80edaf74336ece1f986ba330fdb8ee0d6c0d68fe82bc96be \ + --hash=sha256:ee2922902c45ae8ccada2c5b501ab86c36525b883eff4255313a253a3160861c \ + --hash=sha256:efd7b85f94a6f21e4932043973a7ba2613b059c4a000551892ac9f1d11f5baf3 \ + --hash=sha256:f7057c9a337546edc7973c0d3ba84ddcdf0daa14533c2065749c9075001090e6 \ + --hash=sha256:fa160448684b4e94d80416c0fa4aac48967a969efe22931448d853ada8baf926 \ + --hash=sha256:fc09d0aa354569bc501d4e787133afc08552722d3ab34836a80547331bb5d4a0 + # via -r requirements-opencode-review-ci.txt sortedcontainers==2.4.0 \ --hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \ --hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0 diff --git a/requirements-opencode-review-ci.txt b/requirements-opencode-review-ci.txt index 1e9a42f6a0..452ce2f074 100644 --- a/requirements-opencode-review-ci.txt +++ b/requirements-opencode-review-ci.txt @@ -4,6 +4,15 @@ coverage==7.15.4 # collection. Matches the >=6.100 floor used by consumer repos (e.g. contextual-orchestrator). hypothesis>=6.100 interrogate==1.7.0 +# maturin (MIT/Apache-2.0, permissive) builds the PyO3 extension module for +# maturin/PyO3 projects (e.g. fast-mlsirm) inside the offline coverage sandbox, +# so `python3 -m coverage run -m pytest` can import the compiled `_core` +# module instead of failing collection with `ImportError: cannot import name +# '_core'`. See fast-mlsirm#1907. +maturin==1.15.0 pytest==9.1.1 pytest-cov==7.1.0 +# Full-suite collection imports the Noema document parser and workflow YAML. +-r requirements-noema-document-ci.txt +PyYAML==6.0.3 uv==0.12.7 diff --git a/requirements-pip-audit-ci-hashes.txt b/requirements-pip-audit-ci-hashes.txt index 7a41a3d2d8..9a331a0d62 100644 --- a/requirements-pip-audit-ci-hashes.txt +++ b/requirements-pip-audit-ci-hashes.txt @@ -391,7 +391,9 @@ tomli-w==1.2.0 \ --hash=sha256:188306098d013b691fcadc011abd66727d3c414c571bb01b1a174ba8c983cf90 \ --hash=sha256:2dd14fac5a47c27be9cd4c976af5a12d87fb1f0b4512f81d69cce3b35ae25021 # via pip-audit -urllib3==2.7.0 \ - --hash=sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c \ - --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 - # via requests +urllib3==2.8.0 \ + --hash=sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3 \ + --hash=sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63 + # via + # -r requirements-pip-audit-ci.txt + # requests diff --git a/requirements-pip-audit-ci.txt b/requirements-pip-audit-ci.txt index 684087ba51..bec9276178 100644 --- a/requirements-pip-audit-ci.txt +++ b/requirements-pip-audit-ci.txt @@ -1 +1,2 @@ pip-audit==2.10.1 +urllib3==2.8.0 diff --git a/requirements-strix-ci-hashes.txt b/requirements-strix-ci-hashes.txt index 9e705850b5..ba3198c876 100644 --- a/requirements-strix-ci-hashes.txt +++ b/requirements-strix-ci-hashes.txt @@ -140,10 +140,11 @@ annotated-types==0.7.0 \ --hash=sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53 \ --hash=sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89 # via pydantic -anyio==4.14.0 \ - --hash=sha256:b47c1f9ccf73e67021df785332508f99379c68fa7d0684e8e3492cb1d4b23f89 \ - --hash=sha256:dd9b7a2a9799ed6552fde617b2c5df02b7fdd7d88392fc48101e51bae46164d9 +anyio==4.14.2 \ + --hash=sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494 \ + --hash=sha256:cfa139f3ed1a23ee8f88a145ddb5ac7605b8bbfd8592baacd7ce3d8bb4313c7f # via + # -r requirements-strix-ci.txt # google-genai # gql # httpx @@ -1119,24 +1120,45 @@ jsonschema-specifications==2025.9.1 \ --hash=sha256:98802fee3a11ee76ecaca44429fda8a41bff98b00a0f2838151b113f210cc6fe \ --hash=sha256:b540987f239e745613c7a9176f3edb72b832a4ac465cf02712288397832b5e8d # via jsonschema -litellm==1.94.1 \ - --hash=sha256:001be1cde7950f2ae484e450ab2f8e93ab8791e5e8d4da560d21f2fb456b0b47 \ - --hash=sha256:07c1771315d7d26e242ef90b9336bcbc49a52158ff72ee640b4f8160cc963147 \ - --hash=sha256:156c62022320bccab7c3507b6b13400b26e55b74c799e5a4a2d5bf904a77368f \ - --hash=sha256:1b0bc4a2373e54f2bd4c13f8ef9fda3839bfb2e1173fb4bcea3150b07d4c59bc \ - --hash=sha256:2103e9b155d6545b48936d2ac2e614661613adb9e3d081c58c7303ca5dd6c656 \ - --hash=sha256:44e55a55270dee8bb85e063940c368d32040e6db66765c55db4b884fc002d4ef \ - --hash=sha256:66bc95498af3ab687ce7570704cb274bcf1d78049afa87a9f5f64db45b72847d \ - --hash=sha256:a6f5274876f20dd5c9e53ba3da502e94f5b3c681c5027a0398231d0caae4aacd \ - --hash=sha256:af37356cf5b325a2887c40ff772b39e1e0865b988297c544b29123ffb13fd1b9 \ - --hash=sha256:b0145d6b9fb718d12b7242ce5c975123f4dbfecd7b8ed1eb6a6939b0e506c946 \ - --hash=sha256:c5c9247d9fea8fe7cda851f7b15db560ee547a8325a0af86048967edf3ccfa15 \ - --hash=sha256:cfef0468bda9c1ba8f554bebc2966f08436f1ead98017e7ed2d7663ece77f1c2 \ - --hash=sha256:d14e5812b5f36af2ab45461ee0c925251bc07daf65c33b8f2ce3fd3ec1235eae \ - --hash=sha256:e9b6d92e305d96bdadb8a5ccd343b1ac188de142fbd6c91f72c75416b8c25c48 \ - --hash=sha256:e9effe4c1e9206740b4bb4c98142ea1f71bae57e49df007cd25ef24b0ce4563f \ - --hash=sha256:ffa9a6cd9b6205d60b02ffc0b7f077a03693d835b06d2a34bfeaabb4f073c08a +litellm==1.94.3 \ + --hash=sha256:0ddb60a439cc361e69bb4f550a78d1656c8edad6753f8fece14f60bbd0fcd054 \ + --hash=sha256:1017f29a6f72a16ef1b026208b3e52c7197b91ae848b396c817bfb051f86c300 \ + --hash=sha256:136960922878cbc66065346583a60fd0d35f3b1dbe853d636d10da39f049399f \ + --hash=sha256:14f06f1486aa4eef0684a2088539548e3647495fa119edc2b8145c5cfee3ca24 \ + --hash=sha256:1594d231e005f6a99e319310f2e3c0d33f26d8c746e90ec42fedc2b413f16573 \ + --hash=sha256:1ec0ff6e81acbfa85b4af90f9460c46a7872a28d53aaeee956ad7d289f47edc4 \ + --hash=sha256:2098c818ce5d8139eb46a851f26d1ac5caac1502e19d5223af4930b31682c461 \ + --hash=sha256:45b9f56f02a5417b249ecaf6f01f2531e744b17b701aaea441741ed9431ba35e \ + --hash=sha256:4a21e3f5cc72b18bd424009e5ad9addcee5ebdd726c7b26cadb4c87f865f5f1b \ + --hash=sha256:56bb6b303e2e341eb55570fd0cfa5734e5d89e8a1a5ee0096e7bb0bd9f27f9e2 \ + --hash=sha256:57a82dbbf1528aa530f021f1e6fe7528331c7d5f95fa755a17f984623eaf29ae \ + --hash=sha256:584b705ece98e7b7a2fc3cf4b720711d0b786e4d131de5c4469c34cabc3f55e0 \ + --hash=sha256:5b7ef13f56e167c76ca5dfc2e6bba8e52faf6371a540c00585ee7cff81af4ba0 \ + --hash=sha256:68b5b8c4ec8d51e4a2d02cf5405b6dd85be06b8ccf0205b7b0b30c1d3c02aebe \ + --hash=sha256:6ae267172e1c615346523faf6de6cac2f21937bda2e15e0af8c5d3291476a4e5 \ + --hash=sha256:87a343c86304daa6be5f96493f1eb0796d57142886f21f8d0815e1712d2bfc18 \ + --hash=sha256:93e13c1919f8eaeb3bee9a9160c6339d1fc3913254981a1e152023c5815667cc \ + --hash=sha256:9d0fabd73ca571771773a892de1c57e208349c99a3bbc51cd049a326f7a6f6e2 \ + --hash=sha256:9e339bea866f313487b1d7e150374360700f7ed80cac41722f7ddb072b9b685e \ + --hash=sha256:9f0529505cd5cceed220f89b311f4403fa076beae18069e9df31c4e59b226871 \ + --hash=sha256:a18386278e02a2b7f8d73bf836f1c7945133becca7894ee0641dbacc6473322d \ + --hash=sha256:ac40df3e6f412a2e87f61553640922d63c8114e433c85992461317b5f67ce78f \ + --hash=sha256:b6984b3b8032ab6807aa7c56423e961a5c4220d6b93a348dbf37ec1162b68e2d \ + --hash=sha256:b6ba10b699165a9a46811f2d3abe92967dc1c2d16f972d1057dae5c5c9cd460b \ + --hash=sha256:c0eee30a6d7c38fd4702339a5a53648f140ed8e5307baa321215d0d10dd9e535 \ + --hash=sha256:c526656e85ee1be21038c75774c094b97e7439e649dc13f61cb434700199990a \ + --hash=sha256:ccf58062430fdfd53d1ebb02d55d2316ad1068005dbb4186a522039077489f9b \ + --hash=sha256:d2027a4e492fd85702ffdbcc43be770a450c521b8c8c0c995e6f7d2fb6df1d49 \ + --hash=sha256:d8eedd2dc2e504ea8452392c4c66c102ed7ccd204307d72420fd073552b59ea9 \ + --hash=sha256:de2b3cc1a5cb5b9c7e393b55a0da58cdbe46f0b373b215d4422e30b2d5dec29b \ + --hash=sha256:deac3f3efdb5373729f37f612d2469696bbc928baef5f7c80ae7a2b0940b7939 \ + --hash=sha256:e05cabfc94633e47b725bed0851ecf18809117d23e8d6ef9abaa6059c2712261 \ + --hash=sha256:e4255bc09a27d99e8c1526baf76ff307e16d3543f51fda9c87c51a71c99c8ff7 \ + --hash=sha256:f8913c9f10ef3fb1baf4ab16fc77e110f92c1c0a9a16fa0f86d1b90b417e063f \ + --hash=sha256:f8c9a92d6165570065349b8d324e7b58800cb993c39b85a5f3c7458b5d926f3d \ + --hash=sha256:fafb59618a85f3c96d38a123ac53187c75e722a6b4e34d305063661262f62ea8 # via + # -r requirements-strix-ci.txt # openai-agents # strix-agent markdown-it-py==4.2.0 \ @@ -1811,17 +1833,19 @@ pygments==2.20.0 \ --hash=sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f \ --hash=sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176 # via rich -pyjwt==2.13.0 \ - --hash=sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423 \ - --hash=sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728 - # via mcp +pyjwt==2.15.0 \ + --hash=sha256:7a3742debf6b879e912dbb9819ceec1594be812452b78c5f2e2dfc56564954f8 \ + --hash=sha256:b11c5f9791d7bf51c2b39a81ed669f6b2dbbd669df2942f6c60167e9e3d1abe4 + # via + # -r requirements-strix-ci.txt + # mcp pyopenssl==26.4.0 \ --hash=sha256:28dfcce0162b9211413e26dfbfdf1d24317fbeba18fc93c12400a1856b2a0bc7 \ --hash=sha256:f0eb0cb2d581d3ad2b9c489468485e7f2ab6727d08401bcf9d824c3caddf3c1c # via google-auth -pypdf==6.16.1 \ - --hash=sha256:63fec31c4092ae50b6729beedcb469055b60d20c834bde1c402df241f371f644 \ - --hash=sha256:c4d1b43ddae921387321cf63936cd16a7743b91d2da92f165c149a195c972ba9 +pypdf==6.19.0 \ + --hash=sha256:7e5d6e730e7dae87d560a2cee218b852f6498c8be61966f3cd02ead971e48d14 \ + --hash=sha256:bbc43aca292369ccc6cbc8a921991ecf2538a3587ab5a116eff06c321d647155 # via strix-agent python-dateutil==2.9.0.post0 \ --hash=sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3 \ @@ -2346,10 +2370,11 @@ typing-inspection==0.4.4 \ # mcp # pydantic # pydantic-settings -urllib3==2.7.0 \ - --hash=sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c \ - --hash=sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897 +urllib3==2.8.0 \ + --hash=sha256:0cf3cae568d36aa9576b28dfb35f11328f1cb974ca7647d9475ebb86c75ac6e3 \ + --hash=sha256:63bf2ead4c879426ebf22ef2a781eeb4aa3b4ae798a0435506f8687fd5bb9b63 # via + # -r requirements-strix-ci.txt # docker # requests uvicorn==0.49.0 \ diff --git a/requirements-strix-ci.txt b/requirements-strix-ci.txt index 19093441e9..3a31a6c2c1 100644 --- a/requirements-strix-ci.txt +++ b/requirements-strix-ci.txt @@ -1,5 +1,9 @@ strix-agent==1.5.3 +litellm==1.94.3 +anyio==4.14.2 +pyjwt==2.15.0 openai[httpx2]==2.54.0 +urllib3==2.8.0 aiohttp==3.14.3 google-cloud-aiplatform==1.133.0 protobuf<8.0.0 diff --git a/scripts/ci/actions_queue_health.py b/scripts/ci/actions_queue_health.py new file mode 100644 index 0000000000..7b1cc5e49f --- /dev/null +++ b/scripts/ci/actions_queue_health.py @@ -0,0 +1,575 @@ +#!/usr/bin/env python3 +"""Queue-health CLI with stable identity and audit-provenance guarantees. + +Shared parsing and reporting primitives live in ``actions_queue_health_core.py``. +This entrypoint owns collection and the consistency boundary that binds active-run evidence to +a stable pull-request view, carries stable workflow identity, and exports the +exact timestamp used for queue-age calculations. +""" + +from __future__ import annotations + +import importlib.util +import sys +import time +from datetime import datetime, timezone +from pathlib import Path +from urllib.parse import quote + +_CORE_MODULE_PATH = Path(__file__).with_name("actions_queue_health_core.py") +_CORE_MODULE_SPEC = importlib.util.spec_from_file_location( + "actions_queue_health_core", _CORE_MODULE_PATH +) +if _CORE_MODULE_SPEC is None or _CORE_MODULE_SPEC.loader is None: # pragma: no cover + raise RuntimeError("unable to load queue-health core module") +_core_module = importlib.util.module_from_spec(_CORE_MODULE_SPEC) +sys.modules.setdefault("actions_queue_health_core", _core_module) +_CORE_MODULE_SPEC.loader.exec_module(_core_module) + +for core_symbol_name, core_symbol in vars(_core_module).items(): + if not core_symbol_name.startswith("__"): + globals()[core_symbol_name] = core_symbol + +_CORE_NORMALISE_RUN = _core_module._normalise_run +_CORE_BUILD_REPORT = _core_module.build_report +TERMINAL_DIAGNOSTIC_STATUSES = ("startup_failure", "cancelled", "failure") +TARGET_TERMINAL_DIAGNOSTIC_STATUSES = ("cancelled",) +TERMINAL_DIAGNOSTIC_MAX_API_PAGES = MAX_API_PAGES + + +def _normalise_run( + repository_name: str, + workflow_run: dict[str, Any], + workflow_jobs: list[dict[str, Any]], +) -> dict[str, Any]: + """Normalize one run while preserving stable GitHub workflow identity.""" + if not isinstance(workflow_run, dict): + raise QueueHealthError("workflow run must be an object") + workflow_id = workflow_run.get("workflow_id") + if workflow_id is not None and ( + isinstance(workflow_id, bool) + or not isinstance(workflow_id, int) + or workflow_id <= 0 + ): + raise QueueHealthError("workflow id must be a positive integer") + + normalized_run = _CORE_NORMALISE_RUN( + repository_name, workflow_run, workflow_jobs + ) + workflow_name = normalized_run["workflow_name"] + normalized_run["workflow_id"] = workflow_id + normalized_run["workflow_identity"] = ( + f"workflow_id:{workflow_id}" + if workflow_id is not None + else f"workflow_name:{workflow_name}" + ) + return normalized_run + + +_core_module._normalise_run = _normalise_run + + +def _read_pull_request_snapshot( + pulls_endpoint: str, *, runner: Runner +) -> list[dict[str, Any]]: + """Read and normalize one bounded open-pull-request identity snapshot.""" + pull_request_entries = _list_payload( + github_json(pulls_endpoint, paginate=True, runner=runner), + "pulls", + max_items=MAX_API_PAGE_SIZE * MAX_API_PAGES, + ) + return sorted( + (_normalise_pull_request(pull_request) for pull_request in pull_request_entries), + key=lambda pull_request: pull_request["number"], + ) + + +def _pull_request_identity_view( + pull_requests: list[dict[str, Any]], +) -> dict[int, tuple[str, str]]: + """Return the number/state/head view that must stay stable during collection.""" + return { + pull_request["number"]: ( + str(pull_request.get("state") or ""), + str(pull_request.get("head_sha") or ""), + ) + for pull_request in pull_requests + } + + +def collect_snapshot( + repositories: Sequence[str], + *, + runner: Runner = subprocess.run, + generated_at: str | None = None, +) -> dict[str, Any]: + """Collect active and pre-job terminal evidence bound to stable PR identities.""" + validated_repositories = sorted( + {_repository_name(repository_name) for repository_name in repositories} + ) + if len(validated_repositories) != len(repositories): + raise QueueHealthError("collection repository list contains duplicates") + + snapshot_timestamp = generated_at or datetime.now(timezone.utc).isoformat().replace( + "+00:00", "Z" + ) + parse_timestamp(snapshot_timestamp) + collected_repositories: list[dict[str, Any]] = [] + collection_errors: list[dict[str, str]] = [] + active_statuses = ("in_progress", "pending", "queued", "requested", "waiting") + + for repository_name in validated_repositories: + try: + repository_metadata = github_json( + f"repos/{repository_name}", runner=runner + ) + if not isinstance(repository_metadata, dict): + raise QueueHealthError( + f"repository metadata for {repository_name} is not an object" + ) + pulls_endpoint = ( + f"repos/{repository_name}/pulls?state=open&per_page={MAX_API_PAGE_SIZE}" + ) + try: + initial_pull_requests = _read_pull_request_snapshot( + pulls_endpoint, runner=runner + ) + except IncompletePullRequestIdentity: + time.sleep(PULL_REQUEST_RETRY_DELAY_SECONDS) + initial_pull_requests = _read_pull_request_snapshot( + pulls_endpoint, runner=runner + ) + except QueueHealthError as collection_error: + collection_errors.append( + {"repository": repository_name, "error": str(collection_error)} + ) + continue + + try: + active_snapshots: list[dict[int, dict[str, Any]]] = [] + for status_order in (active_statuses, tuple(reversed(active_statuses))): + active_snapshot: dict[int, dict[str, Any]] = {} + for workflow_status in status_order: + workflow_runs = _list_payload( + github_json( + f"repos/{repository_name}/actions/runs?status={workflow_status}" + f"&per_page={WORKFLOW_RUN_PAGE_SIZE}", + paginate=True, + max_pages=ACTIVE_RUN_MAX_API_PAGES, + runner=runner, + ), + "workflow_runs", + max_items=( + WORKFLOW_RUN_PAGE_SIZE * ACTIVE_RUN_MAX_API_PAGES + ), + ) + for workflow_run in workflow_runs: + active_snapshot[workflow_run["id"]] = workflow_run + active_snapshots.append(active_snapshot) + + first_snapshot, second_snapshot = active_snapshots + first_run_states = { + workflow_run_id: str(workflow_run.get("status") or "").upper() + for workflow_run_id, workflow_run in first_snapshot.items() + } + second_run_states = { + workflow_run_id: str(workflow_run.get("status") or "").upper() + for workflow_run_id, workflow_run in second_snapshot.items() + } + if first_run_states != second_run_states: + raise QueueHealthError( + "active workflow run snapshot changed during collection" + ) + + try: + final_pull_requests = _read_pull_request_snapshot( + pulls_endpoint, runner=runner + ) + except IncompletePullRequestIdentity: + time.sleep(PULL_REQUEST_RETRY_DELAY_SECONDS) + try: + final_pull_requests = _read_pull_request_snapshot( + pulls_endpoint, runner=runner + ) + except QueueHealthError as retry_error: + raise QueueHealthError( + "pull-request identity validation failed: " + f"{retry_error}" + ) from retry_error + + if ( + _pull_request_identity_view(initial_pull_requests) + != _pull_request_identity_view(final_pull_requests) + ): + raise QueueHealthError( + "pull-request identity snapshot changed during collection" + ) + + pull_requests_by_number = { + pull_request["number"]: pull_request + for pull_request in final_pull_requests + } + terminal_diagnostic_snapshot: dict[int, dict[str, Any]] = {} + current_head_shas = sorted( + {pull_request["head_sha"] for pull_request in final_pull_requests} + ) + for current_head_sha in current_head_shas: + encoded_head_sha = quote(current_head_sha, safe="") + workflow_runs = _list_payload( + github_json( + f"repos/{repository_name}/actions/runs?status=completed" + f"&head_sha={encoded_head_sha}" + f"&per_page={WORKFLOW_RUN_PAGE_SIZE}", + paginate=True, + max_pages=TERMINAL_DIAGNOSTIC_MAX_API_PAGES, + runner=runner, + ), + "workflow_runs", + max_items=( + WORKFLOW_RUN_PAGE_SIZE * TERMINAL_DIAGNOSTIC_MAX_API_PAGES + ), + ) + for workflow_run in workflow_runs: + if str(workflow_run.get("conclusion") or "").lower() not in ( + TERMINAL_DIAGNOSTIC_STATUSES + ): + continue + terminal_diagnostic_snapshot[workflow_run["id"]] = workflow_run + + for terminal_status in TARGET_TERMINAL_DIAGNOSTIC_STATUSES: + target_workflow_runs = _list_payload( + github_json( + f"repos/{repository_name}/actions/runs?status={terminal_status}" + "&event=pull_request_target" + f"&per_page={WORKFLOW_RUN_PAGE_SIZE}", + paginate=True, + max_pages=TERMINAL_DIAGNOSTIC_MAX_API_PAGES, + runner=runner, + ), + "workflow_runs", + max_items=( + WORKFLOW_RUN_PAGE_SIZE * TERMINAL_DIAGNOSTIC_MAX_API_PAGES + ), + ) + for workflow_run in target_workflow_runs: + normalized_candidate = _normalise_run( + repository_name, workflow_run, [] + ) + identity_state, _ = _run_identity( + normalized_candidate, pull_requests_by_number + ) + if identity_state != "current_head": + continue + terminal_diagnostic_snapshot[normalized_candidate["id"]] = ( + workflow_run + ) + + observed_snapshot = dict(second_snapshot) + observed_snapshot.update(terminal_diagnostic_snapshot) + runs_by_id: dict[int, dict[str, Any]] = {} + for workflow_run_id, workflow_run in observed_snapshot.items(): + normalized_run = _normalise_run( + repository_name, workflow_run, [] + ) + identity_state, _ = _run_identity( + normalized_run, pull_requests_by_number + ) + needs_job_evidence = ( + identity_state == "current_head" + and ( + normalized_run["status"] + in {"QUEUED", "IN_PROGRESS", "WAITING"} + or normalized_run["conclusion"] + in {status.upper() for status in TERMINAL_DIAGNOSTIC_STATUSES} + ) + ) + if not needs_job_evidence: + runs_by_id[workflow_run_id] = normalized_run + continue + + jobs_payload = github_json( + f"repos/{repository_name}/actions/runs/{workflow_run_id}/jobs" + f"?per_page={MAX_API_PAGE_SIZE}", + paginate=True, + runner=runner, + ) + workflow_jobs = _list_payload( + jobs_payload, + "jobs", + max_items=MAX_API_PAGE_SIZE * MAX_API_PAGES, + ) + runs_by_id[workflow_run_id] = _normalise_run( + repository_name, workflow_run, workflow_jobs + ) + + try: + post_evidence_pull_requests = _read_pull_request_snapshot( + pulls_endpoint, runner=runner + ) + except IncompletePullRequestIdentity: + time.sleep(PULL_REQUEST_RETRY_DELAY_SECONDS) + try: + post_evidence_pull_requests = _read_pull_request_snapshot( + pulls_endpoint, runner=runner + ) + except QueueHealthError as retry_error: + raise QueueHealthError( + "pull-request identity validation failed: " + f"{retry_error}" + ) from retry_error + if ( + _pull_request_identity_view(final_pull_requests) + != _pull_request_identity_view(post_evidence_pull_requests) + ): + raise QueueHealthError( + "pull-request identity snapshot changed during evidence collection" + ) + except QueueHealthError as collection_error: + collection_errors.append( + {"repository": repository_name, "error": str(collection_error)} + ) + continue + + collected_repositories.append( + { + "full_name": repository_name, + "default_branch": str( + repository_metadata.get("default_branch") or "" + ), + "pull_requests": final_pull_requests, + "runs": sorted( + runs_by_id.values(), key=lambda workflow_run: workflow_run["id"] + ), + } + ) + + return { + "generated_at": snapshot_timestamp, + "repositories": collected_repositories, + "collection_errors": collection_errors, + } + + +def _normalized_snapshot_runs( + snapshot: dict[str, Any], +) -> dict[tuple[str, int], dict[str, Any]]: + """Index normalized run metadata for additive report provenance fields.""" + normalized_runs: dict[tuple[str, int], dict[str, Any]] = {} + snapshot_repositories = snapshot.get("repositories") + if not isinstance(snapshot_repositories, list): + return normalized_runs + for repository_entry in snapshot_repositories: + if not isinstance(repository_entry, dict): + continue + repository_name = repository_entry.get("full_name") + workflow_runs = repository_entry.get("runs") or [] + if not isinstance(repository_name, str) or not isinstance(workflow_runs, list): + continue + for workflow_run in workflow_runs: + if not isinstance(workflow_run, dict): + continue + workflow_jobs = workflow_run.get("jobs") or [] + if not isinstance(workflow_jobs, list): + workflow_jobs = [] + normalized_run = _normalise_run( + repository_name, workflow_run, workflow_jobs + ) + normalized_runs[(repository_name, normalized_run["id"])] = normalized_run + return normalized_runs + + +def build_report( + snapshot: dict[str, Any], + *, + now: datetime | None = None, + queue_age_slo_seconds: int = DEFAULT_QUEUE_AGE_SLO_SECONDS, +) -> dict[str, Any]: + """Build the v1 report with stable workflow identity and age provenance.""" + normalized_runs = _normalized_snapshot_runs(snapshot) + report = _CORE_BUILD_REPORT( + snapshot, + now=now, + queue_age_slo_seconds=queue_age_slo_seconds, + ) + + for report_row in report["runs"]: + run_metadata = normalized_runs.get( + (report_row["repository"], report_row["run_id"]) + ) + if run_metadata is None: # pragma: no cover - core report guarantees the row. + continue + report_row["workflow_id"] = run_metadata["workflow_id"] + report_row["workflow_identity"] = run_metadata["workflow_identity"] + report_row["run_conclusion"] = run_metadata.get("conclusion", "") + report_row["jobs_materialized"] = bool(run_metadata["jobs"]) + matching_job = next( + ( + workflow_job + for workflow_job in run_metadata["jobs"] + if workflow_job["id"] == report_row["job_id"] + ), + None, + ) + report_row["admission_state"] = ( + "runner_assigned" if report_row["runner_assigned"] else "runner_not_assigned" + ) + if matching_job and matching_job.get("created_at"): + report_row["queue_age_started_at"] = matching_job["created_at"] + report_row["queue_age_source"] = "job_created_at" + else: + report_row["queue_age_started_at"] = run_metadata.get("created_at", "") + report_row["queue_age_source"] = "run_created_at" + if ( + report_row["identity_state"] == "current_head" + and report_row["run_conclusion"] == "STARTUP_FAILURE" + and not report_row["jobs_materialized"] + ): + report_row["admission_state"] = "startup_failure_before_job_materialization" + report_row["blocker"] = "startup_failure_before_job_materialization" + report_row["recommended_action"] = ( + "inspect_actions_control_plane_without_leaf_bypass" + ) + elif ( + report_row["identity_state"] == "current_head" + and report_row["run_conclusion"] == "CANCELLED" + and matching_job is not None + and matching_job.get("conclusion") == "CANCELLED" + and not report_row["runner_assigned"] + and matching_job.get("steps_count") == 0 + ): + report_row["admission_state"] = "cancelled_before_runner_assignment" + report_row["blocker"] = "cancelled_before_runner_assignment" + report_row["recommended_action"] = ( + "inspect_actions_control_plane_without_leaf_bypass" + ) + elif ( + report_row["identity_state"] == "current_head" + and report_row["run_conclusion"] == "FAILURE" + and matching_job is not None + and matching_job.get("conclusion") == "FAILURE" + and not report_row["runner_assigned"] + and matching_job.get("steps_count") == 0 + ): + report_row["execution_state"] = "terminal_pre_execution_failure" + report_row["admission_state"] = "terminal_pre_execution_failure" + report_row["blocker"] = ( + "terminal_pre_execution_failure_before_runner_assignment" + ) + report_row["recommended_action"] = ( + "inspect_actions_control_plane_without_leaf_bypass" + ) + + current_pending_rows = [ + report_row + for report_row in report["runs"] + if report_row["is_pending"] + and report_row["identity_state"] == "current_head" + ] + lane_run_ids: dict[tuple[str, int, str], set[int]] = {} + lane_workflow_names: dict[tuple[str, int, str], str] = {} + for report_row in current_pending_rows: + lane_identity = ( + report_row["repository"], + report_row["pull_request_number"], + report_row["workflow_identity"], + ) + lane_run_ids.setdefault(lane_identity, set()).add(report_row["run_id"]) + lane_workflow_names.setdefault( + lane_identity, report_row["workflow_name"] + ) + + duplicate_pending_lanes = [ + { + "repository": lane_identity[0], + "pull_request_number": lane_identity[1], + "workflow_identity": lane_identity[2], + "workflow_name": lane_workflow_names[lane_identity], + "count": len(workflow_run_ids), + } + for lane_identity, workflow_run_ids in sorted(lane_run_ids.items()) + if len(workflow_run_ids) > 1 + ] + report["duplicate_pending_lanes"] = duplicate_pending_lanes + report["summary"]["duplicate_pending_lane_count"] = len( + duplicate_pending_lanes + ) + cancelled_before_runner_assignment_count = sum( + report_row.get("admission_state") == "cancelled_before_runner_assignment" + for report_row in report["runs"] + ) + report["summary"]["cancelled_before_runner_assignment_count"] = ( + cancelled_before_runner_assignment_count + ) + terminal_pre_execution_failure_count = sum( + report_row.get("admission_state") == "terminal_pre_execution_failure" + for report_row in report["runs"] + ) + report["summary"]["terminal_pre_execution_failure_count"] = ( + terminal_pre_execution_failure_count + ) + if cancelled_before_runner_assignment_count: + external_action = ( + "Inspect Actions runner admission, billing/usage, runner-group policy, " + "scheduler capacity, and cancellation provenance; cancelled pre-runner " + "evidence remains incomplete." + ) + if external_action not in report["summary"]["external_actions"]: + report["summary"]["external_actions"].append(external_action) + report["summary"]["external_actions"].sort() + if terminal_pre_execution_failure_count: + external_action = ( + "Inspect Actions control-plane admission, billing/usage, runner-group policy, " + "and scheduler state; terminal failure without runner assignment or executed " + "steps is not an executed product/security failure." + ) + if external_action not in report["summary"]["external_actions"]: + report["summary"]["external_actions"].append(external_action) + report["summary"]["external_actions"].sort() + return report + + +def main( + argv: Sequence[str] | None = None, *, stderr: TextIO = sys.stderr +) -> int: + """Collect or load a snapshot, write reports, and return a stable CLI status.""" + cli_arguments = parse_args(argv) + try: + queue_snapshot = ( + load_snapshot(cli_arguments.snapshot) + if cli_arguments.snapshot + else collect_snapshot(load_allowlist(cli_arguments.allowlist)) + ) + evaluation_time = ( + parse_timestamp(cli_arguments.now) + if cli_arguments.now + else datetime.now(timezone.utc) + ) + queue_report = build_report( + queue_snapshot, + now=evaluation_time, + queue_age_slo_seconds=cli_arguments.queue_age_slo_seconds, + ) + write_reports( + queue_report, cli_arguments.output_json, cli_arguments.output_html + ) + except (OSError, QueueHealthError, ValueError) as report_error: + print(f"ERROR: queue-health report failed: {report_error}", file=stderr) + return 2 + + breach_count = queue_report["summary"]["unassigned_slo_breached_count"] + if breach_count: + print( + "::warning::Actions queue-health found " + f"{breach_count} unassigned current-head SLO breach(es)." + ) + print( + "QUEUE_HEALTH_RESULT=" + f"observed={queue_report['summary']['observed_job_count']} " + f"pending={queue_report['summary']['pending_job_count']} " + f"slo_breaches={breach_count}" + ) + return 0 + + +if __name__ == "__main__": # pragma: no cover - exercised through CLI tests. + raise SystemExit(main()) diff --git a/scripts/ci/actions_queue_health_core.py b/scripts/ci/actions_queue_health_core.py new file mode 100644 index 0000000000..ab600efdbc --- /dev/null +++ b/scripts/ci/actions_queue_health_core.py @@ -0,0 +1,701 @@ +"""Produce a read-only, exact-head GitHub Actions queue-health report. + +The collector intentionally treats queued, cancelled, skipped, missing, and +unlinked evidence as incomplete. It never cancels runs, changes branches, or +turns an unavailable runner into a successful check. +""" + +from __future__ import annotations + +import argparse +import html +import json +import re +import subprocess +from collections.abc import Callable, Sequence +from datetime import datetime, timezone +from pathlib import Path +from typing import Any + +REPOSITORY_PATTERN = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$") +QUEUE_STATES = {"QUEUED", "IN_PROGRESS", "PENDING", "REQUESTED"} +TERMINAL_STATES = {"COMPLETED"} +DEFAULT_QUEUE_AGE_SLO_SECONDS = 900 +SCHEMA_VERSION = "actions.queue_health.v1" +MAX_API_PAGE_SIZE = 100 +WORKFLOW_RUN_PAGE_SIZE = 50 +MAX_API_PAGES = 20 +ACTIVE_RUN_MAX_API_PAGES = 1 +GITHUB_API_TIMEOUT_SECONDS = 30 +PULL_REQUEST_RETRY_DELAY_SECONDS = 1 +PAGINATED_PAGES_KEY = "_queue_health_pages" +Runner = Callable[..., subprocess.CompletedProcess[str]] + + +class QueueHealthError(ValueError): + """Raised when a queue-health input or trusted read is invalid.""" + + +class IncompletePullRequestIdentity(QueueHealthError): + """Raised when a pull-request read omits exact head or base identity.""" + + +def parse_timestamp(value: str) -> datetime: + """Parse an explicit UTC timestamp and reject ambiguous local time.""" + if not isinstance(value, str) or not value.strip(): + raise QueueHealthError("timestamp must be a non-empty string") + try: + parsed = datetime.fromisoformat(value.strip().replace("Z", "+00:00")) + except ValueError as exc: + raise QueueHealthError(f"invalid timestamp: {value!r}") from exc + if parsed.tzinfo is None: + raise QueueHealthError("timestamp must include a timezone") + return parsed.astimezone(timezone.utc) + + +def _repository_name(value: Any) -> str: + """Validate and return one owner/repository identifier.""" + if not isinstance(value, str) or not REPOSITORY_PATTERN.fullmatch(value): + raise QueueHealthError(f"invalid repository identifier: {value!r}") + if any(segment in {".", ".."} for segment in value.split("/")): + raise QueueHealthError(f"invalid repository identifier: {value!r}") + return value + + +def load_allowlist(path: Path) -> list[str]: + """Load a unique, sorted repository allowlist from a JSON array/object.""" + try: + payload = json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as exc: + raise QueueHealthError(f"unable to load repository allowlist: {exc}") from exc + values = payload.get("repositories") if isinstance(payload, dict) else payload + if not isinstance(values, list) or not values: + raise QueueHealthError("repository allowlist must be a non-empty JSON array") + repositories = sorted({_repository_name(value) for value in values}) + if len(repositories) != len(values): + raise QueueHealthError("repository allowlist contains duplicates") + return repositories + + +def _list_payload( + payload: Any, + key: str, + *, + max_items: int = MAX_API_PAGE_SIZE * MAX_API_PAGES, +) -> list[dict[str, Any]]: + """Extract one bounded GitHub list response without accepting under-collection.""" + declared_total_counts: list[Any] = [] + if isinstance(payload, dict) and PAGINATED_PAGES_KEY in payload: + pages = payload[PAGINATED_PAGES_KEY] + if not isinstance(pages, list) or not pages or len(pages) > MAX_API_PAGES: + raise QueueHealthError(f"GitHub response field {key!r} exceeds the bounded page count") + page_values = [] + for page in pages: + if isinstance(page, list): + page_values.extend(page) + elif isinstance(page, dict): + page_items = page.get(key) + if not isinstance(page_items, list): + raise QueueHealthError(f"GitHub response field {key!r} page must contain an array") + page_values.extend(page_items) + if "total_count" in page: + declared_total_counts.append(page["total_count"]) + else: + raise QueueHealthError(f"GitHub response field {key!r} page must be an array or object") + values = page_values + else: + values = payload if isinstance(payload, list) else payload.get(key) if isinstance(payload, dict) else None + if isinstance(payload, dict) and "total_count" in payload: + declared_total_counts.append(payload["total_count"]) + if not isinstance(values, list) or not all(isinstance(value, dict) for value in values): + raise QueueHealthError(f"GitHub response field {key!r} must be an array of objects") + if key == "workflow_runs" and any( + isinstance(value.get("id"), bool) + or not isinstance(value.get("id"), int) + or value["id"] <= 0 + for value in values + ): + raise QueueHealthError("workflow run id must be a positive integer") + if isinstance(payload, dict) and PAGINATED_PAGES_KEY in payload: + record_identities: list[tuple[str, int]] = [] + for value in values: + record_id = value.get("id") + if not isinstance(record_id, bool) and isinstance(record_id, int) and record_id > 0: + record_identities.append(("id", record_id)) + continue + record_number = value.get("number") + if ( + not isinstance(record_number, bool) + and isinstance(record_number, int) + and record_number > 0 + ): + record_identities.append(("number", record_number)) + continue + else: + raise QueueHealthError( + f"GitHub response field {key!r} paginated records must have a positive integer id or number" + ) + if len(record_identities) != len(set(record_identities)): + raise QueueHealthError( + f"GitHub response field {key!r} contains a duplicate record identity across pages" + ) + if declared_total_counts: + if any(isinstance(total_count, bool) or not isinstance(total_count, int) for total_count in declared_total_counts): + raise QueueHealthError(f"GitHub response field {key!r} has invalid total counts") + total_count = max(declared_total_counts) + if total_count < len(values) or total_count > max_items: + raise QueueHealthError(f"GitHub response field {key!r} exceeds the bounded page size") + if PAGINATED_PAGES_KEY in payload and total_count != len(values): + raise QueueHealthError(f"GitHub response field {key!r} is incompletely paginated") + return values + + +def github_json( + path: str, + *, + paginate: bool = False, + max_pages: int = MAX_API_PAGES, + runner: Runner = subprocess.run, +) -> Any: + """Read one GitHub REST endpoint through ``gh`` without shell evaluation.""" + if not path.startswith("repos/"): + raise QueueHealthError(f"GitHub endpoint is outside repository scope: {path}") + pages: list[Any] = [] + page_size_match = re.search(r"(?:[?&])per_page=(\d+)(?:&|$)", path) + page_size = int(page_size_match.group(1)) if page_size_match else MAX_API_PAGE_SIZE + page_numbers = range(1, max_pages + 1) if paginate else range(1, 2) + for page_number in page_numbers: + page_path = path + if paginate and page_number > 1: + page_path = f"{path}{'&' if '?' in path else '?'}page={page_number}" + try: + result = runner( + ["gh", "api", page_path], + capture_output=True, + text=True, + check=False, + timeout=GITHUB_API_TIMEOUT_SECONDS, + ) + except subprocess.TimeoutExpired as exc: + raise QueueHealthError( + f"GitHub API read timed out after {GITHUB_API_TIMEOUT_SECONDS} seconds for {page_path}" + ) from exc + if result.returncode != 0: + detail = (result.stderr or result.stdout or "GitHub API read failed").strip() + raise QueueHealthError(f"GitHub API read failed for {page_path}: {detail[:400]}") + try: + payload = json.loads(result.stdout) + except json.JSONDecodeError as exc: + raise QueueHealthError(f"GitHub API returned invalid JSON for {page_path}") from exc + if not paginate: + return payload + pages.append(payload) + values = payload if isinstance(payload, list) else None + total_count = payload.get("total_count") if isinstance(payload, dict) else None + if isinstance(payload, dict): + values = next((value for value in payload.values() if isinstance(value, list)), None) + if not isinstance(values, list): + raise QueueHealthError(f"GitHub API page has no bounded array for {page_path}") + collected = sum( + len(page) if isinstance(page, list) else len(next((value for value in page.values() if isinstance(value, list)), [])) + for page in pages + ) + if (type(total_count) is int and total_count <= collected) or len(values) < page_size: + return {PAGINATED_PAGES_KEY: pages} + raise QueueHealthError( + f"GitHub API pagination exceeds {max_pages} pages for {path}" + ) + + +def _normalise_pull_request( + pull_request: dict[str, Any], *, allow_normalized: bool = False +) -> dict[str, Any]: + """Keep only exact-head identity fields needed for queue classification. + + Empty or missing ``head_sha``, ``base_ref``, ``base_repository``, or + ``updated_at`` values are treated as an incomplete identity — the same + as a missing ``head``/``base`` object — so a transient, partially + populated GitHub API response triggers the caller's bounded retry + instead of being silently accepted and later misclassifying an active + run as obsolete. + """ + if not isinstance(pull_request, dict): + raise QueueHealthError("pull request entry must be an object") + number = pull_request.get("number") + if allow_normalized and "head" not in pull_request and "base" not in pull_request: + if not all( + isinstance(pull_request.get(field), str) and pull_request.get(field) + for field in ("base_ref", "base_repository", "head_sha", "updated_at") + ): + raise IncompletePullRequestIdentity( + "normalized pull request identity fields must be non-empty strings" + ) + if isinstance(number, bool) or not isinstance(number, int) or number <= 0: + raise QueueHealthError("pull request number must be a positive integer") + return { + "number": number, + "state": pull_request.get("state", "open"), + "base_ref": pull_request["base_ref"], + "base_repository": pull_request["base_repository"], + "head_sha": pull_request["head_sha"], + "updated_at": pull_request["updated_at"], + } + head = pull_request.get("head") + base = pull_request.get("base") + if not isinstance(head, dict) or not isinstance(base, dict): + raise IncompletePullRequestIdentity("pull request head and base must be objects") + if isinstance(number, bool) or not isinstance(number, int) or number <= 0: + raise QueueHealthError("pull request number must be a positive integer") + head_sha = head.get("sha", "") + base_ref = base.get("ref", "") + base_repository = ( + (base.get("repo") or {}).get("full_name", "") if isinstance(base.get("repo"), dict) else "" + ) + updated_at = pull_request.get("updated_at", "") + if not all( + isinstance(value, str) and value for value in (head_sha, base_ref, base_repository, updated_at) + ): + raise IncompletePullRequestIdentity( + "pull request head, base, and updated_at identity fields must be non-empty" + ) + return { + "number": number, + "state": pull_request.get("state", "open"), + "base_ref": base_ref, + "base_repository": base_repository, + "head_sha": head_sha, + "updated_at": updated_at, + } + + +def _normalise_job(job: dict[str, Any]) -> dict[str, Any]: + """Keep job state and runner assignment evidence without log contents. + + Preserves the job's own ``created_at`` (when GitHub scheduled that + specific job) separately from the parent run's ``created_at``, so a + job that only became eligible after an earlier stage in the same + in-progress run finished is not measured against the whole run's age. + """ + if not isinstance(job, dict): + raise QueueHealthError("workflow job entry must be an object") + job_id = job.get("id") + if isinstance(job_id, bool) or not isinstance(job_id, int) or job_id <= 0: + raise QueueHealthError("job id must be a positive integer") + runner_id = job.get("runner_id") + if isinstance(runner_id, bool) or not isinstance(runner_id, int): + runner_id = 0 + if "steps" in job: + workflow_steps = job["steps"] + if workflow_steps is not None and not isinstance(workflow_steps, list): + raise QueueHealthError("workflow job steps must be an array or null") + steps_count = len(workflow_steps) if isinstance(workflow_steps, list) else None + else: + steps_count = job.get("steps_count") + if steps_count is not None and ( + isinstance(steps_count, bool) + or not isinstance(steps_count, int) + or steps_count < 0 + ): + raise QueueHealthError( + "normalized workflow job steps_count must be a non-negative integer or null" + ) + return { + "id": job_id, + "name": str(job.get("name") or "unnamed job"), + "status": str(job.get("status") or "").upper(), + "conclusion": str(job.get("conclusion") or "").upper(), + "runner_id": runner_id, + "runner_name": str(job.get("runner_name") or ""), + "created_at": str(job.get("created_at") or ""), + "steps_count": steps_count, + } + + +def _normalise_run(repository: str, run: dict[str, Any], jobs: list[dict[str, Any]]) -> dict[str, Any]: + """Keep run identity and job state required for deterministic reporting. + + Accepts a pull-request link either in GitHub's raw shape + (``{"number": ..., "head": {"sha": ...}}``) or in the flattened shape + this function itself emits (``{"number": ..., "head_sha": ...}``), so + re-normalising an already-normalised run loaded back from a collected + snapshot (as ``build_report`` does) does not silently zero out the + linked head SHA that exact-head identity resolution depends on. + """ + if not isinstance(run, dict): + raise QueueHealthError("workflow run entry must be an object") + if not isinstance(jobs, list) or not all(isinstance(job, dict) for job in jobs): + raise QueueHealthError("workflow run jobs must be an array of objects") + run_id = run.get("id") + if isinstance(run_id, bool) or not isinstance(run_id, int) or run_id <= 0: + raise QueueHealthError("workflow run id must be a positive integer") + pull_requests = run.get("pull_requests", []) + if pull_requests is None: + pull_requests = [] + if not isinstance(pull_requests, list) or not all(isinstance(item, dict) for item in pull_requests): + raise QueueHealthError("workflow run pull_requests must be an array of objects") + links = [] + for item in pull_requests: + number = item.get("number") + if isinstance(number, bool) or not isinstance(number, int) or number <= 0: + raise QueueHealthError("workflow run pull request number must be positive") + if "head" not in item and isinstance(item.get("head_sha"), str): + links.append({"number": number, "head_sha": item["head_sha"]}) + continue + head = item.get("head", {}) + if head is None: + head = {} + if not isinstance(head, dict): + raise QueueHealthError("workflow run pull request head must be an object") + links.append({"number": number, "head_sha": str(head.get("sha") or "")}) + return { + "repository": repository, + "id": run_id, + "workflow_name": str(run.get("name") or run.get("workflow_name") or "unnamed workflow"), + "event": str(run.get("event") or "unknown"), + "status": str(run.get("status") or "").upper(), + "conclusion": str(run.get("conclusion") or "").upper(), + "head_sha": str(run.get("head_sha") or ""), + "created_at": str(run.get("created_at") or ""), + "updated_at": str(run.get("updated_at") or ""), + "run_attempt": run.get("run_attempt", 1), + "concurrency_group": str(run.get("concurrency_group") or "unavailable_from_actions_api"), + "pull_requests": sorted(links, key=lambda item: item["number"]), + "jobs": sorted((_normalise_job(job) for job in jobs), key=lambda item: item["id"]), + } + + +def load_snapshot(path: Path) -> dict[str, Any]: + """Load a JSON snapshot for offline, deterministic report generation.""" + try: + payload = json.loads(path.read_text(encoding="utf-8")) + except (OSError, json.JSONDecodeError) as exc: + raise QueueHealthError(f"unable to load queue-health snapshot: {exc}") from exc + if not isinstance(payload, dict): + raise QueueHealthError("queue-health snapshot root must be an object") + return payload + + +def _run_identity(run: dict[str, Any], pull_requests: dict[int, dict[str, Any]]) -> tuple[str, int | None]: + """Resolve one run to current-head, obsolete, or unlinked identity. + + Compares the open pull request's head SHA against the *linked* + pull-request head SHA carried on the run (``run["pull_requests"][*] + ["head_sha"]``), never against the run-level ``head_sha``. For + ``pull_request_target``-triggered runs, GitHub reports the run-level + ``head_sha`` as the base-branch commit that was checked out, not the + pull request's head commit; only the linked pull-request entry carries + the real head SHA that was reviewed. Using the run-level value there + would misclassify a genuinely current, active required-workflow run as + ``obsolete`` and skip fetching its job evidence. + """ + links = run.get("pull_requests") or [] + for link in links: + number = link.get("number") + pull_request = pull_requests.get(number) + if pull_request and pull_request.get("head_sha") == link.get("head_sha"): + return "current_head", number + if links: + return "obsolete", links[0].get("number") + return "unlinked", None + + +def _job_state(job: dict[str, Any]) -> tuple[str, bool, bool]: + """Return normalized execution state, pending flag, and runner assignment. + + GitHub's ``waiting`` job status (a job paused on an environment or + deployment approval) is incomplete pending evidence just like + ``queued``/``in_progress`` — it must remain visible with its own + blocker and action rather than silently dropping out of the pending + count as unclassified ``unknown`` evidence. + """ + status = str(job.get("status") or "").upper() + conclusion = str(job.get("conclusion") or "").upper() + assigned = bool(job.get("runner_name")) or (isinstance(job.get("runner_id"), int) and job.get("runner_id", 0) > 0) + if status == "WAITING": + return "waiting_approval", True, assigned + if status in QUEUE_STATES: + return ("queued_assigned" if assigned else "queued_unassigned"), True, assigned + if status in TERMINAL_STATES or conclusion: + return "terminal", False, assigned + return "unknown", False, assigned + + +def _format_age(created_at: str, now: datetime) -> int: + """Return non-negative queue age seconds from an explicit timestamp.""" + created = parse_timestamp(created_at) + return max(0, int((now - created).total_seconds())) + + +def build_report( + snapshot: dict[str, Any], + *, + now: datetime | None = None, + queue_age_slo_seconds: int = DEFAULT_QUEUE_AGE_SLO_SECONDS, +) -> dict[str, Any]: + """Classify every observed job without treating incomplete evidence as success.""" + if queue_age_slo_seconds < 0: + raise QueueHealthError("queue age SLO must not be negative") + generated_at = parse_timestamp(snapshot.get("generated_at")) + if now is not None and (not isinstance(now, datetime) or now.tzinfo is None): + raise QueueHealthError("evaluation time must include a timezone") + report_now = (now or datetime.now(timezone.utc)).astimezone(timezone.utc) + repositories = snapshot.get("repositories") + if not isinstance(repositories, list): + raise QueueHealthError("queue-health snapshot repositories must be an array") + raw_collection_errors = snapshot.get("collection_errors", []) + if raw_collection_errors is None: + raw_collection_errors = [] + if not isinstance(raw_collection_errors, list): + raise QueueHealthError("queue-health collection_errors must be an array") + collection_errors: list[dict[str, str]] = [] + for item in raw_collection_errors: + if not isinstance(item, dict): + raise QueueHealthError("queue-health collection error must be an object") + repository_name = _repository_name(item.get("repository")) + error = item.get("error") + if not isinstance(error, str) or not error: + raise QueueHealthError("queue-health collection error must contain text") + collection_errors.append({"repository": repository_name, "error": error}) + + rows: list[dict[str, Any]] = [] + seen_repositories: set[str] = set() + for repository in repositories: + if not isinstance(repository, dict): + raise QueueHealthError("queue-health repository entry must be an object") + full_name = _repository_name(repository.get("full_name")) + if full_name in seen_repositories: + raise QueueHealthError(f"duplicate repository entry {full_name}") + seen_repositories.add(full_name) + pull_request_entries = repository.get("pull_requests", []) + if pull_request_entries is None: + pull_request_entries = [] + if not isinstance(pull_request_entries, list): + raise QueueHealthError(f"pull requests for {full_name} must be an array") + pull_requests: dict[int, dict[str, Any]] = {} + for pull_request in pull_request_entries: + normalized = _normalise_pull_request(pull_request, allow_normalized=True) + if normalized["number"] in pull_requests: + raise QueueHealthError(f"duplicate pull request {normalized['number']} for {full_name}") + pull_requests[normalized["number"]] = normalized + runs = repository.get("runs", []) + if runs is None: + runs = [] + if not isinstance(runs, list): + raise QueueHealthError(f"runs for {full_name} must be an array") + run_ids: set[int] = set() + for raw_run in runs: + if not isinstance(raw_run, dict): + raise QueueHealthError("workflow run entry must be an object") + raw_jobs = raw_run.get("jobs", []) + if raw_jobs is None: + raw_jobs = [] + if not isinstance(raw_jobs, list): + raise QueueHealthError("workflow run jobs must be an array") + run = _normalise_run(full_name, raw_run, raw_jobs) + if run["id"] in run_ids: + raise QueueHealthError(f"duplicate workflow run {run['id']} for {full_name}") + run_ids.add(run["id"]) + identity, pull_request_number = _run_identity(run, pull_requests) + jobs = run["jobs"] + for job in jobs or [{"id": run["id"], "name": "run", "status": run.get("status")}]: + state, pending, assigned = _job_state(job) + # Prefer the job's own created_at: for a job with `needs:` + # dependencies inside an already in-progress run, GitHub + # sets it when the job became eligible, which can be long + # after the run itself started. Falling back to the run's + # created_at only applies to the synthetic run-level job + # used when no job evidence was fetched. + age_created_at = job.get("created_at") or run.get("created_at") + age_seconds = _format_age(age_created_at, report_now) + slo_breached = pending and age_seconds > queue_age_slo_seconds + if identity == "obsolete": + blocker = "obsolete_run_requires_identity_confirmed_cleanup" + action = "owner_cleanup_after_exact_identity_confirmation" + elif identity == "unlinked": + blocker = "run_not_linked_to_pull_request" + action = "reconcile_run_identity_before_cleanup" + elif state == "waiting_approval": + blocker = "environment_or_deployment_approval_required" + action = "reviewer_or_owner_approve_pending_environment_deployment" + elif pending and not assigned and slo_breached: + blocker = "external_runner_assignment_or_capacity" + action = "owner_check_runner_billing_policy_and_concurrency" + elif pending: + blocker = "current_head_required_evidence_incomplete" + action = "wait_for_runner_or_escalate_after_slo" + else: + blocker = None + action = "none" + rows.append( + { + "repository": full_name, + "workflow_name": run.get("workflow_name", "unnamed workflow"), + "run_id": run.get("id"), + "run_attempt": run.get("run_attempt", 1), + "job_id": job.get("id"), + "job_name": job.get("name", "unnamed job"), + "event": run.get("event", "unknown"), + "head_sha": run.get("head_sha", ""), + "pull_request_number": pull_request_number, + "identity_state": identity, + "status": job.get("status", ""), + "conclusion": job.get("conclusion", ""), + "execution_state": state, + "is_pending": pending, + "runner_assigned": assigned, + "created_at": run.get("created_at", ""), + "updated_at": run.get("updated_at", ""), + "queue_age_seconds": age_seconds, + "slo_breached": slo_breached, + "concurrency_group": run.get("concurrency_group", "unavailable_from_actions_api"), + "obsolete": identity == "obsolete", + "blocker": blocker, + "recommended_action": action, + } + ) + + rows.sort(key=lambda row: (row["repository"], row["run_id"], row["job_id"])) + pending = [row for row in rows if row["is_pending"]] + current_pending = [row for row in pending if row["identity_state"] == "current_head"] + lane_run_ids: dict[tuple[str, int, str], set[int]] = {} + for row in current_pending: + lane = ( + row["repository"], + row["pull_request_number"], + row["workflow_name"], + ) + lane_run_ids.setdefault(lane, set()).add(row["run_id"]) + duplicate_lanes = [ + { + "repository": key[0], + "pull_request_number": key[1], + "workflow_name": key[2], + "count": len(run_ids), + } + for key, run_ids in sorted(lane_run_ids.items()) + if len(run_ids) > 1 + ] + external_actions = sorted( + { + "Inspect GitHub-hosted runner assignment, Actions billing/usage, runner-group policy, environment approval, and concurrency saturation; queued evidence remains incomplete." + for row in rows + if row["blocker"] == "external_runner_assignment_or_capacity" + } + ) + summary = { + "observed_job_count": len(rows), + "pending_job_count": len(pending), + "current_head_pending_count": len(current_pending), + "unassigned_slo_breached_count": sum( + row["identity_state"] == "current_head" + and row["execution_state"] == "queued_unassigned" + and row["slo_breached"] + for row in rows + ), + "obsolete_job_count": sum(row["obsolete"] for row in rows), + "unlinked_job_count": sum(row["identity_state"] == "unlinked" for row in rows), + "duplicate_pending_lane_count": len(duplicate_lanes), + "terminal_job_count": sum(row["execution_state"] == "terminal" for row in rows), + "collection_error_count": len(collection_errors), + "external_actions": external_actions, + } + return { + "schema_version": SCHEMA_VERSION, + "generated_at": generated_at.isoformat().replace("+00:00", "Z"), + "evaluated_at": report_now.isoformat().replace("+00:00", "Z"), + "queue_age_slo_seconds": queue_age_slo_seconds, + "repositories": sorted(_repository_name(repository["full_name"]) for repository in repositories), + "collection_errors": collection_errors, + "summary": summary, + "duplicate_pending_lanes": duplicate_lanes, + "runs": rows, + "limitations": [ + "The Actions REST API does not expose the evaluated concurrency group for every run; unavailable values are reported explicitly.", + "This read-only slice never cancels runs or changes branch/check state.", + ], + } + + +def render_html(report: dict[str, Any]) -> str: + """Render a keyboard-readable HTML report with escaped untrusted fields.""" + summary = report["summary"] + rows = report["runs"] + table_rows = [] + for row in rows: + table_rows.append( + "" + + f'{html.escape(str(row["repository"]))}' + + "".join( + f"{html.escape(str(row[field]))}" + for field in ( + "workflow_name", + "run_id", + "job_name", + "identity_state", + "execution_state", + "head_sha", + "queue_age_seconds", + "blocker", + ) + ) + + "" + ) + body = "".join(table_rows) or 'No queued or in-progress jobs observed.' + collection_error_section = "" + if report.get("collection_errors"): + collection_error_section = ( + "

" + "Incomplete collection evidence

    " + + "".join( + "
  • " + + html.escape(str(item["repository"])) + + ": " + + html.escape(str(item["error"])) + + "
  • " + for item in report["collection_errors"] + ) + + "
" + ) + return ( + "\n" + '' + "GitHub Actions queue health" + "" + '
' + "

GitHub Actions queue health

" + + collection_error_section + + f"

Evaluated at ; queue-age SLO: {report['queue_age_slo_seconds']} seconds.

" + f"

Observed jobs: {summary['observed_job_count']}; current-head pending: {summary['current_head_pending_count']}; SLO breaches: {summary['unassigned_slo_breached_count']}.

" + '' + "" + + "".join(f"" for field in ( + "repository", "workflow_name", "run_id", "job_name", "identity_state", "execution_state", "head_sha", "queue_age_seconds", "blocker" + )) + + f"{body}
Run and job evidence; queued evidence is not a passing check.
{field.replace('_', ' ').title()}
\n" + ) + + +def write_reports(report: dict[str, Any], json_path: Path, html_path: Path) -> None: + """Write deterministic JSON and accessible HTML reports.""" + json_path.parent.mkdir(parents=True, exist_ok=True) + html_path.parent.mkdir(parents=True, exist_ok=True) + json_path.write_text( + json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + ) + html_path.write_text(render_html(report), encoding="utf-8") + + +def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace: + """Parse live-collection or offline-report CLI arguments.""" + parser = argparse.ArgumentParser(description=__doc__) + source = parser.add_mutually_exclusive_group(required=True) + source.add_argument("--snapshot", type=Path) + source.add_argument("--allowlist", type=Path) + parser.add_argument("--output-json", type=Path, required=True) + parser.add_argument("--output-html", type=Path, required=True) + parser.add_argument("--queue-age-slo-seconds", type=int, default=DEFAULT_QUEUE_AGE_SLO_SECONDS) + parser.add_argument("--now", help="Explicit timezone-aware evaluation time for deterministic reports") + return parser.parse_args(argv) diff --git a/scripts/ci/agent_mention_router.py b/scripts/ci/agent_mention_router.py index 46332cfc2f..68e544a6cf 100755 --- a/scripts/ci/agent_mention_router.py +++ b/scripts/ci/agent_mention_router.py @@ -110,7 +110,7 @@ f"repos/{CENTRAL_AUTOMATION_REPOSITORY}/actions/artifacts" ) LEDGER_ARTIFACT_PREFIX = "cwl-agent-invocation-" -REPOSITORY_RE = re.compile(r"^ContextualWisdomLab/[A-Za-z0-9_.-]+$") +REPOSITORY_RE = re.compile(r"^ContextualWisdomLab/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") HEAD_SHA_RE = re.compile(r"^[0-9a-fA-F]{40}$") BASE_BRANCH_RE = re.compile(r"^(?!-)[A-Za-z0-9._/-]+$") ACTOR_RE = re.compile(r"^[A-Za-z0-9-]+$") diff --git a/scripts/ci/agent_mention_sweep.py b/scripts/ci/agent_mention_sweep.py index 50e0a84f17..5b56fdcf4f 100755 --- a/scripts/ci/agent_mention_sweep.py +++ b/scripts/ci/agent_mention_sweep.py @@ -22,8 +22,8 @@ ) from redact_sensitive_log import redact_text -ORG_NAME_RE = re.compile(r"^[A-Za-z0-9_.-]+$") -REPOSITORY_RE = re.compile(r"^ContextualWisdomLab/[A-Za-z0-9_.-]+$") +ORG_NAME_RE = re.compile(r"^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") +REPOSITORY_RE = re.compile(r"^ContextualWisdomLab/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") REPOSITORY_SOURCES = frozenset({"organization", "installation"}) REPOSITORY_ROTATION_SECONDS = 5 * 60 # The sweep-organization-agent-mentions job has a 900s (15-minute) GitHub diff --git a/scripts/ci/audit_org_codeql_coverage.py b/scripts/ci/audit_org_codeql_coverage.py index f9fb2eaf17..bdbc835491 100644 --- a/scripts/ci/audit_org_codeql_coverage.py +++ b/scripts/ci/audit_org_codeql_coverage.py @@ -66,6 +66,43 @@ def _is_analysis_fresh_and_successful( return parsed >= now - timedelta(days=CODEQL_ANALYSIS_FRESHNESS_DAYS) +def _default_setup_scans_a_language(repository: dict[str, Any]) -> bool: + """Return True when default-setup is configured AND has languages enabled. + + ``state == "configured"`` alone is not coverage. Measured 2026-09-07: + ``life-os``, ``aFIPC`` and ``inkspan`` all report ``configured`` with an + **empty** ``languages`` list and no ``schedule``; ``life-os`` has zero CodeQL + analyses of any language as a result, while still satisfying the + configured-state check this function replaces. A default setup with nothing + enabled is a commitment to scan nothing. + + A missing ``default_setup_languages`` key fails closed rather than falling + back to the state alone, which would silently restore that gap. The audit + workflow collects the field in the same change that introduced this check, + so the key is absent only when the payload predates them both. + """ + if repository.get("default_setup_state") != "configured": + return False + languages = repository.get("default_setup_languages") + return isinstance(languages, list) and bool(languages) + + +def auditable_repositories( + repositories: list[dict[str, Any]], +) -> list[dict[str, Any]]: + """Return the repositories this audit actually examines. + + Archived repositories are excluded: they cannot run workflows or code + scanning, so a lack of coverage there is not a product gap. Counting them + as examined is what let ``main`` report success over an empty subject set. + """ + return [ + repository + for repository in repositories + if not repository.get("archived") + ] + + def repositories_without_codeql( repositories: list[dict[str, Any]], now: datetime | None = None ) -> list[dict[str, Any]]: @@ -81,15 +118,15 @@ def repositories_without_codeql( """ current = now or datetime.now(timezone.utc) uncovered: list[dict[str, Any]] = [] - for repository in repositories: - if repository.get("archived"): - continue + for repository in auditable_repositories(repositories): # "configured" is GitHub's own forward-looking commitment to run # CodeQL going forward (like a scheduled cron guarantee), not a # one-time historical scan that can go stale -- so it does not need # the same freshness check as latest_codeql_analysis below. Do not - # "fix" this into requiring a completed scan. - has_default_setup = repository.get("default_setup_state") == "configured" + # "fix" this into requiring a completed scan. It does need the + # commitment to cover at least one language: see + # _default_setup_scans_a_language. + has_default_setup = _default_setup_scans_a_language(repository) has_fresh_analysis = _is_analysis_fresh_and_successful( repository.get("latest_codeql_analysis"), current ) @@ -98,13 +135,30 @@ def repositories_without_codeql( return uncovered +def _coverage_gap_reason(repository: dict[str, Any]) -> str: + """Return the gap description that tells the operator what to change. + + "Default setup is on but scans nothing" and "there is no coverage at all" + need different fixes -- enable languages on the existing setup, versus set + coverage up -- so they are reported as different sentences. + """ + if repository.get("default_setup_state") == "configured": + return ( + f"{repository.get('name')} has CodeQL default-setup configured with no " + "languages enabled, so it scans nothing and produces no analyses" + ) + return ( + f"{repository.get('name')} has no CodeQL coverage from any source " + "(no default-setup, no recent analysis)" + ) + + def audit_codeql_coverage( repositories: list[dict[str, Any]], now: datetime | None = None ) -> list[str]: """Return one human-readable error per repository with zero CodeQL coverage.""" return [ - f"{repository.get('name')} has no CodeQL coverage from any source " - "(no default-setup, no recent analysis)" + _coverage_gap_reason(repository) for repository in repositories_without_codeql(repositories, now) ] @@ -137,6 +191,24 @@ def main(argv: list[str] | None = None) -> int: print(f"ERROR: unable to load repository JSON: {exc}", file=sys.stderr) return 2 + audited = auditable_repositories(repositories) + if not audited: + # An audit that examined nothing is not a clean organization, and + # "PASS: all 0 repositories have real CodeQL coverage" reads as + # success. The count that matters is what was examined, not what was + # supplied: an empty payload and a payload of nothing but archived + # repositories both reach zero subjects, and only the first was caught + # when this guard counted `repositories`. The calling workflow refuses + # an enumeration missing its known-private sentinel repositories, but + # the script is directly runnable against a JSON path or stdin, so the + # guard has to live here too. + print( + f"ERROR: this run audited nothing " + f"(0 of {len(repositories)} repositories were eligible)", + file=sys.stderr, + ) + return 2 + errors = audit_codeql_coverage(repositories) if errors: for error in errors: @@ -147,7 +219,7 @@ def main(argv: list[str] | None = None) -> int: ) return 1 - print(f"PASS: all {len(repositories)} repositories have real CodeQL coverage") + print(f"PASS: all {len(audited)} repositories have real CodeQL coverage") return 0 diff --git a/scripts/ci/codeql_ghas_configuration_identity.py b/scripts/ci/codeql_ghas_configuration_identity.py new file mode 100644 index 0000000000..527afa12e3 --- /dev/null +++ b/scripts/ci/codeql_ghas_configuration_identity.py @@ -0,0 +1,448 @@ +#!/usr/bin/env python3 +"""Prove GHAS CodeQL base/head configuration identity continuity. + +GitHub Advanced Security computes PR-introduced alerts by pairing each CodeQL +configuration present on the protected base with the same identity on the PR +head. A Default setup baseline such as ``Default setup /language:rust`` is the +tuple ``(dynamic/github-code-scanning/codeql:analyze, /language:rust)``. When +the head is missing that identity, GHAS reports a neutral +``configuration not found`` result even if a central dispatch scan already +passed (ContextualWisdomLab/.github#2133). + +This module is the executable contract for that pairing rule. It never uploads +SARIF, never disables Default setup, and never synthesizes a status: callers +supply authenticated analysis payloads and receive a fail-closed verdict. +""" + +from __future__ import annotations + +import argparse +import json +import os +import re +import sys +import time +import urllib.error +import urllib.parse +import urllib.request +from typing import Any, Iterable, Mapping, Sequence + +DEFAULT_SETUP_ANALYSIS_KEY = "dynamic/github-code-scanning/codeql:analyze" +CODEQL_TOOL_NAME = "CodeQL" +GITHUB_API_AUTHORITY = "api.github.com" +MAX_ANALYSES_PAGES = 1000 +MAX_HTTP_ERROR_DIAGNOSTIC_BYTES = 400 + + +class ConfigurationIdentityError(RuntimeError): + """Report a fail-closed GHAS configuration-identity contract failure.""" + + +class _RejectRedirects(urllib.request.HTTPRedirectHandler): + """Prevent authenticated GitHub REST requests from creating redirect requests.""" + + def redirect_request( + self, + _request: urllib.request.Request, + _file_pointer: Any, + _code: int, + _message: str, + _headers: Any, + _new_url: str, + ) -> None: + """Refuse every redirect so bearer headers never cross the reviewed authority.""" + return None + + +_GITHUB_API_OPENER = urllib.request.build_opener(_RejectRedirects()) + + +def language_category(language: str) -> str: + """Return the CodeQL category string GHAS uses for one language.""" + normalized = str(language or "").strip().lower() + if not normalized: + raise ConfigurationIdentityError("language is required for a GHAS category") + if any(ch.isspace() for ch in normalized) or "/" in normalized: + raise ConfigurationIdentityError(f"language is not a safe CodeQL category token: {language!r}") + return f"/language:{normalized}" + + +def configuration_identity(analysis_key: str, category: str) -> tuple[str, str]: + """Normalize one GHAS configuration identity as ``(analysis_key, category)``.""" + key = str(analysis_key or "").strip() + cat = str(category or "").strip() + if not key or not cat: + raise ConfigurationIdentityError("analysis_key and category are both required") + return key, cat + + +def default_setup_identity(language: str) -> tuple[str, str]: + """Return the Default setup identity GHAS shows as ``Default setup /language:X``.""" + return configuration_identity(DEFAULT_SETUP_ANALYSIS_KEY, language_category(language)) + + +def iter_codeql_identities( + analyses: Sequence[Mapping[str, Any]], + *, + commit_sha: str | None = None, +) -> set[tuple[str, str]]: + """Collect CodeQL ``(analysis_key, category)`` identities from analysis payloads. + + When ``commit_sha`` is set, only analyses bound to that exact commit are + kept. Non-mapping rows and non-CodeQL tools are ignored. + """ + wanted = str(commit_sha or "").strip().lower() or None + found: set[tuple[str, str]] = set() + for row in analyses: + if not isinstance(row, Mapping): + continue + tool = row.get("tool") + tool_name = "" + if isinstance(tool, Mapping): + tool_name = str(tool.get("name") or "") + elif isinstance(tool, str): + tool_name = tool + if tool_name != CODEQL_TOOL_NAME: + continue + if wanted is not None: + sha = str(row.get("commit_sha") or "").strip().lower() + if sha != wanted: + continue + try: + found.add( + configuration_identity( + str(row.get("analysis_key") or ""), + str(row.get("category") or ""), + ) + ) + except ConfigurationIdentityError: + continue + return found + + +def missing_base_identities( + base_identities: Iterable[tuple[str, str]], + head_identities: Iterable[tuple[str, str]], + *, + language: str | None = None, +) -> list[tuple[str, str]]: + """Return base identities absent from the head, optionally limited to one language.""" + base_set = {configuration_identity(*item) for item in base_identities} + head_set = {configuration_identity(*item) for item in head_identities} + missing = base_set - head_set + if language is not None: + category = language_category(language) + missing = {item for item in missing if item[1] == category} + return sorted(missing) + + +def pairing_ready( + base_analyses: Sequence[Mapping[str, Any]], + head_analyses: Sequence[Mapping[str, Any]], + *, + base_sha: str, + head_sha: str, + language: str, +) -> tuple[bool, list[tuple[str, str]]]: + """Return whether GHAS can pair base/head CodeQL identities for ``language``.""" + base_ids = iter_codeql_identities(base_analyses, commit_sha=base_sha) + head_ids = iter_codeql_identities(head_analyses, commit_sha=head_sha) + category = language_category(language) + base_for_language = {item for item in base_ids if item[1] == category} + if not base_for_language: + # No base configuration for this language means GHAS will not demand one + # on the head for introduced-alert computation of that language. + return True, [] + missing = missing_base_identities(base_for_language, head_ids, language=language) + return not missing, missing + + +def format_identity(identity: tuple[str, str]) -> str: + """Render one identity the way GHAS titles Default setup warnings.""" + analysis_key, category = identity + if analysis_key == DEFAULT_SETUP_ANALYSIS_KEY: + return f"Default setup {category}" + return f"{analysis_key} {category}" + + +def _require_github_api_url(url: str) -> str: + """Reject any REST target outside canonical HTTPS ``api.github.com`` authority.""" + try: + parsed = urllib.parse.urlsplit(url) + except ValueError as exc: + raise ConfigurationIdentityError( + "GitHub API URL must use canonical https://api.github.com authority" + ) from exc + if ( + parsed.scheme != "https" + or parsed.netloc != GITHUB_API_AUTHORITY + or not parsed.path.startswith("/") + or parsed.fragment + ): + raise ConfigurationIdentityError( + "GitHub API URL must use canonical https://api.github.com authority" + ) + return url + + +def _request_json( + url: str, *, token: str, timeout_seconds: int, + response_headers: dict[str, str] | None = None, +) -> Any: + """GET canonical REST JSON; optionally expose Link without changing the result.""" + url = _require_github_api_url(url) + request = urllib.request.Request( + url, + headers={ + "Accept": "application/vnd.github+json", + "Authorization": f"Bearer {token}", + "X-GitHub-Api-Version": "2022-11-28", + "User-Agent": "cwl-codeql-ghas-configuration-identity", + }, + method="GET", + ) + try: + with _GITHUB_API_OPENER.open(request, timeout=timeout_seconds) as response: + payload = response.read().decode("utf-8") + if response_headers is not None: + headers: Any = getattr(response, "headers", {}) + if hasattr(headers, "get_all"): + response_headers["Link"] = ", ".join(headers.get_all("Link", [])) + else: + response_headers["Link"] = headers.get("Link", "") + except UnicodeDecodeError as exc: + raise ConfigurationIdentityError("GitHub API returned invalid UTF-8") from exc + except urllib.error.HTTPError as exc: + try: + body = exc.read(MAX_HTTP_ERROR_DIAGNOSTIC_BYTES).decode( + "utf-8", errors="replace" + ) + finally: + exc.close() + raise ConfigurationIdentityError( + f"GitHub API GET failed with HTTP {exc.code}: {body}" + ) from exc + except (urllib.error.URLError, TimeoutError, OSError) as exc: + raise ConfigurationIdentityError( + f"GitHub API transport failed: {type(exc).__name__}" + ) from exc + if not payload.strip(): + if response_headers is not None: + raise ConfigurationIdentityError("GitHub analyses API returned an empty body; evidence incomplete") + return [] + try: + return json.loads(payload) + except json.JSONDecodeError as exc: + raise ConfigurationIdentityError("GitHub API returned invalid JSON") from exc + + +def _next_analyses_page( + link: str, *, endpoint: str, params: Mapping[str, str], page: int, + token: str, timeout_seconds: int, repository_ids: dict[str, int], +) -> str: + """Validate GitHub's Link grammar and bind next to the same sequential query. + + Only canonical HTTPS, this repository's analyses endpoint, and unchanged + ref/tool/per_page filters may receive the bearer header. A terminal last + relation must name the current page and preserve those same invariants. + Unknown, duplicate, malformed or non-sequential relations abort rather + than prove completeness. + """ + if not link: + return "" + relations: dict[str, str] = {} + for part in link.split(","): + match = re.fullmatch(r'\s*<([^<>\s]+)>\s*;\s*rel="(next|prev|first|last)"\s*', part) + if match is None or match.group(2) in relations: + raise ConfigurationIdentityError("malformed or duplicate analyses pagination Link") + relations[match.group(2)] = match.group(1) + target = relations.get("next", "") or relations.get("last", "") + if not target: + return "" + _require_github_api_url(target) + parsed = urllib.parse.urlsplit(target) + try: + query = urllib.parse.parse_qs(parsed.query, keep_blank_values=True, strict_parsing=True) + except ValueError as exc: + raise ConfigurationIdentityError("malformed analyses pagination query") from exc + expected = {key: [value] for key, value in params.items()} + expected["page"] = [str(page + 1 if "next" in relations else page)] + if query != expected: + raise ConfigurationIdentityError("analyses pagination changed filters or page sequence") + if parsed.path != urllib.parse.urlsplit(endpoint).path: + alias = re.fullmatch(r"/repositories/([1-9][0-9]*)/code-scanning/analyses", parsed.path) + if alias is None: + raise ConfigurationIdentityError("analyses pagination changed endpoint") + if "id" not in repository_ids: + metadata = _request_json( + endpoint.removesuffix("/code-scanning/analyses"), + token=token, timeout_seconds=timeout_seconds, + ) + repo_id = metadata.get("id") if isinstance(metadata, dict) else None + if type(repo_id) is not int or repo_id <= 0: + raise ConfigurationIdentityError("analyses pagination repository ID could not be verified") + repository_ids["id"] = repo_id + if alias.group(1) != str(repository_ids["id"]): + raise ConfigurationIdentityError("analyses pagination changed repository ID") + if "next" not in relations: + return "" + if page >= MAX_ANALYSES_PAGES: + raise ConfigurationIdentityError("analyses pagination page limit exceeded; evidence incomplete") + return target + + +def list_codeql_analyses( + repository: str, + *, + token: str, + ref: str | None = None, + per_page: int = 100, + timeout_seconds: int = 30, +) -> list[dict[str, Any]]: + """Collect complete CodeQL history (at most 1000 pages), optionally by ref. + + Follow only sequential Link targets bound to the original repository and + ref/tool/page-size query. Transport, malformed links or the page budget + abort the collection: callers never receive partial identity evidence. + """ + if not repository or re.fullmatch(r"[A-Za-z0-9_-]+/[A-Za-z0-9_.-]+", repository) is None: + raise ConfigurationIdentityError("repository must be owner/name") + if repository.split("/")[1] in {".", ".."}: + raise ConfigurationIdentityError("repository must be owner/name") + if type(per_page) is not int or not 1 <= per_page <= 100: + raise ConfigurationIdentityError("per_page must be an integer from 1 to 100") + if not token: + raise ConfigurationIdentityError("token is required to list analyses") + params: dict[str, str] = {"per_page": str(per_page), "tool_name": CODEQL_TOOL_NAME} + if ref: + params["ref"] = ref + query = urllib.parse.urlencode(params) + endpoint = f"https://api.github.com/repos/{repository}/code-scanning/analyses" + url = f"{endpoint}?{query}" + rows: list[dict[str, Any]] = [] + page = 1 + repository_ids: dict[str, int] = {} + while url: + headers: dict[str, str] = {} + payload = _request_json( + url, token=token, timeout_seconds=timeout_seconds, response_headers=headers, + ) + if not isinstance(payload, list): + raise ConfigurationIdentityError("code-scanning analyses response was not a list") + rows.extend(row for row in payload if isinstance(row, dict)) + url = _next_analyses_page( + headers["Link"], endpoint=endpoint, params=params, page=page, + token=token, timeout_seconds=timeout_seconds, repository_ids=repository_ids, + ) + page += 1 + return rows + + +def wait_for_language_pairing( + *, + repository: str, + token: str, + base_ref: str, + base_sha: str, + head_ref: str, + head_sha: str, + language: str, + attempts: int = 30, + sleep_seconds: float = 20.0, + sleeper: Any = time.sleep, +) -> list[tuple[str, str]]: + """Poll until the head carries every base CodeQL identity for ``language``. + + Returns the empty list on success. Raises ConfigurationIdentityError when + the budget is exhausted with identities still missing. + """ + if attempts < 1: + raise ConfigurationIdentityError("attempts must be at least 1") + last_missing: list[tuple[str, str]] = [] + for attempt in range(1, attempts + 1): + base_analyses = list_codeql_analyses(repository, token=token, ref=base_ref) + head_analyses = list_codeql_analyses(repository, token=token, ref=head_ref) + ready, missing = pairing_ready( + base_analyses, + head_analyses, + base_sha=base_sha, + head_sha=head_sha, + language=language, + ) + if ready: + return [] + last_missing = missing + rendered = ", ".join(format_identity(item) for item in missing) or "" + print( + f"GHAS configuration identity not yet continuous for {language} " + f"(attempt {attempt}/{attempts}): missing {rendered}", + file=sys.stderr, + ) + if attempt < attempts: + sleeper(sleep_seconds) + rendered = ", ".join(format_identity(item) for item in last_missing) or "" + raise ConfigurationIdentityError( + "GHAS cannot pair base/head CodeQL configuration identities for " + f"{language}; missing on head: {rendered}" + ) + + +def _build_parser() -> argparse.ArgumentParser: + """Build the CLI parser used by the CodeQL scan-dispatch handler.""" + parser = argparse.ArgumentParser( + description=( + "Fail closed unless the PR head publishes every protected-base " + "CodeQL configuration identity for one language." + ) + ) + parser.add_argument("--repository", required=True, help="owner/name target repository") + parser.add_argument("--base-ref", required=True, help="protected base ref, e.g. refs/heads/main") + parser.add_argument("--base-sha", required=True, help="exact protected base SHA") + parser.add_argument("--head-ref", required=True, help="PR head ref, e.g. refs/pull/1/head") + parser.add_argument("--head-sha", required=True, help="exact PR head SHA") + parser.add_argument("--language", required=True, help="CodeQL language token, e.g. rust") + parser.add_argument( + "--attempts", + type=int, + default=int(os.environ.get("GHAS_CONFIG_IDENTITY_ATTEMPTS", "30")), + help="bounded poll attempts while Default setup finishes slower languages", + ) + parser.add_argument( + "--sleep-seconds", + type=float, + default=float(os.environ.get("GHAS_CONFIG_IDENTITY_SLEEP_SECONDS", "20")), + help="delay between poll attempts in seconds", + ) + return parser + + +def main(argv: Sequence[str] | None = None) -> int: + """CLI entry: wait for GHAS base/head identity continuity, then exit 0/1.""" + parser = _build_parser() + args = parser.parse_args(argv) + token = str(os.environ.get("GH_TOKEN") or os.environ.get("GITHUB_TOKEN") or "").strip() + try: + wait_for_language_pairing( + repository=args.repository, + token=token, + base_ref=args.base_ref, + base_sha=args.base_sha, + head_ref=args.head_ref, + head_sha=args.head_sha, + language=args.language, + attempts=args.attempts, + sleep_seconds=args.sleep_seconds, + ) + except ConfigurationIdentityError as exc: + print(f"::error::{exc}", file=sys.stderr) + return 1 + print( + "GHAS CodeQL configuration identity is continuous for " + f"{args.language} on {args.repository} " + f"(base={args.base_sha[:12]} head={args.head_sha[:12]})." + ) + return 0 + + +if __name__ == "__main__": # pragma: no cover - exercised through ``main`` tests + raise SystemExit(main()) diff --git a/scripts/ci/codeql_sarif_gate.py b/scripts/ci/codeql_sarif_gate.py index 3b232c3bdb..fb751ba90d 100644 --- a/scripts/ci/codeql_sarif_gate.py +++ b/scripts/ci/codeql_sarif_gate.py @@ -33,19 +33,66 @@ def iter_sarif_files(root: Path) -> list[Path]: return sorted(root.rglob("*.sarif")) -def _rule_for_result(result: dict[str, Any], rules: list[Any]) -> dict[str, Any]: - """Resolve the SARIF rule definition referenced by a result.""" - rules_by_id = { - str(rule.get("id") or ""): rule for rule in rules if isinstance(rule, dict) - } - rule = rules_by_id.get(str(result.get("ruleId") or ""), {}) - if rule: - return rule - rule_index = result.get("ruleIndex") - if isinstance(rule_index, int) and 0 <= rule_index < len(rules): - candidate = rules[rule_index] - if isinstance(candidate, dict): +UNRESOLVED_RULE_LEVEL = "unresolved-rule" + + +def _component_rules(result: dict[str, Any], tool: dict[str, Any]) -> list[Any] | None: + """Return the rules of the tool component a result references (SARIF 2.1.0 §3.54). + + No ``rule.toolComponent`` means the driver. Otherwise the reference selects one of + ``tool.extensions`` by ``index``, ``guid``, or ``name``; an unmatched reference + returns ``None`` so the caller can fail closed instead of consulting the wrong + component (issue #2150). + """ + reference = result.get("rule") if isinstance(result.get("rule"), dict) else {} + component_ref = reference.get("toolComponent") + if not isinstance(component_ref, dict): + return (tool.get("driver") or {}).get("rules") or [] + extensions = [ext for ext in tool.get("extensions") or [] if isinstance(ext, dict)] + index = component_ref.get("index") + if isinstance(index, int): + if 0 <= index < len(extensions): + return extensions[index].get("rules") or [] + return None + for key in ("guid", "name"): + wanted = component_ref.get(key) + if wanted is not None: + for extension in extensions: + if extension.get(key) == wanted: + return extension.get("rules") or [] + return None + return None + + +def _rule_for_result(result: dict[str, Any], tool: dict[str, Any]) -> dict[str, Any] | None: + """Resolve the SARIF rule definition a result references, or ``None`` if it cannot be. + + Resolution order inside the referenced component: ``rule.index`` (validated + against the declared id), then id lookup (``ruleId`` / ``rule.id``), then the + legacy ``ruleIndex``. Colliding ids across components stay distinct because + lookup never leaves the referenced component. + """ + rules = _component_rules(result, tool) + if rules is None: + return None + reference = result.get("rule") if isinstance(result.get("rule"), dict) else {} + declared_ids = {str(v) for v in (result.get("ruleId"), reference.get("id")) if v} + if len(declared_ids) > 1: + return None + declared_id = next(iter(declared_ids), "") + for index in (reference.get("index"), result.get("ruleIndex")): + if isinstance(index, int): + candidate = rules[index] if 0 <= index < len(rules) else None + if not isinstance(candidate, dict): + return None + if declared_id and str(candidate.get("id") or "") != declared_id: + return None return candidate + if declared_id: + for rule in rules: + if isinstance(rule, dict) and str(rule.get("id") or "") == declared_id: + return rule + return None return {} @@ -56,11 +103,16 @@ def _is_medium_plus(score: float | None, level: str, security_rule: bool) -> boo return security_rule and level in SEVERITY_LEVELS -def _finding_from_result(result: dict[str, Any], rules: list[Any]) -> Finding | None: - """Build a `Finding` for one SARIF result, or None if it doesn't gate the PR.""" +def _finding_from_result(result: dict[str, Any], tool: dict[str, Any]) -> Finding | None: + """Build a `Finding` for one SARIF result, or None if it doesn't gate the PR. + + A result whose rule reference cannot be resolved and that carries no explicit + security-severity gates as ``unresolved-rule`` rather than passing silently. + """ if not isinstance(result, dict) or result.get("suppressions"): return None - rule = _rule_for_result(result, rules) + resolved = _rule_for_result(result, tool) + rule = resolved or {} result_properties = result.get("properties") or {} rule_properties = rule.get("properties") or {} raw_score = result_properties.get("security-severity", rule_properties.get("security-severity")) @@ -71,7 +123,9 @@ def _finding_from_result(result: dict[str, Any], rules: list[Any]) -> Finding | level = str(result.get("level") or (rule.get("defaultConfiguration") or {}).get("level") or "none").lower() tags = {str(tag).lower() for tag in rule_properties.get("tags") or []} security_rule = "security" in tags or any(tag.startswith("external/cwe/") for tag in tags) - if not _is_medium_plus(score, level, security_rule): + if resolved is None and score is None: + level = UNRESOLVED_RULE_LEVEL + elif not _is_medium_plus(score, level, security_rule): return None physical = ((result.get("locations") or [{}])[0].get("physicalLocation") or {}) artifact = (physical.get("artifactLocation") or {}).get("uri") or "unknown" @@ -95,12 +149,12 @@ def gather_findings(root: Path) -> tuple[list[Finding], int, int]: for path in paths: payload = json.loads(path.read_text(encoding="utf-8")) for run in payload.get("runs") or []: - rules = ((run.get("tool") or {}).get("driver") or {}).get("rules") or [] + tool = run.get("tool") if isinstance(run.get("tool"), dict) else {} for result in run.get("results") or []: if not isinstance(result, dict): continue total_results += 1 - finding = _finding_from_result(result, rules) + finding = _finding_from_result(result, tool) if finding is not None: findings.append(finding) return findings, total_results, len(paths) diff --git a/scripts/ci/collect_release_strix_bindings.py b/scripts/ci/collect_release_strix_bindings.py new file mode 100644 index 0000000000..8162ba36c1 --- /dev/null +++ b/scripts/ci/collect_release_strix_bindings.py @@ -0,0 +1,379 @@ +#!/usr/bin/env python3 +"""Collect one current-attempt Strix binding per licensed dependency.""" + +from __future__ import annotations + +import argparse +import hashlib +import json +import sys +import tempfile +from pathlib import Path +from typing import Any, BinaryIO, Callable, Iterable, Mapping + +try: + from scripts.ci import release_dependency_gate as gate + from scripts.ci.scan_release_native_links import _reader, scan + from scripts.ci.verify_release_distribution_set import ( + DIGEST_RE, + MAX_CONTROL_BYTES, + DistributionSetError, + _archive, + _artifact, + _digest, + _json_bytes, + _members, + _timestamp, + fetch_artifact, + verify_distribution_set, + ) +except ImportError: # pragma: no cover - trusted direct `python3 -I` invocation + sys.path.insert(0, str(Path(__file__).resolve().parent)) + import release_dependency_gate as gate + from scan_release_native_links import _reader, scan + from verify_release_distribution_set import ( + DIGEST_RE, + MAX_CONTROL_BYTES, + DistributionSetError, + _archive, + _artifact, + _digest, + _json_bytes, + _members, + _timestamp, + fetch_artifact, + verify_distribution_set, + ) + + +def collect_bindings( + capture_root: Path, + license_report: Path, + plan_path: Path, + artifacts: Iterable[Any], + attempt: Any, + *, + repository: str, + source_sha: str, + control_sha: str, + run_id: int, + run_attempt: int, + fetch: Callable[[str, int, BinaryIO], None], + report_path: Path, + verified_distributions: list[dict[str, Any]], + verdict_path: Path, + record_artifact_id: int, + record_artifact_digest: str, + archive_report_path: Path | None = None, + verified_scope_path: Path | None = None, + native_report_path: Path | None = None, + source_root: Path | None = None, +) -> gate.GateReport: + """Accept the exact matrix result set, then rerun the full gate unchanged.""" + + if (not isinstance(attempt, Mapping) or type(attempt.get("id")) is not int + or attempt["id"] != run_id or type(attempt.get("run_attempt")) is not int + or attempt["run_attempt"] != run_attempt or attempt.get("head_sha") != control_sha): + raise gate.GateError(gate.STRIX_BINDING_UNBOUND, "workflow attempt differs from collector") + started = _timestamp(attempt.get("run_started_at")) + expected = gate.strix_fanout_plan( + capture_root, license_report, control_sha, run_id, run_attempt, archive_report_path + ) + plan = gate.load_json(plan_path) + if plan != expected or plan["source_repository"] != repository or plan["source_sha"] != source_sha: + raise gate.GateError(gate.STRIX_BINDING_UNBOUND, "fanout plan differs from trusted capture") + listed: dict[str, Mapping[str, Any]] = {} + for item in artifacts: + if not isinstance(item, Mapping) or not isinstance(item.get("name"), str): + raise gate.GateError(gate.STRIX_BINDING_MALFORMED, "artifact metadata is invalid") + if item["name"] in listed: + raise gate.GateError(gate.STRIX_BINDING_UNBOUND, "duplicate artifact name in run") + listed[item["name"]] = item + prefix = f"release-strix-binding-a{run_attempt}-" + expected_names = {row["artifact_name"] for row in plan["dependencies"]} + if {name for name in listed if name.startswith(prefix)} != expected_names: + raise gate.GateError(gate.STRIX_BINDING_MISSING, "matrix binding artifact set is incomplete or has extras") + bindings = capture_root / "strix" / "bindings" + if (bindings.exists() or bindings.is_symlink() or report_path.exists() + or report_path.is_symlink() or verdict_path.exists() or verdict_path.is_symlink()): + raise gate.GateError(gate.STRIX_BINDING_UNBOUND, "collector destination already exists") + if not verified_distributions or type(record_artifact_id) is not int or record_artifact_id <= 0: + raise gate.GateError(gate.STRIX_BINDING_UNBOUND, "verified distribution set is unavailable") + _artifact(listed, "reproducibility-record", record_artifact_id, + _digest(record_artifact_digest), run_id, control_sha, started) + if any(not isinstance(row, Mapping) for row in verified_distributions): + raise gate.GateError( + gate.STRIX_BINDING_UNBOUND, + "verified distribution report contains a malformed row", + ) + wheel_filenames = [ + row.get("file") for row in verified_distributions + if row.get("leg") != "sdist" and isinstance(row.get("file"), str) + ] + sdist_filenames = [ + row.get("file") for row in verified_distributions + if row.get("leg") == "sdist" and isinstance(row.get("file"), str) + ] + if not wheel_filenames or len(sdist_filenames) != 1: + raise gate.GateError( + gate.STRIX_BINDING_UNBOUND, + "verified distribution report lacks wheel/sdist coverage", + ) + native_report_sha256 = None + native_link_analyzer = None + try: + with tempfile.TemporaryDirectory( + prefix=".release-distributions-", dir=bindings.parent + ) as distribution_scratch: + canonical_distributions = verify_distribution_set( + artifacts, + attempt, + repository=repository, + source_sha=source_sha, + control_sha=control_sha, + run_id=run_id, + run_attempt=run_attempt, + record_artifact_id=record_artifact_id, + record_artifact_digest=record_artifact_digest, + wheel_filename=wheel_filenames[0], + sdist_filename=sdist_filenames[0], + fetch=fetch, + output_dir=Path(distribution_scratch) / "verified", + ) + if native_report_path is not None: + if (native_report_path.is_symlink() or not native_report_path.is_file() + or native_report_path.stat().st_size > MAX_CONTROL_BYTES): + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, "native link report is missing or oversized") + native_bytes = native_report_path.read_bytes() + native_payload = _json_bytes(native_bytes) + if native_payload != scan( + {"verified_distributions": canonical_distributions}, + Path(distribution_scratch) / "verified", source_sha, _reader() + ): + raise gate.GateError(gate.SOURCE_HASH_MISMATCH, + "native links differ from immutable distribution bytes") + native_report_sha256 = hashlib.sha256(native_bytes).hexdigest() + native_link_analyzer = native_payload["analyzer"] + except DistributionSetError as error: + raise gate.GateError( + gate.STRIX_BINDING_UNBOUND, + "distribution set failed immutable artifact verification", + ) from error + if verified_distributions != canonical_distributions: + raise gate.GateError( + gate.STRIX_BINDING_UNBOUND, + "verified distribution report differs from immutable artifacts", + ) + seen_ids: set[int] = {record_artifact_id} + with tempfile.TemporaryDirectory(prefix=".strix-bindings-", dir=bindings.parent) as scratch: + staging = Path(scratch) + for row in plan["dependencies"]: + name = row["artifact_name"] + item = listed[name] + artifact_id = item.get("id") + digest = item.get("digest") + if (type(artifact_id) is not int or artifact_id in seen_ids + or not isinstance(digest, str)): + raise gate.GateError(gate.STRIX_BINDING_UNBOUND, "binding artifact ID or digest is invalid") + _artifact(listed, name, artifact_id, digest, run_id, control_sha, started) + seen_ids.add(artifact_id) + member_name = f"{row['slug']}.json" + with _archive(repository, artifact_id, digest, fetch) as archive: + member = _members(archive, {member_name})[member_name] + if member.file_size > MAX_CONTROL_BYTES: + raise gate.GateError(gate.STRIX_BINDING_MALFORMED, "binding JSON exceeds size limit") + raw = archive.read(member) + payload = _json_bytes(raw) + if (not isinstance(payload, Mapping) or payload.get("schema") != gate.BINDING_SCHEMA + or payload.get("source_sha") != source_sha + or payload.get("control_sha") != control_sha + or type(payload.get("run_id")) is not int or payload["run_id"] != run_id + or type(payload.get("run_attempt")) is not int + or payload["run_attempt"] != run_attempt + or not isinstance(payload.get("fixture"), Mapping) + or payload["fixture"].get("id") != row["key"] + or payload["fixture"].get("sha256") != row["fixture_sha256"]): + raise gate.GateError(gate.STRIX_BINDING_UNBOUND, f"{row['key']}: binding differs from plan") + (staging / member_name).write_bytes(raw) + staging.rename(bindings) + scope_identities: list[dict[str, Any]] | None = None + variant_identities: list[dict[str, Any]] | None = None + if verified_scope_path is not None: + scope = gate.load_json(verified_scope_path) + rows = scope.get("verified_scope_evidence") if isinstance(scope, Mapping) else None + variants = scope.get("verified_runtime_variants") if isinstance(scope, Mapping) else None + if (not isinstance(rows, list) or len(rows) != 13 + or not all(isinstance(row, Mapping) for row in rows) + or not isinstance(variants, list) or len(variants) != 3 + or not all(isinstance(row, Mapping) for row in variants)): + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, "verified scope set is incomplete") + scope_identities = [{key: row.get(key) for key in + ("leg", "artifact_id", "artifact_name", "artifact_digest")} + for row in rows] + if (any(not isinstance(row["leg"], str) + or row["artifact_name"] != f"repro-digest-{row['leg']}" + or type(row["artifact_id"]) is not int or row["artifact_id"] <= 0 + or not isinstance(row["artifact_digest"], str) + or DIGEST_RE.fullmatch(row["artifact_digest"]) is None + for row in scope_identities) + or len({row["leg"] for row in scope_identities}) != 13 + or len({row["artifact_id"] for row in scope_identities}) != 13 + or sum(row["leg"] == "sdist" for row in scope_identities) != 1): + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, "verified scope identities are malformed") + used_ids = seen_ids | {row.get("artifact_id") for row in verified_distributions} + if any(row["artifact_id"] in used_ids for row in scope_identities): + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, "scope artifact ID overlaps distribution set") + for row in scope_identities: + _artifact(listed, row["artifact_name"], row["artifact_id"], + row["artifact_digest"], run_id, control_sha, started) + variant_identities = [{key: row.get(key) for key in + ("leg", "arch", "artifact_id", "artifact_name", "artifact_digest")} + for row in variants] + if (any(not isinstance(row["leg"], str) or row["arch"] != "x86_64" + or row["artifact_name"] != f"repro-macos-x86-{row['leg']}" + or type(row["artifact_id"]) is not int or row["artifact_id"] <= 0 + or not isinstance(row["artifact_digest"], str) + or DIGEST_RE.fullmatch(row["artifact_digest"]) is None + for row in variant_identities) + or len({row["leg"] for row in variant_identities}) != 3 + or len({row["artifact_id"] for row in variant_identities}) != 3 + or {row["leg"] for row in variant_identities} + != {f"universal2-apple-darwin-py{version}" for version in ("3.12", "3.13", "3.14")} + or any(row["artifact_id"] in used_ids | {item["artifact_id"] for item in scope_identities} + for row in variant_identities)): + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, "Intel runtime identities are malformed") + for row in variant_identities: + _artifact(listed, row["artifact_name"], row["artifact_id"], + row["artifact_digest"], run_id, control_sha, started) + for row in scope_identities: + _artifact(listed, row["artifact_name"], row["artifact_id"], + row["artifact_digest"], run_id, control_sha, started) + report = gate.gate(capture_root, stage=gate.FULL_STAGE, + **({"source_root": source_root} if source_root is not None else {})) + archive_reviews = [] + build_reviews = [] + tool_reviews = [] + if archive_report_path is not None and report.passed: + archive_payload = gate.load_json(archive_report_path) + by_key = {row["key"]: row for row in archive_payload["archives"]} + build_by_key = {row["key"]: row for row in archive_payload["build_packages"]} + tool_by_key = {row["key"]: row for row in archive_payload["build_tools"]} + for row in plan["dependencies"]: + if not {"runtime_archive", "build_package", "build_tool"} & row.keys(): + continue + build = "build_package" in row + tool = "build_tool" in row + approved = (tool_by_key if tool else build_by_key if build else by_key)[row["key"]] + dependency = gate.Dependency("github-release" if tool else "pypi", + approved["name"], approved["version"]) + failures = gate.validate_strix_binding( + bindings / f"{row['slug']}.json", dependency, + {"source_sha256": approved["source_sha256"]}, row["fixture_sha256"], + source_sha, fixture_key=row["key"], + ) + report.failures.extend(failures) + review = {"key": row["key"], "package_key": approved["package_key"], + "source_sha256": approved["source_sha256"], + "license": approved["license"], + "fixture_sha256": row["fixture_sha256"], + "legs": approved["legs"]} + (tool_reviews if tool else build_reviews if build else archive_reviews).append(review) + report_payload = report.to_json() + if archive_report_path is not None: + report_payload["runtime_archive_reviews"] = sorted(archive_reviews, key=lambda row: row["key"]) + report_payload["build_package_reviews"] = sorted(build_reviews, key=lambda row: row["key"]) + report_payload["build_tool_reviews"] = sorted(tool_reviews, key=lambda row: row["key"]) + report_path.write_text(json.dumps(report_payload, indent=2, sort_keys=True) + "\n") + if not report.passed: + raise gate.GateError(gate.STRIX_FINDINGS_OPEN, "full gate refused collected bindings") + binding_artifacts = [ + {"key": row["key"], "name": row["artifact_name"], + "id": listed[row["artifact_name"]]["id"], + "digest": listed[row["artifact_name"]]["digest"]} + for row in plan["dependencies"] if not {"runtime_archive", "build_package", "build_tool"} & row.keys() + ] + archive_binding_artifacts = [ + {"key": row["key"], "name": row["artifact_name"], + "id": listed[row["artifact_name"]]["id"], + "digest": listed[row["artifact_name"]]["digest"]} + for row in plan["dependencies"] if "runtime_archive" in row + ] + build_binding_artifacts = [ + {"key": row["key"], "name": row["artifact_name"], + "id": listed[row["artifact_name"]]["id"], + "digest": listed[row["artifact_name"]]["digest"]} + for row in plan["dependencies"] if "build_package" in row + ] + tool_binding_artifacts = [ + {"key": row["key"], "name": row["artifact_name"], + "id": listed[row["artifact_name"]]["id"], + "digest": listed[row["artifact_name"]]["digest"]} + for row in plan["dependencies"] if "build_tool" in row + ] + verdict = { + "schema": "cwl.release-full-set-verdict/1", "result": "PASS", + "source_repository": repository, "source_sha": source_sha, + "control_sha": control_sha, "run_id": run_id, "run_attempt": run_attempt, + "record_artifact_id": record_artifact_id, + "record_artifact_digest": record_artifact_digest, + "distributions": canonical_distributions, + "binding_artifacts": binding_artifacts, + "license_report_sha256": hashlib.sha256(license_report.read_bytes()).hexdigest(), + "gate_report_sha256": hashlib.sha256(report_path.read_bytes()).hexdigest(), + } + if archive_report_path is not None: + verdict["runtime_archive_binding_artifacts"] = archive_binding_artifacts + verdict["build_package_binding_artifacts"] = build_binding_artifacts + verdict["build_tool_binding_artifacts"] = tool_binding_artifacts + verdict["runtime_archive_license_sha256"] = expected["runtime_archive_license_sha256"] + if scope_identities is not None: + verdict["scope_evidence"] = sorted(scope_identities, key=lambda row: row["leg"]) + verdict["runtime_variants"] = sorted(variant_identities, key=lambda row: row["leg"]) + if native_report_sha256 is not None: + verdict["native_links_sha256"] = native_report_sha256 + verdict["native_link_analyzer"] = native_link_analyzer + verdict_path.write_text(json.dumps(verdict, indent=2, sort_keys=True) + "\n") + return report + + +def main() -> None: + """Collect and print the full release dependency review from CLI inputs.""" + + parser = argparse.ArgumentParser() + for name in ( + "capture", "license-report", "plan", "metadata", "attempt", "repository", + "source-sha", "control-sha", "run-id", "run-attempt", "report", + "verified-distributions", "verdict", "record-artifact-id", "record-artifact-digest", + ): + parser.add_argument(f"--{name}", required=True) + parser.add_argument("--runtime-archive-license-report") + parser.add_argument("--verified-scope") + parser.add_argument("--native-report") + parser.add_argument("--source") + args = parser.parse_args() + artifacts = [_json_bytes(line.encode("utf-8")) for line in Path(args.metadata).read_text().splitlines()] + attempt = _json_bytes(Path(args.attempt).read_bytes()) + verified = _json_bytes(Path(args.verified_distributions).read_bytes()) + if not isinstance(verified, Mapping) or not isinstance(verified.get("verified_distributions"), list): + raise gate.GateError(gate.STRIX_BINDING_UNBOUND, "verified distribution report is malformed") + report = collect_bindings( + Path(args.capture), Path(args.license_report), Path(args.plan), + artifacts, attempt, repository=args.repository, source_sha=args.source_sha, + control_sha=args.control_sha, run_id=int(args.run_id), + run_attempt=int(args.run_attempt), fetch=fetch_artifact, + report_path=Path(args.report), + verified_distributions=verified["verified_distributions"], + verdict_path=Path(args.verdict), + record_artifact_id=int(args.record_artifact_id), + record_artifact_digest=args.record_artifact_digest, + archive_report_path=(Path(args.runtime_archive_license_report) + if args.runtime_archive_license_report else None), + verified_scope_path=Path(args.verified_scope) if args.verified_scope else None, + native_report_path=Path(args.native_report) if args.native_report else None, + source_root=Path(args.source) if args.source else None, + ) + print(json.dumps(report.to_json(), sort_keys=True)) + + +if __name__ == "__main__": # pragma: no cover - main() owns the tested CLI contract + main() diff --git a/scripts/ci/contextual_orchestrator_review_launcher.py b/scripts/ci/contextual_orchestrator_review_launcher.py index 2e56809639..f1078eb919 100644 --- a/scripts/ci/contextual_orchestrator_review_launcher.py +++ b/scripts/ci/contextual_orchestrator_review_launcher.py @@ -22,14 +22,24 @@ from __future__ import annotations import argparse +import copy +import dataclasses import json +import logging import os import re +import queue +import threading import sys from pathlib import Path -from typing import Any +from typing import Any, Callable +from urllib.error import HTTPError -from scripts.ci.contextual_orchestrator_review_policy import FREE_POOL_CREDENTIAL_NAMES +from scripts.ci.contextual_orchestrator_review_policy import ( + FREE_POOL_CREDENTIAL_NAMES, + normalize_input_modalities, + provider_account, +) # The vendored server's generic 64 KiB default is intentionally conservative. @@ -42,8 +52,60 @@ # Provider-neutral sampling: several modern endpoints reject non-default # temperatures, while 1.0 is the OpenAI-compatible default. REVIEW_TEMPERATURE = 1.0 -REVIEW_PREFLIGHT_MAX_TOTAL_ROUTES = 12 -REVIEW_PREFLIGHT_PRIMARY_ROUTE_LIMIT = 8 +# Lazy fill (ADR-0029): the catalog is a *candidate* list, probed in its +# tier-then-round-robin order until REVIEW_PREFLIGHT_TARGET_READY routes are +# ready or REVIEW_PREFLIGHT_MAX_PROBES probes are spent, whichever comes first. +# A permanently dead candidate (NIM lists gemma-3-12b/4b but answers 404 on +# every run) then costs one probe instead of a served slot, and a healthy hour +# stops early instead of always probing every candidate. MAX_TOTAL_ROUTES is +# the two-stage total (auto pool: 16 free, up to 8 priced; the production +# ``free`` pool lists all 24). A silent candidate's probe costs up to one +# transport timeout (19 probes took 805 s in one artifact), so MAX_PROBES +# bounds preflight wall time as well as request count. Candidates past the +# probe cap are reached only when the account rule below sets earlier ones +# aside, and the report separates ``skipped_count`` (set aside, never probed) +# from the unreached tail so the evidence stays readable. +REVIEW_PREFLIGHT_MAX_TOTAL_ROUTES = 24 +REVIEW_PREFLIGHT_PRIMARY_ROUTE_LIMIT = 16 +REVIEW_PREFLIGHT_TARGET_READY = 8 +REVIEW_PREFLIGHT_MAX_PROBES = 16 +# Once one credential account has answered 429 to this many probes in a row, +# its remaining candidates are set aside so the walk reaches the other +# accounts' next candidates first: under the real 2026-09-06 candidate order +# (jan's table on #1949) the round-robin would otherwise spend five of sixteen +# probes on an account whose every free route answered 429, and the readiness +# target was unreachable; setting them aside lets the same sixteen probes +# reach both keys' llama routes (catalog position 17, ready in eight of the +# fourteen merged-rule artifacts of 2026-09-06 and reached only this way). +# +# But the rule must not END the walk. When every account is set aside the walk +# stops with most of its probe budget unspent and the stage fails closed -- +# and because deferral needs one ready route (#1947), nothing is served +# either. Measured that day: `.github` run 34016207820 sent six probes across +# all three accounts between 07:49:35.111Z and 07:49:35.767Z, every one +# refused 429, and gave up with ten probes unspent; five runs between 07:24Z +# and 08:05Z read probed 6 / skipped 18 / ready 0. Because the walk is a +# round-robin, "two consecutive 429s" on one account is two requests about +# 310 ms apart (nvidia_nim at .111 and .422). +# +# A refusal is not a verdict on the account. Run 34016093772 was inside its +# own preflight during that burst, and its llama probes on the same two NVIDIA +# keys answered ready at 07:50:58.7 and 07:50:59.0 -- 84 s after those keys +# refused 429. Whether the unspent probes would find a ready route *inside* a +# burst is still unmeasured; that is what `retry_after_s` is for. What is +# certain is that failing closed with two thirds of the budget in hand is +# indefensible, and the cost of spending it is bounded by the probe count, not +# a clock: a refused probe costs about 120 ms, a silent one up to the 90 s +# receive timeout, and the postponed tail contains both (google/gemma-4-31b-it +# answered TimeoutError in 15 of the 19 probes that reached it). See ADR-0029's +# amendment for the full cost table. +# +# So a set-aside candidate is postponed, not banned: once the first pass ends +# with the target unmet and probes left, the postponed candidates are probed +# in catalog order until the budget is spent. Probed 429 routes are still +# deferred (#1947); a candidate the budget never reaches is neither probed nor +# served. +REVIEW_PREFLIGHT_ACCOUNT_SKIP_AFTER_429 = 2 # ADR-0005: a single fixed max_tokens cannot fit every model in a heterogeneous # pool -- some spend internal reasoning tokens before visible content and need # more, others have a real completion ceiling a large budget would exceed. The @@ -62,6 +124,28 @@ # Shared cap on how many candidates in one preflight run may use the # escalation retry above. It bounds request count, never model response time. REVIEW_PREFLIGHT_MAX_ESCALATIONS = 4 +# Probe outcomes the serving gateway itself treats as transient -- it retries +# the same route and then fails over across exactly these statuses +# (contextual_orchestrator.orchestrator.TRANSIENT_HTTP_STATUS at the vendored +# pin; provider_errors.PROVIDER_STATUS_SURFACES marks 429 retryable). A route +# that answered one of them to the 16-token probe is not known to be dead; it +# was rate-limited or unlucky in the second the probe ran, very often because +# the probe itself spent the per-key budget. Discarding it left the serving +# set with nothing to fail over to: on 2026-09-05 a noema-review preflight +# rejected 11 of 12 routes -- six of them with 429 -- served the one ready +# route for 542 s and returned 502. Such routes are kept as *deferred*, ranked +# after every ready route, so failover has somewhere to go. Only a route that +# *answered* with one of these statuses qualifies (a probe that timed out +# records no http_status and stays rejected), so deferral never admits, on the +# strength of a probe that already showed it, the silent route whose serving +# request would spend the gateway's full retry budget in 90 s timeouts. Keep +# this set in sync with the vendored orchestrator's; a status the gateway +# would not retry must not be deferred. +REVIEW_PREFLIGHT_DEFERRABLE_HTTP_STATUS = frozenset({408, 409, 425, 429, 500, 502, 503, 504, 529}) +# Subtracted from a deferred route's catalog priority so the orchestrator's +# ranking (higher priority first; catalog priorities are 0..-11) never places a +# deferred route ahead of a ready one. +REVIEW_PREFLIGHT_DEFERRED_PRIORITY_PENALTY = 1000 class ReviewPreflightError(RuntimeError): @@ -178,6 +262,9 @@ def _report_rows( "model": model_id, "agent_id": str(getattr(model, "agent_id", None) or f"{provider}_{model_id}"), "is_free": (provider, model_id) in free_route_identities, + "input_modalities": normalize_input_modalities( + getattr(model, "input_modalities", None) + ), "prompt_price_per_1k": getattr(model, "prompt_price_per_1k", None), "completion_price_per_1k": getattr(model, "completion_price_per_1k", None), "currency_code": getattr(model, "currency_code", None), @@ -214,6 +301,51 @@ def _safe_http_status(exc: Exception) -> int | None: return None +def _safe_retry_after_seconds(exc: Exception) -> int | None: + """Return the response's ``Retry-After`` delay in whole seconds, if it sent one. + + Recorded so the evidence can answer a question this codebase cannot + answer today: when a preflight probe is refused with 429, do the + providers say how long the refusal lasts? The 2026-09-06 artifacts show + every probe of a burst refused inside a second (`.github` 34016207820 and + four sibling boots), with nothing in the evidence about how long the + refusal window actually was. Only the delta-seconds + form is read; the HTTP-date form and anything out of range record + nothing, because a wrong number here would be worse than no number. + This is evidence only -- no code waits on it (ADR-0003). + + Args: + exc: The exception a probe attempt raised. + + Returns: + The delay in seconds, or ``None`` when the response carried no + usable ``Retry-After`` header. + """ + headers = getattr(exc, "headers", None) + get_header = getattr(headers, "get", None) + if not callable(get_header): + return None + try: + raw = get_header("Retry-After") + except Exception: # noqa: BLE001 - a hostile header mapping is not evidence + return None + # ``isdecimal`` rather than ``isdigit``: a provider controls this header, + # and ``"²".isdigit()`` is True while ``int("²")`` raises. This + # runs inside the probe walk's exception handler, so a ValueError here + # would escape ``_preflight_review_agents`` -- whose callers catch only + # ``ReviewPreflightError`` -- and kill the boot before any evidence file + # is written. Reject more than five decimal characters before conversion: + # the accepted ceiling is 86400, and Python deliberately rejects very + # long integer strings before ``int`` can return a value. + if not isinstance(raw, str): + return None + normalized = raw.strip() + if len(normalized) > 5 or not normalized.isdecimal(): + return None + seconds = int(normalized) + return seconds if 0 <= seconds <= 86400 else None + + def _response_finish_reason(response: object) -> str | None: """Return a bounded ``finish_reason`` string from an OpenAI-compatible response. @@ -267,16 +399,41 @@ def _record_provider_exception(row: dict[str, object], exc: Exception) -> None: row: The in-progress per-route evidence row to update. exc: The exception a probe attempt raised. """ - row["status"] = "rejected" - error_type = type(exc).__name__ - row["error_type"] = ( - error_type if error_type.isidentifier() and len(error_type) <= 64 else "provider_error" - ) - http_status = _safe_http_status(exc) - if http_status is not None: - row["http_status"] = http_status - row.pop("finish_reason", None) - row.pop("reasoning_without_content", None) + try: + row["status"] = "rejected" + error_type = type(exc).__name__ + row["error_type"] = ( + error_type + if error_type.isidentifier() and len(error_type) <= 64 + else "provider_error" + ) + http_status = _safe_http_status(exc) + if http_status is not None: + row["http_status"] = http_status + retry_after = _safe_retry_after_seconds(exc) + if retry_after is not None: + row["retry_after_s"] = retry_after + row.pop("finish_reason", None) + row.pop("reasoning_without_content", None) + finally: + if isinstance(exc, HTTPError): + exc.close() + + +def _demote_agent(agent: object, penalty: int) -> object: + """Return a copy of ``agent`` whose ``priority`` is lowered by ``penalty``. + + Serving agents are frozen ``ModelAgent`` dataclasses, so the copy goes + through :func:`dataclasses.replace`; the plain objects tests use are + shallow-copied and assigned. A missing ``priority`` counts as 0, matching + the dataclass default. + """ + priority = int(getattr(agent, "priority", 0)) - penalty + if dataclasses.is_dataclass(agent) and not isinstance(agent, type): + return dataclasses.replace(agent, priority=priority) + demoted = copy.copy(agent) + demoted.priority = priority + return demoted def _response_has_reasoning_without_content(response: object) -> bool: @@ -318,7 +475,8 @@ def _response_has_reasoning_without_content(response: object) -> bool: def _preflight_review_agents( - agents: list[object], *, client: Any, escalations_used: int = 0 + agents: list[object], *, client: Any, escalations_used: int = 0, + claim_escalation: Callable[[], bool] | None = None ) -> tuple[list[object], dict[str, object]]: """Probe each route with the runtime request contract and keep ready routes. @@ -368,9 +526,24 @@ def _preflight_review_agents( response, both fields are absent entirely (there is no response to describe) rather than silently retaining the base attempt's values. + Candidates are probed lazily in catalog order (ADR-0029): probing stops + once ``REVIEW_PREFLIGHT_TARGET_READY`` routes are ready or + ``REVIEW_PREFLIGHT_MAX_PROBES`` probes have been spent, so a dead + candidate costs one probe rather than a served slot and a healthy pool is + not probed to exhaustion. An account that has answered 429 to + ``REVIEW_PREFLIGHT_ACCOUNT_SKIP_AFTER_429`` consecutive probes has its + remaining candidates postponed behind the other accounts' candidates; + once the first pass ends with the target unmet and budget left, the + postponed candidates are probed in catalog order (a 429 is an answer + about the instant, not the account). Unprobed candidates get no + ``routes`` row; ``skipped_count`` counts the postponed candidates the + budget never reached, ``postponed_probed_count`` the ones it did, and + ``candidate_count - probed_count - skipped_count`` the unreached tail. + Args: agents: Selected zero-cost model agents. client: Vendored ``ModelClient``-compatible transport. + claim_escalation: Optional atomic reservation shared by concurrent probes. escalations_used: Escalations already spent earlier in this same preflight run (e.g. by a prior stage), so the shared budget is honored across calls rather than restarted at zero. @@ -386,7 +559,41 @@ def _preflight_review_agents( """ viable: list[object] = [] routes: list[dict[str, object]] = [] - for agent in agents: + consecutive_429: dict[str, int] = {} + # Candidates the account rule set aside in the first pass, in catalog + # order. They are probed in a second pass while budget is left and the + # target is unmet; the ones that pass never reaches are the skipped ones. + postponed: list[object] = [] + postponed_probed = 0 + # One entry per probe, in probe order: ``routes[i]`` describes + # ``probed[i]``. A postponed candidate joins both only when its probe + # runs, so the deferral pass below must pair rows with this list, not + # with ``agents``. + probed: list[object] = [] + walk = iter(agents) + second_pass = False + # A dedicated sentinel, not ``None``: ``None`` is a legal element of a + # candidate list and would silently truncate the walk. + exhausted = object() + while True: + if len(viable) >= REVIEW_PREFLIGHT_TARGET_READY or len(routes) >= REVIEW_PREFLIGHT_MAX_PROBES: + break + agent = next(walk, exhausted) + if agent is exhausted: + if second_pass or not postponed: + break + walk = iter(postponed) + second_pass = True + continue + account = provider_account(str(getattr(agent, "provider_name", "") or "unknown")) + if second_pass: + postponed_probed += 1 + elif consecutive_429.get(account, 0) >= REVIEW_PREFLIGHT_ACCOUNT_SKIP_AFTER_429: + postponed.append(agent) + continue + # Cleared here; only a 429 answer below restores it, incremented. + streak_429 = consecutive_429.pop(account, 0) + probed.append(agent) row: dict[str, object] = { "agent_id": str(getattr(agent, "id", "")), "provider": str(getattr(agent, "provider_name", "") or "unknown"), @@ -407,6 +614,8 @@ def _preflight_review_agents( response = client.proxy_send_once(agent, "chat/completions", base_payload) except Exception as exc: # noqa: BLE001 - sanitize at the provider boundary _record_provider_exception(row, exc) + if row.get("http_status") == 429: + consecutive_429[account] = streak_429 + 1 routes.append(row) continue if _chat_response_has_text(response): @@ -456,11 +665,30 @@ def _preflight_review_agents( # a specific policy without real telemetry on which candidates # actually need escalation would itself be the kind of unjustified # heuristic this design rejects elsewhere. - if not budget_signature or escalations_used >= REVIEW_PREFLIGHT_MAX_ESCALATIONS: - row["status"] = "rejected" - row["error_type"] = ( - "invalid_chat_response" if not budget_signature else "escalation_budget_exhausted" + # The second pass never draws on the shared escalation budget. That + # budget is one counter for the whole run, spent in catalog order and + # carried into the priced fallback stage (#1458). Candidates in the + # second pass are ones the account rule had set aside and the previous + # design never probed at all, so letting them claim escalations would + # take them from stages that had them before: measured on a two-stage + # run where every primary candidate on one account answered 429, the + # priced fallback candidate that needs its escalation is denied one and + # the run stops serving a route it used to serve. + if ( + not budget_signature + or second_pass + or ( + not claim_escalation() if claim_escalation is not None + else escalations_used >= REVIEW_PREFLIGHT_MAX_ESCALATIONS ) + ): + row["status"] = "rejected" + if not budget_signature: + row["error_type"] = "invalid_chat_response" + elif second_pass: + row["error_type"] = "escalation_reserved_for_first_pass" + else: + row["error_type"] = "escalation_budget_exhausted" routes.append(row) continue escalations_used += 1 @@ -507,11 +735,36 @@ def _preflight_review_agents( ) routes.append(row) + # Deferral pass: a route rejected with a status the serving gateway would + # retry and fail over across is kept behind the ready routes instead of + # being discarded -- but only once at least one route is ready. With no + # ready route the run still fails this stage exactly as before, so + # _preflight_with_fallback's "priced catalog only after every primary + # route rejects" contract (ADR-0005) is unchanged. ``routes`` holds one + # row per *probed* agent in probe order (every branch above appends once), + # and ``probed`` the matching agents -- a postponed candidate is in both + # once its second-pass probe has run, and in neither otherwise. + deferred: list[object] = [] + if viable: + for agent, row in zip(probed, routes): + if ( + row.get("status") == "rejected" + and row.get("http_status") in REVIEW_PREFLIGHT_DEFERRABLE_HTTP_STATUS + ): + row["status"] = "deferred" + deferred.append(_demote_agent(agent, REVIEW_PREFLIGHT_DEFERRED_PRIORITY_PENALTY)) report: dict[str, object] = { "contract": "strix-plain-chat-preflight-v2", - "probed_count": len(agents), + "candidate_count": len(agents), + "probed_count": len(routes), "ready_count": len(viable), - "rejected_count": len(agents) - len(viable), + "deferred_count": len(deferred), + "rejected_count": len(routes) - len(viable) - len(deferred), + "skipped_count": len(postponed) - postponed_probed, + "postponed_probed_count": postponed_probed, + "target_ready": REVIEW_PREFLIGHT_TARGET_READY, + "probe_budget": REVIEW_PREFLIGHT_MAX_PROBES, + "account_skip_after_429": REVIEW_PREFLIGHT_ACCOUNT_SKIP_AFTER_429, "escalations_used": escalations_used, "escalation_budget": REVIEW_PREFLIGHT_MAX_ESCALATIONS, "routes": routes, @@ -520,35 +773,162 @@ def _preflight_review_agents( raise ReviewPreflightError( "no provider route passed the Strix plain-chat preflight", report ) - return viable, report + return [*viable, *deferred], report + + +def _preflight_review_agents_concurrently( + agents: list[object], *, client: Any, escalations_used: int = 0 +) -> tuple[list[object], dict[str, object]]: + """Fill the validated pool without waiting for one pending inference. + + Reuse the serial route validator; share the existing probe and escalation + budgets across at most MAX_PROBES outstanding calls. Pending calls are + neither cancelled nor classified as unavailable. Only completed ready + routes and explicitly retryable responses enter the serving pool. + """ + completed: queue.Queue = queue.Queue() + budget_lock = threading.Lock() + sealed = False + rows: list[dict[str, object]] = [] + probed: list[object] = [] + ready: list[object] = [] + streaks: dict[str, int] = {} + postponed: list[object] = [] + postponed_probed = 0 + walk = iter(agents) + second_pass = False + outstanding = 0 + exhausted = object() + + def claim() -> bool: + """Atomically reserve one of the shared escalated attempts.""" + nonlocal escalations_used + with budget_lock: + if sealed or escalations_used >= REVIEW_PREFLIGHT_MAX_ESCALATIONS: + return False + escalations_used += 1 + return True + + def probe(index: int, agent: object, postponed_probe: bool) -> None: + """Publish a completed sanitized route result or an unexpected exception.""" + try: + try: + _, report = _preflight_review_agents( + [agent], client=client, + escalations_used=(REVIEW_PREFLIGHT_MAX_ESCALATIONS if postponed_probe else 0), + claim_escalation=(None if postponed_probe else claim), + ) + except ReviewPreflightError as exc: + report = exc.report + row = report["routes"][0] + if postponed_probe and row.get("error_type") == "escalation_budget_exhausted": + row["error_type"] = "escalation_reserved_for_first_pass" + completed.put((index, row)) + except BaseException as exc: # propagate worker faults, never a review verdict + completed.put((index, exc)) + + try: + while len(ready) < REVIEW_PREFLIGHT_TARGET_READY: + try: + index, outcome = completed.get_nowait() + except queue.Empty: + agent = next(walk, exhausted) if len(probed) < REVIEW_PREFLIGHT_MAX_PROBES else exhausted + if agent is exhausted and not second_pass and postponed: + walk = iter(postponed) + second_pass = True + continue + if agent is not exhausted: + account = provider_account(str(getattr(agent, "provider_name", "") or "unknown")) + if second_pass: + postponed_probed += 1 + elif streaks.get(account, 0) >= REVIEW_PREFLIGHT_ACCOUNT_SKIP_AFTER_429: + postponed.append(agent) + continue + index = len(probed) + probed.append(agent) + rows.append({ + "agent_id": str(getattr(agent, "id", "")), + "provider": str(getattr(agent, "provider_name", "") or "unknown"), + "model": str(getattr(agent, "model", "")), + "status": "pending", + }) + outstanding += 1 + # Lifecycle cancellation is owned by the sidecar process. + # Daemons avoid an interpreter exit joining a pending model. + threading.Thread(target=probe, args=(index, agent, second_pass), daemon=True).start() + continue + if not outstanding: + break + index, outcome = completed.get() + outstanding -= 1 + if isinstance(outcome, BaseException): + raise outcome + rows[index] = outcome + agent = probed[index] + account = provider_account(str(getattr(agent, "provider_name", "") or "unknown")) + streaks[account] = streaks.get(account, 0) + 1 if outcome.get("http_status") == 429 else 0 + if outcome.get("status") == "ready": + ready.append(agent) + finally: + with budget_lock: + sealed = True + + ready = [agent for agent, row in zip(probed, rows) if row["status"] == "ready"] + deferred: list[object] = [] + if ready: + for agent, row in zip(probed, rows): + if row.get("status") == "rejected" and row.get("http_status") in REVIEW_PREFLIGHT_DEFERRABLE_HTTP_STATUS: + row["status"] = "deferred" + deferred.append(_demote_agent(agent, REVIEW_PREFLIGHT_DEFERRED_PRIORITY_PENALTY)) + report = { + "contract": "strix-plain-chat-preflight-v2", + "candidate_count": len(agents), "probed_count": len(probed), + "ready_count": len(ready), "deferred_count": len(deferred), + "rejected_count": sum(row["status"] == "rejected" for row in rows), + "pending_count": sum(row["status"] == "pending" for row in rows), + "skipped_count": len(postponed) - postponed_probed, + "postponed_probed_count": postponed_probed, + "target_ready": REVIEW_PREFLIGHT_TARGET_READY, + "probe_budget": REVIEW_PREFLIGHT_MAX_PROBES, + "account_skip_after_429": REVIEW_PREFLIGHT_ACCOUNT_SKIP_AFTER_429, + "escalations_used": escalations_used, + "escalation_budget": REVIEW_PREFLIGHT_MAX_ESCALATIONS, + "routes": rows, + } + if not ready: + raise ReviewPreflightError("no provider route passed the Strix plain-chat preflight", report) + return [*ready, *deferred], report def _preflight_with_fallback( - primary_agents: list[object], fallback_agents: list[object], *, client: Any + primary_agents: list[object], fallback_agents: list[object], *, client: Any, + preflight: Callable | None = None ) -> tuple[list[object], dict[str, object], bool]: """Use the priced catalog only after every primary route rejects. The two stages share ADR-0005's one ``REVIEW_PREFLIGHT_MAX_ESCALATIONS`` budget for the whole preflight run, not one budget each: the primary stage's ending ``escalations_used`` is passed as the fallback stage's - starting point, so a run that rejects all 8 primary routes and then - probes 4 fallback routes still spends at most 4 escalations total (12 - base attempts + 4 escalations). This bounds request count, not individual + starting point, so a run that rejects all 16 primary candidates and then + probes 8 fallback candidates still spends at most 4 escalations total (at + most ``REVIEW_PREFLIGHT_MAX_PROBES`` base attempts per stage + 4 + escalations). This bounds request count, not individual model response or sidecar readiness time. Both stages' reports remain in the result: the fallback (or sole) stage's report carries the run's final, cumulative ``escalations_used``, and ``primary_attempt`` nests the primary stage's own report -- including its own ``escalations_used`` -- whenever a fallback stage ran at all. """ + preflight = preflight or _preflight_review_agents try: - viable, report = _preflight_review_agents(primary_agents, client=client) + viable, report = preflight(primary_agents, client=client) return viable, report, False except ReviewPreflightError as primary_error: if not fallback_agents: raise escalations_used = int(primary_error.report.get("escalations_used", 0)) try: - viable, report = _preflight_review_agents( + viable, report = preflight( fallback_agents, client=client, escalations_used=escalations_used ) except ReviewPreflightError as fallback_error: @@ -580,8 +960,9 @@ def _log_preflight_rejections(report: dict[str, object]) -> None: if not isinstance(routes, list): return for row in routes: - if not isinstance(row, dict) or row.get("status") != "rejected": + if not isinstance(row, dict) or row.get("status") not in ("rejected", "deferred"): continue + event = f"preflight_route_{row['status']}" # Re-validate rather than trust the caller's own sanitization: this # print reaches the sidecar's sanitized stderr stream unchanged, so an # out-of-contract value here (not a plain identifier) must degrade to @@ -601,13 +982,13 @@ def _log_preflight_rejections(report: dict[str, object]) -> None: http_status = row.get("http_status") if isinstance(http_status, int) and not isinstance(http_status, bool) and 100 <= http_status <= 599: print( - f"preflight_route_rejected provider={provider} " + f"{event} provider={provider} " f"error_type={error_type} http_status={http_status}", file=sys.stderr, ) else: print( - f"preflight_route_rejected provider={provider} error_type={error_type}", + f"{event} provider={provider} error_type={error_type}", file=sys.stderr, ) @@ -628,6 +1009,10 @@ def _bounded_primary_catalog_limit( total_limit = min(requested_limit, REVIEW_PREFLIGHT_MAX_TOTAL_ROUTES) if pool == "auto" and has_free_rows: return min(total_limit, REVIEW_PREFLIGHT_PRIMARY_ROUTE_LIMIT) + # ADR-0029: the production pool is ``free`` (no fallback stage) and lists + # the full two-stage budget. Candidates past REVIEW_PREFLIGHT_MAX_PROBES + # are reached only when the account-skip rule frees probes; the report's + # ``skipped_count`` keeps that tail distinguishable from an early stop. return total_limit @@ -673,6 +1058,62 @@ def _catalog_account_cap(default: int) -> int: return int(os.environ.get("ORCHESTRATOR_CATALOG_ACCOUNT_CAP", str(default))) +DEFAULT_SIDECAR_LOG_LEVEL = "DEBUG" +SIDECAR_LOG_FORMAT = "%(asctime)s %(levelname)s %(name)s %(message)s" + + +def _sidecar_log_level() -> str: + """Return the log level the review sidecar configures for its orchestrator process. + + Defaults to ``DEBUG`` because that is where ``contextual_orchestrator`` + records the per-request trace a failed review needs afterwards: every + provider attempt (``provider_attempt``), its classified failure + (``provider_attempt_failed`` with error type and transient flag), backoff, + and circuit events are ``_LOGGER.debug`` calls, while the default + ``WARNING`` level keeps only ``provider_exhausted``/``circuit_opened``. At + the vendored pin none of those DEBUG sites logs a prompt, payload, or + response body; the one free-text field is ``provider_attempt_failed``'s + ``error_message`` (the exception text, which can quote an upstream error + body), and the sidecar pipes this process's stderr through the allow-list + sanitizer before it reaches disk, so only lines the sanitizer recognises + -- and only their structured fields -- become CI evidence. On + 2026-09-05 a 3122 s ``noema-review`` failure could not be attributed to + "six ready routes, two retry layers, 548 s per hop" from the job log alone + because this trace was never emitted. Override with + ``ORCHESTRATOR_SIDECAR_LOG_LEVEL``. + """ + return os.environ.get("ORCHESTRATOR_SIDECAR_LOG_LEVEL", DEFAULT_SIDECAR_LOG_LEVEL) + + +def _configure_sidecar_logging(configure_logging: Callable[[str], None]) -> str: + """Configure the orchestrator process's logging for CI evidence. + + ``configure_logging`` is ``contextual_orchestrator.debug_logging.configure_logging`` + (injected so this module stays importable without the vendored package): + it installs the root level with ``basicConfig(force=True)``. Its default + formatter carries no timestamp, and a per-attempt trace without + timestamps cannot yield per-hop durations, so every root handler is then + given :data:`SIDECAR_LOG_FORMAT`. + + Returns: + The level name that was applied. + + Raises: + SystemExit: If ``ORCHESTRATOR_SIDECAR_LOG_LEVEL`` is not a level name + the orchestrator accepts; a misspelt level must not silently leave + the process at ``WARNING``. + """ + level = _sidecar_log_level() + try: + configure_logging(level) + except ValueError as exc: + raise SystemExit(f"ORCHESTRATOR_SIDECAR_LOG_LEVEL is invalid: {exc}") from None + formatter = logging.Formatter(SIDECAR_LOG_FORMAT) + for handler in logging.getLogger().handlers: + handler.setFormatter(formatter) + return level + + def _with_discovery_counts( report: dict[str, object], rows: list[dict[str, Any]], @@ -782,6 +1223,7 @@ def main(argv: list[str] | None = None) -> int: parser.add_argument("--zdr-endpoints", default=None, help="Optional OpenRouter /api/v1/endpoints/zdr JSON path") parser.add_argument("--require-zdr", action="store_true") parser.add_argument("--pool", choices=("free", "auto"), default="free") + parser.add_argument("--zero-cost-only", action="store_true", help="Exclude priced routes and priced fallback even for auto") args = parser.parse_args(argv) from contextual_orchestrator.credentials import get_credential @@ -802,7 +1244,9 @@ def main(argv: list[str] | None = None) -> int: parse_discovery_report, provider_account, ) + from contextual_orchestrator.debug_logging import configure_logging + _configure_sidecar_logging(configure_logging) registered = register_review_credentials(os.environ) auth_token = args.auth_token or get_credential(REVIEW_AUTH_CREDENTIAL_NAME) if not auth_token: @@ -815,7 +1259,7 @@ def main(argv: list[str] | None = None) -> int: try: discovered, discovery_errors = discover_all_models() - except Exception as exc: # pragma: no cover - provider/networking failure is runtime-only + except Exception as exc: raise SystemExit(f"review sidecar discovery failed: {exc}") from exc _log_discovery_errors(discovery_errors) routable_discovered = _routable_discovered_models(discovered) @@ -826,7 +1270,7 @@ def main(argv: list[str] | None = None) -> int: model_id = getattr(model, "model_id", "") if not is_general_chat_agent_model_id(model_id) or not _has_text_output(model): continue - if args.pool == "free" and _route_identity(model) not in free_route_identities: + if (args.pool == "free" or args.zero_cost_only) and _route_identity(model) not in free_route_identities: continue selected_models.append(model) if not selected_models: @@ -856,7 +1300,7 @@ def main(argv: list[str] | None = None) -> int: zdr_endpoints=zdr_endpoints, checker=is_zdr_model, ) - requested_catalog_limit = int(os.environ.get("ORCHESTRATOR_CATALOG_LIMIT", "12")) + requested_catalog_limit = int(os.environ.get("ORCHESTRATOR_CATALOG_LIMIT", "24")) primary_limit = _bounded_primary_catalog_limit( requested_catalog_limit, pool=args.pool, has_free_rows=bool(admitted_free_rows) ) @@ -871,8 +1315,10 @@ def main(argv: list[str] | None = None) -> int: account_cap=_catalog_account_cap(DEFAULT_ACCOUNT_CAP), zdr_endpoints=zdr_endpoints, require_zdr=args.require_zdr, - pool=args.pool, + pool="free" if args.zero_cost_only else args.pool, ) + result["report"]["requested_pool"] = f"orchestrator/{args.pool}" + result["report"]["zero_cost_only"] = args.zero_cost_only result["report"] = _with_discovery_counts( result["report"], normalized_rows, provider_account=provider_account ) @@ -891,6 +1337,7 @@ def main(argv: list[str] | None = None) -> int: ) if ( args.pool == "auto" + and not args.zero_cost_only and admitted_free_rows and admitted_priced_rows and fallback_limit @@ -926,7 +1373,8 @@ def main(argv: list[str] | None = None) -> int: ) try: agents, preflight_report, fallback_used = _preflight_with_fallback( - agents, fallback_agents, client=client + agents, fallback_agents, client=client, + preflight=_preflight_review_agents_concurrently, ) except ReviewPreflightError as exc: _write_json(args.preflight_out, exc.report) @@ -960,5 +1408,5 @@ def main(argv: list[str] | None = None) -> int: return 0 -if __name__ == "__main__": # pragma: no cover +if __name__ == "__main__": raise SystemExit(main()) diff --git a/scripts/ci/contextual_orchestrator_review_policy.py b/scripts/ci/contextual_orchestrator_review_policy.py index 910b8da3a9..2169dcc027 100644 --- a/scripts/ci/contextual_orchestrator_review_policy.py +++ b/scripts/ci/contextual_orchestrator_review_policy.py @@ -13,6 +13,7 @@ from __future__ import annotations import argparse +import itertools import json import math import re @@ -142,6 +143,29 @@ def _normalize_cost_evidence( ) +def normalize_input_modalities(value: object) -> list[str]: + """Preserve explicit input evidence as normalized catalog-tag tokens. + + Match the pinned contextual-orchestrator ``input:`` grammar. + Missing evidence remains empty, never guessed text. Malformed nonempty + evidence fails closed rather than silently dropping provider exclusions. + """ + if value is None: + return [] + if not isinstance(value, (list, tuple)): + raise PolicyError("input_modalities must be a list or tuple of tokens") + normalized: list[str] = [] + for item in value: + if not isinstance(item, str): + raise PolicyError("input_modalities contains a non-string token") + token = item.strip().casefold() + if not re.fullmatch(r"[a-z][a-z0-9_-]*", token): + raise PolicyError("input_modalities contains an invalid token") + if token not in normalized: + normalized.append(token) + return normalized + + def _bytez_non_token_price_evidence( *, is_free: bool, @@ -239,6 +263,7 @@ def parse_discovery_report(report: Mapping[str, Any]) -> list[dict[str, Any]]: "provider": provider, "model": model, "agent_id": str(candidate_id), + "input_modalities": normalize_input_modalities(row.get("input_modalities")), "is_free": is_free, "cost_evidence": cost_evidence, "prompt_price_per_1k": prompt_price, @@ -273,6 +298,21 @@ def _free_pool_source_admitted(row: Mapping[str, Any]) -> bool: ) +def _route_tier(row: Mapping[str, Any], zdr_endpoints: frozenset[str]) -> tuple[int, int]: + """Return the ``(cost rank, ZDR rank)`` tier a route is selected within. + + Free routes rank before priced ones and ZDR-attested routes before + unattested ones; the tier is what the catalog fill must never reorder, + while accounts inside one tier may be interleaved freely. + """ + attested = is_zdr_model( + str(row["provider"]), + model=str(row["model"]), + zdr_endpoints=zdr_endpoints, + ) + return (_COST_EVIDENCE_RANK[_cost_evidence(row)], 0 if attested else 1) + + def build_zdr_prioritized_catalog( rows: Iterable[Mapping[str, Any]], *, @@ -317,27 +357,36 @@ def build_zdr_prioritized_catalog( ] eligible_rows.sort( key=lambda row: ( - _COST_EVIDENCE_RANK[_cost_evidence(row)], - 0 - if is_zdr_model( - str(row["provider"]), - model=str(row["model"]), - zdr_endpoints=zdr_endpoints, - ) - else 1, + *_route_tier(row, zdr_endpoints), str(row["provider"]), str(row["model"]), ) ) + # Fill each (cost, ZDR) tier round-robin across independently credentialed + # accounts. A plain sorted fill let the alphabetically first account take + # its whole cap before the next account saw a slot: on 2026-09-05 the review + # sidecar admitted 62 free routes across three accounts and served + # 8 nvidia_nim + 4 nvidia_nim_sub + 0 openrouter (limit 12, cap 8), so a + # stalled NVIDIA endpoint had no other account to fail over to + # (ContextualWisdomLab/.github#1476, contextual-orchestrator#1045). per_account: Counter[str] = Counter() picked: list[Mapping[str, Any]] = [] - for row in eligible_rows: - account = provider_account(str(row["provider"])) - if per_account[account] >= account_cap: - continue - per_account[account] += 1 - picked.append(row) + for _tier, tier_rows in itertools.groupby( + eligible_rows, key=lambda row: _route_tier(row, zdr_endpoints) + ): + queues: dict[str, list[Mapping[str, Any]]] = {} + for row in tier_rows: + queues.setdefault(provider_account(str(row["provider"])), []).append(row) + while queues and len(picked) < limit: + for account in list(queues): + if per_account[account] >= account_cap or not queues[account]: + del queues[account] + continue + picked.append(queues[account].pop(0)) + per_account[account] += 1 + if len(picked) >= limit: + break if len(picked) >= limit: break @@ -370,6 +419,9 @@ def build_zdr_prioritized_catalog( "review", f"cost:{evidence}", "zdr" if zdr else "non-zdr", + *(f"input:{value}" for value in normalize_input_modalities( + row.get("input_modalities") + )), ], "priority": -rank, "disabled": False, @@ -441,14 +493,23 @@ def build_zdr_prioritized_catalog( def _load_zdr_endpoints(path: str | None) -> frozenset[str]: - """Load exact provider/model keys from an OpenRouter ZDR feed file.""" + """Load exact provider/model keys from an OpenRouter ZDR feed file. + + Each feed row's ``model_id`` is the discovery slug contextual-orchestrator + reports as ``model`` (e.g. ``"inclusionai/ling-3.0-flash-vl:free"``); + ``model_name`` is a human display string (e.g. "DeepSeek: DeepSeek V4.1 + Flash") and is never used to build a route key. ``provider_name`` is the + feed's serving-provider label (e.g. "Novita"). Rows missing either + ``model_id`` or ``provider_name`` are skipped; there is no fallback to + the display name. + """ if not path: return frozenset() payload = json.loads(Path(path).read_text(encoding="utf-8")) keys: set[str] = set() for endpoint in payload.get("data", []): provider = endpoint.get("provider_name") - model = endpoint.get("model_name") + model = endpoint.get("model_id") if provider and model: keys.add(_route_key(str(provider), str(model))) keys.add(_route_key("openrouter", str(model))) diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index a96e854a51..c9fa4ef81e 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-2e414d15ba58f28597751b625a8a2f00fc9fadcf}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-01bf92a3ec67a0e1f9b68978eb16b60301e985fd}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. @@ -35,14 +35,21 @@ SIDECAR_LOG_SANITIZER="$ORG_REPO_ROOT/scripts/ci/sanitize_contextual_orchestrato # finishes, letting the shell script wait for a deterministic marker instead # of guessing whether the async sanitizer has caught up. SIDECAR_DISCOVERY_DIAGNOSTICS_SENTINEL="discovery_diagnostics_complete" -CATALOG_LIMIT="${ORCHESTRATOR_CATALOG_LIMIT:-12}" +CATALOG_LIMIT="${ORCHESTRATOR_CATALOG_LIMIT:-24}" # Each KV credential is an independent account, including two credentials for # the same vendor or endpoint. The account cap prevents one credential from -# consuming the bounded twelve-route preflight catalog without inventing a -# provider-family equivalence relation. +# consuming the bounded preflight candidate list (24 candidates, probed lazily +# to a readiness target -- ADR-0029) without inventing a provider-family +# equivalence relation. CATALOG_ACCOUNT_CAP="${ORCHESTRATOR_CATALOG_ACCOUNT_CAP:-8}" ORCHESTRATOR_GITHUB_ENV="${GITHUB_ENV:-}" -sidecar_python="$(command -v python3)" +sidecar_python="${SIDECAR_PYTHON:-$(command -v python3)}" +# setup-python with update-environment=false leaves the consumer's library path. +# Bind this process to the selected interpreter's matching shared runtime. +sidecar_python_lib="$(dirname "$(dirname "$(realpath "$(command -v "$sidecar_python")")")")/lib" +if [ -f "$sidecar_python_lib/libpython3.12.so.1.0" ]; then + export LD_LIBRARY_PATH="$sidecar_python_lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +fi log() { printf '[contextual-orchestrator-sidecar] %s\n' "$*"; } @@ -62,6 +69,22 @@ if [ "$provider_secret_count" -lt 1 ]; then fi log "provider secrets present: $provider_secret_count of 5" +# A full self-hosted runner disk (cwlab-s1-04, 2026-09-29) surfaced as a venv or +# pip failure and was misread as a provider outage. Name the cause up front: +# warn when space is low, stop only when the venv install cannot fit. +SIDECAR_LOW_FREE_KIB=$((2 * 1024 * 1024)) +SIDECAR_MIN_FREE_KIB=$((512 * 1024)) +runner_temp_free_kib="$(df -Pk "${RUNNER_TEMP:-/tmp}" | awk 'NR == 2 { print $4 }')" +case "$runner_temp_free_kib" in + ''|*[!0-9]*) fail "could not read free space for ${RUNNER_TEMP:-/tmp}" ;; +esac +if [ "$runner_temp_free_kib" -lt "$SIDECAR_MIN_FREE_KIB" ]; then + fail "runner disk has $((runner_temp_free_kib / 1024)) MiB free under ${RUNNER_TEMP:-/tmp}; sidecar provisioning needs at least 512 MiB. Reclaim runner disk before rerunning." +elif [ "$runner_temp_free_kib" -lt "$SIDECAR_LOW_FREE_KIB" ]; then + printf '::warning::runner disk has %s MiB free under %s; reclaim runner disk before it fills.\n' \ + "$((runner_temp_free_kib / 1024))" "${RUNNER_TEMP:-/tmp}" +fi + ORCHESTRATOR_TOKEN="${ORCHESTRATOR_TOKEN:-$($sidecar_python -c 'import secrets; print(secrets.token_urlsafe(32))')}" case "$ORCHESTRATOR_TOKEN" in *$'\r'*|*$'\n'*) fail "ORCHESTRATOR_TOKEN must not contain CR or LF" ;; @@ -100,6 +123,12 @@ requirements_lock="$ORCHESTRATOR_SOURCE/requirements.lock" if [ ! -f "$requirements_lock" ]; then fail "vendored orchestrator is missing its hash-pinned requirements.lock" fi +# The pinned lock includes CPython 3.12 wheels; isolate them from consumer runtimes. +"$sidecar_python" -c 'import sys; sys.exit(0 if sys.version_info[:2] == (3, 12) else "sidecar requires Python 3.12 for its pinned wheel hashes")' +# RUNNER_TEMP persists on self-hosted runners; a cancelled job can leave a +# half-written pip that a plain re-run of venv keeps. Always rebuild. +"$sidecar_python" -m venv --clear "$ORCHESTRATOR_WORK/.venv" +sidecar_python="$ORCHESTRATOR_WORK/.venv/bin/python" log "installing hash-pinned orchestrator dependencies at ${checked_out}" "$sidecar_python" -m pip install --quiet --disable-pip-version-check --no-cache-dir \ --require-hashes \ @@ -108,10 +137,15 @@ log "installing hash-pinned orchestrator dependencies at ${checked_out}" PYTHONPATH="$ORCHESTRATOR_SOURCE:$ORG_REPO_ROOT" "$sidecar_python" -c \ 'from contextual_orchestrator.credentials import get_credential; from contextual_orchestrator.model_discovery import discover_all_models, free_discovered_models; from contextual_orchestrator.orchestrator import ModelClient, TaskOrchestrator, load_agents; from contextual_orchestrator.review_gateway import register_review_credentials; from contextual_orchestrator.server import SecurityConfig, serve' PYTHONPATH="$ORCHESTRATOR_SOURCE:$ORG_REPO_ROOT" "$sidecar_python" - <<'PY' -import contextlib +import faulthandler + +# Fatal startup diagnostics contain stack locations, never frame locals. +faulthandler.enable() + import http.client import io import json +import logging import threading from contextual_orchestrator.orchestrator import ModelAgent, ModelClient, TaskOrchestrator @@ -150,7 +184,10 @@ thread.start() try: connection = http.client.HTTPConnection("127.0.0.1", server.server_address[1], timeout=5) expected_rejection_log = io.StringIO() - with contextlib.redirect_stderr(expected_rejection_log): + capture = logging.StreamHandler(expected_rejection_log) + server_logger = logging.getLogger("contextual_orchestrator.server") + server_logger.addHandler(capture) + try: connection.request( "POST", "/v1/chat/completions", @@ -164,6 +201,8 @@ try: response = connection.getresponse() assert response.status == 413, response.status response.read() + finally: + server_logger.removeHandler(capture) assert ( "request_failed status=413 code=request_too_large" in expected_rejection_log.getvalue() @@ -282,14 +321,12 @@ case "$orchestrator_pool" in free) pool_args=(--pool "$orchestrator_pool") ;; + auto) + # Auto resolves the gateway alias; the explicit fence excludes priced candidates and fallback. + pool_args=(--pool "$orchestrator_pool" --zero-cost-only) + ;; *) - # GitHub Actions Workflow usage of contextual-orchestrator is pinned to - # orchestrator/free: the org has not solved cost-safe free+ZDR routing - # well enough yet to justify a priced-inclusive "auto" pool in central CI, - # so "auto" is rejected here even though the launcher's own --pool flag - # (a general-purpose CLI also used outside GitHub Actions) still accepts - # it. - fail "CONTEXTUAL_ORCHESTRATOR_POOL must be free" + fail "CONTEXTUAL_ORCHESTRATOR_POOL must be free or auto" ;; esac @@ -371,6 +408,10 @@ until curl -fsSL "http://${ORCHESTRATOR_HOST}:${ORCHESTRATOR_PORT}/healthz" >/de fail "sidecar exited before healthz (status ${sidecar_status}); stderr: $(sed -n '1,20p' "$sidecar_stderr")" fi i=$((i + 1)) + if [ "$((i % 60))" -eq 0 ]; then + # Only report file presence; provider content stays in sanitized artifacts. + log "startup pending: polls=${i} discovery=$([ -s "$discovery_report" ] && echo present || echo absent) catalog=$([ -s "$catalog_file" ] && echo present || echo absent) policy=$([ -s "$policy_report" ] && echo present || echo absent) preflight=$([ -s "$preflight_report" ] && echo present || echo absent)" + fi sleep 1 done if [ ! -s "$preflight_report" ]; then @@ -688,4 +729,7 @@ fi log "policy evidence summary:" sed -n '1,80p' "$policy_report" || true log "runtime preflight summary:" -sed -n '1,160p' "$preflight_report" || true +# 16 probed routes at 8-10 lines each plus the header run past the old +# 160-line cap exactly in the dead hour the summary matters most (ADR-0029); +# the artifact copy was always complete, only the job-log echo was cut. +sed -n '1,400p' "$preflight_report" || true diff --git a/scripts/ci/current_head_run_coalescer.py b/scripts/ci/current_head_run_coalescer.py index ae40b85ac4..948c80cd01 100644 --- a/scripts/ci/current_head_run_coalescer.py +++ b/scripts/ci/current_head_run_coalescer.py @@ -29,6 +29,7 @@ API_TIMEOUT_SECONDS = 30 CANCELLATION_POLL_ATTEMPTS = 6 CANCELLATION_POLL_INTERVAL_SECONDS = 1.0 +QUEUE_START_RACE_RE = re.compile(r"\bHTTP\s*409\b") class CoalescingRefused(RuntimeError): @@ -373,7 +374,24 @@ def _fetch_run(repo: str, run_id: int) -> dict[str, Any]: def _cancel_run(repo: str, run_id: int) -> None: """Cancel one run and prove GitHub reached its terminal cancelled state.""" - _run_json(["gh", "api", "-X", "POST", f"repos/{repo}/actions/runs/{run_id}/cancel"]) + cancel_args = ["gh", "api", "-X", "POST", f"repos/{repo}/actions/runs/{run_id}/cancel"] + try: + _run_json(cancel_args) + except RuntimeError as exc: + # GitHub can race a queued run into startup between the candidate + # fetch and POST, returning HTTP 409 instead of accepting cancel. + # Re-read the authoritative run state; never turn an unknown + # cancellation error into a successful result or another mutation. + if not QUEUE_START_RACE_RE.search(str(exc)): + raise + current = _fetch_run(repo, run_id) + if current.get("status") == "completed" and current.get("conclusion") == "cancelled": + return + if current.get("status") != "queued": + raise CoalescingRefused(f"workflow run {run_id} is no longer queued after HTTP 409") from exc + raise CoalescingRefused( + f"workflow run {run_id} remained queued after HTTP 409; preserving it" + ) from exc for attempt in range(CANCELLATION_POLL_ATTEMPTS): run_data = _fetch_run(repo, run_id) if run_data.get("status") == "completed" and run_data.get("conclusion") == "cancelled": diff --git a/scripts/ci/law_ai_agent_ci.sh b/scripts/ci/law_ai_agent_ci.sh new file mode 100755 index 0000000000..542359159c --- /dev/null +++ b/scripts/ci/law_ai_agent_ci.sh @@ -0,0 +1,257 @@ +#!/usr/bin/env bash +# Law CI: isolate the consumer, native test database, tool cache and built artifacts. +# Shell function comments are the docstring contract; Python blocks use docstrings. +set -euo pipefail +SELF="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/$(basename -- "${BASH_SOURCE[0]}")" + +# Verify pytest's JUnit evidence, including every nested suite and executed count. +verify_junit() { + python3 - "$1" <<'PY' +"""Reject skips, failures, errors and empty JUnit evidence.""" +import sys +import xml.etree.ElementTree as ET +suites = list(ET.parse(sys.argv[1]).getroot().iter("testsuite")) +valid = bool(suites) and sum(int(s.get("tests", "0")) for s in suites) > 0 +valid &= all(int(s.get(k, "0")) == 0 for s in suites for k in ("skipped", "failures", "errors")) +valid &= not any(ET.parse(sys.argv[1]).getroot().iter("skipped")) +sys.exit(0 if valid else 1) +PY +} + +# Refuse ambiguous or stale artifact directories rather than selecting a glob match. +verify_dist() { + python3 - "$1" <<'PY' +"""Require exactly one regular non-symlink wheel and source archive.""" +import sys +from pathlib import Path +entries = list(Path(sys.argv[1]).iterdir()) +valid = len(entries) == 2 and all(p.is_file() and not p.is_symlink() for p in entries) +valid &= sum(p.name.endswith(".whl") for p in entries) == 1 +valid &= sum(p.name.endswith(".tar.gz") for p in entries) == 1 +sys.exit(0 if valid else 1) +PY +} + +# Validate owned canonical immediate-child paths and refuse every symlink component. +validate_owned_root() { + python3 - "$RUNNER_TEMP" "$1" "${2:-}" <<'PY' +"""Authorize only private current-user scratch roots and optional state markers.""" +import os +import stat +import sys +from pathlib import Path +base, root = map(Path, sys.argv[1:3]) +valid = base.is_absolute() and root.is_absolute() +for path in (base, root): + for component in (path, *path.parents): + valid &= not component.is_symlink() +valid &= root.parent == base and root.name.startswith("lawci.") +valid &= root.is_dir() and root.resolve().parent == base.resolve() +if root.exists(): + info = root.stat() + valid &= info.st_uid == os.getuid() and stat.S_IMODE(info.st_mode) == 0o700 +if sys.argv[3]: + marker = Path(sys.argv[3]) + valid &= marker.parent == base and not marker.is_symlink() and marker.is_file() + if marker.is_file(): + info = marker.stat() + valid &= info.st_uid == os.getuid() and stat.S_IMODE(info.st_mode) == 0o600 +sys.exit(0 if valid else 1) +PY +} + +# Remove only this lane's private scratch cluster; failed stop leaves evidence for retry. +cleanup_root() { + local root="$1" pg_bin="$2" cluster="$1/data/private/postgres/cluster" boundary_path + validate_owned_root "$root" || return 1 + for boundary_path in \ + "$root/data" "$root/data/private" "$root/data/private/postgres" \ + "$cluster" "$cluster/postmaster.pid"; do + if [[ -L "$boundary_path" ]]; then + printf '%s\n' 'law-ci: PostgreSQL cluster boundary is symlinked; scratch retained' >&2 + return 1 + fi + done + if [[ -e "$cluster" && ! -d "$cluster" ]]; then + printf '%s\n' 'law-ci: PostgreSQL cluster path is not a directory; scratch retained' >&2 + return 1 + fi + if [[ -d "$cluster" ]]; then + if [[ ! -f "$cluster/postmaster.pid" ]]; then + printf '%s\n' 'law-ci: PostgreSQL cluster PID metadata is missing; shutdown is inconclusive; scratch retained' >&2 + return 1 + fi + "$pg_bin/pg_ctl" -D "$cluster" -m fast -w stop || return 1 + if [[ -e "$cluster/postmaster.pid" ]]; then + printf '%s\n' 'law-ci: PostgreSQL stop returned success but PID metadata remains; scratch retained' >&2 + return 1 + fi + fi + rm -rf -- "$root" +} + +# Preserve the failing child status and turn a failed database cleanup into failure. +on_exit() { + local status=$? + trap - EXIT INT TERM + if ! cleanup_root "$scratch" "$pg_bin"; then + printf '%s\n' 'law-ci: cleanup failed; owned scratch retained' >&2 + [[ "$status" != 0 ]] || status=1 + else + if [[ "$state_owned" == 1 ]]; then rm -f -- "$state"; fi + fi + exit "$status" +} + +# Execute committed source in a fresh short scratch root, never mutate the checkout. +run_ci() { + [[ "${LAW_CI_SOURCE_SHA:-}" =~ ^[0-9a-f]{40}$ ]] && + [[ "$(git -C "${LAW_CI_SOURCE:-}" rev-parse HEAD)" == "$LAW_CI_SOURCE_SHA" ]] || { + printf '%s\n' 'law-ci: source identity refused' >&2; return 1; + } + [[ "${LAW_CI_PYTHON_VERSION:-}" == 3.12 || "${LAW_CI_PYTHON_VERSION:-}" == 3.14 ]] + [[ "$(uv --version)" == 'uv 0.12.5 '* ]] + pg_bin="$(pg_config --bindir)" + for tool in initdb pg_ctl createdb psql; do test -x "$pg_bin/$tool"; done + test -d "${RUNNER_TEMP:?}" + umask 077 + scratch="$(mktemp -d "$RUNNER_TEMP/lawci.XXXXXX")" + state_owned=0 + state="$RUNNER_TEMP/law-ci-state-${LAW_CI_PYTHON_VERSION}" + trap on_exit EXIT + trap 'exit 130' INT + trap 'exit 143' TERM + test ! -e "$state" && test ! -L "$state" + (set -o noclobber; printf '%s\n' "$scratch" > "$state") + state_owned=1 + # Native Unix socket paths must remain shorter than the platform socket limit. + test "${#scratch}" -lt 65 + export LAW_AGENT_ROOT="$scratch" + export LAW_AGENT_POSTGRES_ROOT="$LAW_AGENT_ROOT" + export LAW_AGENT_POSTGRES_USER + LAW_AGENT_POSTGRES_USER="$(id -un)" + export LAW_AGENT_POSTGRES_PORT=55439 + local private="$scratch/data/private/postgres" + mkdir -p "$private/socket" "$scratch/source" "$scratch/out" "$scratch/outside" + chmod 700 "$scratch" "$private" "$private/socket" + # Consumer safety code requires this exact cluster/socket identity and test database. + export LAW_AGENT_TEST_DATABASE_URL="dbname=law_agent_test user=$LAW_AGENT_POSTGRES_USER host='$private/socket' port=$LAW_AGENT_POSTGRES_PORT" + unset LAW_AGENT_DATABASE_URL PGHOSTADDR PGSERVICE PGSERVICEFILE PGSYSCONFDIR PGOPTIONS PYTHONPATH PYTHONHOME VIRTUAL_ENV + export UV_CACHE_DIR="$scratch/uv-cache" UV_PROJECT_ENVIRONMENT="$scratch/venv" + export UV_PYTHON="$LAW_CI_PYTHON_VERSION" UV_NO_CACHE=1 + printf '%s\n' 'law-ci: fresh native test cluster' + "$pg_bin/initdb" -D "$private/cluster" -A trust -U "$LAW_AGENT_POSTGRES_USER" --encoding=UTF8 --no-locale > "$scratch/initdb.log" + "$pg_bin/pg_ctl" -D "$private/cluster" -l "$scratch/server.log" \ + -o "-k \"$private/socket\" -p $LAW_AGENT_POSTGRES_PORT -c listen_addresses='' -c shared_buffers=32MB -c max_connections=15 -c statement_timeout=10s -c lock_timeout=2s" -w start + "$pg_bin/createdb" -h "$private/socket" -p "$LAW_AGENT_POSTGRES_PORT" law_agent_test + git -C "$LAW_CI_SOURCE" archive "$LAW_CI_SOURCE_SHA" | tar -x -C "$scratch/source" + cd "$scratch/source" + printf '%s\n' 'law-ci: locked source quality gates' + uv sync --locked + uv run --locked ruff check . + uv run --locked ruff format --check . + uv run --locked mypy + # Fixtures need the complete archived migration inventory before suite collection. + # Retain source evidence outside the checkout for the independent installed smoke. + # Use the consumer's real test_only approval, not the broader development CLI path. + uv run --locked python - "$scratch/source-migrations.json" <<'PY' +"""Commit the verified migrations to the fresh, approved test-only database.""" +from hashlib import sha256 +import json +import os +from pathlib import Path +import re +import sys +if sys.version_info[:2] != tuple(map(int, os.environ["LAW_CI_PYTHON_VERSION"].split("."))): + raise SystemExit("law-ci: Python version mismatch") +# Check lexical ancestors before traversal or importing the archived product package. +resources = Path("src/law_ai_agent/migrations").absolute() +for component in reversed((resources, *resources.parents)): + if component.is_symlink() or not component.is_dir(): + raise SystemExit("law-ci: source migration inventory invalid") +inventory = [] +versions = set() +for resource in sorted(resources.iterdir(), key=lambda path: path.name): + match = re.fullmatch(r"([0-9]{3})_[a-z][a-z0-9]*(?:_[a-z0-9]+)*\.sql", resource.name) + if not match or not resource.is_file() or resource.is_symlink(): + raise SystemExit("law-ci: source migration inventory invalid") + version = int(match[1]) + script = resource.read_text() + if version == 0 or version in versions or not script.strip(): + raise SystemExit("law-ci: source migration inventory invalid") + versions.add(version) + # Preserve PostgresStore.migrate's read_text()/script.encode() checksum semantics. + inventory.append({"name": resource.name, "version": version, + "checksum": sha256(script.encode()).hexdigest()}) +if not inventory: + raise SystemExit("law-ci: source migration inventory invalid") +from law_ai_agent.postgres import PostgresStore, connect +with connect(os.environ["LAW_AGENT_TEST_DATABASE_URL"], test_only=True) as connection: + PostgresStore(connection).migrate() + connection.commit() + actual = [{"version": row["version"], "checksum": row["checksum"]} + for row in connection.execute( + "SELECT version, checksum FROM law_agent.migration ORDER BY version")] + expected = [{"version": row["version"], "checksum": row["checksum"]} for row in inventory] + if actual != expected: + raise SystemExit("law-ci: database migration inventory mismatch") +Path(sys.argv[1]).write_text(json.dumps(inventory)) +PY + uv run --locked pytest --cov --cov-fail-under=90 --cov-report=term-missing --junitxml="$scratch/junit.xml" + bash "$SELF" verify-junit "$scratch/junit.xml" + printf '%s\n' 'law-ci: fresh build and installed artifact smoke' + uv build --out-dir "$scratch/out" --no-create-gitignore + bash "$SELF" verify-dist "$scratch/out" + # Prime dependency wheels into a new lane-local cache, then prove real offline install. + uv venv --python "$LAW_CI_PYTHON_VERSION" "$scratch/prime" + UV_NO_CACHE=0 uv pip install --python "$scratch/prime/bin/python" "$scratch/out/"*.whl + uv venv --python "$LAW_CI_PYTHON_VERSION" "$scratch/installed" + UV_NO_CACHE=0 uv pip install --offline --python "$scratch/installed/bin/python" "$scratch/out/"*.whl + cd "$scratch/outside" + "$scratch/installed/bin/python" -I - "$scratch/source-migrations.json" <<'PY' +"""Verify packaged migration resources outside the source checkout.""" +from hashlib import sha256 +from importlib.resources import files +import json +from pathlib import Path +import sys +import law_ai_agent +if not Path(law_ai_agent.__file__).resolve().is_relative_to(Path(sys.prefix).resolve()): + raise SystemExit("law-ci: installed package prefix mismatch") +expected = json.loads(Path(sys.argv[1]).read_text()) +resources = sorted(files("law_ai_agent").joinpath("migrations").iterdir(), key=lambda path: path.name) +if not expected or [resource.name for resource in resources] != [row["name"] for row in expected]: + raise SystemExit("law-ci: installed migration inventory mismatch") +for resource, row in zip(resources, expected): + if not resource.is_file(): + raise SystemExit("law-ci: installed migration inventory mismatch") + script = resource.read_text() + if not script.strip() or sha256(script.encode()).hexdigest() != row["checksum"]: + raise SystemExit("law-ci: installed migration inventory mismatch") +PY + "$scratch/installed/bin/python" -I -m law_ai_agent --help > "$scratch/installed-help.log" + printf '%s\n' 'law-ci: quality, zero-skip database and installed artifact gates passed' +} + +# Recover a lane-local marker on GitHub cancellation; never source marker content. +cleanup_state() { + local version state root pg_bin + for version in 3.12 3.14; do + state="${RUNNER_TEMP:?}/law-ci-state-$version" + test -e "$state" || continue + test ! -L "$state" && test -f "$state" + IFS= read -r root < "$state" + validate_owned_root "$root" "$state" + pg_bin="$(pg_config --bindir)" + cleanup_root "$root" "$pg_bin" + rm -f -- "$state" + done +} + +case "${1:-}" in + verify-junit) verify_junit "$2" ;; + verify-dist) verify_dist "$2" ;; + run) run_ci ;; + cleanup) cleanup_state ;; + *) printf '%s\n' 'law-ci: unknown operation' >&2; exit 1 ;; +esac diff --git a/scripts/ci/materialize_base_javascript_packages.py b/scripts/ci/materialize_base_javascript_packages.py index 489364f92e..5937161896 100644 --- a/scripts/ci/materialize_base_javascript_packages.py +++ b/scripts/ci/materialize_base_javascript_packages.py @@ -58,7 +58,10 @@ def _github_actions_escape(value: object) -> str: def _git(repo_root: pathlib.Path, *args: str) -> bytes: - """Run one read-only git command in the materialized repository.""" + """Run one read-only git command in the materialized repository. + + Git failures remain bounded diagnostics and never become trusted input. + """ completed = subprocess.run( ["git", "-C", str(repo_root), *args], check=False, @@ -72,7 +75,10 @@ def _git(repo_root: pathlib.Path, *args: str) -> bytes: def _regular_base_paths(repo_root: pathlib.Path, base_sha: str) -> set[str]: - """Return regular blob paths from the exact validated base commit.""" + """Return regular blob paths from the exact validated base commit. + + Symlink-like and traversal paths are excluded before content is materialized. + """ entries = _git(repo_root, "ls-tree", "-r", "-z", "--full-tree", base_sha) paths: set[str] = set() for raw_entry in entries.split(b"\0"): @@ -102,7 +108,10 @@ def _regular_base_paths(repo_root: pathlib.Path, base_sha: str) -> set[str]: def base_pnpm_projects( repo_root: pathlib.Path, base_sha: str ) -> list[tuple[str, str, dict[str, bytes]]]: - """Return exact base pnpm inputs grouped by lockfile directory.""" + """Return exact base pnpm inputs grouped by lockfile directory. + + Each project must declare an exact package-manager version and regular inputs. + """ if not SHA_RE.fullmatch(base_sha): raise ValueError("base SHA must be exactly 40 hexadecimal characters") @@ -183,7 +192,10 @@ def base_pnpm_projects( def base_npm_projects( repo_root: pathlib.Path, base_sha: str ) -> list[tuple[str, str, dict[str, bytes]]]: - """Return exact base npm inputs grouped by lockfile directory.""" + """Return exact base npm inputs grouped by lockfile directory. + + Vestigial locks and unsafe workspace paths are excluded from the trusted set. + """ if not SHA_RE.fullmatch(base_sha): raise ValueError("base SHA must be exactly 40 hexadecimal characters") @@ -268,7 +280,10 @@ def base_npm_projects( def _lock_blob_sha(repo_root: pathlib.Path, revision_sha: str, lock_path: str) -> str: - """Return the exact Git blob SHA for one validated revision lockfile.""" + """Return the exact Git blob SHA for one validated revision lockfile. + + The identity binds materialized dependency bytes to the reviewed revision. + """ raw_blob = _git(repo_root, "rev-parse", f"{revision_sha}:{lock_path}") blob_sha = raw_blob.decode("ascii", errors="strict").strip() if not SHA_RE.fullmatch(blob_sha): @@ -279,7 +294,10 @@ def _lock_blob_sha(repo_root: pathlib.Path, revision_sha: str, lock_path: str) - def validate_head_npm_lock(lock_path: str, lock_content: bytes) -> None: - """Fail closed unless a changed HEAD npm lock is registry- and hash-bounded.""" + """Fail closed unless a changed HEAD npm lock is registry- and hash-bounded. + + Registry URLs, workspace links, and integrity values are checked without installation. + """ try: lock_data: Any = json.loads(lock_content.decode("utf-8")) except (UnicodeDecodeError, json.JSONDecodeError) as exc: @@ -372,7 +390,10 @@ def validate_head_npm_lock(lock_path: str, lock_content: bytes) -> None: def _validate_pnpm_tarball_url( lock_path: str, package_key: str, tarball_url: str ) -> None: - """Fail closed unless one pnpm tarball URL is an npm-registry HTTPS URL.""" + """Fail closed unless one pnpm tarball URL is an npm-registry HTTPS URL. + + Userinfo, ports, query strings, fragments, and alternate hosts are rejected. + """ parsed = urllib.parse.urlsplit(tarball_url) try: parsed_port = parsed.port @@ -516,7 +537,10 @@ def materialize( output_dir: pathlib.Path, head_sha: str | None = None, ) -> list[dict[str, str]]: - """Write trusted base and bounded HEAD inputs under Docker-context-safe paths.""" + """Write trusted base and bounded HEAD inputs under Docker-context-safe paths. + + Manifest records retain revision and lock-blob identity for downstream verification. + """ if output_dir.exists() and output_dir.is_symlink(): raise ValueError("output directory must not be a symlink") output_dir.mkdir(parents=True, exist_ok=True) @@ -624,7 +648,10 @@ def materialize( def main(argv: list[str] | None = None) -> int: - """Materialize trusted JavaScript locks and report their exact revisions.""" + """Materialize trusted JavaScript locks and report their exact revisions. + + Invalid or unsafe input returns a bounded non-zero diagnostic for the caller. + """ parser = argparse.ArgumentParser() parser.add_argument("--repo-root", required=True, type=pathlib.Path) parser.add_argument("--base-sha", required=True) diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index a052123547..99d228d5e1 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -22,6 +22,7 @@ import urllib.parse import urllib.request from typing import Any +from urllib.error import HTTPError try: import tomllib @@ -42,7 +43,11 @@ r"git\+https://github\.com/ContextualWisdomLab/" r"(?P[A-Za-z0-9_.-]{1,100})\.git@" r"(?P[0-9a-fA-F]{40})" + r"(?:\s*;\s*python_full_version\s*>=\s*'" + r"(?P[0-9]+)\." + r"(?P[0-9]+)')?" ) +TRUSTED_COVERAGE_PYTHON_MAJOR_MINOR = (3, 14) UV_EXPORT_TIMEOUT_SECONDS = 120 TRUSTED_UV_VERSION = "0.12.1" TRUSTED_UV_TARGET_TRIPLE = "x86_64-unknown-linux-gnu" @@ -304,6 +309,12 @@ def _partition_uv_export(content: bytes) -> tuple[bytes, list[dict[str, str]]]: match = UV_EXACT_ORG_VCS_RE.fullmatch(line) if match is None: raise ValueError("uv export contains an unsupported dependency line") + minimum_python_major = match.group("minimum_python_major") + minimum_python_minor = match.group("minimum_python_minor") + if minimum_python_major is not None and ( + int(minimum_python_major), int(minimum_python_minor) + ) > TRUSTED_COVERAGE_PYTHON_MAJOR_MINOR: + raise ValueError("uv export contains an unsupported dependency line") dependency = { "package": match.group("package"), "import_name": re.sub( @@ -366,8 +377,11 @@ def _download_trusted_uv_archive() -> bytes: break payload.extend(chunk) except OSError as exc: + error_type = type(exc).__name__ + if isinstance(exc, HTTPError): + exc.close() raise RuntimeError( - f"trusted uv archive download failed: {type(exc).__name__}" + f"trusted uv archive download failed: {error_type}" ) from exc if len(payload) > TRUSTED_UV_DOWNLOAD_MAX_BYTES: diff --git a/scripts/ci/materialize_base_rust_dependencies.py b/scripts/ci/materialize_base_rust_dependencies.py new file mode 100644 index 0000000000..f56871a083 --- /dev/null +++ b/scripts/ci/materialize_base_rust_dependencies.py @@ -0,0 +1,533 @@ +#!/usr/bin/env python3 +"""Materialize an offline Cargo vendor directory from a validated base commit. + +The sandboxed coverage-measurement container runs with ``--network=none`` (see +``opencode-review-dispatch.yml``'s "Measure test and docstring evidence" step). Python and +JavaScript dependencies already have an offline path through +``materialize_base_python_requirements.py`` and ``materialize_base_javascript_packages.py``, which +run here -- on the runner, before the network-isolated container exists -- and bake a base-pinned +dependency closure into the trusted image. Rust/Cargo had no equivalent: every coverage run against +a Rust crate (directly via ``cargo llvm-cov``, or indirectly through a PyO3/maturin extension a +Python test suite imports) needed ``index.crates.io``, which the offline container can never reach. +Confirmed live across ``fast-mlsirm`` PRs #1868-#1892 (dispatch runs 34884397167 and siblings): +``cargo llvm-cov`` failed with ``Could not resolve host: index.crates.io``, and the generic Python +pytest path failed at collection with ``ImportError: cannot import name '_core'`` because nothing in +the sandbox ever builds the compiled extension. Both surfaced as a generic "Coverage gate: failure", +indistinguishable from a real regression in the pull request. + +This mirrors the Python materializer's trust model: only the validated base commit's Cargo +manifests are read (never the pull request's), and vendoring itself uses Cargo's own built-in +per-package checksum verification (every ``[[package]]`` entry in a lock file carries a +``checksum``), so no separate hash-pin parser is needed the way ``requirements*.txt`` needed one. + +An explicit ``--head-sha`` opts into a narrower lock-repair intake: every Cargo manifest +remains byte-identical to base, every registry record (including resolved dependency edges) +must already occur in the base lock union, and local identities must already be base-pinned. +Only lock bytes are overlaid; Cargo vendor --locked still verifies the unchanged manifests +and package checksums. Separate provenance records the base manifests and head lock blobs. +""" + +from __future__ import annotations + +import argparse +import json +import pathlib +import re +import subprocess +import sys +import tempfile + +try: + import tomllib +except ModuleNotFoundError: # pragma: no cover - exercised by Python 3.10 CI. + import tomli as tomllib + + +SHA_RE = re.compile(r"^[0-9a-fA-F]{40}$") +CARGO_VENDOR_TIMEOUT_SECONDS = 600 + + +def _git(repo_root: pathlib.Path, *args: str) -> bytes: + """Run one read-only git command against the materialized repository.""" + completed = subprocess.run( + ["git", "-C", str(repo_root), *args], + check=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + if completed.returncode != 0: + stderr = completed.stderr.decode("utf-8", errors="replace").strip() + raise RuntimeError(f"git {args[0]} failed: {stderr}") + return completed.stdout + + +def _regular_cargo_blob_paths(repo_root: pathlib.Path, base_sha: str) -> list[str]: + """Return tracked, non-symlink ``Cargo.toml``/``Cargo.lock`` paths at ``base_sha``.""" + entries = _git(repo_root, "ls-tree", "-r", "-z", "--full-tree", base_sha) + paths: list[str] = [] + for raw_entry in entries.split(b"\0"): + if not raw_entry: + continue + metadata, separator, raw_path = raw_entry.partition(b"\t") + if not separator: + raise RuntimeError("git ls-tree returned a malformed entry") + fields = metadata.split() + if len(fields) != 3: + raise RuntimeError("git ls-tree returned malformed metadata") + mode, object_type, _object_id = ( + field.decode("ascii", errors="strict") for field in fields + ) + path = raw_path.decode("utf-8", errors="surrogateescape") + candidate = pathlib.PurePosixPath(path) + if ( + object_type != "blob" + or not mode.startswith("100") + or candidate.is_absolute() + or ".." in candidate.parts + ): + continue + if candidate.name in ("Cargo.toml", "Cargo.lock"): + paths.append(path) + return sorted(paths) + + +def _is_workspace_manifest(content: bytes) -> bool: + """Return whether one ``Cargo.toml`` blob declares a ``[workspace]`` table.""" + try: + parsed = tomllib.loads(content.decode("utf-8")) + except (UnicodeDecodeError, tomllib.TOMLDecodeError) as exc: + raise RuntimeError("could not parse a tracked base Cargo.toml") from exc + return "workspace" in parsed + + +def _select_vendor_roots( + repo_root: pathlib.Path, base_sha: str, cargo_paths: list[str] +) -> list[str]: + """Return every directory whose base lock must be vendored, primary root first. + + A base tree may legitimately hold several lock roots: the standard cargo-fuzz + layout declares ``[workspace]`` in both the repository root and ``fuzz/`` so the + fuzz crate opts out of the parent workspace, and the two locks resolve *different* + crate sets. Selecting one root and dropping the rest would silently vendor an + incomplete closure, so every root is vendored into one shared directory via + ``cargo vendor --sync`` and every lock is asserted with ``--locked``. + + What still fails closed is a root that cannot be reconciled at all: a manifest + declaring a workspace with no sibling ``Cargo.lock``, or a lock with no sibling + ``Cargo.toml``. Those are unresolvable rather than merely plural. + """ + manifests = { + (path.rsplit("/", 1)[0] if "/" in path else ".") + for path in cargo_paths + if path.endswith("Cargo.toml") + } + locks = { + (path.rsplit("/", 1)[0] if "/" in path else ".") + for path in cargo_paths + if path.endswith("Cargo.lock") + } + for manifest_path in sorted( + path for path in cargo_paths if path.endswith("Cargo.toml") + ): + content = _git(repo_root, "show", f"{base_sha}:{manifest_path}") + if not _is_workspace_manifest(content): + continue + manifest_dir = manifest_path.rsplit("/", 1)[0] if "/" in manifest_path else "." + if manifest_dir not in locks: + raise RuntimeError( + f"base Cargo workspace root {manifest_dir} has no sibling Cargo.lock" + ) + for lock_dir in sorted(locks): + if lock_dir not in manifests: + raise RuntimeError( + f"base Cargo.lock at {lock_dir} has no sibling Cargo.toml" + ) + if not locks: + return [] + # Deterministic order with the repository root first when it is one of the roots, + # so the primary --manifest-path is stable across runs and hosts. + ordered = sorted(locks, key=lambda root: (root != ".", root)) + return ordered + + +def _placeholder_target_paths(manifest_content: bytes) -> list[str]: + """Return package target source paths a manifest needs present to parse. + + ``cargo vendor`` never compiles anything -- it only resolves and downloads the locked + dependency graph -- but Cargo still refuses to *parse* a package manifest whose declared + targets do not exist on disk. Real source is never required for vendoring, so this returns + the conventional and any explicitly declared target paths; the caller writes empty + placeholder files at each one. + """ + try: + parsed = tomllib.loads(manifest_content.decode("utf-8")) + except (UnicodeDecodeError, tomllib.TOMLDecodeError): + return [] + if "package" not in parsed: + return [] + paths = {"src/lib.rs", "src/main.rs"} + lib_path = ( + parsed.get("lib", {}).get("path") + if isinstance(parsed.get("lib"), dict) + else None + ) + if isinstance(lib_path, str): + paths.add(lib_path) + for bin_target in ( + parsed.get("bin", []) if isinstance(parsed.get("bin"), list) else [] + ): + bin_path = bin_target.get("path") if isinstance(bin_target, dict) else None + if isinstance(bin_path, str): + paths.add(bin_path) + return sorted(paths) + + +def _reconstruct_base_tree( + repo_root: pathlib.Path, + base_sha: str, + cargo_paths: list[str], + work_dir: pathlib.Path, +) -> None: + """Write every tracked base Cargo manifest into ``work_dir`` at its repository path. + + Each package manifest's conventional/declared target paths also get an empty placeholder + file -- see :func:`_placeholder_target_paths` for why real source is never needed here. + """ + for path in cargo_paths: + content = _git(repo_root, "show", f"{base_sha}:{path}") + destination = work_dir / pathlib.Path(*pathlib.PurePosixPath(path).parts) + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_bytes(content) + if destination.name == "Cargo.toml": + for target_path in _placeholder_target_paths(content): + target_relative_path = pathlib.PurePosixPath(target_path) + if ( + target_relative_path.is_absolute() + or ".." in target_relative_path.parts + ): + raise RuntimeError( + "Cargo target path must stay inside its manifest root: " + f"{target_path}" + ) + target_destination = destination.parent / pathlib.Path( + *target_relative_path.parts + ) + target_destination.parent.mkdir(parents=True, exist_ok=True) + if not target_destination.exists(): + target_destination.write_bytes(b"") + + +def _run_cargo_vendor( + manifest_path: pathlib.Path, + vendor_dir: pathlib.Path, + sync_manifests: list[pathlib.Path] | None = None, +) -> subprocess.CompletedProcess[bytes]: + """Vendor the union of the base manifests, asserting every lock stays unchanged. + + ``--sync`` adds each further root's manifest to the same vendor directory, so no + root's dependencies are dropped. ``--locked`` makes cargo refuse to re-resolve: + without it a lock that disagrees with its manifest would be quietly updated and + the vendored set would no longer be the committed closure. + """ + command = [ + "cargo", + "vendor", + "--locked", + "--manifest-path", + str(manifest_path), + "--versioned-dirs", + ] + for sync_manifest in sync_manifests or []: + command.extend(["--sync", str(sync_manifest)]) + command.append(str(vendor_dir)) + return subprocess.run( + command, + check=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + timeout=CARGO_VENDOR_TIMEOUT_SECONDS, + ) + + +def _normalized_lock_records(content: bytes) -> list[dict]: + """Compare bounded lock records with uniquely resolved dependency identities.""" + parsed = tomllib.loads(content.decode("utf-8")) + packages = parsed.get("package") + if ( + set(parsed) - {"version", "package"} + or parsed.get("version") not in {3, 4} + or not isinstance(packages, list) + or len(packages) > 10000 + ): + raise ValueError("head intake requires a bounded version 3/4 Cargo lock") + identities = {} + by_name = {} + for package in packages: + if not isinstance(package, dict): + raise ValueError("invalid Cargo lock package") + source = package.get("source") + allowed = {"name", "version", "dependencies"} + if source is not None: + allowed |= {"source", "checksum"} + if ( + source != "registry+https://github.com/rust-lang/crates.io-index" + or not isinstance(package.get("checksum"), str) + or not re.fullmatch(r"[0-9a-f]{64}", package["checksum"]) + ): + raise ValueError( + "head intake requires existing crates.io checksum pins" + ) + if set(package) - allowed or any( + not isinstance(package.get(k), str) or not package[k] + for k in ("name", "version") + ): + raise ValueError("unsupported Cargo lock package fields") + identity = (package["name"], package["version"], source) + if identity in identities: + raise ValueError("duplicate Cargo lock package identity") + identities[identity] = package + by_name.setdefault(identity[0], []).append(identity) + records = [] + for package in packages: + dependencies = package.get("dependencies", []) + if not isinstance(dependencies, list) or len(dependencies) > 10000: + raise ValueError("invalid Cargo lock dependencies") + edges = [] + for dependency in dependencies: + if not isinstance(dependency, str): + raise ValueError("invalid Cargo lock dependency identity") + parts = dependency.split() + if not 1 <= len(parts) <= 3: + raise ValueError("unsupported Cargo lock dependency identity") + candidates = [ + identity + for identity in by_name.get(parts[0], []) + if (len(parts) < 2 or identity[1] == parts[1]) + and (len(parts) < 3 or f"({identity[2]})" == parts[2]) + ] + if len(candidates) != 1: + raise ValueError("missing or ambiguous Cargo lock dependency identity") + edges.append(candidates[0]) + if len(edges) != len(set(edges)): + raise ValueError("duplicate Cargo lock dependency edge") + records.append({**package, "dependencies": sorted(edges, key=repr)}) + return records + + +def _validated_head_locks( + repo_root: pathlib.Path, base_sha: str, head_sha: str, cargo_paths: list[str] +) -> tuple[dict[str, bytes], dict]: + """Allow head locks only to recombine records pinned in the base lock union. + + Manifest bytes remain from base. Local-package closure still requires the + unchanged base manifests to pass Cargo vendor --locked; this function does + not authorize new registry records, git sources or package checksums. + """ + if not SHA_RE.fullmatch(head_sha): + raise ValueError("head SHA must be exactly 40 hexadecimal characters") + head_paths = _regular_cargo_blob_paths(repo_root, head_sha) + if head_paths != cargo_paths: + raise ValueError("head Cargo manifest/lock paths must equal base") + changed = _git(repo_root, "diff", "--name-only", "-z", base_sha, head_sha).split( + b"\0" + ) + if any( + path and pathlib.PurePosixPath(path.decode()).name == "Cargo.toml" + for path in changed + ): + raise ValueError("head Cargo manifests must remain byte-identical to base") + + def lock_bytes(revision: str, path: str) -> bytes: + """Read a revision-pinned lock after checking its bounded blob size.""" + size = int(_git(repo_root, "cat-file", "-s", f"{revision}:{path}")) + if size > 16 * 1024 * 1024: + raise ValueError("Cargo lock exceeds bounded size") + return _git(repo_root, "show", f"{revision}:{path}") + + paths = [path for path in cargo_paths if path.endswith("Cargo.lock")] + base_records = [] + for path in paths: + base_records.extend(_normalized_lock_records(lock_bytes(base_sha, path))) + registry_records = { + json.dumps(row, sort_keys=True) for row in base_records if row.get("source") + } + local_identities = { + (row["name"], row["version"]) for row in base_records if not row.get("source") + } + locks = {} + receipts = [] + for path in paths: + content = lock_bytes(head_sha, path) + for row in _normalized_lock_records(content): + if row.get("source"): + if json.dumps(row, sort_keys=True) not in registry_records: + raise ValueError( + "head registry record differs from base lock union" + ) + elif (row["name"], row["version"]) not in local_identities: + raise ValueError("head local identity differs from base lock union") + locks[path] = content + receipts.append( + { + "path": path, + "base_lock_blob": _git(repo_root, "rev-parse", f"{base_sha}:{path}") + .decode() + .strip(), + "lock_blob": _git(repo_root, "rev-parse", f"{head_sha}:{path}") + .decode() + .strip(), + } + ) + return locks, { + "manifest_revision": base_sha.lower(), + "lock_revision": head_sha.lower(), + "locks": receipts, + } + + +def materialize( + repo_root: pathlib.Path, + base_sha: str, + output_dir: pathlib.Path, + *, + vendor_dir_for_config: str | None = None, + head_sha: str | None = None, +) -> list[str]: + """Vendor base manifests with base locks or explicitly bounded head locks. + + Returns the list of source-tree-relative ``Cargo.lock`` paths that were vendored. An empty + list means no Rust project (or no lock file) exists at the base commit, which is not an + error -- most repositories reviewed by this pipeline have no Rust code at all. + + ``vendor_dir_for_config`` overrides the ``directory = `` path written into + ``cargo-config.toml``. Vendoring runs on the runner (this materializer's own working + directory), but the vendored files are later copied into the trusted coverage image at a + fixed path; the emitted config must name that final in-image path, not the runner's + temporary one. + """ + if not SHA_RE.fullmatch(base_sha): + raise ValueError("base SHA must be exactly 40 hexadecimal characters") + if output_dir.exists() and output_dir.is_symlink(): + raise ValueError("output directory must not be a symlink") + output_dir.mkdir(parents=True, exist_ok=True) + + resolved_repo = repo_root.resolve() + cargo_paths = _regular_cargo_blob_paths(resolved_repo, base_sha) + vendor_roots = _select_vendor_roots(resolved_repo, base_sha, cargo_paths) + manifest: list[str] = [] + head_locks, provenance = ( + _validated_head_locks(resolved_repo, base_sha, head_sha, cargo_paths) + if head_sha is not None + else ({}, None) + ) + if vendor_roots: + primary_root, *additional_roots = vendor_roots + + def _manifest_for(root: str, base: pathlib.Path) -> pathlib.Path: + """Return the reconstructed manifest path for one validated root.""" + return base / ("Cargo.toml" if root == "." else f"{root}/Cargo.toml") + + def _lock_for(root: str) -> str: + """Return the repository-relative lock path for one validated root.""" + return "Cargo.lock" if root == "." else f"{root}/Cargo.lock" + + with tempfile.TemporaryDirectory() as work_dir: + work_path = pathlib.Path(work_dir) + _reconstruct_base_tree(resolved_repo, base_sha, cargo_paths, work_path) + for path, content in head_locks.items(): + (work_path / path).write_bytes(content) + manifest_path = _manifest_for(primary_root, work_path) + sync_manifests = [ + _manifest_for(root, work_path) for root in additional_roots + ] + # Every root's lock is reported, so a failure names the whole vendored set + # rather than only the primary root. + lock_path = ", ".join(_lock_for(root) for root in vendor_roots) + vendor_dir = output_dir / "vendor" + try: + completed = _run_cargo_vendor(manifest_path, vendor_dir, sync_manifests) + except FileNotFoundError as exc: + raise RuntimeError( + f"could not run trusted cargo vendor for base manifest {lock_path}: " + "cargo is not installed or not on PATH on this runner " + "(self-hosted runners keep it in ~/.cargo/bin)" + ) from exc + except (OSError, subprocess.TimeoutExpired) as exc: + raise RuntimeError( + f"could not run trusted cargo vendor for base manifest {lock_path}: " + f"{type(exc).__name__}" + ) from exc + if completed.returncode != 0: + stderr = completed.stderr.decode("utf-8", errors="replace") + normalized_stderr = " ".join(stderr.split()) + detail = ( + normalized_stderr[:500] + if normalized_stderr + else (f"exit status {completed.returncode}") + ) + raise RuntimeError( + f"cargo vendor failed for base lock {lock_path}: {detail}" + ) + config_text = completed.stdout + if vendor_dir_for_config is not None: + config_text = config_text.replace( + str(vendor_dir).encode("utf-8"), + vendor_dir_for_config.encode("utf-8"), + ) + (output_dir / "cargo-config.toml").write_bytes(config_text) + manifest = [_lock_for(root) for root in vendor_roots] + + if provenance is not None: + (output_dir / "lock-provenance.json").write_text( + json.dumps(provenance, indent=2) + "\n" + ) + (output_dir / "manifest.json").write_text( + json.dumps(manifest, indent=2, sort_keys=True) + "\n", + encoding="utf-8", + ) + return manifest + + +def main(argv: list[str] | None = None) -> int: + """Materialize the base Cargo vendor directory and report what was selected.""" + parser = argparse.ArgumentParser() + parser.add_argument("--repo-root", required=True, type=pathlib.Path) + parser.add_argument("--base-sha", required=True) + parser.add_argument("--head-sha", default=None) + parser.add_argument("--output-dir", required=True, type=pathlib.Path) + parser.add_argument("--vendor-dir-for-config", default=None) + args = parser.parse_args(argv) + + try: + manifest = materialize( + args.repo_root, + args.base_sha, + args.output_dir, + vendor_dir_for_config=args.vendor_dir_for_config, + head_sha=args.head_sha, + ) + except (OSError, RuntimeError, ValueError) as exc: + print( + f"::error::Could not materialize base Rust dependencies: {exc}", + file=sys.stderr, + ) + return 1 + + if manifest: + if args.head_sha: + print( + f"Materialized Cargo vendor directory from base manifests and bounded head locks: {manifest[0]}." + ) + else: + print( + f"Materialized trusted base Cargo vendor directory from {manifest[0]}." + ) + else: + print( + "No tracked Cargo.lock exists at the validated base SHA; Rust vendoring skipped." + ) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/ci/materialize_public_review_source.py b/scripts/ci/materialize_public_review_source.py new file mode 100644 index 0000000000..e0747410a5 --- /dev/null +++ b/scripts/ci/materialize_public_review_source.py @@ -0,0 +1,173 @@ +"""공개 PR의 고정 base/head를 credential-free Git child로 재구성한다. + +신뢰된 metadata가 명시한 public 대상만 받는다. API 재조회, 자격증명, +artifact fallback 및 제품 코드 실행은 하지 않는다. 실행 전 runner의 독점 +job directory와 시스템 Git/Python은 신뢰 경계다. 동일 UID의 경쟁 writer나 +Actions worker ancestor의 자격증명까지 이 helper가 격리한다고 주장하지 않는다. +""" +from __future__ import annotations + +import argparse +import os +from pathlib import Path, PurePosixPath +import re +import shutil +import stat +import subprocess +import tempfile +from typing import Callable + + +class MaterializationError(RuntimeError): + """민감한 외부 출력 없이 source admission 실패를 표시한다.""" + + +GIT_CONFIG = ( + "credential.helper=", "credential.interactive=false", "http.extraHeader=", + "http.followRedirects=false", "http.sslVerify=true", "http.proxy=", + "protocol.allow=never", "protocol.https.allow=always", "core.hooksPath=/dev/null", + "core.attributesFile=/dev/null", "core.fsmonitor=false", "core.sshCommand=/usr/bin/false", + "commit.gpgSign=false", "tag.gpgSign=false", "merge.gpgSign=false", + "core.autocrlf=false", "core.protectHFS=true", "core.protectNTFS=true", + "submodule.recurse=false", "fetch.recurseSubmodules=false", "gc.auto=0", + "maintenance.auto=false", "user.name=github-actions[bot]", + "user.email=41898282+github-actions[bot]@users.noreply.github.com", +) + + +def _temporary_root(path: Path) -> Path: + """실제 runner temp와 모든 조상에서 symlink/비디렉터리를 거부한다.""" + if not path.is_absolute() or any(ord(c) < 32 for c in str(path)): + raise MaterializationError("invalid temporary root") + for part in (path, *path.parents): + mode = part.lstat().st_mode + if not stat.S_ISDIR(mode): + raise MaterializationError("invalid temporary root") + info = path.stat() + if path == Path("/") or info.st_uid != os.getuid() or info.st_mode & 0o022: + raise MaterializationError("invalid temporary root") + return path + + +def _environment(home: Path) -> dict[str, str]: + """상속 env 대신 고정된 Git 실행 환경만 전달한다.""" + return { + "PATH": "/usr/bin:/bin", "HOME": str(home), "XDG_CONFIG_HOME": str(home), + "TMPDIR": str(home), "LC_ALL": "C", "LANG": "C", + "GIT_CONFIG_NOSYSTEM": "1", "GIT_CONFIG_SYSTEM": "/dev/null", + "GIT_CONFIG_GLOBAL": "/dev/null", "GIT_ATTR_NOSYSTEM": "1", + "GIT_TERMINAL_PROMPT": "0", "GIT_NO_REPLACE_OBJECTS": "1", + "GIT_EDITOR": "/usr/bin/false", "GIT_SEQUENCE_EDITOR": "/usr/bin/false", + "GIT_MERGE_AUTOEDIT": "no", "GIT_PAGER": "/bin/cat", + } + + +def _admit_entries(inventory: bytes) -> None: + """Checkout 전에 archive와 같은 regular-file/tree 경계를 검사한다.""" + for record in inventory.split(b"\0"): + if not record: + continue + try: + metadata, raw_path = record.split(b"\t", 1) + mode, kind, _oid = metadata.split(b" ") + name = raw_path.decode("utf-8") + except (ValueError, UnicodeError): + raise MaterializationError("invalid source entry") from None + path = PurePosixPath(name) + if (mode not in (b"100644", b"100755") or kind != b"blob" + or not name or path.is_absolute() or "\\" in name + or any(ord(c) < 32 for c in name) + or any(p in ("", ".", "..") or p.casefold() == ".git" for p in name.split("/"))): + raise MaterializationError("invalid source entry") + + +def materialize_public_source( + *, repository: str, base_sha: str, head_sha: str, is_private: str, + runner_temp: Path, run_id: str, run_attempt: str, + git_executable: Path = Path("/usr/bin/git"), runner: Callable = subprocess.run, +) -> Path: + """정상 공개 tuple의 완전한 history와 merge tree를 job 전용 경로에 만든다.""" + if is_private != "false": + raise MaterializationError("explicit public visibility required") + if (not re.fullmatch(r"[A-Za-z0-9][A-Za-z0-9-]{0,38}/[A-Za-z0-9_.][A-Za-z0-9_.-]{0,99}", repository) + or repository.split("/")[-1] in (".", "..")): + raise MaterializationError("invalid repository") + if not all(re.fullmatch(r"[0-9a-f]{40}", value) for value in (base_sha, head_sha)): + raise MaterializationError("invalid commit SHA") + if not all(re.fullmatch(r"[1-9][0-9]{0,19}", value) for value in (run_id, run_attempt)): + raise MaterializationError("invalid run identity") + if not git_executable.is_absolute() or not git_executable.is_file(): + raise MaterializationError("trusted git executable unavailable") + root = _temporary_root(runner_temp) + output = root / f"opencode-coverage-{run_id}-{run_attempt}" + try: + output.mkdir(mode=0o700) + except FileExistsError: + raise MaterializationError("output already exists") from None + identity = output.stat() + succeeded = False + try: + with tempfile.TemporaryDirectory(prefix="public-source-home-", dir=root) as private_home: + environment = _environment(Path(private_home)) + + def git(*args: str) -> bytes: + """고정 env/config로 Git만 실행하고 외부 오류 텍스트를 숨긴다.""" + argv = [str(git_executable)] + for value in GIT_CONFIG: + argv.extend(("-c", value)) + argv.extend(args) + try: + result = runner(argv, cwd=output, env=environment, + stdin=subprocess.DEVNULL, stdout=subprocess.PIPE, + stderr=subprocess.PIPE, shell=False, check=False, timeout=300) + except (OSError, subprocess.TimeoutExpired): + raise MaterializationError("git command failed") from None + if result.returncode: + raise MaterializationError("git command failed") + return result.stdout + + git("init", "-q", "--template=", "--object-format=sha1", ".") + git("fetch", "--no-tags", "--no-recurse-submodules", + f"https://github.com/{repository}.git", base_sha, head_sha) + for commit in (base_sha, head_sha): + if (git("cat-file", "-t", commit).strip() != b"commit" + or git("rev-parse", "--verify", f"{commit}^{{commit}}").strip() != commit.encode()): + raise MaterializationError("invalid commit object") + _admit_entries(git("ls-tree", "-rz", commit)) + git("config", "--local", "core.hooksPath", "/dev/null") + git("checkout", "--detach", base_sha) + git("merge", "--no-ff", "--no-edit", head_sha) + _admit_entries(git("ls-tree", "-rz", "HEAD")) + succeeded = True + return output + finally: + if not succeeded: + # 동일 UID의 경쟁 writer는 caller의 독점 job directory 계약 범위다. + current = output.lstat() + if stat.S_ISDIR(current.st_mode) and (current.st_dev, current.st_ino) == (identity.st_dev, identity.st_ino): + shutil.rmtree(output) + + +def main(argv: list[str] | None = None) -> int: + """환경의 실제 run identity를 사용하고 실패 시 nonzero를 반환한다.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--repository", required=True) + parser.add_argument("--base-sha", required=True) + parser.add_argument("--head-sha", required=True) + parser.add_argument("--is-private", required=True) + args = parser.parse_args(argv) + try: + materialize_public_source( + repository=args.repository, base_sha=args.base_sha, head_sha=args.head_sha, + is_private=args.is_private, runner_temp=Path(os.environ.get("RUNNER_TEMP", "")), + run_id=os.environ.get("GITHUB_RUN_ID", ""), run_attempt=os.environ.get("GITHUB_RUN_ATTEMPT", ""), + ) + except (MaterializationError, OSError): + print("::error::Public review source materialization refused.") + return 1 + print("Materialized exact public base/head without credentials or artifacts.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/ci/noema-document-reader/package-lock.json b/scripts/ci/noema-document-reader/package-lock.json new file mode 100644 index 0000000000..bea758cdf9 --- /dev/null +++ b/scripts/ci/noema-document-reader/package-lock.json @@ -0,0 +1,1319 @@ +{ + "name": "noema-document-reader-runtime", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "noema-document-reader-runtime", + "version": "1.0.0", + "dependencies": { + "@rhwp/core": "0.7.7", + "hwp-mcp": "0.3.0" + } + }, + "node_modules/@hono/node-server": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.1.tgz", + "integrity": "sha512-ELuehkj5VCBdgEw9zs+ivkKwyzzUCSQuE96YmiPvn1ECBoZCczbFXJLeEGMTYjphP6gydh4pHMqEYPVMYUVgQg==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4" + } + }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.31.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.31.0.tgz", + "integrity": "sha512-UvTMgnNlnIBO/22ob2RcVGDlcvOslQs8T59+FTGdA0L27a39fdGF/EDETNtDVK4DZGpwomlsYpRdA8UXcVL/pw==", + "license": "MIT", + "dependencies": { + "@hono/node-server": "^1.19.9 || ^2.0.5", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@rhwp/core": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@rhwp/core/-/core-0.7.7.tgz", + "integrity": "sha512-FHWTdOO+YPY4SSOaFrGGc98AkzrnQy+IIZYng3C00Wqg3+BcaN0uk0cYx0YS4bwtefrPsT6b15fcG6rpNU8iyw==", + "license": "MIT" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "license": "MIT", + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.1.1.tgz", + "integrity": "sha512-EKN1vKAMcZ8MlYMpaNuxN6R9yakzH6uajHcHVTqWJzvu5pWw9DyhbP35HH8MVBQ+dZjAfDxk+A8NiR9KWaXiyQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.7.0.tgz", + "integrity": "sha512-hOwV7WOxXfjRpAM1DSJWZDXx3GhplwD8IfwuwvogD8i1Qnkgosw/H45s4ZnFAUHDAhPjlY9hLBvJhKmGMyY26g==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "ip-address": "^10.2.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.7.tgz", + "integrity": "sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==", + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/hwp-mcp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/hwp-mcp/-/hwp-mcp-0.3.0.tgz", + "integrity": "sha512-+CYrAT5cKOpf6GCHyXRvw4SY/Z5GqBxO+Za92PyHCwseU8C6T3A3AR2bVv6hNQbG4SL6GJsgyfeaKjmpMut/Fw==", + "license": "MIT", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.0.0", + "@rhwp/core": "0.7.x", + "jszip": "^3.10.1" + }, + "bin": { + "hwp-mcp": "dist/server.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.7.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.1.tgz", + "integrity": "sha512-4OUAqU9Z1i3vCnS05hzGiFnEMDpQ+62pAD/MVQOp83fYyNC8GleCqaS0QikQBmcWCrKFiUs/B8ztRRiYOAXuCA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/jose": { + "version": "6.2.12", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.12.tgz", + "integrity": "sha512-9NiFmJEex0sy2Dk58j2UGBSHgUs2ypF9eZSu4L6vjOX3Dp96Sw1F3uL+H+D1sx02jZZdzUT0HgvCy59CuvXcWw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "license": "BSD-2-Clause" + }, + "node_modules/jszip": { + "version": "3.10.2", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.2.tgz", + "integrity": "sha512-3l+rb15IOWtUhU0H5MFqES/T6Kh7abYwjosBey/vD6hDt8zoEffkSC5Ws5SGtgVw3gBx2NEbhTeSW1+kWkpyTQ==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", + "integrity": "sha512-yz3xRaG20c6/BOzvYoDaGtPmGscs7YivItZEEqe6GbwNfHuxu9YNmvnEkMzKldAGY4/80pRcQRZSEnhquk9XuQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/negotiator/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "license": "MIT", + "engines": { + "node": ">=16.20.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/qs": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/zod": { + "version": "4.6.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.4.tgz", + "integrity": "sha512-AXSD6hvGdvRjajG/l1cC+d6IrhH+sjmPKtYeQdJIK8MFJl3LyClzS+o/YsVC+zQZPupAaeH5skwwm8YqYH7BqA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "license": "ISC", + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } + } + } +} diff --git a/scripts/ci/noema-document-reader/package.json b/scripts/ci/noema-document-reader/package.json new file mode 100644 index 0000000000..d6626d18ed --- /dev/null +++ b/scripts/ci/noema-document-reader/package.json @@ -0,0 +1,15 @@ +{ + "name": "noema-document-reader-runtime", + "private": true, + "version": "1.0.0", + "dependencies": { + "@rhwp/core": "0.7.7", + "hwp-mcp": "0.3.0" + }, + "overrides": { + "fast-uri": "3.1.8", + "ip-address": "10.7.1", + "@modelcontextprotocol/sdk": "1.31.0", + "proxy-addr": "2.0.8" + } +} diff --git a/scripts/ci/noema_bootstrap_producer.py b/scripts/ci/noema_bootstrap_producer.py new file mode 100644 index 0000000000..5a891b8ff4 --- /dev/null +++ b/scripts/ci/noema_bootstrap_producer.py @@ -0,0 +1,409 @@ +"""Preserve authenticated phase issuance bytes in explicit private custody. + +The request and revalidation collaborators belong to the existing trusted App +issuer/runtime owner. This module neither authenticates arbitrary dictionaries +nor admits native execution from source acceptance or token-action metadata. +""" +from __future__ import annotations + +import json +import os +import stat +import sys +import subprocess +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +if str(ROOT) not in sys.path: + sys.path.insert(0, str(ROOT)) + +from scripts.ci.premerge_review_tooling import _unique_object + + +MAX_RESPONSE_BYTES = 2 * 1024 * 1024 + + +def materialize_tooling(source_root, target_root, raw_manifest, *, accepted_digest, + admission, private_parent): + """Copy exactly externally admitted inert source into a separate namespace. + + Supplied acceptance must be independently authenticated by the runtime owner. + This checks its contract and bytes; it never creates acceptance evidence. + The parent is held exclusively by that owner, not defended against same-UID + or privileged namespace mutation by directory permission bits alone. + """ + import hashlib + import sys + from scripts.ci.premerge_review_tooling import load_noema_tooling + parent, source, target = map(Path, (private_parent, source_root, target_root)) + if (not parent.is_absolute() or not source.is_absolute() or not target.is_absolute() + or target.parent != parent or source == target or parent.is_relative_to(source)): + raise ValueError('bootstrap namespace refused') + if any(path.is_symlink() for path in (parent, *parent.parents, source)): + raise ValueError('bootstrap namespace refused') + info = parent.lstat() + if (not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid() + or info.st_mode & 0o077 or os.path.lexists(target)): + raise ValueError('bootstrap namespace refused') + if admission.get('namespace') != str(parent): + raise ValueError('bootstrap namespace differs from independent admission') + for field in ('isolated_clean_job', 'native_custody', 'supplier_cost', + 'supplier_retention', 'issuer_phase_separation'): + if admission.get(field) is not True: + raise ValueError('bootstrap execution evidence is not accepted') + _validate_runtime(admission, parent) + tooling = load_noema_tooling(source, raw_manifest, accepted_digest=accepted_digest, + interpreter=Path(sys.executable).resolve(), phase_environment=lambda _: {}) + inventory = json.loads(raw_manifest)['files'] + target.mkdir(mode=0o700) + for name, digest in inventory.items(): + raw = (source / name).read_bytes() + if hashlib.sha256(raw).hexdigest() != digest: + raise ValueError('bootstrap source changed during delivery') + destination = target / name + destination.parent.mkdir(mode=0o700, parents=True, exist_ok=True) + with os.fdopen(os.open(destination, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o400), 'wb') as stream: + stream.write(raw) + tooling._verify() + load_noema_tooling(target, raw_manifest, accepted_digest=accepted_digest, + interpreter=Path(sys.executable).resolve(), phase_environment=lambda _: {}) + return target + + +def _validate_runtime(admission, parent): + """Rebind explicit namespace, interpreter, dependencies and required admission.""" + import hashlib + parent = Path(parent) + if not parent.is_absolute() or any(path.is_symlink() for path in (parent, *parent.parents)): + raise ValueError('bootstrap namespace refused') + info = parent.lstat() + if (not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid() + or info.st_mode & 0o077 or admission.get('namespace') != str(parent)): + raise ValueError('bootstrap namespace refused') + for field in ('isolated_clean_job', 'native_custody', 'supplier_cost', + 'supplier_retention', 'issuer_phase_separation'): + if admission.get(field) is not True: + raise ValueError('bootstrap execution evidence unavailable') + interpreter = admission.get('interpreter') + actual_interpreter = Path(sys.executable).resolve() + if (not isinstance(interpreter, dict) or set(interpreter) != {'path', 'sha256'} + or interpreter['path'] != str(actual_interpreter) + or hashlib.sha256(actual_interpreter.read_bytes()).hexdigest() != interpreter['sha256']): + raise ValueError('bootstrap interpreter differs from independent admission') + dependencies = admission.get('dependencies') + if not isinstance(dependencies, dict): + raise ValueError('bootstrap dependency inventory unavailable') + for name, digest in dependencies.items(): + path = Path(name) + if (not path.is_absolute() or path.is_symlink() or not path.is_file() + or hashlib.sha256(path.read_bytes()).hexdigest() != digest): + raise ValueError('bootstrap dependency differs from independent admission') + + +def _response(raw): + """Parse bounded original API bytes without reflecting credential payloads.""" + if not isinstance(raw, bytes) or not 0 < len(raw) <= MAX_RESPONSE_BYTES: + raise ValueError('issuer response size or type refused') + try: + value = json.loads(raw, object_pairs_hook=_unique_object) + except (UnicodeError, ValueError): + raise ValueError('issuer response schema refused') from None + if not isinstance(value, dict): + raise ValueError('issuer response schema refused') + return value + + +def run_bootstrap_review(source_root, raw_manifest, *, accepted_digest, admission, + private_parent, fetch_live, request, revalidate, + inference_key): + """Compose actual admitted tooling, original App response and two children. + + Trusted runtime callbacks are externally held authority; this function only + checks their supplied admission and orders preparation before the separately + minted write token. No supplier policy is inferred from source or fixtures. + """ + import hashlib + import subprocess + from scripts.ci.premerge_review_tooling import ( + load_noema_tooling, phase_environment_from_issuance, + require_prepared_envelope, require_review_scope, + ) + scope = admission['scope'] + parent = Path(private_parent) + source = Path(source_root) + target = materialize_tooling(source, parent / 'tooling', raw_manifest, + accepted_digest=accepted_digest, admission=admission, private_parent=parent) + if not isinstance(inference_key, str) or not inference_key.strip(): + raise ValueError('existing inference authority unavailable') + envs = {} + def fence(): + """Revalidate supplier, source, admitted scope and external authority.""" + revalidate() + if hashlib.sha256(raw_manifest).hexdigest() != accepted_digest: + raise ValueError('bootstrap source pin changed') + live = fetch_live() + require_review_scope(scope, live) + from scripts.ci.premerge_review_tooling import admit_tooling + inventory = json.loads(raw_manifest)['files'] + files = {name: (target / name).read_bytes() for name in inventory} + admit_tooling(raw_manifest, accepted_digest=accepted_digest, source_files=files) + def issue(phase): + """Store exact authenticated response privately and consume its grant.""" + fence() + path = parent / (phase + '.json') + produce_issuance(phase, scope=scope, installation_id=admission['installation_id'], + app_id=admission['app_id'], output=path, private_parent=parent, + request=request, revalidate=fence) + fence() + response = _response(path.read_bytes()) + return phase_environment_from_issuance(phase, response, + scope=scope, reviewer_actor=scope['reviewer'], + installation_id=admission['installation_id'], + inference_key=inference_key if phase == 'prepare' else None) + fence() + envs['prepare'] = issue('prepare') + tooling = load_noema_tooling(target, raw_manifest, + accepted_digest=accepted_digest, interpreter=Path(__import__('sys').executable), + phase_environment=lambda phase: envs[phase]) + verdict = parent / 'verdict.json' + if tooling.prepare_verdict(scope['repository'], scope['number'], scope['head'], verdict) != 0: + raise ValueError('model preparation failed') + require_prepared_envelope(verdict, scope) + fence() + # No write grant can be requested until preparation and live revalidation. + envs['publish'] = issue('publish') + fence() + require_prepared_envelope(verdict, scope) + return tooling.publish_verdict(scope['repository'], scope['number'], scope['head'], verdict) + + +def produce_issuance(phase, *, scope, installation_id, app_id, output, + private_parent, request, revalidate): + """Request exact phase grant and exclusively store the unchanged response. + + Authenticated request supplies original response bytes from the same App + transport. The caller must independently bind that collaborator, namespace, + and installations; structural checks alone do not establish issuer authority. + Publish revalidation must include the prepared envelope and live head/base. + No inference credential is consumed here or attached to an issuer request. + """ + if phase not in ('prepare', 'publish'): + raise ValueError('issuer phase refused') + if any(type(value) is not int or value < 1 for value in (installation_id, app_id)): + raise ValueError('issuer identity refused') + repository = scope['repository'] + # Validate repository grammar with the same canonical controller contract. + import re + if not isinstance(repository, str) or not re.fullmatch( + r'ContextualWisdomLab/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+', repository): + raise ValueError('issuer repository refused') + if scope['reviewer'] != 'cwl-noema-review[bot]': + raise ValueError('issuer actor refused') + parent, target = Path(private_parent), Path(output) + if not parent.is_absolute() or not target.is_absolute() or target.parent != parent: + raise ValueError('issuer output namespace refused') + for ancestor in (parent, *parent.parents): + if ancestor.is_symlink(): + raise ValueError('issuer output namespace refused') + info = parent.lstat() + if not stat.S_ISDIR(info.st_mode) or info.st_uid != os.getuid() or info.st_mode & 0o077: + raise ValueError('issuer output namespace refused') + if os.path.lexists(target): + raise ValueError('issuer output already exists') + revalidate() + app = _response(request('GET', '/app', None)) + if type(app.get('id')) is not int or app.get('id') != app_id or app.get('slug') != 'cwl-noema-review': + raise ValueError('issuer App identity refused') + revalidate() + installation = _response(request('GET', '/repos/' + repository + '/installation', None)) + if (type(installation.get('id')) is not int or type(installation.get('app_id')) is not int + or installation.get('id') != installation_id or installation.get('app_id') != app_id): + raise ValueError('issuer installation refused') + revalidate() + permissions = {'contents': 'read', 'metadata': 'read', + 'pull_requests': 'read' if phase == 'prepare' else 'write'} + raw = request('POST', f'/app/installations/{installation_id}/access_tokens', + {'repositories': [repository.split('/', 1)[1]], 'permissions': permissions}) + response = _response(raw) + if response.get('permissions') != permissions: + raise ValueError('issuer phase grant refused') + repositories = response.get('repositories') + if (not isinstance(repositories, list) or len(repositories) != 1 + or not isinstance(repositories[0], dict) + or repositories[0].get('full_name') != repository): + raise ValueError('issuer response repository refused') + if not isinstance(response.get('token'), str) or not response['token'].strip(): + raise ValueError('issuer token missing') + revalidate() + current = parent.lstat() + if (current.st_dev, current.st_ino, current.st_mode, current.st_uid) != ( + info.st_dev, info.st_ino, info.st_mode, info.st_uid): + raise ValueError('issuer output custody changed') + fd = os.open(target, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600) + try: + with os.fdopen(fd, 'wb') as stream: + stream.write(raw) + stream.flush() + os.fsync(stream.fileno()) + except BaseException: + # Retain incomplete private bytes for the existing custody owner; + # never overwrite/retry a secret-bearing output after ambiguous I/O. + raise + return target + + +def _private_bytes(path, parent): + """Read one bounded private single-link credential input without diagnostics.""" + path, parent = Path(path), Path(parent) + if not path.is_absolute() or path.parent != parent or parent.is_symlink(): + raise ValueError('private credential namespace refused') + fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW) + try: + info = os.fstat(fd) + if (not stat.S_ISREG(info.st_mode) or info.st_nlink != 1 + or info.st_uid != os.getuid() or info.st_mode & 0o077): + raise ValueError('private credential custody refused') + raw = os.read(fd, MAX_RESPONSE_BYTES + 1) + if not 0 < len(raw) <= MAX_RESPONSE_BYTES: + raise ValueError('private credential size refused') + return raw + finally: + os.close(fd) + + +def authenticated_transport(jwt_file, private_parent, *, opener=None): + """Use the existing externally supplied App JWT on fixed GitHub routes only. + + The runtime owner provides and refreshes this private credential; this helper + neither signs a JWT nor reads an App private key. Injected openers are trusted + test collaborators, not PR inputs. No redirect or ambient proxy is admitted. + """ + import re + import urllib.request + jwt = _private_bytes(jwt_file, private_parent).decode('ascii') + if not jwt or any(character.isspace() for character in jwt): + raise ValueError('existing App authentication unavailable') + class NoRedirect(urllib.request.HTTPRedirectHandler): + """Reject credential forwarding before any redirected request dispatch.""" + def redirect_request(self, req, fp, code, msg, headers, newurl): + """Keep every response on the independently fixed issuer origin.""" + return None + if opener is None: + opener = urllib.request.build_opener( + urllib.request.ProxyHandler({}), NoRedirect()).open + def request(method, route, payload): + """Return unchanged bounded body bytes from an exact authenticated request.""" + get = method == 'GET' and (route == '/app' or re.fullmatch( + r'/repos/ContextualWisdomLab/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+/installation', route)) + post = method == 'POST' and re.fullmatch(r'/app/installations/[1-9][0-9]*/access_tokens', route) + if not (get or post) or (get and payload is not None): + raise ValueError('issuer endpoint refused') + body = json.dumps(payload, allow_nan=False).encode() if post else None + req = urllib.request.Request('https://api.github.com' + route, data=body, + method=method, headers={'Authorization': 'Bearer ' + jwt, + 'Accept': 'application/vnd.github+json', 'X-GitHub-Api-Version': '2022-11-28', + 'Content-Type': 'application/json'}) + with opener(req, timeout=60) as response: + headers = getattr(response, 'headers', {}) + length = headers.get('Content-Length') + raw = response.read(MAX_RESPONSE_BYTES + 1) + if (not isinstance(raw, bytes) or not 0 < len(raw) <= MAX_RESPONSE_BYTES + or (length is not None and (not str(length).isdigit() or int(length) != len(raw)))): + raise ValueError('issuer response framing refused') + return raw + return request + + +def main(argv=None): + """Consume independently pinned bootstrap inputs, refusing absent admission.""" + import argparse + import hashlib + import re + parser = argparse.ArgumentParser(description=__doc__) + modes = parser.add_mutually_exclusive_group(required=True) + modes.add_argument('--materialize', action='store_true') + modes.add_argument('--issue-prepare', action='store_true') + modes.add_argument('--issue-publish', action='store_true') + modes.add_argument('--revalidate-publish', action='store_true') + modes.add_argument('--revalidate-prepare', action='store_true') + parser.add_argument('--private-parent', type=Path, required=True) + parser.add_argument('--source-root', type=Path, required=True) + parser.add_argument('--manifest', type=Path, required=True) + parser.add_argument('--accepted-digest', required=True) + parser.add_argument('--admission', type=Path, required=True) + parser.add_argument('--admission-digest', required=True) + parser.add_argument('--repo', required=True) + parser.add_argument('--pr-number', type=int, required=True) + parser.add_argument('--expected-head', required=True) + parser.add_argument('--app-jwt-file', type=Path) + parser.add_argument('--verdict-file', type=Path) + args = parser.parse_args(argv) + try: + if not all(re.fullmatch(r'[0-9a-f]{64}', value) for value in + (args.accepted_digest, args.admission_digest)): + raise ValueError('bootstrap independent pins unavailable') + raw = args.manifest.read_bytes() + admission_raw = args.admission.read_bytes() + if hashlib.sha256(admission_raw).hexdigest() != args.admission_digest: + raise ValueError('bootstrap admission pin refused') + admission = _response(admission_raw) + scope = admission['scope'] + if (scope['repository'], scope['number'], scope['head']) != ( + args.repo, args.pr_number, args.expected_head): + raise ValueError('bootstrap target refused') + if args.materialize: + materialize_tooling(args.source_root, args.private_parent / 'tooling', raw, + accepted_digest=args.accepted_digest, admission=admission, + private_parent=args.private_parent) + return 0 + from scripts.ci.premerge_review_tooling import ( + require_prepared_envelope, require_review_scope, load_noema_tooling, + ) + def fence(): + """Bind original pins, actual admitted tooling and fresh live metadata.""" + if (args.manifest.read_bytes() != raw + or args.admission.read_bytes() != admission_raw): + raise ValueError('bootstrap external pins changed') + tooling = args.private_parent / 'tooling' + _validate_runtime(admission, args.private_parent) + load_noema_tooling(tooling, raw, accepted_digest=args.accepted_digest, + interpreter=Path(sys.executable), phase_environment=lambda _: {}) + pr = json.loads(subprocess.run(['gh', 'api', f'repos/{args.repo}/pulls/{args.pr_number}'], + capture_output=True, text=True, check=True, timeout=60).stdout) + require_review_scope(scope, {'repository':pr['base']['repo']['full_name'], + 'number':pr['number'],'head':pr['head']['sha'],'base':pr['base']['sha'], + 'author':pr['user']['login'],'reviewer':scope['reviewer'], + 'state':str(pr['state']).upper(),'draft':pr['draft']}) + if (args.manifest.read_bytes() != raw + or args.admission.read_bytes() != admission_raw): + raise ValueError('bootstrap external pins changed during live read') + load_noema_tooling(tooling, raw, accepted_digest=args.accepted_digest, + interpreter=Path(sys.executable), phase_environment=lambda _: {}) + for field in ('isolated_clean_job', 'native_custody', 'supplier_cost', + 'supplier_retention', 'issuer_phase_separation'): + if admission.get(field) is not True: + raise ValueError('bootstrap execution evidence unavailable') + _validate_runtime(admission, args.private_parent) + if args.issue_publish or args.revalidate_publish: + if args.verdict_file != args.private_parent / 'verdict.json': + raise ValueError('bootstrap envelope namespace refused') + require_prepared_envelope(args.verdict_file, scope) + if args.revalidate_publish or args.revalidate_prepare: + fence() + return 0 + if args.app_jwt_file is None: + raise ValueError('authenticated issuer transport unavailable') + fence() + transport = authenticated_transport(args.app_jwt_file, args.private_parent) + phase = 'publish' if args.issue_publish else 'prepare' + produce_issuance(phase, scope=scope, installation_id=admission['installation_id'], + app_id=admission['app_id'], output=args.private_parent / (phase+'.json'), + private_parent=args.private_parent, request=transport, revalidate=fence) + return 0 + except (OSError, ValueError, KeyError, TypeError, subprocess.SubprocessError): + print('Noema bootstrap refused: independent issuer or custody input unavailable', file=sys.stderr) + return 1 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/scripts/ci/noema_existing_app_issuer.cjs b/scripts/ci/noema_existing_app_issuer.cjs new file mode 100644 index 0000000000..9e43907388 --- /dev/null +++ b/scripts/ci/noema_existing_app_issuer.cjs @@ -0,0 +1,255 @@ +/* Existing-App phase issuer. Only trusted workflow steps supply Secret inputs. + * No key/JWT/token is projected into stdout, argv, artifacts or child env. + * Callbacks below are trusted fixture seams, never PR-provided code. + */ +'use strict'; +const crypto = require('node:crypto'); +const fs = require('node:fs'); +const path = require('node:path'); +const https = require('node:https'); +const {spawnSync} = require('node:child_process'); +const LIMIT = 2 * 1024 * 1024; + +function signApp(clientId, pem, now) { + if (typeof clientId !== 'string' || !/^[A-Za-z0-9_.-]+$/.test(clientId) || + typeof pem !== 'string' || !Number.isSafeInteger(now)) throw new Error('signer refused'); + const key = crypto.createPrivateKey(pem); + if (key.asymmetricKeyType !== 'rsa' || key.asymmetricKeyDetails.modulusLength < 2048) + throw new Error('signer refused'); + const encode = value => Buffer.from(JSON.stringify(value)).toString('base64url'); + const body = encode({alg:'RS256',typ:'JWT'}) + '.' + encode({iat:now-60,exp:now+300,iss:clientId}); + return body + '.' + crypto.sign('RSA-SHA256',Buffer.from(body),key).toString('base64url'); +} + +function parse(raw, captureToken) { + if (!Buffer.isBuffer(raw) || !raw.length || raw.length > LIMIT) throw new Error('response refused'); + const text = new TextDecoder('utf-8',{fatal:true}).decode(raw); + const value = JSON.parse(text); + if (!value || Array.isArray(value) || typeof value !== 'object') throw new Error('response refused'); + // Native JSON.parse validates syntax but silently keeps the last duplicate. + // Walk that validated grammar to reject duplicates at every object depth. + let cursor=0,duplicate=false,duplicateToken=false; + function whitespace(){while(/\s/.test(text[cursor]||'') && cursor128)throw new Error('response refused');whitespace(); + const c=text[cursor]; + if(c==='"'){string();return;} + if(c==='{' || c==='['){ + cursor++;whitespace();const keys=new Set(),end=c==='{'?'}':']'; + if(text[cursor]===end){cursor++;return;} + for(;;){ + if(c==='{'){ + const key=string();if(keys.has(key)){duplicate=true;if(depth===0 && key==='token')duplicateToken=true;}keys.add(key); + whitespace();cursor++; // colon: syntax was already validated above + } + walk(depth+1);whitespace();const separator=text[cursor++]; + if(separator===end)break;whitespace(); + } + return; + } + const m=text.slice(cursor).match(/^(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)/); + if(!m)throw new Error('response refused');cursor+=m[0].length; + } + walk(0); + if(captureToken && !duplicateToken && typeof value.token==='string' && value.token.trim() && !/\s/.test(value.token)) + captureToken(value.token); + if(duplicate)throw new Error('response duplicate refused'); + return value; +} + +function transport(method, route, payload, credential) { + if (!['GET','POST','DELETE'].includes(method) || + !/^\/(app|repos\/ContextualWisdomLab\/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+\/installation|app\/installations\/[1-9][0-9]*\/access_tokens|installation\/token)$/.test(route)) + return Promise.reject(new Error('endpoint refused')); + return new Promise((resolve,reject) => { + const body = payload === null ? null : Buffer.from(JSON.stringify(payload)); + const req = https.request({hostname:'api.github.com',port:443,path:route,method, + headers:{Authorization:'Bearer '+credential,Accept:'application/vnd.github+json', + 'X-GitHub-Api-Version':'2022-11-28','User-Agent':'cwl-noema-issuer', + ...(body ? {'Content-Type':'application/json','Content-Length':body.length}: {})}}, res => { + const chunks=[];let size=0; + res.on('data',chunk=>{size+=chunk.length;if(size>LIMIT){res.destroy();reject(new Error('response refused'));}else chunks.push(chunk);}); + res.on('aborted',()=>reject(new Error('response refused'))); + res.on('error',()=>reject(new Error('response refused'))); + res.on('end',()=>{ + const length=res.headers['content-length']; + if (res.statusCode !== (method==='DELETE'?204:method==='POST'?201:200) || + !res.complete || (length!==undefined && (!/^[0-9]+$/.test(length) || Number(length)!==size))) + {const error=new Error('response refused');error.statusCode=res.statusCode;reject(error);} + else resolve(Buffer.concat(chunks)); + }); + }); + req.setTimeout(60000,()=>req.destroy(new Error('request refused'))); + req.on('error',()=>reject(new Error('request refused'))); + if(body)req.write(body);req.end(); + }); +} + +function privateParent(parent) { + if (!path.isAbsolute(parent) || path.resolve(parent)!==parent) throw new Error('custody refused'); + for(let p=parent;;p=path.dirname(p)){ + if(fs.lstatSync(p).isSymbolicLink())throw new Error('custody refused'); + if(path.dirname(p)===p)break; + } + const s=fs.lstatSync(parent); + if(!s.isDirectory() || s.uid!==process.getuid() || (s.mode&0o077))throw new Error('custody refused'); + return s; +} + +async function runPhase(options) { + const {phase,scope,appId,installationId,privateParent:parent,clientId,privateKey, + fence,controller,transport:request=transport,now=()=>Math.floor(Date.now()/1000)}=options; + if (!['prepare','publish'].includes(phase) || !Number.isSafeInteger(appId) || appId<1 || + !Number.isSafeInteger(installationId) || installationId<1 || + !/^ContextualWisdomLab\/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$/.test(scope.repository) || + scope.reviewer!=='cwl-noema-review[bot]')throw new Error('identity refused'); + const initial=privateParent(parent);const target=path.join(parent,phase+'.json'); + if(fs.existsSync(target) || fs.lstatSync(parent).ino!==initial.ino)throw new Error('delivery refused'); + const tokens=[];const expiries=new Map();let delivered=false;let primary;let result;let cleanupFailed=false; + async function issue(grant, readToken) { + // Short-lived App authentication is created inside each issuer phase, not + // supplied by an absent external JWT producer or retained across the model. + const jwt=signApp(clientId,privateKey,now()); + const app=parse(await request('GET','/app',null,jwt)); + if(app.id!==appId || app.slug!=='cwl-noema-review')throw new Error('identity refused'); + if(readToken)await fence(readToken,phase); + const install=parse(await request('GET','/repos/'+scope.repository+'/installation',null,jwt)); + if(install.id!==installationId || install.app_id!==appId)throw new Error('identity refused'); + if(readToken)await fence(readToken,phase); + const permissions={contents:'read',metadata:'read',pull_requests:grant}; + const raw=await request('POST','/app/installations/'+installationId+'/access_tokens', + {repositories:[scope.repository.split('/')[1]],permissions},jwt); + const response=parse(raw, token => tokens.push(token)); + // Capture a safely parsed token before checking grant/expiry/delivery so + // rejected issuance is still revocation-owned. Never use it for fallback. + if(!tokens.includes(response.token))throw new Error('token refused'); + if(Object.keys(response.permissions||{}).length!==3 || + Object.entries(permissions).some(([k,v])=>response.permissions[k]!==v) || + !Array.isArray(response.repositories) || response.repositories.length!==1 || + response.repositories[0].full_name!==scope.repository)throw new Error('grant refused'); + const expires=Date.parse(response.expires_at); + if(!Number.isFinite(expires) || expires<=now()*1000+60000)throw new Error('expiry refused'); + expiries.set(response.token,expires); + return {raw,token:response.token}; + } + try { + const read=await issue('read'); + await fence(read.token,phase); + let selected=read; + if(phase==='publish'){ + selected=await issue('write',read.token); + await fence(read.token,phase); + } + const current=privateParent(parent); + if(current.dev!==initial.dev || current.ino!==initial.ino || current.mode!==initial.mode) + throw new Error('custody refused'); + const fd=fs.openSync(target,fs.constants.O_WRONLY|fs.constants.O_CREAT|fs.constants.O_EXCL|fs.constants.O_NOFOLLOW,0o600); + delivered=true; + try{fs.writeFileSync(fd,selected.raw);fs.fsyncSync(fd);}finally{fs.closeSync(fd);} + const env={PATH:'/usr/bin:/bin',GH_TOKEN:read.token}; + // The controller's live metadata read uses the read grant; its publisher + // child receives the separately issued write grant from the original file. + result=await controller(target,env); + if(result!==0)throw new Error('controller refused'); + } catch(error){primary=error;} + finally { + for(const token of tokens){ + try{await request('DELETE','/installation/token',null,token);} + catch(error){ + if(!(error.statusCode===401 && expiries.has(token) && expiries.get(token)<=now()*1000))cleanupFailed=true; + } + } + if(delivered){try{fs.unlinkSync(target);}catch{cleanupFailed=true;}} + } + if(cleanupFailed)throw new Error('issuer cleanup unconfirmed'); + if(primary)throw primary; + return result; +} + +module.exports={signApp,runPhase,transport}; + +async function main(argv=process.argv.slice(2), environment=process.env) { + const values={}; + const names=['phase','private-parent','source-root','manifest','accepted-digest','admission', + 'admission-digest','repo','pr-number','expected-head','verdict-file']; + if(argv.length%2)throw new Error('arguments refused'); + for(let i=0;i!values[n]) || !['prepare','publish'].includes(values.phase) || + !/^[1-9][0-9]*$/.test(values['pr-number']) || !/^[a-f0-9]{40}$/.test(values['expected-head'])) + throw new Error('arguments refused'); + const phase=values.phase,parent=values['private-parent'];privateParent(parent); + const admissionRaw=fs.readFileSync(values.admission); + if(!/^[a-f0-9]{64}$/.test(values['admission-digest']) || + crypto.createHash('sha256').update(admissionRaw).digest('hex')!==values['admission-digest']) + throw new Error('admission refused'); + const admission=parse(admissionRaw),scope=admission.scope; + if(scope.repository!==values.repo || scope.number!==Number(values['pr-number']) || + scope.head!==values['expected-head'] || values['verdict-file']!==path.join(parent,'verdict.json')) + throw new Error('scope refused'); + const python=admission.interpreter?.path; + if(!python || !path.isAbsolute(python) || + !/^[a-f0-9]{64}$/.test(admission.interpreter.sha256) || + crypto.createHash('sha256').update(fs.readFileSync(python)).digest('hex')!==admission.interpreter.sha256) + throw new Error('interpreter refused'); + const pythonIdentity=fs.statSync(python); + const pythonRealpath=fs.realpathSync(python); + const pinnedNode=admission.dependencies?.[fs.realpathSync(process.execPath)]; + if(!pinnedNode || crypto.createHash('sha256').update(fs.readFileSync(process.execPath)).digest('hex')!==pinnedNode) + throw new Error('issuer interpreter refused'); + const root=path.resolve(__dirname,'../..'); + const common=['--private-parent',parent,'--source-root',values['source-root'], + '--manifest',values.manifest,'--accepted-digest',values['accepted-digest'], + '--admission',values.admission,'--admission-digest',values['admission-digest'], + '--repo',values.repo,'--pr-number',values['pr-number'],'--expected-head',values['expected-head']]; + function child(script,args,env){ + // Revalidate after every issuer wait, before either credential-bearing child. + // This is not atomic pathname execution; exclusive runtime custody remains required. + const current=fs.statSync(python); + if(!current.isFile() || fs.realpathSync(python)!==pythonRealpath || + ['dev','ino','mode','uid','gid'].some(key=>current[key]!==pythonIdentity[key]) || + crypto.createHash('sha256').update(fs.readFileSync(python)).digest('hex')!==admission.interpreter.sha256) + throw new Error('interpreter refused'); + const result=spawnSync(python,['-I','-B',path.join(root,'scripts/ci',script),...args], + {env,cwd:root,stdio:'ignore'}); + if(result.error || result.signal || result.status===null)throw new Error('child refused'); + return result.status; + } + const runtimePath=environment.PATH; + if(!runtimePath)throw new Error('runtime refused'); + const fence=async(token,requested)=>{ + const mode=requested==='publish'?'--revalidate-publish':'--revalidate-prepare'; + const args=[mode,...common]; + if(requested==='publish')args.push('--verdict-file',values['verdict-file']); + if(child('noema_bootstrap_producer.py',args,{PATH:runtimePath,GH_TOKEN:token})!==0) + throw new Error('scope refused'); + }; + const controller=async(file,metadataEnvironment)=>{ + // Do not inherit Secret/JWT, workflow write token or inference into publisher. + const env={PATH:runtimePath,GH_TOKEN:metadataEnvironment.GH_TOKEN}; + if(phase==='prepare'){ + if(!environment.LLM_GATEWAY_API_KEY)throw new Error('inference refused'); + env.LLM_GATEWAY_API_KEY=environment.LLM_GATEWAY_API_KEY; + } + const args=['--'+phase+'-production','--tooling-root',path.join(parent,'tooling'), + '--manifest',values.manifest,'--accepted-digest',values['accepted-digest'], + '--admission',values.admission,'--admission-digest',values['admission-digest'], + '--repo',values.repo,'--pr-number',values['pr-number'],'--expected-head',values['expected-head'], + '--verdict-file',values['verdict-file'],'--phase-issuance',file]; + return child('premerge_review_tooling.py',args,env); + }; + return runPhase({phase,scope,appId:admission.app_id,installationId:admission.installation_id, + privateParent:parent,clientId:environment.NOEMA_GITHUB_APP_CLIENT_ID, + privateKey:environment.NOEMA_GITHUB_APP_PRIVATE_KEY,fence,controller}); +} +module.exports.main=main; +if(require.main===module){ + main().then(status=>{process.exitCode=status;}).catch(()=>{ + // Fixed, credential-free disposition also covers unconfirmed revocation. + console.error('Noema existing-App issuer refused; issuance or cleanup unconfirmed');process.exitCode=1; + }); +} diff --git a/scripts/ci/noema_hwp_mcp_reader.mjs b/scripts/ci/noema_hwp_mcp_reader.mjs new file mode 100644 index 0000000000..ccd32683b7 --- /dev/null +++ b/scripts/ci/noema_hwp_mcp_reader.mjs @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +import { existsSync, readFileSync } from "node:fs"; +import { createRequire } from "node:module"; +import { join } from "node:path"; +import { pathToFileURL } from "node:url"; + +const [, , sourceRoot, filePath] = process.argv; +if (!sourceRoot || !filePath || !existsSync(sourceRoot)) { + process.stderr.write("hwp-mcp source directory and document path are required\n"); + process.exit(2); +} + +try { + const hwpPackage = JSON.parse(readFileSync(join(sourceRoot, "package.json"), "utf8")); + const require = createRequire(pathToFileURL(join(sourceRoot, "package.json"))); + const rhwpPackage = JSON.parse( + readFileSync(require.resolve("@rhwp/core/package.json"), "utf8"), + ); + if (hwpPackage.name !== "hwp-mcp" || hwpPackage.version !== "0.3.0") { + throw new Error("unexpected hwp-mcp package identity"); + } + if (rhwpPackage.name !== "@rhwp/core" || rhwpPackage.version !== "0.7.7") { + throw new Error("unexpected rhwp package identity"); + } + const documentModule = await import(pathToFileURL(join(sourceRoot, "dist/core/document.js"))); + const toolsModule = await import(pathToFileURL(join(sourceRoot, "dist/tools/read.js"))); + const document = await documentModule.openDocument(filePath); + documentModule.closeDocument(document); + const text = await toolsModule.readHwp({ file_path: filePath }); + if ( + !text || + /^(?:파일 읽기 오류|File not found|텍스트 추출 오류|text extraction error)/i.test(text) + ) { + throw new Error("hwp-mcp returned an extraction error"); + } + process.stdout.write(`${text}\n`); +} catch (error) { + process.stderr.write("hwp-mcp/rhwp document extraction failed\n"); + process.exit(1); +} diff --git a/scripts/ci/noema_preflight_capacity.py b/scripts/ci/noema_preflight_capacity.py new file mode 100644 index 0000000000..b9bed218b1 --- /dev/null +++ b/scripts/ci/noema_preflight_capacity.py @@ -0,0 +1,163 @@ +"""Classify an all-429 review-sidecar preflight as provider capacity (#2148). + +The sidecar launcher writes ``strix_runs/contextual-orchestrator-preflight.json`` +(contract ``strix-plain-chat-preflight-v2``) before it exits on a failed +preflight. When every probed route was refused with HTTP 429 and none is ready, +the private-target ZDR pool is rate-limited rather than broken, which is the same +``provider_capacity_unavailable`` class ADR-0031 already re-dispatches after a +gateway failure. This module emits the same step outputs as +``two_phase._emit_transport_capacity_outputs`` (via the stdlib-only +``noema_transport_redispatch`` helpers) so the existing bounded +re-dispatch step can consume them. It never changes the job result: the +provisioning step has already failed and review remains required. +""" + +from __future__ import annotations + +import argparse +import json +import os +import stat +import re +import sys +from pathlib import Path +from typing import Any + +if __package__ in (None, ""): # pragma: no cover - executed as a workflow script + sys.path.insert(0, str(Path(__file__).resolve().parents[2])) + +# Stdlib-only on purpose: this runs on the runner's bare python3 after the +# sidecar step failed, before the HWP reader step installs defusedxml, so it +# must not import noema_review_gate (whose document import needs it). +from scripts.ci import noema_transport_redispatch as gate # noqa: E402 + +PREFLIGHT_CONTRACT = "strix-plain-chat-preflight-v2" +PREFLIGHT_CAPACITY_HTTP_STATUS = 429 +PREFLIGHT_CAPACITY_ROUTE_STATUSES = frozenset({"rejected", "deferred"}) +MAX_PREFLIGHT_REPORT_BYTES = 256 * 1024 + + +def _exact_int(value: Any) -> int | None: + """Return ``value`` only when it is a real ``int`` (``bool`` is rejected).""" + return value if type(value) is int else None + + +def _stage_retry_after(report: Any) -> list[int] | None: + """Return one all-429 stage's in-cap ``retry_after_s`` values, or None if not all-429. + + A stage qualifies only when ``ready_count`` is 0, ``probed_count`` is at + least 1, ``routes`` holds exactly ``probed_count`` rows, and every row is a + rejected or deferred route whose ``http_status`` is the integer 429. + """ + if not isinstance(report, dict) or report.get("contract") != PREFLIGHT_CONTRACT: + return None + probed = _exact_int(report.get("probed_count")) + routes = report.get("routes") + if _exact_int(report.get("ready_count")) != 0 or probed is None or probed < 1: + return None + if not isinstance(routes, list) or len(routes) != probed: + return None + waits: list[int] = [] + for row in routes: + if not isinstance(row, dict): + return None + if row.get("status") not in PREFLIGHT_CAPACITY_ROUTE_STATUSES: + return None + if _exact_int(row.get("http_status")) != PREFLIGHT_CAPACITY_HTTP_STATUS: + return None + wait = _exact_int(row.get("retry_after_s")) + if wait is not None and 1 <= wait <= gate.TRANSPORT_REDISPATCH_RETRY_AFTER_MAX_SECONDS: + waits.append(wait) + return waits + + +def classify_preflight_report(report: Any) -> tuple[int, int | None] | None: + """Return ``(probed_count, retry_after_seconds)`` for an all-429 report, else None. + + A nested ``primary_attempt`` (a fallback stage also ran) must itself be + all-429. ``retry_after_seconds`` is the longest provider-stated wait inside + ADR-0031's existing cap, or None so the deterministic jitter applies. + """ + waits = _stage_retry_after(report) + if waits is None: + return None + probed = report["probed_count"] + if "primary_attempt" in report: + primary_waits = _stage_retry_after(report["primary_attempt"]) + if primary_waits is None: + return None + waits.extend(primary_waits) + probed += report["primary_attempt"]["probed_count"] + return probed, (max(waits) if waits else None) + + +def load_preflight_report(path: Path) -> Any: + """Return the parsed report, or None when it is missing, oversized, or not JSON.""" + try: + if path.parent.is_symlink(): + return None + flags = os.O_RDONLY | os.O_NONBLOCK | getattr(os, "O_NOFOLLOW", 0) + with os.fdopen(os.open(path, flags), "rb") as handle: + metadata = os.fstat(handle.fileno()) + if not stat.S_ISREG(metadata.st_mode) or metadata.st_nlink != 1: + return None + raw = handle.read(MAX_PREFLIGHT_REPORT_BYTES + 1) + if len(raw) > MAX_PREFLIGHT_REPORT_BYTES: + return None + return json.loads(raw.decode("utf-8")) + except (OSError, UnicodeDecodeError, ValueError, RecursionError): + return None + + +def emit_preflight_capacity_outputs(path: Path, *, expected_head: str) -> dict[str, str]: + """Write the ADR-0031 transport outputs for one failed sidecar preflight.""" + classified = classify_preflight_report(load_preflight_report(path)) + if classified is None: + outputs = {"transport_capacity_unavailable": "false", "transport_retry_eligible": "false"} + gate.append_github_output(outputs) + return outputs + probed, retry_after = classified + retry_attempt = gate.current_transport_retry_attempt() + delay = gate.transport_redispatch_delay_seconds( + transport_retry_attempt=retry_attempt, + head_sha=expected_head, + retry_after_seconds=retry_after, + ) + outputs = { + "transport_capacity_unavailable": "true", + "transport_retry_eligible": "true" if delay is not None else "false", + "transport_http_status": str(PREFLIGHT_CAPACITY_HTTP_STATUS), + "provider_attempt_count": str(probed), + } + if delay is not None: + outputs["transport_retry_delay_seconds"] = str(delay) + outputs["transport_retry_next_attempt"] = str(retry_attempt + 1) + print( + "::notice::Noema sidecar preflight was all-429 (provider capacity unavailable); " + f"bounded continuation re-dispatch is eligible in {delay}s " + f"(attempt {retry_attempt + 1}/{gate.MAX_TRANSPORT_REDISPATCH_ATTEMPTS})." + ) + else: + print( + "::error::Noema sidecar preflight was all-429 (provider capacity unavailable); " + "automatic re-dispatch budget is exhausted. Review remains required." + ) + gate.append_github_output(outputs) + return outputs + + +def main(argv: list[str]) -> int: + """Classify one preflight report; always exit 0 because the job already failed.""" + parser = argparse.ArgumentParser() + parser.add_argument("--preflight-report", required=True, type=Path) + parser.add_argument("--expected-head", required=True) + args = parser.parse_args(argv) + if not re.fullmatch(r"[0-9a-f]{40}", args.expected_head): + print("::error::--expected-head must be a canonical lowercase 40-character Git SHA.") + return 0 + emit_preflight_capacity_outputs(args.preflight_report, expected_head=args.expected_head) + return 0 + + +if __name__ == "__main__": # pragma: no cover + raise SystemExit(main(sys.argv[1:])) diff --git a/scripts/ci/noema_review_document.py b/scripts/ci/noema_review_document.py new file mode 100644 index 0000000000..4594f5434b --- /dev/null +++ b/scripts/ci/noema_review_document.py @@ -0,0 +1,224 @@ +"""Extract bounded review text from office documents without model access. + +DOCX is a ZIP/XML container whose text can be read with the Python standard +library. HWP and HWPX stay delegated to the reviewed hwp-mcp/rhwp reader; this +module only supplies a temporary local file and validates the subprocess +contract. +""" + +from __future__ import annotations + +import io +import os +import subprocess +import tempfile +import zipfile +from pathlib import PurePosixPath + +from defusedxml import ElementTree as ET +from defusedxml.common import DefusedXmlException + + +MAX_DOCUMENT_BYTES = 8 * 1024 * 1024 +MAX_DOCUMENT_ZIP_ENTRIES = 2048 +MAX_DOCUMENT_ZIP_UNCOMPRESSED_BYTES = 64 * 1024 * 1024 +MAX_DOCUMENT_TEXT_BYTES = 256 * 1024 +HWP_READER_ENV = "NOEMA_HWP_MCP_SOURCE" +HWP_READER_TIMEOUT_SECONDS = 45 + +W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main" +M_NS = "http://schemas.openxmlformats.org/officeDocument/2006/math" +W = f"{{{W_NS}}}" +M = f"{{{M_NS}}}" + + +class DocumentReadError(RuntimeError): + """A document could not be converted to bounded review text.""" + + +def extract_review_document(path: str, raw: bytes) -> str: + """Return text for one supported document path or fail closed. + + The input bytes are obtained from the exact GitHub content ref by the + caller. HWP/HWPX bytes are never decoded as UTF-8 and never sent to an + external service; the configured reader runs as a local subprocess only. + """ + if len(raw) > MAX_DOCUMENT_BYTES: + raise DocumentReadError("document exceeds the bounded 8 MiB review input") + suffix = PurePosixPath(path).suffix.lower() + if suffix == ".docx": + return _extract_docx(raw) + if suffix in {".hwp", ".hwpx"}: + return _extract_hwp_with_reviewed_reader(path, raw) + raise DocumentReadError(f"unsupported review document format: {suffix or ''}") + + +def _extract_docx(raw: bytes) -> str: + """Extract paragraphs, tables, and Office Math text from one DOCX.""" + try: + with zipfile.ZipFile(io.BytesIO(raw)) as archive: + infos = archive.infolist() + if len(infos) > MAX_DOCUMENT_ZIP_ENTRIES: + raise DocumentReadError("DOCX archive has too many entries") + if ( + sum(info.file_size for info in infos) + > MAX_DOCUMENT_ZIP_UNCOMPRESSED_BYTES + ): + raise DocumentReadError( + "DOCX archive exceeds the bounded unpacked size" + ) + try: + document_xml = archive.read("word/document.xml") + except KeyError as exc: + raise DocumentReadError( + "DOCX archive has no word/document.xml" + ) from exc + except DocumentReadError: + raise + except (zipfile.BadZipFile, OSError, ValueError) as exc: + raise DocumentReadError("DOCX archive is malformed") from exc + + try: + root = ET.fromstring(document_xml) + except (ET.ParseError, DefusedXmlException) as exc: + raise DocumentReadError("DOCX document.xml is malformed") from exc + + body = root.find(f"{W}body") + if body is None: + raise DocumentReadError("DOCX document.xml has no document body") + + sections: list[str] = [] + table_number = 0 + for child in body: + if child.tag == f"{W}p": + text = _paragraph_text(child) + if text: + sections.append(text) + elif child.tag == f"{W}tbl": + table_number += 1 + table = _table_markdown(child, table_number) + if table: + sections.append(table) + + text = "\n\n".join(sections).strip() + if not text: + raise DocumentReadError("DOCX contains no readable text") + return _bounded_text(text) + + +def _paragraph_text(paragraph: ET.Element) -> str: + """Keep visible Word text, tabs, breaks, and Office Math runs.""" + parts: list[str] = [] + for element in paragraph.iter(): + if element.tag in {f"{W}t", f"{W}instrText", f"{M}t"}: + parts.append(element.text or "") + elif element.tag == f"{W}tab": + parts.append("\t") + elif element.tag in {f"{W}br", f"{W}cr"}: + parts.append("\n") + return "".join(parts).strip() + + +def _table_markdown(table: ET.Element, table_number: int) -> str: + """Render a DOCX table as bounded, reviewer-readable Markdown.""" + rows: list[list[str]] = [] + for row in table.findall(f"{W}tr"): + cells: list[str] = [] + for cell in row.findall(f"{W}tc"): + paragraphs = [_paragraph_text(p) for p in cell.findall(f".//{W}p")] + value = "\n".join(text for text in paragraphs if text).strip() + cells.append(value.replace("|", "\\|")) + if cells: + rows.append(cells) + if not rows: + return "" + + width = max(len(row) for row in rows) + normalized = [row + [""] * (width - len(row)) for row in rows] + lines = [f"### Table {table_number} ({len(normalized)} rows x {width} columns)"] + lines.append("| " + " | ".join(normalized[0]) + " |") + lines.append("| " + " | ".join("---" for _ in range(width)) + " |") + lines.extend("| " + " | ".join(row) + " |" for row in normalized[1:]) + return "\n".join(lines) + + +def _extract_hwp_with_reviewed_reader(path: str, raw: bytes) -> str: + """Delegate HWP/HWPX parsing to the reviewed hwp-mcp/rhwp source tree.""" + source = os.environ.get(HWP_READER_ENV, "").strip() + if not source: + raise DocumentReadError( + "reviewed hwp-mcp/rhwp reader is not configured; " + f"set {HWP_READER_ENV} to its trusted source directory" + ) + reader = os.path.join(os.path.dirname(__file__), "noema_hwp_mcp_reader.mjs") + with tempfile.NamedTemporaryFile( + prefix="noema-document-", suffix=PurePosixPath(path).suffix + ) as handle: + handle.write(raw) + handle.flush() + try: + completed = subprocess.run( + [os.environ.get("NOEMA_NODE_EXECUTABLE") or "node", reader, source, handle.name], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + check=False, + shell=False, + timeout=HWP_READER_TIMEOUT_SECONDS, + ) + except subprocess.TimeoutExpired as exc: + raise DocumentReadError( + "reviewed hwp-mcp/rhwp reader timed out after " + f"{HWP_READER_TIMEOUT_SECONDS} seconds" + ) from exc + except OSError as exc: + raise DocumentReadError( + "reviewed hwp-mcp/rhwp reader could not start" + ) from exc + if completed.returncode != 0: + raise DocumentReadError( + f"reviewed hwp-mcp/rhwp reader failed (exit {completed.returncode})" + ) + if len(completed.stdout) > MAX_DOCUMENT_TEXT_BYTES: + raise DocumentReadError( + "reviewed hwp-mcp/rhwp reader exceeded the bounded output" + ) + try: + text = completed.stdout.decode("utf-8").strip() + except UnicodeDecodeError as exc: + raise DocumentReadError( + "reviewed hwp-mcp/rhwp reader returned non-UTF-8 text" + ) from exc + if not text: + raise DocumentReadError("reviewed hwp-mcp/rhwp reader returned empty text") + return _bounded_text(text) + + +def _bounded_text(text: str) -> str: + """Bound reader output before it enters the review prompt.""" + encoded = text.encode("utf-8") + if len(encoded) <= MAX_DOCUMENT_TEXT_BYTES: + return text + clipped = encoded[:MAX_DOCUMENT_TEXT_BYTES].decode("utf-8", errors="ignore") + omitted = len(encoded) - len(clipped.encode("utf-8")) + return f"{clipped}\n[document text truncated; {omitted} bytes omitted]" + + +def _main() -> int: + """Provide a local, byte-safe smoke-test CLI for one document.""" + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("path") + args = parser.parse_args() + try: + with open(args.path, "rb") as handle: + text = extract_review_document(args.path, handle.read()) + except (OSError, DocumentReadError) as exc: + print(str(exc), file=os.sys.stderr) + return 1 + print(text) + return 0 + + +if __name__ == "__main__": + raise SystemExit(_main()) diff --git a/scripts/ci/noema_review_gate.py b/scripts/ci/noema_review_gate.py index 5ab7e830f3..156f5aafa3 100644 --- a/scripts/ci/noema_review_gate.py +++ b/scripts/ci/noema_review_gate.py @@ -6,6 +6,7 @@ import argparse import ast import base64 +import binascii import hashlib import http.client import ipaddress @@ -20,9 +21,20 @@ import urllib.parse import urllib.request from collections.abc import Sequence +from pathlib import PurePosixPath from typing import Any from scripts.ci.opencode_review_normalize_output import changed_file_is_material +from scripts.ci.noema_review_document import DocumentReadError, extract_review_document +from scripts.ci.noema_transport_redispatch import ( # noqa: F401 + MAX_TRANSPORT_REDISPATCH_ATTEMPTS, + TRANSPORT_REDISPATCH_JITTER_MAX_SECONDS, + TRANSPORT_REDISPATCH_JITTER_MIN_SECONDS, + TRANSPORT_REDISPATCH_RETRY_AFTER_MAX_SECONDS, + append_github_output, + current_transport_retry_attempt, + transport_redispatch_delay_seconds, +) PRIMARY_REVIEW_AUTHORS = { @@ -60,6 +72,8 @@ MAX_THREAD_BODY_CHARS = 1200 MAX_ALLOWED_LOCATIONS_JSON_BYTES = 32 * 1024 MAX_HTTP_ERROR_BODY_BYTES = 16 * 1024 +# ADR-0031: transport-capacity class after gateway failover (not caller retries). +TRANSPORT_CAPACITY_HTTP_STATUSES = frozenset({429, 500, 502, 503, 504}) DIFF_HUNK_RE = re.compile(r"^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@") SAFE_MODEL_IDENTIFIER_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._:/@+-]{0,199}$") @@ -206,6 +220,47 @@ class NoemaModelOutputError(RuntimeError): class NoemaTransportError(RuntimeError): """Raised when the bounded review transport cannot produce usable evidence.""" + def __init__( + self, + message: str, + *, + capacity_unavailable: bool = False, + http_status: int | None = None, + provider_attempt_count: int | None = None, + retry_after_seconds: int | None = None, + ) -> None: + """Record typed transport metadata without embedding secrets in attributes.""" + super().__init__(message) + self.capacity_unavailable = capacity_unavailable + self.http_status = http_status + self.provider_attempt_count = provider_attempt_count + self.retry_after_seconds = retry_after_seconds + + +def is_provider_capacity_http_status(status: int | None) -> bool: + """Return whether an HTTP status is a post-failover provider-capacity class.""" + return type(status) is int and status in TRANSPORT_CAPACITY_HTTP_STATUSES + + +def parse_http_retry_after_seconds(headers: Any) -> int | None: + """Return a whole-seconds Retry-After delay capped for continuation scheduling. + + Only the delta-seconds form is accepted. HTTP-date values and out-of-range + numbers record nothing so a hostile header cannot invent an unbounded wait. + """ + get_header = getattr(headers, "get", None) + if not callable(get_header): + return None + try: + raw = get_header("Retry-After") + except Exception: # noqa: BLE001 - hostile header mappings are not evidence + return None + if not isinstance(raw, str) or not raw.strip().isdecimal(): + return None + seconds = int(raw.strip()) + if seconds < 1 or seconds > TRANSPORT_REDISPATCH_RETRY_AFTER_MAX_SECONDS: + return None + return seconds def _stable_failure_diagnostic(exc: BaseException) -> str: @@ -302,6 +357,7 @@ def graphql(query: str, **fields: str | int) -> dict[str, Any]: number title body + author { login } isDraft state headRefOid @@ -471,6 +527,18 @@ def current_actor() -> str: return "" +def require_distinct_pr_author(pr: dict[str, Any], actor: str) -> str: + """Require a known PR author distinct from the active reviewer actor.""" + author = pr.get("author") + login = author.get("login") if isinstance(author, dict) else None + if not isinstance(login, str) or not login.strip(): + raise RuntimeError("Noema pull request author identity could not be verified") + normalized = login.strip().casefold() + if not actor or actor.strip().casefold() == normalized: + raise RuntimeError("Noema reviewer actor must be distinct from the pull request author") + return normalized + + def fetch_diff(repo: str, number: int) -> tuple[str, bool]: """Fetch the PR diff and truncate it to the bounded LLM prompt size.""" diff = run(["gh", "api", f"repos/{repo}/pulls/{number}", "-H", "Accept: application/vnd.github.v3.diff"]) @@ -736,7 +804,7 @@ def fetch_changed_files(repo: str, number: int) -> list[tuple[str, str]]: def fetch_file_content_at_ref(repo: str, path: str, ref: str) -> str: - """Fetch one repository text file at an exact Git ref through GitHub.""" + """Fetch one repository file at an exact Git ref through GitHub.""" encoded_path = urllib.parse.quote(path, safe="/") encoded_ref = urllib.parse.quote(ref, safe="") content = run( @@ -744,14 +812,39 @@ def fetch_file_content_at_ref(repo: str, path: str, ref: str) -> str: "gh", "api", f"repos/{repo}/contents/{encoded_path}?ref={encoded_ref}", - "--jq", - ".content // empty", + "--header", + "Accept: application/vnd.github.object+json", ] ) - compact = "".join(content.split()) - if not compact: - return "" - return base64.b64decode(compact).decode("utf-8", errors="replace") + try: + response = json.loads(content) + except json.JSONDecodeError as exc: + raise RuntimeError("GitHub content response was malformed") from exc + if ( + not isinstance(response, dict) + or type(response.get("size")) is not int + or response["size"] < 0 + or not isinstance(response.get("content"), str) + ): + raise RuntimeError("GitHub content response was malformed") + if response.get("encoding") != "base64": + raise RuntimeError("GitHub file content unavailable: API omitted the encoded body") + compact = "".join(response["content"].split()) + if not compact and response["size"]: + raise RuntimeError("GitHub file content unavailable: nonempty file has no encoded body") + try: + raw = base64.b64decode(compact, validate=True) + except (binascii.Error, ValueError) as exc: + raise RuntimeError("GitHub content response contained malformed base64") from exc + if len(raw) != response["size"]: + raise RuntimeError("GitHub content response size did not match the decoded body") + suffix = PurePosixPath(path).suffix.lower() + if suffix in {".docx", ".hwp", ".hwpx"}: + try: + return extract_review_document(path, raw) + except DocumentReadError as exc: + raise RuntimeError(f"document extraction failed: {exc}") from exc + return raw.decode("utf-8", errors="replace") def fetch_merge_base_sha(repo: str, base_sha: str, head_sha: str) -> str: @@ -1353,6 +1446,7 @@ def _extract_http_error_telemetry(exc: urllib.error.HTTPError) -> dict[str, str if terminal_reason is not None: telemetry["terminal_reason"] = terminal_reason if isinstance(attempts, list) and attempts and len(attempts) <= 64: + telemetry["provider_attempt_count"] = len(attempts) last_attempt = attempts[-1] if isinstance(last_attempt, dict): provider_name = _safe_model_identifier(last_attempt.get("provider_name")) @@ -1379,6 +1473,7 @@ def _extract_http_error_served_model(exc: urllib.error.HTTPError) -> str | None: def _format_gateway_error_telemetry(telemetry: dict[str, str | int]) -> str: """Format only allowlisted scalar receipt fields for a public Actions log.""" ordered_keys = ( + "provider_attempt_count", "provider_name", "upstream_phase", "attempt_number", @@ -1524,7 +1619,7 @@ def call_llm( """ api_url = os.environ.get("NOEMA_LLM_API_URL", "").strip() api_key = os.environ.get("NOEMA_LLM_API_KEY", "").strip() - model = os.environ.get("NOEMA_LLM_MODEL", "").strip() or "orchestrator/free" + model = os.environ.get("NOEMA_LLM_MODEL", "").strip() or "orchestrator/auto" if not api_url or not api_key: raise RuntimeError( "Noema LLM review unavailable: NOEMA_LLM_API_URL or NOEMA_LLM_API_KEY is not configured." @@ -1634,25 +1729,48 @@ def call_llm( validate_substantive_verdict(verdict, diff, changed_paths) except (RuntimeError, urllib.error.URLError, http.client.HTTPException, OSError) as exc: gateway_telemetry: dict[str, str | int] = {} + http_status: int | None = None + retry_after_seconds: int | None = None if isinstance(exc, urllib.error.HTTPError): - active_phase = "response_error" - gateway_telemetry = _extract_http_error_telemetry(exc) - model_value = gateway_telemetry.get("served_model") - served_model = model_value if isinstance(model_value, str) else None + try: + active_phase = "response_error" + http_status = exc.code if type(exc.code) is int else None + retry_after_seconds = parse_http_retry_after_seconds(exc.headers) + gateway_telemetry = _extract_http_error_telemetry(exc) + model_value = gateway_telemetry.get("served_model") + served_model = model_value if isinstance(model_value, str) else None + finally: + exc.close() elapsed = time.monotonic() - attempt_started current_failure = _stable_failure_diagnostic(exc) model_note = served_model or "unknown" gateway_note = _format_gateway_error_telemetry(gateway_telemetry) + capacity_unavailable = is_provider_capacity_http_status(http_status) + capacity_note = ( + " outcome=provider_capacity_unavailable" + if capacity_unavailable + else "" + ) print( f"::warning::Noema gateway attempt outcome=failed phase={active_phase} " f"duration={elapsed:.1f}s served_model={model_note}; " "caller attempts=1 (gateway owns repair/failover)." + + capacity_note + (f" gateway {gateway_note}" if gateway_note else "") ) suffix = ( f"; caller attempts=1, duration={elapsed:.1f}s, " f"phase={active_phase}, served_model={model_note}" + (f", gateway {gateway_note}" if gateway_note else "") + + ( + ", outcome=provider_capacity_unavailable" + if capacity_unavailable + else "" + ) + ) + provider_attempt_count = gateway_telemetry.get("provider_attempt_count") + attempt_count = ( + provider_attempt_count if type(provider_attempt_count) is int else None ) if isinstance(exc, NoemaModelOutputError): raise NoemaModelOutputError( @@ -1660,7 +1778,11 @@ def call_llm( ) from None if isinstance(exc, (urllib.error.URLError, http.client.HTTPException, OSError)): raise NoemaTransportError( - f"Noema gateway transport failed: {type(exc).__name__}: {current_failure}{suffix}" + f"Noema gateway transport failed: {type(exc).__name__}: {current_failure}{suffix}", + capacity_unavailable=capacity_unavailable, + http_status=http_status, + provider_attempt_count=attempt_count, + retry_after_seconds=retry_after_seconds, ) from exc raise RuntimeError( f"Noema review failed closed: {current_failure}{suffix}" @@ -1773,6 +1895,7 @@ def inspect_and_review(repo: str, number: int, expected_head: str) -> int: actor = current_actor() if not actor: raise RuntimeError("Noema reviewer identity could not be verified") + require_distinct_pr_author(pr, actor) if actor in PRIMARY_REVIEW_AUTHORS: raise RuntimeError( f"Current token actor {actor!r} is already a primary review actor; " @@ -1795,6 +1918,7 @@ def inspect_and_review(repo: str, number: int, expected_head: str) -> int: except RuntimeError: print("Pull request closed or its head changed during review; stale verdict was not published.") return 0 + require_distinct_pr_author(current_pr, actor) submit_review(repo, number, current_pr, actor, verdict) return 0 diff --git a/scripts/ci/noema_review_handoff.py b/scripts/ci/noema_review_handoff.py index 29f6142a98..3f4a8a20a7 100644 --- a/scripts/ci/noema_review_handoff.py +++ b/scripts/ci/noema_review_handoff.py @@ -26,7 +26,7 @@ from redact_sensitive_log import redact_text -REPOSITORY_RE = re.compile(r"^ContextualWisdomLab/[A-Za-z0-9_.-]+$") +REPOSITORY_RE = re.compile(r"^ContextualWisdomLab/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") SHA_RE = re.compile(r"^[0-9a-fA-F]{40}$") GH_COMMAND_TIMEOUT_SECONDS = 60.0 MAX_TRANSIENT_BACKOFF_MULTIPLIER = 4 @@ -195,7 +195,7 @@ def dispatch_noema( *, runner: GhRunner = run_gh, ) -> None: - """Dispatch the target repository's default-branch Noema workflow.""" + """Dispatch the central handler while retaining the exact consumer identity.""" payload = { "event_type": "noema-review", "client_payload": { @@ -205,7 +205,7 @@ def dispatch_noema( }, } runner( - ["api", "-X", "POST", f"repos/{repo}/dispatches", "--input", "-"], + ["api", "-X", "POST", "repos/ContextualWisdomLab/.github/dispatches", "--input", "-"], json.dumps(payload), ) diff --git a/scripts/ci/noema_transport_redispatch.py b/scripts/ci/noema_transport_redispatch.py new file mode 100644 index 0000000000..c2b8e9c177 --- /dev/null +++ b/scripts/ci/noema_transport_redispatch.py @@ -0,0 +1,70 @@ +"""Stdlib-only Noema continuation helpers for startup and model failures.""" + +from __future__ import annotations + +import hashlib +import os +import re + +MAX_TRANSPORT_REDISPATCH_ATTEMPTS = 2 +TRANSPORT_REDISPATCH_JITTER_MIN_SECONDS = 60 +TRANSPORT_REDISPATCH_JITTER_MAX_SECONDS = 180 +TRANSPORT_REDISPATCH_RETRY_AFTER_MAX_SECONDS = 300 + + +def transport_redispatch_delay_seconds( + *, + transport_retry_attempt: int, + head_sha: str, + retry_after_seconds: int | None = None, +) -> int | None: + """Return the post-failure scheduling delay, or None when the re-dispatch bound is spent. + + ``transport_retry_attempt`` is the number of automatic capacity re-dispatches + already performed for this head (0 on the first failure). Prefer a capped + gateway ``Retry-After`` when present; otherwise use deterministic jitter in + ``[TRANSPORT_REDISPATCH_JITTER_MIN_SECONDS, TRANSPORT_REDISPATCH_JITTER_MAX_SECONDS]`` + keyed by head SHA and attempt so concurrent failures do not stampede. + """ + if transport_retry_attempt < 0 or transport_retry_attempt >= MAX_TRANSPORT_REDISPATCH_ATTEMPTS: + return None + if retry_after_seconds is not None: + if ( + type(retry_after_seconds) is int + and 1 <= retry_after_seconds <= TRANSPORT_REDISPATCH_RETRY_AFTER_MAX_SECONDS + ): + return retry_after_seconds + return None + digest = hashlib.sha256( + f"{head_sha.strip().lower()}:{transport_retry_attempt}".encode("utf-8") + ).digest() + span = ( + TRANSPORT_REDISPATCH_JITTER_MAX_SECONDS - TRANSPORT_REDISPATCH_JITTER_MIN_SECONDS + 1 + ) + offset = int.from_bytes(digest[:4], "big") % span + return TRANSPORT_REDISPATCH_JITTER_MIN_SECONDS + offset + + +def current_transport_retry_attempt() -> int: + """Parse the retry counter; invalid values exhaust the automatic budget.""" + raw = os.environ.get("NOEMA_TRANSPORT_RETRY_ATTEMPT") + if raw is None or raw == "null": + return 0 + if not re.fullmatch(r"[0-9]{1,2}", raw): + return MAX_TRANSPORT_REDISPATCH_ATTEMPTS + value = int(raw) + return min(value, MAX_TRANSPORT_REDISPATCH_ATTEMPTS) + + +def append_github_output(values: dict[str, str]) -> None: + """Append allowlisted step outputs when running under GitHub Actions.""" + path = (os.environ.get("GITHUB_OUTPUT") or "").strip() + if not path or not values: + return + with open(path, "a", encoding="utf-8") as handle: + for key, value in values.items(): + if not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", key): + continue + if any(ch in value for ch in ("\n", "\r", "\0")): + continue + handle.write(f"{key}={value}\n") diff --git a/scripts/ci/opencode_queue_priority.py b/scripts/ci/opencode_queue_priority.py new file mode 100755 index 0000000000..75ec11c58b --- /dev/null +++ b/scripts/ci/opencode_queue_priority.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python3 +"""Operator runbook: put priority PRs first in the OpenCode dispatch queue. + +One OpenCode runner serves every repository first-in first-out, so a release +PR can wait behind a day of other reviews. This script lists the queued +``opencode-review-dispatch`` runs and classifies each against its live PR: + +* ``stale``: the PR is closed or its head moved; the run would fail validation. +* ``keep``: the PR carries the priority label, applied by someone with write, + maintain or admin permission (a fork author cannot jump the queue). +* ``cancel_current``: every other current-head run. + +It always prints metrics (deferred count, oldest deferred age, priority queue +positions). ``--post-issue`` writes the cancel list to an issue *before* +anything is cancelled, and ``--apply`` cancels only runs that are still queued. +Cancelled PRs are not lost: the scheduler re-dispatches any PR without a +current-head verdict, or an operator reruns its ``opencode-review`` job. + +GitHub's ``status=queued`` run listing is eventually consistent and can omit +queued runs, so counts are a lower bound and a missed run is never cancelled. +""" + +from __future__ import annotations + +import argparse +import json +import re +import subprocess +import sys +from dataclasses import dataclass +from datetime import datetime, timezone + +CENTRAL = "ContextualWisdomLab/.github" +WORKFLOW = "opencode-review-dispatch.yml" +TRUSTED_PERMISSIONS = frozenset({"admin", "maintain", "write"}) +TITLE_RE = re.compile(r"ContextualWisdomLab/([A-Za-z0-9_.-]+)#(\d+)@([0-9a-f]{7,40})") + + +@dataclass(frozen=True) +class QueuedRun: + """Identify one queued OpenCode review run.""" + + run_id: int + created_at: datetime + repo: str + pr: int + head: str + + +@dataclass(frozen=True) +class PrState: + """Describe the live pull request state used to classify a queued run.""" + + state: str + head: str + priority: bool + + +@dataclass(frozen=True) +class Plan: + """Partition ordered queued runs by priority and validity.""" + + keep: tuple[QueuedRun, ...] + cancel_current: tuple[QueuedRun, ...] + cancel_stale: tuple[QueuedRun, ...] + ordered: tuple[QueuedRun, ...] + + +def trusted_priority(label: str, labeled_by: list[tuple[str, str]]) -> bool: + """Return whether ``label`` was applied by an actor with write access or above.""" + return any(name == label and perm in TRUSTED_PERMISSIONS for name, perm in labeled_by) + + +def plan(runs: list[QueuedRun], live: dict[tuple[str, int], PrState]) -> Plan: + """Split queued runs into keep / cancel-current / cancel-stale, oldest first.""" + ordered = tuple(sorted(runs, key=lambda r: (r.created_at, r.run_id))) + keep, current, stale = [], [], [] + for r in ordered: + s = live.get((r.repo, r.pr)) + if s is None or s.state != "OPEN" or not s.head.startswith(r.head): + stale.append(r) + elif s.priority: + keep.append(r) + else: + current.append(r) + return Plan(tuple(keep), tuple(current), tuple(stale), ordered) + + +def metrics(p: Plan, *, now: datetime) -> dict: + """Summarise the backlog so starvation is visible rather than hidden.""" + oldest = min((r.created_at for r in p.cancel_current), default=None) + position = {r.run_id: i for i, r in enumerate(p.ordered, 1)} + return { + "queued": len(p.ordered), + "deferred": len(p.cancel_current), + "stale": len(p.cancel_stale), + "oldest_deferred_hours": round((now - oldest).total_seconds() / 3600, 1) if oldest else 0.0, + "priority_positions": {f"{r.repo}#{r.pr}": position[r.run_id] for r in p.keep}, + } + + +def _gh(*args: str, stdin: str | None = None) -> str: + """Run a GitHub CLI command and return its standard output.""" + + return subprocess.run(["gh", *args], input=stdin, capture_output=True, text=True, check=True).stdout + + +def fetch_queued() -> list[QueuedRun]: + """Return queued dispatch runs with parseable pull request identities.""" + + out = _gh("api", "--paginate", f"repos/{CENTRAL}/actions/workflows/{WORKFLOW}/runs?status=queued&per_page=100", + "--jq", ".workflow_runs[]|[.id,.created_at,.display_title]|@json") + runs = [] + for line in out.splitlines(): + run_id, created, title = json.loads(line) + m = TITLE_RE.search(title) + if m: + runs.append(QueuedRun(int(run_id), datetime.fromisoformat(created.replace("Z", "+00:00")), + f"ContextualWisdomLab/{m.group(1)}", int(m.group(2)), m.group(3))) + return runs + + +def fetch_live(keys: set[tuple[str, int]], label: str) -> dict[tuple[str, int], PrState]: + """Fetch live pull request states and trusted priority labels for keys.""" + + live: dict[tuple[str, int], PrState] = {} + perms: dict[tuple[str, str], str] = {} + keys_sorted = sorted(keys) + for i in range(0, len(keys_sorted), 40): + chunk = keys_sorted[i:i + 40] + query = "query{" + " ".join( + f'p{k}:repository(owner:"{repo.split("/")[0]}",name:"{repo.split("/")[1]}")' + f'{{pullRequest(number:{pr}){{state headRefOid labels(first:30){{nodes{{name}}}} ' + f'timelineItems(last:50,itemTypes:[LABELED_EVENT]){{nodes{{... on LabeledEvent{{label{{name}} actor{{login}}}}}}}}}}}}' + for k, (repo, pr) in enumerate(chunk)) + "}" + data = json.loads(_gh("api", "graphql", "-f", f"query={query}")).get("data") or {} + for k, (repo, pr) in enumerate(chunk): + node = ((data.get(f"p{k}") or {}).get("pullRequest")) or {} + if not node: + continue + labeled_by = [] + if any(n["name"] == label for n in node["labels"]["nodes"]): + for ev in node["timelineItems"]["nodes"]: + if ev.get("label", {}).get("name") != label or not ev.get("actor"): + continue + login = ev["actor"]["login"] + if (repo, login) not in perms: + try: + perms[(repo, login)] = _gh("api", f"repos/{repo}/collaborators/{login}/permission", + "--jq", ".permission").strip() + except subprocess.CalledProcessError: + perms[(repo, login)] = "none" + labeled_by.append((label, perms[(repo, login)])) + live[(repo, pr)] = PrState(node["state"], node["headRefOid"], trusted_priority(label, labeled_by)) + return live + + +def main(argv: list[str] | None = None) -> int: + """Print queue metrics and optionally record and cancel selected runs.""" + + ap = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + ap.add_argument("--label", default="review-priority") + ap.add_argument("--include-current", action="store_true", help="also cancel non-priority current-head runs") + ap.add_argument("--post-issue", metavar="OWNER/REPO#N", help="post the cancel list here before cancelling") + ap.add_argument("--apply", action="store_true", help="cancel runs that are still queued") + args = ap.parse_args(argv) + if args.apply and not args.post_issue: + ap.error("--apply requires --post-issue so the cancel list is recorded first") + + runs = fetch_queued() + p = plan(runs, fetch_live({(r.repo, r.pr) for r in runs}, args.label)) + m = metrics(p, now=datetime.now(timezone.utc)) + print(json.dumps(m, indent=2)) + targets = list(p.cancel_stale) + (list(p.cancel_current) if args.include_current else []) + table = "run_id\tcreated_at\trepository\tpr\thead_sha\treason\n" + "".join( + f"{r.run_id}\t{r.created_at.isoformat()}\t{r.repo}\t{r.pr}\t{r.head}\t" + f"{'stale' if r in p.cancel_stale else 'deferred'}\n" for r in targets) + print(table, end="") + if args.post_issue: + repo, number = args.post_issue.split("#") + body = (f"## OpenCode queue priority runbook\n\nMetrics: `{json.dumps(m)}`\n\n" + f"Label `{args.label}` (maintainer-applied) kept: {len(p.keep)}. To cancel: {len(targets)}.\n\n" + f"
Cancel list (TSV)\n\n```tsv\n{table}```\n
\n") + _gh("issue", "comment", number, "-R", repo, "--body-file", "-", stdin=body) + if args.apply: + cancelled = 0 + for r in targets: + status = _gh("api", f"repos/{CENTRAL}/actions/runs/{r.run_id}", "--jq", ".status").strip() + if status == "queued": + _gh("api", "-X", "POST", f"repos/{CENTRAL}/actions/runs/{r.run_id}/cancel") + cancelled += 1 + print(f"cancelled={cancelled}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/scripts/ci/opencode_repository_dispatch_targets.json b/scripts/ci/opencode_repository_dispatch_targets.json index dd82dd1fd0..00d183f903 100644 --- a/scripts/ci/opencode_repository_dispatch_targets.json +++ b/scripts/ci/opencode_repository_dispatch_targets.json @@ -32,6 +32,7 @@ "ContextualWisdomLab/hyosung-itx-slogan-brief", "ContextualWisdomLab/inkspan", "ContextualWisdomLab/kaefa", + "ContextualWisdomLab/k-csap-skills", "ContextualWisdomLab/keyverse", "ContextualWisdomLab/learning-management-platform", "ContextualWisdomLab/life-os", @@ -46,11 +47,13 @@ "ContextualWisdomLab/nonnest2", "ContextualWisdomLab/pg-erd-cloud", "ContextualWisdomLab/pg-llm-batch", + "ContextualWisdomLab/procurement-workbench", "ContextualWisdomLab/psychometrics-commons", "ContextualWisdomLab/quarantine-sandbox-runtime", "ContextualWisdomLab/saju-caldav", "ContextualWisdomLab/scopeweave", "ContextualWisdomLab/semantic-data-portal", + "ContextualWisdomLab/tokyo-travel-2026", "ContextualWisdomLab/wardnet", "ContextualWisdomLab/xtrm-lead-pi-outbound", "ContextualWisdomLab/xtrmLLMBatchPython" diff --git a/scripts/ci/opencode_review_receipt_gate.py b/scripts/ci/opencode_review_receipt_gate.py index 4dcb24af88..5c86b1fc8e 100644 --- a/scripts/ci/opencode_review_receipt_gate.py +++ b/scripts/ci/opencode_review_receipt_gate.py @@ -149,6 +149,7 @@ def evaluate_receipts( if not SHA_RE.fullmatch(head_sha): return None, "receipt gate requires a 40-character head SHA" stale_hits = 0 + dismissed_publishers: set[str] = set() for review in reversed(list(reviews)): if not isinstance(review, Mapping): continue @@ -156,6 +157,17 @@ def evaluate_receipts( if commit and commit.lower() != head_sha.lower(): stale_hits += 1 continue + author = review_author(review) + if author in dismissed_publishers: + continue + if ( + author in FORMAL_AUTHORS + and str(review.get("state") or "").upper() == "DISMISSED" + and review_matches_head(review, head_sha) + and review.get("id") + ): + dismissed_publishers.add(author) + continue ok, reason = is_formal_receipt(review, head_sha, is_draft=is_draft) if ok: return review, reason @@ -166,6 +178,8 @@ def evaluate_receipts( if reason.startswith("stale"): stale_hits += 1 continue + if dismissed_publishers: + return None, "current-head OpenCode publisher review was dismissed" if stale_hits: return ( None, diff --git a/scripts/ci/organization_commercial_readiness_loop.py b/scripts/ci/organization_commercial_readiness_loop.py index 9657bd2d4d..a8d5419c7e 100644 --- a/scripts/ci/organization_commercial_readiness_loop.py +++ b/scripts/ci/organization_commercial_readiness_loop.py @@ -26,7 +26,7 @@ DEFAULT_ORGANIZATION = "ContextualWisdomLab" -ORGANIZATION_RE = re.compile(r"^[A-Za-z0-9_.-]+$") +ORGANIZATION_RE = re.compile(r"^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") ENTRYPOINT_MARKER = "# cwl-org-commercial-entrypoint: v1" CENTRAL_REPOSITORY = f"{DEFAULT_ORGANIZATION}/.github" CENTRAL_REPAIR_EVENT = "pr-review-fix-scheduler" diff --git a/scripts/ci/personal_review_route.py b/scripts/ci/personal_review_route.py new file mode 100644 index 0000000000..01c53bc505 --- /dev/null +++ b/scripts/ci/personal_review_route.py @@ -0,0 +1,57 @@ +"""Trusted personal LiteLLM route; never infer cost/privacy from auto or HTTP200.""" +from __future__ import annotations + +import argparse +import json +from pathlib import Path + +BASE_URL = 'https://litellm.poinnetworks.net/v1' +MODEL = 'personal-litellm/auto' + + +def require_admission(*, zero_cost: bool, zero_retention: bool, private_target: bool, + public_auto_authorized: bool = False) -> None: + """Admit owner-authorized public routing; private supplier gates stay intact.""" + authorized_public = private_target is False and public_auto_authorized is True + if zero_cost is not True and not authorized_public: + raise RuntimeError('STOP: personal auto zero-cost supplier is not attested') + if private_target and zero_retention is not True: + raise RuntimeError('STOP: private personal auto zero-retention supplier is not attested') + + +def configure_opencode(path: Path) -> None: + """Reset only provider/model routing, preserving read-only agent permissions.""" + source = path.read_text(encoding='utf-8') + # Generated workflow configs are JSON; tracked JSONC uses full-line comments. + data = json.loads('\n'.join(line for line in source.splitlines() + if not line.lstrip().startswith('//'))) + data.update(model=MODEL, small_model=MODEL, enabled_providers=['personal-litellm']) + data['provider'] = {'personal-litellm': { + 'npm': '@ai-sdk/openai-compatible', 'name': 'Personal LiteLLM auto', + 'options': {'baseURL': BASE_URL, 'apiKey': '{env:LLM_GATEWAY_API_KEY}'}, + 'models': {'auto': {'name': 'Auto', 'tool_call': True, + 'limit': {'context': 200000, 'output': 32768}}}, + }} + path.write_text(json.dumps(data, indent=2) + '\n', encoding='utf-8') + + +def main(argv: list[str] | None = None) -> int: + """Validate trusted nonsecret admission inputs and optionally reset config.""" + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--zero-cost', choices=('true', 'false'), required=True) + parser.add_argument('--zero-retention', choices=('true', 'false'), required=True) + parser.add_argument('--private-target', choices=('true', 'false'), required=True) + parser.add_argument('--public-auto-authorized', choices=('true', 'false'), default='false') + parser.add_argument('--opencode-config', type=Path) + args = parser.parse_args(argv) + require_admission(zero_cost=args.zero_cost == 'true', + zero_retention=args.zero_retention == 'true', + private_target=args.private_target == 'true', + public_auto_authorized=args.public_auto_authorized == 'true') + if args.opencode_config is not None: + configure_opencode(args.opencode_config) + return 0 + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/scripts/ci/pingora_edge_policy.py b/scripts/ci/pingora_edge_policy.py index 33e58ed876..4c9697f1d7 100644 --- a/scripts/ci/pingora_edge_policy.py +++ b/scripts/ci/pingora_edge_policy.py @@ -5,16 +5,48 @@ bounded UTF-8 file content through the GitHub REST API, then rejects active Nginx runtime artifacts while allowing documentation, license text, and source-level negative test fixtures. + +Issue #2193 -- declared research/data artifact paths: a consumer repository may +declare literal path prefixes (``ARTIFACT_PATH_DECLARATION_PATH``) that hold +binary research or data artefacts not shaped like documentation (raw response +workbooks, SPSS ``.sav`` files, serialized model objects, compressed numeric +arrays). That declaration is resolved *only* from the pull request's base ref, +never its head, so a pull request cannot self-authorize admission of its own +binary by adding or widening the declaration in the same diff -- see +``_load_artifact_path_declaration`` and ``evaluate_pull_request``'s ``base_ref`` +parameter. The declaration replaces only the path-shape test +(`_is_known_documentation_path`'s equivalent for declared prefixes); it never +substitutes for content evidence, and an active-runtime-named file +(`_runtime_path_rule`) stays rejected inside a declared prefix exactly as inside +``docs/`` today. + +Suffix decision: most research-data formats (``.xlsx``, ``.sav``, ``.rds``, +``.npz``, ...) have no entry in ``BINARY_DOCUMENT_MAGIC``, which only knows +``.hwpx``/``.pdf``/``.png``. Rather than grow that registry for every such +format, a file under a declared prefix whose suffix has no magic entry is +admitted only when no diff patch is available, the fetched bytes fail to decode +as UTF-8, and their replacement-decoded text contains no prohibited runtime +pattern. That keeps the module's central guarantee honest -- a file that +decodes as valid UTF-8 is never treated as a binary artifact, and one stray +invalid byte cannot conceal a readable runtime command -- while still +admitting genuinely opaque research binaries without maintaining an open-ended +magic-byte catalog. A suffix that *does* have a magic entry keeps that entry's +existing structural evidence check +(``_is_complete_png``, ``_is_complete_hwpx``, or the raw magic-prefix check for +``.pdf``) even under a declared prefix. """ from __future__ import annotations import argparse import base64 +import hashlib +import io import json import os import re import sys +import zipfile import zlib from dataclasses import dataclass from pathlib import PurePosixPath @@ -25,8 +57,18 @@ MAX_FILE_BYTES = 1_048_576 MAX_RESPONSE_BYTES = 16_777_216 -REPOSITORY_RE = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$") +MAX_BLOB_BYTES = 100_000_000 +# Decoded-pixel bound for PNG validation, separate from the HTTP response cap: +# a valid 2238x2052 RGBA screenshot decodes to 18.4 MB. 128 MiB covers 16-bit +# RGBA up to 4K and 8-bit RGBA up to 6K while keeping zlib output bounded. +MAX_PNG_DECODED_BYTES = 134_217_728 +REPOSITORY_RE = re.compile(r"^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") SHA_RE = re.compile(r"^[0-9a-f]{40}$") +# A base ref threaded into evaluate_pull_request may be either a branch name +# (e.g. "main", "release/2026.09") or a commit SHA -- whatever the calling +# workflow already has on the pull_request event without new permissions. +# Bounded charset/length, no ".." traversal, and no leading/trailing "/". +BASE_REF_RE = re.compile(r"^[A-Za-z0-9](?:[A-Za-z0-9._/-]{0,253}[A-Za-z0-9])?$") GITHUB_API_ORIGIN = "https://api.github.com" DOCUMENT_SUFFIXES = frozenset({".md", ".mdx", ".rst", ".adoc", ".txt"}) @@ -35,11 +77,12 @@ # reference). Without this, any such file placed under a documentation # directory still falls through to `_needs_content_scan` -> `True` (binary # files never carry a GitHub diff `patch`), and then `_load_file_content` -# fails closed with a `PolicyError` for any instance over the Contents API's -# 1 MiB base64 ceiling -- rejecting a legitimate research-paper citation +# fails closed with a `PolicyError` for any instance over the Git blob API's +# 100 MB ceiling -- rejecting a legitimate research-paper citation # (this org's own "attach the relevant paper PDF" convention) for a reason # that has nothing to do with the Nginx runtime policy this module enforces. BINARY_DOCUMENT_MAGIC = { + ".hwpx": (b"PK\x03\x04",), ".pdf": (b"%PDF-",), ".png": (b"\x89PNG\r\n\x1a\n",), } @@ -49,6 +92,20 @@ DOCUMENTATION_DIRECTORIES = frozenset({"doc", "docs", "documentation"}) DOCUMENTATION_ROOT_NAMES = frozenset({"readme", "changelog", "changes"}) +# Consumer-repository declaration of research/data artifact path prefixes +# (issue #2193). Resolved *only* from the pull request's base ref -- never +# its head -- so a PR cannot self-authorize admission of its own binary by +# adding or widening the declaration in the same diff; see +# `_load_artifact_path_declaration`. +ARTIFACT_PATH_DECLARATION_PATH = ".github/edge-policy-artifact-paths.txt" +# Parsing-safety bounds only, not a product limit on how many research/data +# artifact locations a repository may declare: they exist so a pathological +# declaration file cannot make policy evaluation walk an unbounded number of +# entries, or match against an unbounded path depth, for every changed file +# in every pull request the required workflow evaluates. +MAX_DECLARED_ARTIFACT_PREFIXES = 64 +MAX_DECLARED_ARTIFACT_PREFIX_DEPTH = 8 + RUNTIME_PATH_NAMES = frozenset({ "dockerfile", "containerfile", @@ -136,19 +193,28 @@ class PolicyError(RuntimeError): class ContentSizeExceededError(PolicyError): - """Raised when a well-formed Contents API response exceeds MAX_FILE_BYTES. - - Distinct from every other ``PolicyError`` cause (a malformed response, a - non-file/non-base64 entry, corrupt base64, a declared size that does not - match the decoded bytes) so a caller can choose to trust a narrow, - path-scoped convention -- a genuinely oversized documentation PDF, the - one case this module cannot verify by content at all -- instead of - failing the whole check closed. Every other content-evidence failure - still fails closed exactly as before. + """Signal a well-formed file above 100 MB for the narrow PDF convention. + + Malformed, truncated, or tampered evidence raises ``PolicyError`` instead + and cannot use that convention. + """ + + +class ArtifactDeclarationNotFoundError(PolicyError): + """Raised when the GitHub API reports no resource at a requested path. + + Distinguished from every other ``PolicyError`` cause via the source + HTTP 404 status specifically, so ``_load_artifact_path_declaration`` can + treat "no declaration file at this base ref" as the repository simply + not having opted into the research/data artifact-path exemption -- + identical to today's behavior -- while every other evidence failure + (malformed JSON, an invalid declared entry, a transient network error) + still fails the whole check closed exactly like any other ``PolicyError``. """ OpenJson = Callable[[str, str], object] +OpenBytes = Callable[[str, str, int], bytes] class NoRedirectHandler(HTTPRedirectHandler): @@ -172,6 +238,85 @@ def _is_known_documentation_path(pure: PurePosixPath) -> bool: ) +def _parse_artifact_path_declaration(text: str) -> tuple[str, ...]: + """Parse a declared research/data artifact path-prefix list. + + One explicit path prefix per non-blank line; no globs or wildcards -- + every entry names a literal directory prefix, matched segment-wise by + ``_declared_prefix_for_path``. Rejects an absolute path, a ``..`` + traversal component, an empty entry, or a bare ``.``/``/``. Bounded by + ``MAX_DECLARED_ARTIFACT_PREFIXES`` (entry count) and + ``MAX_DECLARED_ARTIFACT_PREFIX_DEPTH`` (path segment depth) -- both are + parsing-safety bounds, not a product limit on how many locations a + repository may declare. A malformed entry always raises ``PolicyError`` + naming the offending entry; this never falls back to admitting nothing + or everything. + """ + + prefixes: list[str] = [] + for raw_line in text.splitlines(): + entry = raw_line.strip() + if not entry: + continue + if len(prefixes) >= MAX_DECLARED_ARTIFACT_PREFIXES: + raise PolicyError( + f"Artifact path declaration exceeds {MAX_DECLARED_ARTIFACT_PREFIXES} entries at {entry!r}" + ) + if entry.startswith("/") or entry in (".", "/"): + raise PolicyError(f"Artifact path declaration entry must be a relative path prefix: {entry!r}") + if any(char in entry for char in "*?[]"): + raise PolicyError(f"Artifact path declaration entry must not use glob syntax: {entry!r}") + parts = PurePosixPath(entry).parts + if not parts or any(part in ("", ".", "..") for part in parts): + raise PolicyError(f"Artifact path declaration entry is malformed: {entry!r}") + if len(parts) > MAX_DECLARED_ARTIFACT_PREFIX_DEPTH: + raise PolicyError( + f"Artifact path declaration entry exceeds depth {MAX_DECLARED_ARTIFACT_PREFIX_DEPTH}: {entry!r}" + ) + prefixes.append(entry) + return tuple(prefixes) + + +def _declared_prefix_for_path(path: str, declared_prefixes: Sequence[str]) -> str | None: + """Return the first declared prefix *path* falls under, else ``None``. + + Matched by path segment, not raw string prefix, so a declared ``local`` + does not also match an unrelated ``local-cache`` directory. + """ + + parts = PurePosixPath(path).parts + for prefix in declared_prefixes: + prefix_parts = PurePosixPath(prefix).parts + if parts[: len(prefix_parts)] == prefix_parts: + return prefix + return None + + +def _load_artifact_path_declaration( + *, api_url: str, repository: str, base_ref: str, token: str, opener: OpenJson +) -> tuple[str, ...]: + """Load and parse the research/data artifact path declaration at *base_ref*. + + Resolved **only** from the pull request's base ref -- never its head -- + so a pull request cannot self-authorize admission of its own binary by + adding or widening the declaration in the same diff: a PR that adds or + widens the declaration gets no benefit from it until that change is + itself reviewed and merged into the base branch. + + A declaration file absent from the base ref (HTTP 404) is not a policy + failure: it means the repository has not opted in, identical to today's + behavior before this feature existed. Every other failure to load or + parse it (malformed API shape, an invalid declared entry) still fails + the whole check closed via ``PolicyError``. + """ + + try: + content = _load_file_content(api_url, repository, ARTIFACT_PATH_DECLARATION_PATH, base_ref, token, opener) + except ArtifactDeclarationNotFoundError: + return () + return _parse_artifact_path_declaration(content) + + def _is_documentation_or_source_fixture(path: str) -> bool: """Return whether *path* is prose, license text, or scanner source fixture. @@ -211,7 +356,7 @@ def _is_documentation_or_source_fixture(path: str) -> bool: return False -def _is_binary_documentation_asset(changed: ChangedFile) -> bool: +def _is_binary_documentation_asset(changed: ChangedFile, declared_prefixes: Sequence[str] = ()) -> bool: """Return whether *changed* is a plausibly binary documentation asset. This is only the cheap, patch-presence pre-filter: GitHub's changed-files @@ -222,16 +367,34 @@ def _is_binary_documentation_asset(changed: ChangedFile) -> bool: still confirm this with ``_binary_documentation_evidence_confirms`` before trusting it; a caller without one (this module's own unit tests calling this function directly) is only checking the necessary condition. + + *declared_prefixes* (issue #2193) is the base-ref-only research/data + artifact declaration: it replaces ONLY this function's path-shape test, + never the content evidence a caller still confirms below. A file whose + suffix is a recognized ``BINARY_DOCUMENT_MAGIC`` format (``.hwpx``/ + ``.pdf``/``.png``) is admitted under a declared prefix on the exact same + format evidence documentation paths already require. A file whose + suffix has no magic entry at all (research formats such as ``.xlsx``, + ``.sav``, ``.rds``, ``.npz`` have none) can ONLY be admitted through a + declared prefix, and only on the stricter "no patch + genuinely + non-UTF-8 bytes" evidence ``_binary_documentation_evidence_confirms`` + checks for that case -- a file that decodes as valid UTF-8 must never + be treated as a binary artifact, since that is exactly the case this + scanner exists to inspect. """ - if changed.patch_available: + if changed.patch_available or _runtime_path_rule(changed.path) is not None: return False pure = PurePosixPath(changed.path) - return ( - pure.suffix.lower() in BINARY_DOCUMENT_MAGIC - and _is_known_documentation_path(pure) - and _runtime_path_rule(changed.path) is None - ) + suffix = pure.suffix.lower() + declared_prefix = _declared_prefix_for_path(changed.path, declared_prefixes) + if suffix in BINARY_DOCUMENT_MAGIC: + return ( + _is_known_documentation_path(pure) + or (suffix == ".hwpx" and "evidence" in (part.lower() for part in pure.parts)) + or declared_prefix is not None + ) + return declared_prefix is not None def _runtime_path_rule(path: str) -> str | None: @@ -304,7 +467,17 @@ def _github_open_json(url: str, token: str) -> object: with github_opener.open(request, timeout=30) as response: payload = response.read(MAX_RESPONSE_BYTES + 1) except (HTTPError, URLError, TimeoutError) as exc: - raise PolicyError(f"GitHub API request failed for policy evidence: {type(exc).__name__}") from exc + try: + if isinstance(exc, HTTPError) and exc.code == 404: + raise ArtifactDeclarationNotFoundError( + f"GitHub API reported no resource for policy evidence at {url}" + ) from exc + raise PolicyError( + f"GitHub API request failed for policy evidence: {type(exc).__name__}" + ) from exc + finally: + if isinstance(exc, HTTPError): + exc.close() if len(payload) > MAX_RESPONSE_BYTES: raise PolicyError("GitHub API policy response exceeded the bounded response size") try: @@ -313,6 +486,35 @@ def _github_open_json(url: str, token: str) -> object: raise PolicyError("GitHub API returned malformed JSON policy evidence") from exc +def _github_open_raw_bytes(url: str, token: str, max_bytes: int) -> bytes: + """Read a Git blob with a strict byte limit and no redirect or body logging.""" + + _validate_github_api_url(url) + request = Request( # noqa: S310 - URL is validated immediately above + url, + headers={ + "Accept": "application/vnd.github.raw+json", + "Authorization": f"Bearer {token}", + "X-GitHub-Api-Version": "2022-11-28", + "User-Agent": "cwl-pingora-edge-policy/1", + }, + ) + try: + with github_opener.open(request, timeout=30) as response: + raw = response.read(max_bytes + 1) + except (HTTPError, URLError, TimeoutError) as exc: + try: + raise PolicyError( + f"GitHub raw blob request failed: {type(exc).__name__}" + ) from exc + finally: + if isinstance(exc, HTTPError): + exc.close() + if len(raw) > max_bytes: + raise PolicyError("GitHub raw blob exceeded the bounded response size") + return raw + + def _load_changed_files(api_url: str, repository: str, pull_request: int, token: str, opener: OpenJson) -> tuple[ChangedFile, ...]: """Load every changed-file page while enforcing shape and pagination bounds.""" @@ -364,25 +566,33 @@ def _load_changed_files(api_url: str, repository: str, pull_request: int, token: raise PolicyError("GitHub changed-file pagination exceeded 3,000 files") # pragma: no cover -def _load_raw_file_bytes(api_url: str, repository: str, path: str, head_sha: str, token: str, opener: OpenJson) -> bytes: +def _load_raw_file_bytes( + api_url: str, repository: str, path: str, head_sha: str, token: str, + opener: OpenJson, raw_opener: OpenBytes = _github_open_raw_bytes, +) -> bytes: """Load one final head file's raw decoded bytes from the Contents API. - Raises ``ContentSizeExceededError`` specifically when the declared size - is a well-formed positive integer over ``MAX_FILE_BYTES`` -- a signal a - caller may treat differently from every other, genuinely malformed - response shape, which always raises the base ``PolicyError`` instead. + Files above the inline ceiling are fetched by the exact blob SHA named + by the Contents response at *head_sha*. The bounded raw response must + match both the declared size and the Git blob hash before use. GitHub's Contents API returns two distinct shapes for a file it cannot inline: some responses still report ``encoding: "base64"`` with a ``size`` over the inline-content ceiling and empty/absent ``content``; for files whose blob exceeds that ceiling, GitHub instead reports ``encoding: "none"`` with an accurate ``size`` and no ``content`` at - all. Both are treated as the same size-exceeded evidence; every other - response shape still fails closed. + all. Both shapes use the same verified blob path. Only files above the + Git API's 100 MB blob limit retain ``ContentSizeExceededError``. + + *head_sha* is also reused, unchanged, to fetch a base-ref-scoped file + (the issue #2193 artifact-path declaration): any git ref -- a commit SHA + or a branch name -- works here, so it is URL-encoded rather than assumed + to be the hex-only pull-request head SHA ``evaluate_pull_request`` + validates separately. """ encoded_path = quote(path, safe="/") - url = f"{api_url}/repos/{repository}/contents/{encoded_path}?ref={head_sha}" + url = f"{api_url}/repos/{repository}/contents/{encoded_path}?ref={quote(head_sha, safe='')}" payload = opener(url, token) if not isinstance(payload, Mapping): raise PolicyError(f"GitHub content evidence for {path} is not an object") @@ -390,17 +600,35 @@ def _load_raw_file_bytes(api_url: str, repository: str, path: str, head_sha: str raise PolicyError(f"GitHub content evidence for {path} is not a regular file") encoding = payload.get("encoding") declared_size = payload.get("size") - if encoding == "none": - if isinstance(declared_size, int) and declared_size > MAX_FILE_BYTES: + if isinstance(declared_size, bool) or not isinstance(declared_size, int) or declared_size < 0: + raise PolicyError(f"GitHub content evidence for {path} has a malformed size or content field") + if encoding not in {"none", "base64"}: + raise PolicyError(f"GitHub content evidence for {path} has an invalid encoding") + if declared_size > MAX_FILE_BYTES: + blob_sha = payload.get("sha") + if not isinstance(blob_sha, str) or not SHA_RE.fullmatch(blob_sha): + raise PolicyError(f"GitHub content evidence for {path} has no valid blob SHA") + if declared_size > MAX_BLOB_BYTES: + if payload.get("content", "") != "": + raise PolicyError(f"GitHub content evidence for {path} has contradictory oversized content") raise ContentSizeExceededError(f"GitHub content evidence for {path} exceeds the size contract") + raw = raw_opener(f"{api_url}/repos/{repository}/git/blobs/{blob_sha}", token, declared_size) + if len(raw) != declared_size: + raise PolicyError(f"GitHub raw blob evidence for {path} has a size mismatch") + # Git blob IDs are protocol SHA-1 object IDs, not security signatures. + digest = hashlib.sha1( # nosemgrep: python.lang.security.insecure-hash-algorithms.insecure-hash-algorithm-sha1 + f"blob {len(raw)}\0".encode(), usedforsecurity=False + ) + digest.update(raw) + actual_sha = digest.hexdigest() + if actual_sha != blob_sha: + raise PolicyError(f"GitHub raw blob evidence for {path} has a SHA mismatch") + return raw + if encoding == "none": raise PolicyError(f"GitHub content evidence for {path} has no inline content and no verifiable oversized size") - if encoding != "base64": - raise PolicyError(f"GitHub content evidence for {path} is not a regular base64 file") encoded = payload.get("content") - if not isinstance(encoded, str) or not isinstance(declared_size, int) or declared_size < 0: + if not isinstance(encoded, str): raise PolicyError(f"GitHub content evidence for {path} has a malformed size or content field") - if declared_size > MAX_FILE_BYTES: - raise ContentSizeExceededError(f"GitHub content evidence for {path} exceeds the size contract") try: raw = base64.b64decode("".join(encoded.split()), validate=True) except (ValueError, TypeError) as exc: @@ -410,10 +638,13 @@ def _load_raw_file_bytes(api_url: str, repository: str, path: str, head_sha: str return raw -def _load_file_content(api_url: str, repository: str, path: str, head_sha: str, token: str, opener: OpenJson) -> str: +def _load_file_content( + api_url: str, repository: str, path: str, head_sha: str, token: str, + opener: OpenJson, raw_opener: OpenBytes = _github_open_raw_bytes, +) -> str: """Load one final head file as bounded UTF-8 text from the Contents API.""" - raw = _load_raw_file_bytes(api_url, repository, path, head_sha, token, opener) + raw = _load_raw_file_bytes(api_url, repository, path, head_sha, token, opener, raw_opener) try: return raw.decode("utf-8") except UnicodeDecodeError as exc: @@ -428,34 +659,93 @@ def _binary_documentation_evidence_confirms( head_sha: str, token: str, opener: OpenJson, + raw_opener: OpenBytes = _github_open_raw_bytes, ) -> bool: """Return whether a claimed binary documentation asset is genuine. A missing diff ``patch`` alone is not proof of binary content: GitHub also omits a patch for a textual diff that exceeds its own rendering - limit, well under this module's ``MAX_FILE_BYTES`` content-fetch + limit, well under this module's ``MAX_BLOB_BYTES`` content-fetch ceiling. Whenever the file's raw bytes can be fetched at all, this verifies the declared format's magic prefix instead of trusting patch-presence alone. Only a file whose content evidently exceeds the - Contents API's size ceiling -- the exact case ``_is_binary_documentation_asset`` + Git blob API's size ceiling -- the exact case ``_is_binary_documentation_asset`` exists for, a cited, large research paper -- falls back to trusting the - path+suffix convention for oversized PDFs only; every other + path+suffix convention for PDFs over 100 MB only; every other content-evidence failure (a malformed API response, corrupt base64, a declared size that does not match the decoded bytes) propagates and fails the whole check closed, same as for any other file that needs scanning. + + A suffix with no ``BINARY_DOCUMENT_MAGIC`` entry only reaches this + branch when ``_is_binary_documentation_asset`` admitted it through a + declared research/data artifact prefix (issue #2193), which has no + magic byte to check. That case is confirmed by the strict complement of + the UTF-8 decode ``_load_file_content`` uses for every ordinarily-scanned + file: bytes that fail to decode as UTF-8 are genuinely binary evidence; + bytes that decode cleanly are never admitted this way, so a valid-UTF-8 + file cannot be mistaken for a binary artifact merely by sitting under a + declared prefix -- it still reaches the normal content scan instead. + Inspect readable text even when other bytes are invalid UTF-8, so a stray + binary byte cannot conceal an active runtime command. """ try: - raw = _load_raw_file_bytes(api_url, repository, changed.path, head_sha, token, opener) + raw = _load_raw_file_bytes(api_url, repository, changed.path, head_sha, token, opener, raw_opener) except ContentSizeExceededError: return PurePosixPath(changed.path).suffix.lower() == ".pdf" suffix = PurePosixPath(changed.path).suffix.lower() if suffix == ".png": return _is_complete_png(raw) + if suffix == ".hwpx": + return _is_complete_hwpx(raw) + if suffix not in BINARY_DOCUMENT_MAGIC: + try: + raw.decode("utf-8") + except UnicodeDecodeError: + readable = raw.decode("utf-8", errors="replace") + return not any(pattern.search(readable) for _, pattern in CONTENT_RULES) + return False return raw.startswith(BINARY_DOCUMENT_MAGIC[suffix]) +def _is_complete_hwpx(raw: bytes) -> bool: + """Confirm a bounded HWPX container without extracting document content. + + Require an unprefixed ZIP, its exact end record, unique members, and the + stored HWPX MIME marker plus an unencrypted package manifest. This is + format evidence, not XML document validation or malware inspection. + """ + if not raw.startswith(BINARY_DOCUMENT_MAGIC[".hwpx"][0]): + return False + try: + with zipfile.ZipFile(io.BytesIO(raw)) as archive: + archive_entries = archive.infolist() + member_names = [member_info.filename for member_info in archive_entries] + end_offset = len(raw) - 22 - len(archive.comment) + if end_offset < 0 or raw[end_offset:end_offset + 4] != b"PK\x05\x06": + return False + if int.from_bytes(raw[end_offset + 20:end_offset + 22], "little") != len(archive.comment): + return False + if not archive_entries or archive_entries[0].header_offset != 0: + return False + if member_names[0] != "mimetype" or len(member_names) != len(set(member_names)): + return False + mimetype_info = archive.getinfo("mimetype") + manifest_info = archive.getinfo("Contents/content.hpf") + expected_mimetype = b"application/hwp+zip" + if mimetype_info.flag_bits & 1 or manifest_info.flag_bits & 1: + return False + if mimetype_info.compress_type != zipfile.ZIP_STORED or mimetype_info.file_size != len(expected_mimetype): + return False + if manifest_info.is_dir() or manifest_info.file_size == 0: + return False + with archive.open(mimetype_info) as mimetype_stream: + return mimetype_stream.read(len(expected_mimetype) + 1) == expected_mimetype + except (KeyError, UnicodeError, OSError, ValueError, NotImplementedError, zipfile.BadZipFile): + return False + + def _png_unfilter_row(filtered: bytes, previous: bytes, filter_type: int, bytes_per_pixel: int) -> bytes: """Reconstruct one PNG scanline for bounded indexed-pixel validation.""" @@ -557,7 +847,7 @@ def _is_complete_png(raw: bytes) -> bool: pass_height = (height - y_start + y_step - 1) // y_step row_bytes = (pass_width * channels * bit_depth + 7) // 8 expected_size += pass_height * (row_bytes + 1) - if expected_size > MAX_RESPONSE_BYTES: + if expected_size > MAX_PNG_DECODED_BYTES: return False scanlines.append((pass_height, row_bytes, pass_width)) decoder = zlib.decompressobj() @@ -600,18 +890,20 @@ def _is_complete_png(raw: bytes) -> bool: return False -def _needs_content_scan(changed: ChangedFile) -> bool: +def _needs_content_scan(changed: ChangedFile, declared_prefixes: Sequence[str] = ()) -> bool: """Return whether a changed final file can carry an active edge runtime. A claimed binary documentation asset (``_is_binary_documentation_asset``) exempts here on the cheap, offline pre-filter alone; ``evaluate_pull_request`` never actually relies on that -- it runs ``_binary_documentation_evidence_confirms`` - for that case before this function is even consulted. + for that case before this function is even consulted. *declared_prefixes* + is the base-ref-only research/data artifact declaration from issue + #2193; it is passed straight through to ``_is_binary_documentation_asset``. """ if changed.status == "removed" or _is_documentation_or_source_fixture(changed.path): return False - if _is_binary_documentation_asset(changed): + if _is_binary_documentation_asset(changed, declared_prefixes): return False if not changed.patch_available: return True @@ -633,9 +925,21 @@ def evaluate_pull_request( head_sha: str, event_action: str, token: str, + base_ref: str | None = None, opener: OpenJson = _github_open_json, + raw_opener: OpenBytes = _github_open_raw_bytes, ) -> tuple[Violation, ...]: - """Evaluate one pull request without checking out or executing its content.""" + """Evaluate one pull request without checking out or executing its content. + + *base_ref* (issue #2193) is an optional pull-request base ref -- a + branch name or a commit SHA, whatever the calling workflow already has + on the ``pull_request`` event without new permissions. When given, the + research/data artifact path declaration at ``ARTIFACT_PATH_DECLARATION_PATH`` + is resolved from that ref (never from ``head_sha``) and its declared + prefixes are admitted on the same content-evidence terms as documentation + paths. Omitting it (the default) reproduces this module's exact prior + behavior: no declared prefixes, no declaration fetch at all. + """ if event_action == "closed": return () @@ -647,35 +951,61 @@ def evaluate_pull_request( raise PolicyError("Pull-request head SHA is malformed") if not token: raise PolicyError("GITHUB_TOKEN is required for policy evidence") - changed_files = _load_changed_files(api_url.rstrip("/"), repository, pull_request, token, opener) + if base_ref is not None and (".." in base_ref or not BASE_REF_RE.fullmatch(base_ref)): + raise PolicyError("Pull-request base ref is malformed") + resolved_api_url = api_url.rstrip("/") + declared_prefixes: tuple[str, ...] = () + if base_ref is not None: + declared_prefixes = _load_artifact_path_declaration( + api_url=resolved_api_url, repository=repository, base_ref=base_ref, token=token, opener=opener + ) + changed_files = _load_changed_files(resolved_api_url, repository, pull_request, token, opener) violations: list[Violation] = [] for changed in changed_files: + declared_prefix = _declared_prefix_for_path(changed.path, declared_prefixes) # A claimed binary documentation asset gets its own network-verified # check ahead of _needs_content_scan's patch-presence-only signal: # a missing patch does not by itself prove binary content (GitHub # also omits one for an oversized textual diff), so this confirms # the format's magic prefix whenever the bytes can be fetched at # all, falling back to the path+suffix convention only when the - # content genuinely exceeds the Contents API's size ceiling. A + # content genuinely exceeds the Git blob API's size ceiling. A # removed file has no head content to fetch at all -- _needs_content_scan # already special-cases this the same way for every other file. - if changed.status != "removed" and _is_binary_documentation_asset(changed): + if changed.status != "removed" and _is_binary_documentation_asset(changed, declared_prefixes): if _binary_documentation_evidence_confirms( changed, - api_url=api_url.rstrip("/"), + api_url=resolved_api_url, repository=repository, head_sha=head_sha, token=token, opener=opener, + raw_opener=raw_opener, ): + if declared_prefix is not None: + # Names the reviewed declaration this admission relied + # on, so a reviewer can trace it back to the base ref. + print(_declared_prefix_notice(changed.path, declared_prefix, base_ref)) continue - elif not _needs_content_scan(changed): + elif not _needs_content_scan(changed, declared_prefixes): continue - content = _load_file_content(api_url.rstrip("/"), repository, changed.path, head_sha, token, opener) + content = _load_file_content(resolved_api_url, repository, changed.path, head_sha, token, opener, raw_opener) violations.extend(scan_content(changed.path, content)) return tuple(violations) +def _declared_prefix_notice(path: str, prefix: str, base_ref: str) -> str: + """Render one bounded GitHub workflow notice for a declared-prefix admission.""" + + escaped_path = path.replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A").replace(",", "%2C") + message = ( + f"CWL edge policy admitted a research/data artifact under declared prefix " + f"'{prefix}' (declaration read from base ref '{base_ref}')" + ) + message = message.replace("%", "%25").replace("\r", "%0D").replace("\n", "%0A") + return f"::notice file={escaped_path}::{message}" + + def _annotation(violation: Violation) -> str: """Render one bounded GitHub workflow command annotation.""" @@ -694,6 +1024,15 @@ def build_parser() -> argparse.ArgumentParser: parser.add_argument("--head-sha", required=True) parser.add_argument("--event-action", required=True) parser.add_argument("--api-url", default=GITHUB_API_ORIGIN) + parser.add_argument( + "--base-ref", + default=None, + help=( + "Pull-request base ref (branch name or commit SHA) used to resolve the " + "issue #2193 research/data artifact path declaration. Omit to disable " + "that declaration entirely (identical to this module's prior behavior)." + ), + ) return parser @@ -710,6 +1049,7 @@ def main(argv: Sequence[str] | None = None, environ: Mapping[str, str] | None = head_sha=args.head_sha, event_action=args.event_action, token=env.get("GITHUB_TOKEN", ""), + base_ref=args.base_ref, ) except PolicyError as exc: print(f"::error::Pingora edge policy could not establish complete evidence: {exc}") diff --git a/scripts/ci/place_maturin_extension.py b/scripts/ci/place_maturin_extension.py new file mode 100755 index 0000000000..c6fd690216 --- /dev/null +++ b/scripts/ci/place_maturin_extension.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +"""Copy a built wheel's extension modules into the project's Python source tree. + +Projects such as fast-mlsirm set pytest ``pythonpath = ["python"]``, so the test +suite imports ``python/`` rather than the installed wheel, and an +extension that exists only in site-packages is shadowed (``cannot import name +'_core'``). This mirrors what ``maturin develop`` does: only compiled extension +members are copied, only into package directories the source tree already has. +""" + +from __future__ import annotations + +import pathlib +import sys +import tomllib +import zipfile + +EXTENSION_SUFFIXES = (".so", ".pyd") + + +def _inside(path: pathlib.Path, root: pathlib.Path) -> bool: + """Return whether path is root or is contained by root.""" + + return path == root or root in path.parents + + +def place(wheel: pathlib.Path, project_dir: pathlib.Path) -> list[pathlib.Path]: + """Copy wheel extensions into existing project package directories.""" + + project = project_dir.resolve() + pyproject = tomllib.loads((project / "pyproject.toml").read_text(encoding="utf-8")) + python_source = pyproject.get("tool", {}).get("maturin", {}).get("python-source", ".") + if not isinstance(python_source, str): + raise ValueError("tool.maturin.python-source must be a string") + source_root = (project / python_source).resolve() + if not _inside(source_root, project): + raise ValueError("tool.maturin.python-source escapes the project directory") + + placed = [] + with zipfile.ZipFile(wheel) as archive: + for member in archive.infolist(): + if member.is_dir() or not member.filename.endswith(EXTENSION_SUFFIXES): + continue + target = (source_root / member.filename).resolve() + if not _inside(target, source_root): + raise ValueError(f"wheel member escapes the source tree: {member.filename}") + if not target.parent.is_dir(): + continue + target.write_bytes(archive.read(member)) + placed.append(target) + return placed + + +def main(argv: list[str]) -> int: + """Run extension placement and return its process status.""" + + if len(argv) != 2: + print("usage: place_maturin_extension.py WHEEL PROJECT_DIR", file=sys.stderr) + return 2 + try: + placed = place(pathlib.Path(argv[0]), pathlib.Path(argv[1])) + except (OSError, ValueError, zipfile.BadZipFile, tomllib.TOMLDecodeError) as exc: + print(f"Could not place the built extension in the source tree: {exc}", file=sys.stderr) + return 1 + for path in placed: + print(f"Placed built extension at {path}") + return 0 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/scripts/ci/pr_auto_rebase.py b/scripts/ci/pr_auto_rebase.py index c0f04c3aa2..d28afd0ed8 100755 --- a/scripts/ci/pr_auto_rebase.py +++ b/scripts/ci/pr_auto_rebase.py @@ -80,7 +80,7 @@ ) -REPO_RE = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$") +REPO_RE = re.compile(r"^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") OPEN_PRS_PAGE_SIZE = 25 LABELS_PAGE_SIZE = 50 DEFAULT_MAX_PER_RUN = 10 diff --git a/scripts/ci/pr_review_autofix_context.py b/scripts/ci/pr_review_autofix_context.py index cc7b6fb003..51a36a53f6 100755 --- a/scripts/ci/pr_review_autofix_context.py +++ b/scripts/ci/pr_review_autofix_context.py @@ -19,7 +19,7 @@ from scripts.ci.pr_review_fix_scheduler import current_head_failed_checks -REPO_RE = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$") +REPO_RE = re.compile(r"^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") SHA_RE = re.compile(r"^[0-9a-fA-F]{40}$") _AUTOFIX_CONTROL_PREFIXES = (".github/", "scripts/ci/") _REPAIR_MODES = ("review", "rca", "conflict") diff --git a/scripts/ci/pr_review_fix_scheduler.py b/scripts/ci/pr_review_fix_scheduler.py index bc2868c5a4..23c5c66a9d 100755 --- a/scripts/ci/pr_review_fix_scheduler.py +++ b/scripts/ci/pr_review_fix_scheduler.py @@ -53,7 +53,7 @@ r"" ) -REPO_RE = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$") +REPO_RE = re.compile(r"^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") REPAIR_MODES = frozenset({"review", "rca", "conflict"}) AUTOFIX_RUN_NAME_RE = re.compile( r"^PR Review Autofix (?P[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+)" diff --git a/scripts/ci/pr_review_merge_scheduler.py b/scripts/ci/pr_review_merge_scheduler.py index 051a3bbd5b..1cc18dc818 100755 --- a/scripts/ci/pr_review_merge_scheduler.py +++ b/scripts/ci/pr_review_merge_scheduler.py @@ -139,10 +139,11 @@ def _is_opencode_post_approval_followup( and _argument_value(argument_values, "--max-prs") == "1" and _argument_value(argument_values, "--review-dispatch-limit") == "0" and _argument_value(argument_values, "--merge-mode") - == "direct_or_auto" + == "disabled" + and "--dry-run" in argument_set and "--pr-number" in argument_set and "--no-trigger-reviews" in argument_set - and "--enable-auto-merge" in argument_set + and "--no-enable-auto-merge" in argument_set and "--no-update-branches" in argument_set ) diff --git a/scripts/ci/pr_review_merge_scheduler_core.py b/scripts/ci/pr_review_merge_scheduler_core.py index e97b41074a..5b3eb79961 100644 --- a/scripts/ci/pr_review_merge_scheduler_core.py +++ b/scripts/ci/pr_review_merge_scheduler_core.py @@ -330,6 +330,21 @@ def live_dispatch_head_matches(repo: str, pr: dict[str, Any]) -> bool: # checks in the same operating window instead of leaving them for seven hours. DEFAULT_STALE_OPENCODE_MINUTES = 90 DEFAULT_COVERAGE_RETRY_FLOOR_MINUTES = 60 +# Derived from measured consecutive-push gaps across 4 org repositories +# (419 samples, 2026-09-17): density roughly halves right at 300s (155 +# gaps <=300s vs 31 in (300,600]), the clearest inflection point in an +# otherwise continuous, non-bimodal distribution. See +# docs/doctoring/actions-capacity-root-cause-20260917.md and the PR that +# introduced this constant for the full sample. Only takes effect when +# OPENCODE_REVIEW_COALESCE_ENABLED is set -- see +# head_stable_for_seconds() and its use in dispatch_opencode_review(). +DEFAULT_COALESCE_WINDOW_SECONDS = 300 +# Two 5-minute coalesce-tick cron periods: if no tick completed within this +# horizon, dispatch_opencode_review() fail-opens instead of deferring a head +# that is still inside the settling window. +DEFAULT_COALESCE_TICK_MAX_AGE_SECONDS = 600 +COALESCE_TICK_WORKFLOW_PATH = ".github/workflows/opencode-review-coalesce-tick.yml" +COALESCE_TICK_WORKFLOW_NAME = "OpenCode Review Coalesce Tick" DEFAULT_UPDATE_BRANCH_HEAD_POLL_ATTEMPTS = 6 DEFAULT_UPDATE_BRANCH_HEAD_POLL_SECONDS = 5.0 OPENCODE_WORKFLOW_NAMES = { @@ -344,7 +359,7 @@ def live_dispatch_head_matches(repo: str, pr: dict[str, Any]) -> bool: ACTION_REQUIRED_CONCLUSIONS = {"ACTION_REQUIRED"} GIT_REF_RE = re.compile(r"^(?!-)[A-Za-z0-9._/-]+$") GIT_SHA_RE = re.compile(r"^[0-9a-fA-F]{40}$") -GITHUB_REPOSITORY_RE = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$") +GITHUB_REPOSITORY_RE = re.compile(r"^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") REVIEW_BODY_HEAD_SHA_RE = re.compile(r"Head SHA:\s*`([0-9a-fA-F]{40})`") CHECK_GATED_OPENCODE_CHANGE_REQUEST_MARKER = ( "OpenCode could not approve from deterministic current-head evidence because GitHub Checks have failed." @@ -1193,7 +1208,10 @@ def rest_review_node(review: dict[str, Any]) -> dict[str, Any]: "state": review.get("state"), "body": review.get("body"), "submittedAt": review.get("submitted_at"), - "author": {"login": ((review.get("user") or {}).get("login"))}, + "author": { + "login": ((review.get("user") or {}).get("login")), + **({"__typename": review["user"]["type"]} if (review.get("user") or {}).get("type") is not None else {}), + }, "commit": {"oid": commit_id} if commit_id else None, } @@ -1729,6 +1747,112 @@ def parse_github_datetime(value: str | None) -> datetime | None: return parsed.astimezone(timezone.utc) +def head_committed_at(pr: dict[str, Any]) -> datetime | None: + """Return the current head commit's committed timestamp, if known.""" + nodes = ((pr.get("commits") or {}).get("nodes")) or [] + if not nodes: + return None + commit = nodes[-1].get("commit") or {} + return parse_github_datetime(commit.get("committedDate")) + + +def head_stable_for_seconds( + pr: dict[str, Any], *, now: datetime | None = None +) -> float | None: + """Return how long the current head has existed, or None if unknown. + + Unknown (missing or unparseable commit timestamp) must never gate a + dispatch decision -- callers treat None as "stable" (fail open) so a + read gap here cannot silently withhold a legitimate review forever. + """ + committed_at = head_committed_at(pr) + if committed_at is None: + return None + return ((now or datetime.now(timezone.utc)) - committed_at).total_seconds() + + +def coalesce_window_seconds() -> int: + """Return the configured push-burst coalescing window in seconds.""" + raw = os.environ.get("OPENCODE_REVIEW_COALESCE_WINDOW_SECONDS", "") + try: + parsed = int(raw) + except ValueError: + return DEFAULT_COALESCE_WINDOW_SECONDS + return parsed if parsed >= 0 else DEFAULT_COALESCE_WINDOW_SECONDS + + +def coalesce_enabled() -> bool: + """Return whether push-burst coalescing is enabled for this invocation. + + Defaults to disabled: every existing caller (scan-pr-queue's per-push + and daily-cron invocations) keeps dispatching immediately, exactly as + today, unless this is explicitly turned on. + """ + return os.environ.get("OPENCODE_REVIEW_COALESCE_ENABLED", "").strip().lower() == "true" + + +def coalesce_tick_max_age_seconds() -> int: + """Return how recently a coalesce tick must have completed to keep deferring.""" + raw = os.environ.get("OPENCODE_REVIEW_COALESCE_TICK_MAX_AGE_SECONDS", "") + try: + parsed = int(raw) + except ValueError: + return DEFAULT_COALESCE_TICK_MAX_AGE_SECONDS + return parsed if parsed >= 0 else DEFAULT_COALESCE_TICK_MAX_AGE_SECONDS + + +def coalesce_tick_repository() -> str: + """Return the repository that hosts the scheduled coalesce tick workflow.""" + configured = (os.environ.get("SCHEDULER_REQUIRED_WORKFLOW_REPOSITORY") or "").strip() + return configured or "ContextualWisdomLab/.github" + + +def recent_coalesce_tick_completed( + repo: str, + *, + now: datetime | None = None, + max_age_seconds: int | None = None, +) -> bool: + """Return whether a coalesce tick completed within the fail-open horizon. + + The scheduled tick is the only path that dispatches synchronize-triggered + reviews once coalescing is enabled. When no tick has completed recently, + callers must fail open and dispatch immediately rather than defer forever. + """ + max_age = ( + coalesce_tick_max_age_seconds() + if max_age_seconds is None + else max_age_seconds + ) + if max_age <= 0: + return False + current = now or datetime.now(timezone.utc) + cutoff = current - timedelta(seconds=max_age) + created_filter = cutoff.strftime(">=%Y-%m-%dT%H:%M:%SZ") + tick_repo = repository_dispatch_target(validate_github_repository(repo)) + for run_data in active_workflow_runs( + tick_repo, + ("completed",), + event="schedule", + created=created_filter, + ): + if run_data.get("path") != COALESCE_TICK_WORKFLOW_PATH: + continue + # Skipped ticks (flag off, job-level gate) and cancelled/failed runs + # are not evidence that coalesce dispatch is alive — only a successful + # tick that took a runner and finished its org pass counts. + if str(run_data.get("conclusion") or "").lower() != "success": + continue + completed_at = parse_github_datetime( + run_data.get("updated_at") + or run_data.get("run_started_at") + or run_data.get("created_at") + ) + if completed_at is not None and completed_at >= cutoff: + return True + return False + + def check_run_recency_key( node: dict[str, Any], started_at: datetime | None, index: int ) -> tuple[int, datetime, int]: @@ -1904,6 +2028,11 @@ def opencode_in_progress(pr: dict[str, Any], *, stale_after_minutes: int | None return opencode_progress_state(pr, stale_after_minutes=stale_after) == "running" +def has_in_flight_check_runs(pr: dict[str, Any]) -> bool: + """Return whether any newest current-head check run is still queued or running.""" + return any(running_check_state(node) == "running" for node in latest_check_runs(pr)) + + _STRIX_SUCCESS_CONCLUSIONS = {"SUCCESS"} @@ -2123,28 +2252,79 @@ def has_current_head_approval(pr: dict[str, Any]) -> bool: return current_head_review_state(pr, "APPROVED") +def is_substantive_noema_app_approval(review: dict[str, Any], head: str) -> bool: + """Recognize only the known second-review App's actual publisher contract. + + API author and commit identity remain authoritative. Body markers alone + cannot authorize an arbitrary bot, a personal-token fallback or OpenCode. + """ + if ( + review_author_login(review) not in {"cwl-noema-review", "cwl-noema-review[bot]"} + or (review.get("author") or {}).get("__typename") not in {None, "Bot"} + or not is_bot_review_author(review) + or (review.get("commit") or {}).get("oid") != head + or not re.fullmatch(r"[0-9a-f]{40}", head) + ): + return False + body = str(review.get("body") or "") + footer_marker = "" + if body.count(footer_marker) != 1: + return False + evidence, footer = body.split(footer_marker, 1) + expected_footers = { + ( + f"\n- Result: APPROVE\n- Head SHA: `{head}`\n" + f"- Reviewer credential: `{source}`\n" + "- Actor: `cwl-noema-review[bot]`\n\n" + f"" + ) + for source in ("noema-review-github-app", "noema-review-github-app-refresh") + } + if footer not in expected_footers: + return False + return bool( + evidence.startswith("## Noema LLM review\n") + and re.search( + r"### Reviewed changed lines\n- `[^`\n]+:[1-9][0-9]* \((?:LEFT|RIGHT)\)`: [^\s][^\n]*", + evidence, + ) + and re.search( + r"### Adversarial validation\n- `[^`\n]+:[1-9][0-9]* \((?:LEFT|RIGHT)\)` " + r"falsified: [^\s][^\n]* — [^\s][^\n]*", + evidence, + ) + and re.search(r"(?m)^- Residual risk: [^\s][^\n]*$", evidence) + ) + + def has_independent_current_head_approval(pr: dict[str, Any]) -> bool: - """Return whether an eligible reviewer's latest exact-head policy state approves.""" - author = ((pr.get("author") or {}).get("login") or "").lower() + """Require a non-author human or trusted, substantive second-review App.""" + author = ((pr.get("author") or {}).get("login") or "").lower().removesuffix("[bot]") if not author: return False seen_reviewers: set[str] = set() for review in reversed((pr.get("reviews") or {}).get("nodes") or []): - reviewer = review_author_login(review) + reviewer = review_author_login(review).removesuffix("[bot]") state = (review.get("state") or "").upper() if ( not reviewer or reviewer == author or is_automated_opencode_review(review) or reviewer == "github-actions" - or is_bot_review_author(review) - or not review_matches_current_head(review, pr) + or (reviewer != "cwl-noema-review" and not review_matches_current_head(review, pr)) or state not in {"APPROVED", "CHANGES_REQUESTED", "DISMISSED"} or reviewer in seen_reviewers ): continue + # Even a later malformed approval must retire that actor's older one. seen_reviewers.add(reviewer) - if state == "APPROVED": + if state == "APPROVED" and ( + ( + not is_bot_review_author(review) + and reviewer != "cwl-noema-review" + ) + or is_substantive_noema_app_approval(review, str(pr.get("headRefOid") or "")) + ): return True return False @@ -2926,6 +3106,8 @@ def post_update_branch_followup( return f"{head_note}; bounded admission budget is exhausted" if dispatch_result == "already_running": return f"{head_note}; same-head OpenCode workflow run is already active" + if dispatch_result == "coalescing": + return f"{head_note}; current head is within the push-burst coalescing window" return f"{head_note}; same-head Strix evidence is complete, so OpenCode review was dispatched" @@ -3247,7 +3429,24 @@ def active_review_run_refs( for run_repo in (dispatch_repo,): for run_data in active_workflow_runs(run_repo, statuses): run_name = str(run_data.get("name") or "") - if run_name != workflow and run_name not in workflow_aliases: + # GitHub reports the *rendered* ``run-name:`` in a run's ``name``, + # not the workflow name, and eight workflows here define one -- + # every workflow whose runs this matcher looks for + # (``opencode-review.yml`` = "Required OpenCode Review", + # ``opencode-review-dispatch.yml`` = "OpenCode Review Dispatch", + # ``strix.yml`` = "Strix Security Scan") is among them. Exact + # matching therefore dropped every production dispatch run here, + # before the ``repository_dispatch`` branch below that exists to + # handle it: ``already_running`` never suppressed a same-head + # repeat and ``stale`` never populated, so .github#1529 took 27 + # dispatches on one unchanged head and older-head central runs were + # never cancelled. Sampled 2026-09-07: 100 of 100 + # opencode-review-dispatch runs carry the rendered form, 0 bare. + # Accept it -- the workflow name, then a space, then the suffix. + if not any( + run_name == candidate or run_name.startswith(f"{candidate} ") + for candidate in (workflow, *workflow_aliases) + ): continue run_id = run_data.get("id") if not run_id: @@ -3664,6 +3863,29 @@ def dispatch_opencode_review(repo: str, workflow: str, pr: dict[str, Any], *, dr the original event and leaves the review job skipped. Always use the default-branch dispatch entrypoint after same-head deduplication. """ + if coalesce_enabled(): + age_seconds = head_stable_for_seconds(pr) + window = coalesce_window_seconds() + if age_seconds is not None and age_seconds < window: + tick_recent = ( + recent_coalesce_tick_completed(coalesce_tick_repository()) + if not dry_run + else True + ) + if tick_recent: + print( + "OpenCode review dispatch coalesced: current head is " + f"{age_seconds:.0f}s old, below the {window}s push-burst " + "settling window; a later, stable-head pass will dispatch it." + ) + return "coalescing" + max_age = coalesce_tick_max_age_seconds() + print( + "OpenCode review dispatch coalesce fail-open: current head is " + f"{age_seconds:.0f}s old, below the {window}s push-burst " + "settling window, but no coalesce tick completed within " + f"{max_age}s; dispatching immediately." + ) if not dry_run: require_github_actions_control_actor("inspect-active-opencode-review") current_run_refs, stale_run_refs = active_opencode_run_refs(repo, workflow, pr) @@ -3737,14 +3959,8 @@ def is_strix_scan_check_run(node: dict[str, Any]) -> bool: def dispatch_strix_evidence(repo: str, workflow: str, pr: dict[str, Any], *, dry_run: bool) -> str: """Dispatch same-head Strix workflow evidence before OpenCode reviews.""" - job_id = matching_actions_job_id(pr, is_strix_scan_check_run) - if job_id: - if not dry_run and not review_dispatch_admitted("strix", repo, pr): - return "admission_deferred" - if not dry_run and not live_dispatch_head_matches(repo, pr): - return "stale_head" - rerun_actions_job(repo, job_id, dry_run=dry_run, action="rerun-strix-evidence") - return "rerun" if not dry_run else "dry_run" + # A job rerun retains its original trusted workflow revision. Fresh dispatch + # selects the default-branch runtime and still enforces admission and live head. if dry_run: return "dry_run" require_github_actions_control_actor("inspect-active-strix-evidence") @@ -4138,6 +4354,12 @@ def dispatch_draft_review_only( "draft PR review-only dispatch; current head has completed Strix evidence; " "same-head OpenCode workflow run is already active", ) + if dispatch_result == "coalescing": + return Decision( + number, + "wait", + "draft PR review-only dispatch; current head is within the push-burst coalescing window", + ) return Decision( number, "review_dispatch", @@ -4249,6 +4471,12 @@ def inspect_pr( "wait", f"stacked PR onto {base_ref}; same-head OpenCode workflow run is already active", ) + if dispatch_result == "coalescing": + return Decision( + number, + "wait", + f"stacked PR onto {base_ref}; current head is within the push-burst coalescing window", + ) return Decision( number, "review_dispatch", @@ -4466,6 +4694,12 @@ def request_branch_update(freshness_reason: str, *, suffix: str = "") -> Decisio "wait", "current-head coverage evidence is complete, but a same-head OpenCode workflow run is already active", ) + if dispatch_result == "coalescing": + return decide( + "wait", + "current-head coverage evidence is complete, but the current head is within the " + "push-burst coalescing window", + ) return decide( "review_dispatch", "current-head OpenCode coverage blocker is cleared; same-head OpenCode re-dispatched", @@ -4781,6 +5015,19 @@ def request_branch_update(freshness_reason: str, *, suffix: str = "") -> Decisio f"current head has no OpenCode approval; branch is outdated before review dispatch, " f"but head repo {head_repo} is not writable by the scheduler credential", ) + if has_in_flight_check_runs(pr): + # Updating now would cancel every queued or running check on the + # current head and requeue the pull request behind them. Under a + # saturated runner queue the PR's own delayed scheduler run does + # this on every execution, so no head ever finishes its checks + # (#1935). Deliberately no age cap: a check that never finishes + # keeps the head where it is instead of restarting that loop. + return decide( + "wait", + "current head has no OpenCode approval; branch is outdated before review dispatch, " + "but current-head checks are still queued or running; holding the update so their " + "evidence is not discarded", + ) if merge_state == "BEHIND": freshness_reason = "current head has no OpenCode approval; branch is outdated before review dispatch" else: @@ -4868,6 +5115,12 @@ def request_branch_update(freshness_reason: str, *, suffix: str = "") -> Decisio "wait", "OpenCode review exceeded the status-check retry threshold, but a same-head workflow run is already active", ) + if dispatch_result == "coalescing": + return decide( + "wait", + "OpenCode review exceeded the status-check retry threshold, but the current head is within " + "the push-burst coalescing window", + ) return decide( "review_dispatch", f"OpenCode review exceeded {stale_opencode_minutes} minute retry threshold; same-head OpenCode re-dispatched", @@ -4918,6 +5171,12 @@ def request_branch_update(freshness_reason: str, *, suffix: str = "") -> Decisio "wait", "current head has completed Strix evidence; same-head OpenCode workflow run is already active", ) + if dispatch_result == "coalescing": + return decide( + "wait", + "current head has completed Strix evidence, but the current head is within the " + "push-burst coalescing window", + ) return decide( "review_dispatch", "current head has completed Strix evidence; same-head OpenCode dispatched", diff --git a/scripts/ci/premerge_review_tooling.py b/scripts/ci/premerge_review_tooling.py new file mode 100644 index 0000000000..0961f15652 --- /dev/null +++ b/scripts/ci/premerge_review_tooling.py @@ -0,0 +1,510 @@ +"""Admit independently pinned reviewer source before loading any candidate code. + +This source-only boundary does not issue credentials or admit a runner. Its +caller must obtain accepted_digest independently of PR data and supply the +complete regular-file inventory from the selected immutable Git tree. Merely +hashing a caller's own manifest is not independent source acceptance. +""" + +from __future__ import annotations + +import hashlib +import json +import os +import stat +import re +import tempfile +import subprocess +from pathlib import Path +from collections.abc import Mapping + + +def _unique_object(pairs: list[tuple[str, object]]) -> dict[str, object]: + """Reject duplicate keys before any manifest interpretation.""" + result: dict[str, object] = {} + for key, value in pairs: + if key in result: + raise ValueError("manifest contains duplicate fields") + result[key] = value + return result + + +def _safe_path(value: object) -> bool: + """Accept canonical relative inventory paths without traversal or controls.""" + return ( + isinstance(value, str) + and bool(value) + and "\\" not in value + and not any(ord(character) < 32 for character in value) + and all(part not in {"", ".", ".."} for part in value.split("/")) + ) + + +def admit_tooling( + raw_manifest: bytes, *, accepted_digest: str, source_files: Mapping[str, bytes] +) -> None: + """Reject a manifest that does not match independently accepted bytes.""" + if not isinstance(raw_manifest, bytes) or not 0 < len(raw_manifest) <= 2 * 1024 * 1024: + raise ValueError("manifest size is outside the bounded contract") + if hashlib.sha256(raw_manifest).hexdigest() != accepted_digest: + raise ValueError("tooling does not match the accepted manifest digest") + try: + manifest = json.loads(raw_manifest, object_pairs_hook=_unique_object) + except (UnicodeDecodeError, json.JSONDecodeError) as exc: + raise ValueError("manifest JSON is malformed") from exc + keys = {"schema_version", "repository", "commit", "tree", "entrypoint", "files"} + if not isinstance(manifest, dict) or set(manifest) != keys: + raise ValueError("manifest fields do not match the trusted schema") + if type(manifest["schema_version"]) is not int or manifest["schema_version"] != 1: + raise ValueError("manifest schema version is unsupported") + if manifest["repository"] != "ContextualWisdomLab/.github": + raise ValueError("manifest tooling repository is untrusted") + for field in ("commit", "tree"): + if not isinstance(manifest[field], str) or not re.fullmatch(r"[0-9a-f]{40}", manifest[field]): + raise ValueError("manifest requires immutable commit and tree identities") + inventory = manifest["files"] + if not isinstance(inventory, dict) or not inventory: + raise ValueError("manifest source inventory must be nonempty") + for name, digest in inventory.items(): + if not _safe_path(name) or not isinstance(digest, str) or not re.fullmatch(r"[0-9a-f]{64}", digest): + raise ValueError("manifest source inventory contains invalid paths or digests") + if not _safe_path(manifest["entrypoint"]) or manifest["entrypoint"] not in inventory: + raise ValueError("manifest entrypoint is not in the accepted source inventory") + actual = {name: hashlib.sha256(data).hexdigest() for name, data in source_files.items()} + if manifest["files"] != actual: + raise ValueError("tooling source inventory differs from the accepted manifest") + + +def require_review_scope(expected: Mapping[str, object], live: Mapping[str, object]) -> None: + """Refuse changed PR scope before model preparation or App publication.""" + fields = {"repository", "number", "head", "base", "author", "reviewer"} + if set(expected) != fields or set(live) != fields | {"state", "draft"}: + raise ValueError("review scope fields are incomplete or unexpected") + if type(live["number"]) is not int or type(expected["number"]) is not int or expected["number"] < 1: + raise ValueError("review scope PR number is invalid") + repository = expected["repository"] + if not isinstance(repository, str) or not re.fullmatch( + r"ContextualWisdomLab/(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+", repository + ): + raise ValueError("review scope repository is outside the organization") + for field in ("head", "base"): + if not isinstance(expected[field], str) or not re.fullmatch(r"[0-9a-f]{40}", expected[field]): + raise ValueError("review scope requires immutable head and base") + for field in ("author", "reviewer"): + if not isinstance(expected[field], str) or not expected[field].strip(): + raise ValueError("review scope identity is missing") + if expected["author"].strip().casefold() == expected["reviewer"].strip().casefold(): + raise ValueError("review scope requires an independent reviewer") + if live["state"] != "OPEN" or live["draft"] is not False: + raise ValueError("review scope is closed or draft") + if any(expected[field] != live[field] for field in fields): + raise ValueError("review scope changed after admission") + + +def require_prepared_envelope(path: Path, scope: Mapping[str, object]) -> None: + """Validate compatible eight-field model output before publisher credentials. + + This is same-invocation runner-local custody, not cross-run authenticity. + The independently admitted tooling is responsible for substantive model + validation; no externally supplied envelope is accepted by run_review. + """ + try: + with os.fdopen(os.open(path, os.O_RDONLY | os.O_NOFOLLOW), "rb") as stream: + info = os.fstat(stream.fileno()) + if not stat.S_ISREG(info.st_mode) or info.st_nlink != 1 or info.st_mode & 0o077: + raise ValueError("prepared envelope is not a private single-link regular file") + raw = stream.read(2 * 1024 * 1024 + 1) + if not 0 < len(raw) <= 2 * 1024 * 1024: + raise ValueError("prepared envelope exceeds its bounded size") + payload = json.loads(raw, object_pairs_hook=_unique_object) + except (OSError, UnicodeDecodeError, json.JSONDecodeError) as exc: + raise ValueError("prepared envelope is unavailable or malformed") from exc + keys = {"schema_version", "repository", "pull_request_number", "expected_head", + "expected_base", "reviewer_actor", "pull_request_author", "verdict"} + if not isinstance(payload, dict) or set(payload) != keys: + raise ValueError("prepared envelope does not match compatible producer schema") + if type(payload["schema_version"]) is not int or payload["schema_version"] != 1: + raise ValueError("prepared envelope schema version is invalid") + bound = {"repository": "repository", "number": "pull_request_number", + "head": "expected_head", "base": "expected_base", + "author": "pull_request_author", "reviewer": "reviewer_actor"} + if any(payload[key] != scope[field] for field, key in bound.items()): + raise ValueError("prepared envelope identity differs from admitted scope") + verdict = payload["verdict"] + if not isinstance(verdict, dict) or verdict.get("decision") not in {"approve", "request_changes", "comment"}: + raise ValueError("prepared envelope is missing a model verdict") + + +def require_phase_environment(phase: str, environment: Mapping[str, str]) -> None: + """Reject loader overrides and inference capability in publisher children. + + Metadata alone cannot distinguish read/write GitHub tokens; exact token + scope must still be verified by the trusted runtime admission adapter. + Never include values in refusal diagnostics. + """ + forbidden = {"PYTHONPATH", "PYTHONHOME", "LD_PRELOAD", "LD_LIBRARY_PATH", + "DYLD_INSERT_LIBRARIES", "BASH_ENV", "ENV"} + if any(key in environment for key in forbidden): + raise ValueError("phase environment contains executable-loader overrides") + if phase == "publish": + if any(key in environment for key in ("NOEMA_LLM_API_KEY", "LLM_GATEWAY_API_KEY")): + raise ValueError("phase environment publisher contains inference capability") + elif phase == "prepare": + if environment.get("NOEMA_LLM_MODEL") != "auto": + raise ValueError("phase environment must request literal auto") + else: + raise ValueError("phase environment has an unsupported phase") + + +def accepted_prepare_transport(context): + """Validate only independently pinned workflow/document transport capabilities. + + The enclosing execution admission digest binds these fields, not ambient + environment values. Runtime admission still owns their namespace and supplier. + """ + if context is None: + return {} + if not isinstance(context, dict) or not context or set(context) - { + 'GITHUB_OUTPUT', 'NOEMA_TRANSPORT_RETRY_ATTEMPT', + 'NOEMA_HWP_MCP_SOURCE', 'NOEMA_NODE_EXECUTABLE', 'node_sha256', 'hwp_files', + }: + raise ValueError('prepare transport has unsupported fields') + for field in ('GITHUB_OUTPUT', 'NOEMA_HWP_MCP_SOURCE'): + if field in context: + value = context[field] + if (not isinstance(value, str) or value != value.strip() + or any(ord(character) < 32 or ord(character) == 127 for character in value)): + raise ValueError('prepare transport path differs from consumer identity') + environment = {} + if 'GITHUB_OUTPUT' in context or 'NOEMA_TRANSPORT_RETRY_ATTEMPT' in context: + attempt = context.get('NOEMA_TRANSPORT_RETRY_ATTEMPT') + if not isinstance(attempt, str) or not re.fullmatch(r'[0-2]', attempt): + raise ValueError('prepare retry context is outside the existing bound') + output = context.get('GITHUB_OUTPUT') + if not isinstance(output, str) or not Path(output).is_absolute(): + raise ValueError('prepare output requires an accepted absolute path') + info = Path(output).lstat() + if not stat.S_ISREG(info.st_mode) or info.st_nlink != 1 or info.st_mode & 0o077: + raise ValueError('prepare output is not private regular custody') + environment.update(GITHUB_OUTPUT=output, NOEMA_TRANSPORT_RETRY_ATTEMPT=attempt) + document_keys = {'NOEMA_HWP_MCP_SOURCE', 'NOEMA_NODE_EXECUTABLE', 'node_sha256', 'hwp_files'} + if document_keys & set(context): + if not document_keys <= set(context): + raise ValueError('prepare document transport is incomplete') + source, node = context['NOEMA_HWP_MCP_SOURCE'], context['NOEMA_NODE_EXECUTABLE'] + if not all(isinstance(value, str) and Path(value).is_absolute() for value in (source, node)): + raise ValueError('prepare document supplier paths are not absolute') + root, executable = Path(source), Path(node) + if root.is_symlink() or not root.is_dir() or executable.is_symlink(): + raise ValueError('prepare document supplier is not regular custody') + info = executable.stat() + if not stat.S_ISREG(info.st_mode) or not info.st_mode & 0o111: + raise ValueError('prepare Node supplier is not executable') + digest = context['node_sha256'] + if not isinstance(digest, str) or not re.fullmatch(r'[0-9a-f]{64}', digest): + raise ValueError('prepare Node digest is missing') + if hashlib.sha256(executable.read_bytes()).hexdigest() != digest: + raise ValueError('prepare Node supplier bytes changed') + inventory = context['hwp_files'] + if not isinstance(inventory, dict) or not inventory or any( + not _safe_path(name) or not isinstance(value, str) or not re.fullmatch(r'[0-9a-f]{64}', value) + for name, value in inventory.items() + ): + raise ValueError('prepare HWP inventory is malformed') + actual = {} + for path in root.rglob('*'): + if path.is_symlink() or not (path.is_dir() or path.is_file()): + raise ValueError('prepare HWP supplier has unsupported custody') + if path.is_file(): + actual[path.relative_to(root).as_posix()] = hashlib.sha256(path.read_bytes()).hexdigest() + if actual != inventory: + raise ValueError('prepare HWP supplier bytes changed') + environment.update(NOEMA_HWP_MCP_SOURCE=source, NOEMA_NODE_EXECUTABLE=node) + return environment + + +def phase_environment_from_issuance( + phase, response, *, scope, reviewer_actor, installation_id, inference_key=None, + prepare_transport=None, +): + """Consume a trusted installation-token issuance response without logging it. + + The response must come directly from the existing authenticated App issuer, + not PR data or an artifact. The controller does not mint a token, read a + secret, or establish issuer authenticity by validating this dictionary. + """ + expected_permissions = {"contents": "read", "metadata": "read", + "pull_requests": "read" if phase == "prepare" else "write"} + if phase not in {"prepare", "publish"} or not isinstance(response, dict): + raise ValueError("credential issuance has unsupported phase or shape") + if response.get("permissions") != expected_permissions: + raise ValueError("credential issuance permissions do not match the phase") + repositories = response.get("repositories") + if not isinstance(repositories, list) or len(repositories) != 1: + raise ValueError("credential issuance must be single-repository scoped") + if not isinstance(repositories[0], dict) or repositories[0].get("full_name") != scope["repository"]: + raise ValueError("credential issuance repository does not match the review") + token = response.get("token") + if not isinstance(token, str) or not token.strip(): + raise ValueError("credential issuance returned no usable token") + if reviewer_actor != scope["reviewer"] or not re.fullmatch(r"[a-z0-9][a-z0-9-]*\[bot\]", reviewer_actor): + raise ValueError("credential issuance reviewer actor is not the admitted independent App") + if type(installation_id) is not int or installation_id < 1: + raise ValueError("credential issuance installation identity is invalid") + environment = {"PATH": "/usr/bin:/bin", "GH_TOKEN": token, + "NOEMA_REVIEW_ACTOR": reviewer_actor, + "NOEMA_REVIEW_INSTALLATION_ID": str(installation_id), + "NOEMA_REVIEW_TOKEN_SOURCE": "noema-review-github-app" if phase == "prepare" else "noema-review-github-app-refresh"} + if phase == "prepare": + if not isinstance(inference_key, str) or not inference_key.strip(): + raise ValueError("credential issuance preparation requires existing inference authority") + environment.update(NOEMA_LLM_MODEL="auto", NOEMA_LLM_API_KEY=inference_key, + NOEMA_LLM_API_URL="https://litellm.poinnetworks.net/v1/chat/completions") + environment.update(accepted_prepare_transport(prepare_transport)) + return environment + + +class _NoemaTooling: + """Execute one compatible producer/publisher from a sealed tooling namespace. + + The trusted caller owns the namespace exclusively through both phases and + supplies phase-scoped environments, including separately attested read or + write authority. This adapter does not authenticate an external envelope. + Interpreter and installed third-party dependencies need independent runtime + admission; the manifest binds repository files only. + """ + + def __init__(self, root, raw, accepted_digest, interpreter, phase_environment, child_runner): + """Retain explicit controller dependencies without loading candidate code.""" + self.root = Path(root) + self.raw = raw + self.accepted_digest = accepted_digest + self.interpreter = Path(interpreter) + self.phase_environment = phase_environment + self.child_runner = child_runner + + def _verify(self): + """Re-read complete regular source files before each child invocation.""" + if not self.root.is_absolute() or self.root.is_symlink() or not self.root.is_dir(): + raise ValueError("source inventory root is not an absolute owned directory") + files = {} + for path in self.root.rglob("*"): + if path.is_symlink(): + raise ValueError("source inventory contains a symbolic link") + if path.is_file(): + files[path.relative_to(self.root).as_posix()] = path.read_bytes() + elif not path.is_dir(): + raise ValueError("source inventory contains an unsupported file kind") + admit_tooling(self.raw, accepted_digest=self.accepted_digest, source_files=files) + manifest = json.loads(self.raw) + if manifest["entrypoint"] != ".github/actions/noema-review/two_phase.py": + raise ValueError("source inventory entrypoint is not the compatible Noema tooling") + return self.root / manifest["entrypoint"] + + def _run(self, phase, repo, number, head, path): + """Run a fresh isolated-import child with explicit phase environment only.""" + entrypoint = self._verify() + if not self.interpreter.is_absolute() or not self.interpreter.is_file(): + raise ValueError("trusted interpreter must be an absolute executable file") + environment = self.phase_environment(phase) + require_phase_environment(phase, environment) + # Credential acquisition can yield to a trusted issuer; bind source + # again afterwards so those credentials never reach stale tooling. + entrypoint = self._verify() + command = [str(self.interpreter), "-I", "-B", str(entrypoint), + "--repo", repo, "--pr-number", str(number), "--expected-head", head, + f"--{phase}-verdict-file", str(path)] + result = self.child_runner(command, cwd=self.root, env=environment, check=False) + return result.returncode + + def prepare_verdict(self, repo, number, head, path): + """Consume PR bytes as data through the admitted model producer.""" + return self._run("prepare", repo, number, head, path) + + def publish_verdict(self, repo, number, head, path): + """Publish only through the same compatible trusted tooling source.""" + return self._run("publish", repo, number, head, path) + + +def load_noema_tooling( + root, raw_manifest, *, accepted_digest, interpreter, phase_environment, + child_runner=subprocess.run, +): + """Bind the real two-phase CLI without importing caller-selected modules.""" + tooling = _NoemaTooling(root, raw_manifest, accepted_digest, interpreter, + phase_environment, child_runner) + tooling._verify() + return tooling + + +def run_review( + raw_manifest, *, accepted_digest, source_files, scope, fetch_live, + require_execution_admission, load_tooling, refresh_publisher, private_parent, +): + """Admit source and current scope before invoking trusted runtime collaborators. + + Callbacks are trusted controller dependencies, never PR payload. Their + authority, isolation, supplier policy and source binding require separate + operational acceptance; these checks do not manufacture such authority. + """ + admit_tooling(raw_manifest, accepted_digest=accepted_digest, source_files=source_files) + require_review_scope(scope, fetch_live()) + require_execution_admission() + tooling = load_tooling() + with tempfile.TemporaryDirectory(prefix="premerge-review-", dir=private_parent) as directory: + envelope = Path(directory) / "verdict.json" + result = tooling.prepare_verdict(scope["repository"], scope["number"], scope["head"], envelope) + if result != 0 or not envelope.is_file(): + raise RuntimeError("model preparation did not produce a verdict") + require_prepared_envelope(envelope, scope) + require_review_scope(scope, fetch_live()) + require_execution_admission() + refresh_publisher() + require_review_scope(scope, fetch_live()) + require_prepared_envelope(envelope, scope) + return tooling.publish_verdict(scope["repository"], scope["number"], scope["head"], envelope) + + +def production_main(argv=None): + """Validate independently pinned custody inputs and live scope before setup. + + Files must be supplied by the existing custody owner outside PR data. This + validates supplied evidence, not its authority: independent acceptance of + the digest is an explicit operational prerequisite, never generated here. + """ + import argparse + import sys + + parser = argparse.ArgumentParser(description=__doc__) + modes = parser.add_mutually_exclusive_group(required=True) + modes.add_argument('--admit-production', action='store_true') + modes.add_argument('--run-production', action='store_true') + modes.add_argument('--prepare-production', action='store_true') + modes.add_argument('--publish-production', action='store_true') + parser.add_argument('--phase-issuance', type=Path) + parser.add_argument('--verdict-file', type=Path) + parser.add_argument('--prepare-issuance', type=Path) + parser.add_argument('--publish-issuance', type=Path) + parser.add_argument('--private-parent', type=Path) + parser.add_argument('--tooling-root', type=Path, required=True) + parser.add_argument('--manifest', type=Path, required=True) + parser.add_argument('--accepted-digest', required=True) + parser.add_argument('--admission', type=Path, required=True) + parser.add_argument('--admission-digest', required=True) + parser.add_argument('--repo', required=True) + parser.add_argument('--pr-number', type=int, required=True) + parser.add_argument('--expected-head', required=True) + args = parser.parse_args(argv) + try: + if not all(re.fullmatch(r'[0-9a-f]{64}', value) for value in + (args.accepted_digest, args.admission_digest)): + raise ValueError('independent admission pins are missing or malformed') + raw = args.manifest.read_bytes() + tooling = _NoemaTooling(args.tooling_root, raw, args.accepted_digest, + Path(sys.executable), None, None) + tooling._verify() + admission_raw = args.admission.read_bytes() + if hashlib.sha256(admission_raw).hexdigest() != args.admission_digest: + raise ValueError('execution admission differs from independent pin') + admission = json.loads(admission_raw, object_pairs_hook=_unique_object) + scope = admission['scope'] + if scope['repository'] != args.repo or scope['number'] != args.pr_number or scope['head'] != args.expected_head: + raise ValueError('execution admission target differs from request') + for field in ('isolated_clean_job', 'native_custody', 'supplier_cost', 'supplier_retention', 'issuer_phase_separation'): + if admission.get(field) is not True: + raise ValueError('execution admission required evidence is not accepted') + fetched = subprocess.run(['gh', 'api', f'repos/{args.repo}/pulls/{args.pr_number}'], + check=True, capture_output=True, text=True, timeout=60) + pr = json.loads(fetched.stdout) + live = {'repository': pr['base']['repo']['full_name'], 'number': pr['number'], + 'head': pr['head']['sha'], 'base': pr['base']['sha'], + 'author': pr['user']['login'], 'reviewer': scope['reviewer'], + 'state': str(pr['state']).upper(), 'draft': pr['draft']} + require_review_scope(scope, live) + # Live metadata retrieval may yield while custody is revoked. Refuse + # stale admission before either phase obtains credentials or executes. + if args.admission.read_bytes() != admission_raw or args.manifest.read_bytes() != raw: + raise ValueError('independent custody changed during live scope retrieval') + if args.prepare_production or args.publish_production: + phase = 'prepare' if args.prepare_production else 'publish' + if args.phase_issuance is None or args.verdict_file is None: + raise ValueError('phase issuer output and private verdict custody required') + info = args.phase_issuance.lstat() + if not stat.S_ISREG(info.st_mode) or info.st_nlink != 1 or info.st_mode & 0o077: + raise ValueError('issuer output custody is not private') + response = json.loads(args.phase_issuance.read_bytes(), object_pairs_hook=_unique_object) + environment = phase_environment_from_issuance( + phase, response, scope=scope, reviewer_actor=scope['reviewer'], + installation_id=admission['installation_id'], + inference_key=os.environ.get('LLM_GATEWAY_API_KEY') if phase == 'prepare' else None, + prepare_transport=admission.get('prepare_transport') if phase == 'prepare' else None) + phase_tooling = load_noema_tooling(args.tooling_root, raw, + accepted_digest=args.accepted_digest, interpreter=Path(sys.executable), + phase_environment=lambda requested: environment if requested == phase else {}) + if phase == 'publish': + require_prepared_envelope(args.verdict_file, scope) + status = getattr(phase_tooling, phase + '_verdict')( + args.repo, args.pr_number, args.expected_head, args.verdict_file) + if phase == 'prepare' and status == 0 and args.verdict_file.exists(): + require_prepared_envelope(args.verdict_file, scope) + return status + if args.run_production: + if not args.prepare_issuance or not args.publish_issuance or not args.private_parent: + raise ValueError('phase issuance and private custody are required') + environments = {} + + def phase_environment(phase): + """Expose only the admitted credential for the selected phase.""" + return environments[phase] + + def read_issuance(path, phase): + """Consume private issuer output without logging credential bytes.""" + info = path.lstat() + if not stat.S_ISREG(info.st_mode) or info.st_nlink != 1 or info.st_mode & 0o077: + raise ValueError('issuer output is not private regular custody') + response = json.loads(path.read_bytes(), object_pairs_hook=_unique_object) + return phase_environment_from_issuance( + phase, response, scope=scope, reviewer_actor=scope['reviewer'], + installation_id=admission['installation_id'], + inference_key=os.environ.get('LLM_GATEWAY_API_KEY') if phase == 'prepare' else None, + prepare_transport=admission.get('prepare_transport') if phase == 'prepare' else None, + ) + + def revalidate_admission(): + """Refuse evidence or source drift at every controller boundary.""" + if args.admission.read_bytes() != admission_raw or args.manifest.read_bytes() != raw: + raise ValueError('independent custody changed after admission') + tooling._verify() + + def fetch_live(): + """Re-fetch current scope before each admitted phase.""" + current = subprocess.run(['gh', 'api', f'repos/{args.repo}/pulls/{args.pr_number}'], + check=True, capture_output=True, text=True, timeout=60) + document = json.loads(current.stdout) + return {'repository': document['base']['repo']['full_name'], 'number': document['number'], + 'head': document['head']['sha'], 'base': document['base']['sha'], + 'author': document['user']['login'], 'reviewer': scope['reviewer'], + 'state': str(document['state']).upper(), 'draft': document['draft']} + + def refresh_publisher(): + """Consume separately issued publication authority only after model completion.""" + environments['publish'] = read_issuance(args.publish_issuance, 'publish') + + environments['prepare'] = read_issuance(args.prepare_issuance, 'prepare') + files = {name: (args.tooling_root / name).read_bytes() for name in json.loads(raw)['files']} + return run_review(raw, accepted_digest=args.accepted_digest, source_files=files, + scope=scope, fetch_live=fetch_live, + require_execution_admission=revalidate_admission, + load_tooling=lambda: load_noema_tooling(args.tooling_root, raw, + accepted_digest=args.accepted_digest, interpreter=Path(sys.executable), + phase_environment=phase_environment), + refresh_publisher=refresh_publisher, private_parent=args.private_parent) + return 0 + except (OSError, ValueError, KeyError, TypeError, subprocess.SubprocessError): + # No credential values, response bodies or exception payloads in logs. + print('premerge admission refused: independent admission pins or custody/scope evidence unavailable', file=sys.stderr) + return 1 + + +if __name__ == '__main__': + raise SystemExit(production_main()) diff --git a/scripts/ci/prescreen_release_runtime_archives.py b/scripts/ci/prescreen_release_runtime_archives.py new file mode 100644 index 0000000000..7211b2acf8 --- /dev/null +++ b/scripts/ci/prescreen_release_runtime_archives.py @@ -0,0 +1,418 @@ +#!/usr/bin/env python3 +"""Prescreen exact transported runtime wheels before Strix credentials exist.""" + +from __future__ import annotations + +import argparse +import email.parser +import hashlib +import io +import json +import re +import subprocess +import sys +import zipfile +from pathlib import Path, PurePosixPath +from typing import Any, Mapping + +try: + from scripts.ci import release_dependency_gate as gate + from scripts.ci.scan_release_native_links import NATIVE_MAGIC, TARGET_ARCHES, _links, _reader + from scripts.ci.verify_release_distribution_set import _json_bytes, DistributionSetError + from scripts.ci.verify_release_scope_evidence_set import _runtime_target_architecture +except ImportError: # pragma: no cover - trusted direct `python3 -I` invocation + sys.path.insert(0, str(Path(__file__).resolve().parent)) + import release_dependency_gate as gate + from scan_release_native_links import NATIVE_MAGIC, TARGET_ARCHES, _links, _reader + from verify_release_distribution_set import _json_bytes, DistributionSetError + from verify_release_scope_evidence_set import _runtime_target_architecture + + +def _native_wheel_libraries( + raw: bytes, + target: str, + *, + required_architecture: str | None = None, +) -> list[dict[str, Any]]: + """Inspect native members and require a runtime architecture when supplied.""" + libraries = [] + reader = None + with zipfile.ZipFile(io.BytesIO(raw)) as archive: + for entry in archive.infolist(): + if entry.is_dir(): + continue + with archive.open(entry) as stream: + magic = stream.read(8) + name = entry.filename.lower() + if not (magic.startswith(NATIVE_MAGIC) or name.endswith( + (".so", ".pyd", ".dll", ".dylib", ".a", ".lib", ".exe", ".wasm"))): + continue + if magic.startswith((b"!\n", b"\x00asm")) or name.endswith((".a", ".lib", ".wasm")): + raise gate.GateError(gate.NATIVE_LINK_UNKNOWN, f"{entry.filename}: static or wasm native member needs separate review") + if entry.file_size > 128 * 1024 * 1024: + raise gate.GateError(gate.CAPTURE_INCOMPLETE, f"{entry.filename}: native member exceeds inspection limit") + binary = archive.read(entry) + try: + reader = reader or _reader()["path"] + links = _links(binary, target, reader, allow_subset=True) + except (ValueError, OSError, subprocess.SubprocessError) as error: + raise gate.GateError(gate.NATIVE_LINK_UNKNOWN, f"{entry.filename}: native links could not be inspected") from error + link_architectures = {row["arch"] for row in links} + if (required_architecture is not None + and required_architecture not in link_architectures): + raise gate.GateError( + gate.NATIVE_LINK_UNKNOWN, + f"{entry.filename}: runtime variant requires {required_architecture} architecture", + ) + libraries.append({"path": entry.filename, + "needed": sorted({name for row in links for name in row["needed"]}), + "static_archives": []}) + return libraries + + +def _build_packages(item: Mapping[str, Any], folder: Path) -> list[dict[str, Any]]: + """Review the exact installed files recorded by one build interpreter.""" + leg = item["leg"] + receipt = _json_bytes((folder / f"{leg}.build-first.json").read_bytes()) + if not isinstance(receipt, Mapping) or receipt.get("leg") != leg: + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, f"{leg}: build receipt is malformed") + snapshot = folder / f"{leg}.build-python.zip" + snapshot_bytes = gate.read_archive_snapshot(snapshot) + raw_sha = hashlib.sha256(snapshot_bytes).hexdigest() + if receipt.get("python_snapshot_sha256") != raw_sha or item.get("members", {}).get(snapshot.name) != raw_sha: + raise gate.GateError(gate.SOURCE_HASH_MISMATCH, f"{leg}: build snapshot changed after transport") + packages = receipt.get("python_packages") + if not isinstance(packages, list) or not packages: + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, f"{leg}: build packages are missing") + target = "x86_64-unknown-linux-gnu" if leg == "sdist" else leg.rsplit("-py", 1)[0] + if target == "universal2-apple-darwin": + build_env = receipt.get("build_env") + architecture = {"ARM64": "aarch64", "X64": "x86_64"}.get( + build_env.rsplit("/", 1)[-1] if isinstance(build_env, str) else "") + if architecture is None: + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, f"{leg}: build interpreter architecture is missing") + else: + architecture = next(iter(TARGET_ARCHES[target])) + native_rows = _native_wheel_libraries(snapshot_bytes, target, required_architecture=architecture) + listed_native = {f"{package['name']}/{file['path']}" for package in packages + for file in package["files"]} + if any(row["path"] not in listed_native for row in native_rows): + raise gate.GateError(gate.SCOPE_SET_MISMATCH, f"{leg}: unlisted native build file") + result = [] + with zipfile.ZipFile(snapshot) as archive: + members = {entry.filename: entry for entry in archive.infolist()} + for package in packages: + name, version = package["name"], package["version"] + files = package["files"] + metadata = [file["path"] for file in files if file["path"].endswith(".dist-info/METADATA")] + if len(metadata) != 1: + raise gate.GateError(gate.CAPTURE_INCOMPLETE, f"{leg}: {name} metadata is ambiguous") + metadata_root = PurePosixPath(metadata[0]).parent + def read_file(path: str) -> bytes: + """Read one bounded package file from the captured build snapshot.""" + + entry = members.get(f"{name}/{path}") + if entry is None or entry.file_size > 4 * 1024 * 1024: + raise gate.GateError(gate.CAPTURE_INCOMPLETE, f"{leg}: {name} text file is missing or oversized") + with archive.open(entry) as stream: + return stream.read(4 * 1024 * 1024 + 1) + try: + message = email.parser.BytesParser().parsebytes(read_file(metadata[0])) + except (UnicodeError, ValueError) as error: + raise gate.GateError(gate.CAPTURE_INCOMPLETE, f"{leg}: {name} metadata is unreadable") from error + names, versions = message.get_all("Name", []), message.get_all("Version", []) + if (len(names) != 1 or len(versions) != 1 + or gate.normalize_project_name(names[0]) != name + or versions[0] != version): + raise gate.GateError(gate.CAPTURE_INCOMPLETE, f"{leg}: {name} metadata differs from installed identity") + candidates = {file["path"] for file in files if PurePosixPath(file["path"]).name.upper().startswith( + ("LICENSE", "LICENCE", "COPYING", "NOTICE", "UNLICENSE"))} + for declared in message.get_all("License-File", []): + path = PurePosixPath(declared) + if path.is_absolute() or ".." in path.parts or "\\" in declared: + raise gate.GateError(gate.ARCHIVE_PATH_ESCAPE, f"{leg}: {name} license path is unsafe") + matches = {str(metadata_root / path), str(metadata_root / "licenses" / path)} + present = matches & {file["path"] for file in files} + if not present: + raise gate.GateError(gate.CAPTURE_INCOMPLETE, f"{leg}: {name} declared license is missing") + candidates.update(present) + texts = {} + hashes = {} + total = 0 + for path in sorted(candidates): + data = read_file(path) + total += len(data) + if total > 16 * 1024 * 1024: + raise gate.GateError(gate.CAPTURE_INCOMPLETE, f"{leg}: {name} license text set is oversized") + try: + texts[path] = data.decode("utf-8") + except UnicodeError as error: + raise gate.GateError(gate.CAPTURE_INCOMPLETE, f"{leg}: {name} license text is undecodable") from error + hashes[path] = hashlib.sha256(data).hexdigest() + source_sha = hashlib.sha256(gate.canonical_json({"name": name, "version": version, + "files": files})).hexdigest() + key = f"pypi/{name}@{version}" + evidence = {"source_sha256": source_sha, + "license_expression": message.get("License-Expression", ""), + "license": message.get("License", ""), + "classifiers": message.get_all("Classifier", []), + "license_texts": texts, "license_member_sha256": hashes, + "archive_members": [{"type": "file", "name": file["path"], "linkname": ""} + for file in files], + "install_hook_sources": {}, + "parsed_inputs": [file["path"] for file in files if file["path"].endswith(".py")], + "native_libraries": [{**row, "path": row["path"].removeprefix(f"{name}/")} + for row in native_rows if row["path"].startswith(f"{name}/")], + "known_vulnerabilities": []} + failures, decision, source = gate.evaluate_dependency_license(evidence, key, None) + if failures: + raise gate.GateError(failures[0].code, f"{leg}: {key}: {failures[0].detail}") + native_failures, native_properties = gate.evaluate_native_links( + evidence, key, target=target, leg=leg) + if native_failures: + raise gate.GateError(native_failures[0].code, f"{leg}: {key}: {native_failures[0].detail}") + fixture_key = f"{key}/sha256/{source_sha}" + fixture = gate.build_fixture(gate.Dependency("pypi", name, version), evidence) + fixture["id"] = fixture_key + result.append({"key": fixture_key, "package_key": key, "name": name, + "version": version, "source_sha256": source_sha, + "license": decision.selected, "license_source": source, + "license_member_sha256": hashes, "fixture": fixture, + "native_properties": native_properties, + "fixture_sha256": gate.fixture_digest(fixture), + "legs": [leg], "snapshots": {leg: raw_sha}}) + return result + + +def _maturin_tool(item: Mapping[str, Any], folder: Path) -> dict[str, Any]: + """Bind the actual build executable to reviewed v1.15.0 release assets.""" + leg = item["leg"] + receipt_bytes = (folder / f"{leg}.build-first.json").read_bytes() + second_bytes = (folder / f"{leg}.build-second.json").read_bytes() + members = item.get("members", {}) + if (not isinstance(members, Mapping) + or any(members.get(f"{leg}.build-{name}.json") != hashlib.sha256(raw).hexdigest() + for name, raw in (("first", receipt_bytes), ("second", second_bytes)))): + raise gate.GateError(gate.SOURCE_HASH_MISMATCH, f"{leg}: maturin receipts changed after transport") + receipt = _json_bytes(receipt_bytes) + second = _json_bytes(second_bytes) + data = _json_bytes(Path(__file__).with_name("release_maturin_tool_evidence.json").read_bytes()) + if (not isinstance(data, Mapping) + or data.get("source_repository") != "PyO3/maturin" + or data.get("tag") != "v1.15.0" + or not isinstance(data.get("tag_commit"), str) + or re.fullmatch(r"[0-9a-f]{40}", data["tag_commit"]) is None + or not isinstance(data.get("source_archive_sha256"), str) + or re.fullmatch(r"[0-9a-f]{64}", data["source_archive_sha256"]) is None): + raise gate.GateError( + gate.SOURCE_HASH_MISMATCH, "maturin source provenance is malformed" + ) + target = "x86_64-unknown-linux-gnu" if leg == "sdist" else leg.rsplit("-py", 1)[0] + build_env = receipt.get("build_env") if isinstance(receipt, Mapping) else None + if not isinstance(build_env, str): + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, f"{leg}: maturin build environment is missing") + if target == "universal2-apple-darwin": + key = f"{target}/{build_env.rsplit('/', 1)[-1]}" + valid_env = build_env.startswith("runner:") + elif leg == "sdist": + key = target + valid_env = build_env.startswith("runner:") and build_env.endswith("/X64") + elif target == "x86_64-pc-windows-msvc": + key = target + valid_env = build_env.startswith("runner:") and build_env.endswith("/X64") + else: + key = target + valid_env = build_env.startswith("container:") + asset = data.get("assets", {}).get(key) if isinstance(data, Mapping) else None + if (not valid_env or not isinstance(asset, Mapping) or not isinstance(second, Mapping) + or data.get("schema") != "cwl.release-maturin-tool/1" + or data.get("version") != "1.15.0" + or receipt.get("maturin_version") != "maturin 1.15.0" + or receipt.get("maturin_binary_sha256") != asset.get("binary_sha256") + or any(second.get(field) != receipt.get(field) for field in + ("build_env", "maturin_version", "maturin_binary_sha256")) + or not isinstance(asset.get("asset_sha256"), str) + or not re.fullmatch(r"[0-9a-f]{64}", asset["asset_sha256"])): + raise gate.GateError(gate.SOURCE_HASH_MISMATCH, f"{leg}: maturin executable differs from reviewed asset") + link_rows = asset.get("native_links") + if (not isinstance(link_rows, list) or len(link_rows) != 1 + or not isinstance(link_rows[0], Mapping) + or not isinstance(link_rows[0].get("needed"), list)): + raise gate.GateError(gate.NATIVE_LINK_UNKNOWN, f"{leg}: maturin native links are missing") + sha = asset["binary_sha256"] + texts = data["license_texts"] + evidence = {"source_sha256": sha, "license_expression": data["license_expression"], + "license_texts": texts, + "license_member_sha256": {name: hashlib.sha256(text.encode()).hexdigest() + for name, text in texts.items()}, + "archive_members": [{"type": "file", "name": "maturin", "linkname": ""}], + "install_hook_sources": {}, "parsed_inputs": [], + "native_libraries": [{"path": "maturin", "needed": link_rows[0]["needed"], + "static_archives": []}], "known_vulnerabilities": []} + package_key = "github-release/maturin@1.15.0" + failures, decision, source = gate.evaluate_dependency_license( + evidence, package_key, + {"chosen": data["license_choice"], "rationale": data["license_rationale"]}, + ) + if failures: + raise gate.GateError(failures[0].code, f"{leg}: maturin licence: {failures[0].detail}") + native_failures, native_properties = gate.evaluate_native_links( + evidence, package_key, target=target, leg=leg) + if native_failures: + raise gate.GateError(native_failures[0].code, + f"{leg}: maturin native links: {native_failures[0].detail}") + fixture_key = f"{package_key}/sha256/{sha}" + fixture = gate.build_fixture(gate.Dependency("github-release", "maturin", "1.15.0"), evidence) + fixture["id"] = fixture_key + return {"key": fixture_key, "package_key": package_key, "name": "maturin", + "version": "1.15.0", "source_sha256": sha, + "license": decision.selected, "license_source": source, + "license_member_sha256": evidence["license_member_sha256"], + "native_properties": native_properties, + "fixture": fixture, "fixture_sha256": gate.fixture_digest(fixture), + "source_tag_commit": data["tag_commit"], + "source_archive_sha256": data["source_archive_sha256"], + "asset_archive_sha256": asset["asset_sha256"], + "legs": [leg], "build_envs": {leg: build_env}} + + +def prescreen(scope: Any, root: Path) -> dict[str, list[dict[str, Any]]]: + """Rebind every wheel byte and apply the existing licence decision path.""" + variants = scope.get("verified_runtime_variants") if isinstance(scope, Mapping) else None + if (not isinstance(scope, Mapping) + or not isinstance(scope.get("verified_scope_evidence"), list) + or len(scope["verified_scope_evidence"]) != 13 + or not isinstance(variants, list) or len(variants) != 3): + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, "verified scope evidence is incomplete") + rows: dict[tuple[str, str], dict[str, Any]] = {} + build_rows: dict[str, dict[str, Any]] = {} + tool_rows: dict[str, dict[str, Any]] = {} + seen_legs: set[str] = set() + seen_variants: set[str] = set() + for index, item in enumerate([*scope["verified_scope_evidence"], *variants]): + variant = index >= 13 + if (not isinstance(item, Mapping) or not isinstance(item.get("leg"), str) + or not re.fullmatch(r"[A-Za-z0-9_.+-]+", item["leg"]) + or item["leg"] in {".", ".."} + or (item["leg"] in (seen_variants if variant else seen_legs)) + or item.get("artifact_name") != ( + f"repro-macos-x86-{item['leg']}" if variant else f"repro-digest-{item['leg']}") + or variant and (item.get("arch") != "x86_64" + or not item["leg"].startswith("universal2-apple-darwin-py")) + or not isinstance(item.get("archives"), list)): + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, "scope evidence row is malformed") + leg = item["leg"] + if leg != "sdist" and leg.rpartition("-py")[0] not in TARGET_ARCHES: + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, "runtime archive coverage is incomplete") + runtime_architecture = None + if leg != "sdist": + runtime_name = f"{leg}.runtime.json" + runtime_path = gate._require_regular_file(root / item["artifact_name"] / runtime_name, + gate.SCOPE_UNVERIFIABLE) + if runtime_path.stat().st_size > 1024 * 1024: + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, f"{leg}: runtime receipt is oversized") + runtime_bytes = runtime_path.read_bytes() + if item.get("members", {}).get(runtime_name) != hashlib.sha256(runtime_bytes).hexdigest(): + raise gate.GateError(gate.SOURCE_HASH_MISMATCH, f"{leg}: runtime receipt changed after transport") + try: + runtime_architecture = _runtime_target_architecture(_json_bytes(runtime_bytes), leg, intel=variant) + except DistributionSetError as error: + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, str(error)) from error + if variant: + seen_variants.add(leg) + else: + seen_legs.add(leg) + for package in _build_packages(item, root / item["artifact_name"]): + if package["key"] in build_rows: + build_rows[package["key"]]["legs"].append(leg) + build_rows[package["key"]]["snapshots"][leg] = package["snapshots"][leg] + else: + build_rows[package["key"]] = package + tool = _maturin_tool(item, root / item["artifact_name"]) + if tool["key"] in tool_rows: + tool_rows[tool["key"]]["legs"].append(leg) + tool_rows[tool["key"]]["build_envs"][leg] = tool["build_envs"][leg] + else: + tool_rows[tool["key"]] = tool + if (leg == "sdist" and item["archives"] + or leg != "sdist" and not item["archives"]): + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, f"{leg}: runtime archive set is incomplete") + for archive in item["archives"]: + if (not isinstance(archive, Mapping) + or set(archive) != {"file", "size", "sha256", "name", "version"} + or not isinstance(archive["file"], str) + or not re.fullmatch(r"[A-Za-z0-9_.+-]+\.whl", archive["file"]) + or not isinstance(archive["name"], str) + or not isinstance(archive["version"], str) + or not isinstance(archive["sha256"], str) + or not re.fullmatch(r"[0-9a-f]{64}", archive["sha256"]) + or type(archive["size"]) is not int or archive["size"] <= 0): + raise gate.GateError(gate.SCOPE_UNVERIFIABLE, f"{leg}: archive identity is malformed") + path = root / item["artifact_name"] / archive["file"] + raw = gate.read_archive_snapshot(path) + sha = hashlib.sha256(raw).hexdigest() + if sha != archive["sha256"] or len(raw) != archive["size"]: + raise gate.GateError(gate.SOURCE_HASH_MISMATCH, f"{leg}: archive bytes changed after transport") + key = f"pypi/{archive['name']}@{archive['version']}" + identity = (key, sha) + bound = gate.archive_license_evidence(raw, "pypi") + if bound["source_sha256"] != sha: + raise gate.GateError(gate.SOURCE_HASH_MISMATCH, f"{key}: licence evidence changed") + native_libraries = _native_wheel_libraries( + raw, leg.rsplit("-py", 1)[0], required_architecture=runtime_architecture, + ) + if identity in rows: + if leg not in rows[identity]["legs"]: + rows[identity]["legs"].append(leg) + continue + declared = gate.distribution_declared_metadata(path, archive["name"], archive["version"]) + member_names = [member["name"] for member in bound["archive_members"] + if member["type"] == "file"] + evidence = {**declared, **bound, + "install_hook_sources": {name: "" for name in member_names + if name.endswith(("/setup.py", "/build.rs"))}, + "parsed_inputs": [name for name in member_names if name.endswith(".py")], + "native_libraries": native_libraries, + "known_vulnerabilities": []} + failures, decision, source = gate.evaluate_dependency_license( + evidence, key, None, + ) + if failures: + raise gate.GateError(failures[0].code, f"{key}: {failures[0].detail}") + native_failures, native_properties = gate.evaluate_native_links( + evidence, key, target=leg.rsplit("-py", 1)[0], leg=leg) + if native_failures: + raise gate.GateError(native_failures[0].code, f"{key}: {native_failures[0].detail}") + fixture_key = f"{key}/sha256/{sha}" + fixture = gate.build_fixture(gate.Dependency("pypi", archive["name"], archive["version"]), evidence) + fixture["id"] = fixture_key + rows[identity] = {"key": fixture_key, "package_key": key, "name": archive["name"], + "version": archive["version"], "source_sha256": sha, + "license": decision.selected, "license_source": source, + "license_member_sha256": bound["license_member_sha256"], + "native_properties": native_properties, + "fixture": fixture, "fixture_sha256": gate.fixture_digest(fixture), + "legs": [leg]} + return {"archives": sorted(rows.values(), key=lambda row: (row["key"], row["source_sha256"])), + "build_packages": sorted(build_rows.values(), key=lambda row: row["key"]), + "build_tools": sorted(tool_rows.values(), key=lambda row: row["key"])} + + +def main() -> None: + """Write the prescreened runtime archive license report.""" + + parser = argparse.ArgumentParser() + parser.add_argument("--verified-scope", required=True) + parser.add_argument("--scope-root", required=True) + parser.add_argument("--output", required=True) + args = parser.parse_args() + output = Path(args.output) + if output.exists() or output.is_symlink(): + raise gate.GateError(gate.CAPTURE_INCOMPLETE, "archive license output already exists") + result = prescreen(_json_bytes(Path(args.verified_scope).read_bytes()), Path(args.scope_root)) + output.write_text(json.dumps({"schema": "cwl.release-runtime-archive-licenses/3", + **result}, indent=2, sort_keys=True) + "\n") + + +if __name__ == "__main__": + main() diff --git a/scripts/ci/r_isolated_matrix.py b/scripts/ci/r_isolated_matrix.py new file mode 100644 index 0000000000..02f44f5574 --- /dev/null +++ b/scripts/ci/r_isolated_matrix.py @@ -0,0 +1,27 @@ +"""Bound the R matrix to supported Linux/x64 image aliases, never runner labels.""" +import json +import sys + + +def admit(raw): + """Reject unsupported matrix legs before any R job can be scheduled.""" + rows = json.loads(raw) + if not isinstance(rows, list) or not 1 <= len(rows) <= 64: + raise ValueError('Expected 1..64 matrix objects') + for row in rows: + if not isinstance(row, dict) or not isinstance(row.get('os'), str): + raise ValueError('Expected an OS string in each matrix object') + if not isinstance(row.get('r'), str) or not row['r'].strip(): + raise ValueError('Expected a nonempty R version') + if row['os'] in ('ubuntu-latest', 'ubuntu-24.04'): + continue + raise ValueError('STOP unsupported platform leg: ' + repr(row['os'])) + return rows + + +if __name__ == '__main__': + try: + print(json.dumps(admit(sys.argv[1]), separators=(',', ':'))) + except (ValueError, TypeError, IndexError) as error: + print(str(error), file=sys.stderr) + sys.exit(1) diff --git a/scripts/ci/reconcile_repository_labels.py b/scripts/ci/reconcile_repository_labels.py index d4585877c6..761cfb6749 100644 --- a/scripts/ci/reconcile_repository_labels.py +++ b/scripts/ci/reconcile_repository_labels.py @@ -14,7 +14,7 @@ ORGANIZATION = "ContextualWisdomLab" -REPOSITORY_RE = re.compile(r"^[A-Za-z0-9_.-]+$") +REPOSITORY_RE = re.compile(r"^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") class TaxonomyError(ValueError): diff --git a/scripts/ci/reconcile_repository_metadata.py b/scripts/ci/reconcile_repository_metadata.py index 36a910ffa8..a78bd09725 100644 --- a/scripts/ci/reconcile_repository_metadata.py +++ b/scripts/ci/reconcile_repository_metadata.py @@ -16,12 +16,12 @@ import sys from pathlib import Path from typing import Any -from urllib.error import URLError +from urllib.error import HTTPError, URLError from urllib.request import HTTPRedirectHandler, Request, build_opener ORGANIZATION = "ContextualWisdomLab" -REPOSITORY_RE = re.compile(r"^[A-Za-z0-9_.-]+$") +REPOSITORY_RE = re.compile(r"^(?!.*(?:\.\.|\.$))[A-Za-z0-9_.-]+$") TOPIC_RE = re.compile(r"^[a-z0-9][a-z0-9-]{0,49}$") MAX_DESCRIPTION_CHARS = 350 PAGES_BASE_URL = f"https://{ORGANIZATION.casefold()}.github.io" @@ -38,8 +38,6 @@ class _NoPagesRedirects(HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): """Raise an HTTPError instead of following the redirect.""" - from urllib.error import HTTPError - raise HTTPError(req.full_url, code, msg, headers, fp) @@ -247,7 +245,13 @@ def _pages_publication_ready(repository: str, current: dict[str, Any]) -> None: if not response.read(1): raise RuntimeError(f"GitHub Pages returned empty content for {repository}") except (URLError, TimeoutError, OSError) as exc: - raise RuntimeError(f"GitHub Pages is not reachable for {repository}") from exc + try: + raise RuntimeError( + f"GitHub Pages is not reachable for {repository}" + ) from exc + finally: + if isinstance(exc, HTTPError): + exc.close() def _repository_file_exists(repository: str, default_branch: str, path: str) -> bool: diff --git a/scripts/ci/release_dependency_capture_raw.sh b/scripts/ci/release_dependency_capture_raw.sh new file mode 100755 index 0000000000..3fbb65f048 --- /dev/null +++ b/scripts/ci/release_dependency_capture_raw.sh @@ -0,0 +1,410 @@ +#!/usr/bin/env bash +# Collect raw pre-publish dependency evidence for the central release gate (#2342). +# +# This script only *runs tools and writes their output verbatim*. Every decision +# — license policy, lock/environment reconciliation, archive-escape and +# install-hook detection, Strix binding validation — lives in the unit-tested +# scripts/ci/release_dependency_gate.py, which reads what this writes. Keeping +# the split that way means no untested shell ever decides whether a release may +# publish. +# +# It requires a runner: pip, cargo, readelf, and network access to the indexes. +# It is therefore exercised in GitHub Actions only; see +# .github/workflows/release-dependency-license-strix-gate.yml. +# +# Output layout (consumed by `release_dependency_gate.py capture` and `gate`): +# +# /python/lock.txt the hash-pinned lock that was collected +# /python/installed.json declared identity/licence per fetched +# distribution, in `pip inspect` shape +# /cargo/Cargo.lock the committed Cargo lock +# /cargo/metadata.json cargo metadata --format-version 1 --locked +# //metadata.json declared identity + license fields +# //source.sha256 sha256 of the distribution as fetched +# //members.txt "\t\t" per member +# //licenses/* bundled LICENSE/COPYING/NOTICE verbatim +# //hooks/* setup.py / build.rs sources verbatim +# //native.json dynamic/static link targets per shipped .so +# //parsed_inputs.txt file names the dependency parses + +set -euo pipefail + +RAW_ROOT="" +CAPTURE_ROOT="" +ECOSYSTEMS="" +PYTHON_LOCK="" +PYTHON_INTERPRETER="" +CARGO_MANIFEST="" +CARGO_DEV_MANIFEST="" +DOWNLOAD_ROOT="" +LICENSE_REPORT="" +MODE="capture" + +while [ "$#" -gt 0 ]; do + case "$1" in + --raw-root) RAW_ROOT="$2"; shift 2 ;; + --capture-root) CAPTURE_ROOT="$2"; shift 2 ;; + --ecosystems) ECOSYSTEMS="$2"; shift 2 ;; + --python-lock) PYTHON_LOCK="$2"; shift 2 ;; + --python-interpreter) PYTHON_INTERPRETER="$2"; shift 2 ;; + --cargo-manifest) CARGO_MANIFEST="$2"; shift 2 ;; + --cargo-dev-manifest) CARGO_DEV_MANIFEST="$2"; shift 2 ;; + --download-root) DOWNLOAD_ROOT="$2"; shift 2 ;; + --license-report) LICENSE_REPORT="$2"; shift 2 ;; + --install-gated) MODE="install"; shift ;; + *) echo "ERROR: unknown argument $1" >&2; exit 2 ;; + esac +done + +if [ "$MODE" = "install" ]; then + if [ -z "$PYTHON_LOCK" ] || [ ! -f "$PYTHON_LOCK" ] || [ -z "$DOWNLOAD_ROOT" ]; then + echo "ERROR: --install-gated requires --python-lock and --download-root." >&2 + exit 2 + fi + if [ -z "$LICENSE_REPORT" ] || [ -z "$CAPTURE_ROOT" ]; then + echo "ERROR: --install-gated requires --license-report and --capture-root." >&2 + exit 2 + fi +else + if [ -z "$RAW_ROOT" ] || [ -z "$CAPTURE_ROOT" ] || [ -z "$ECOSYSTEMS" ]; then + echo "ERROR: --raw-root, --capture-root and --ecosystems are required." >&2 + exit 2 + fi + mkdir -p "$RAW_ROOT" "$CAPTURE_ROOT" +fi + +# The pip entry point is a variable only so the wiring can be regression-tested +# without a network: a test points RELEASE_GATE_PIP at a recorder and asserts +# which pip invocations happened, and in what order, for a refused release. +PIP=(python3 -m pip) +if [ -n "${RELEASE_GATE_PIP:-}" ]; then + PIP=("${RELEASE_GATE_PIP}") +fi + +# Resolved from this script's own directory, never from the caller's cwd or an +# environment variable, so the trusted gate cannot be swapped by a PR. +GATE_SCRIPT="$(cd -- "$(dirname -- "$0")" && pwd)/release_dependency_gate.py" + +# pip's global --python re-executes pip against another interpreter, which is how +# the lock-only virtual environment is installed into by the gated install mode. +PIP_TARGET_ARGS=() +if [ -n "$PYTHON_INTERPRETER" ]; then + # `python3 -m venv` uses symlinks by default on POSIX, so a normal virtual + # environment's bin/python *is* a symlink; refusing symlinks outright rejected + # every real venv and made this path unreachable. What must be refused is a + # target that is not a regular executable file, or a dangling link, so the link + # is resolved and the resolved target is checked. + resolved_interpreter="$(cd -- "$(dirname -- "$PYTHON_INTERPRETER")" 2>/dev/null && pwd -P)/$(basename -- "$PYTHON_INTERPRETER")" + while [ -L "$resolved_interpreter" ]; do + link_target="$(readlink -- "$resolved_interpreter")" + case "$link_target" in + /*) resolved_interpreter="$link_target" ;; + *) resolved_interpreter="$(dirname -- "$resolved_interpreter")/$link_target" ;; + esac + done + if [ ! -f "$resolved_interpreter" ] || [ ! -x "$resolved_interpreter" ]; then + echo "ERROR: --python-interpreter must resolve to a regular executable interpreter." >&2 + exit 2 + fi + PIP_TARGET_ARGS=(--python "$PYTHON_INTERPRETER") +fi + +# Record one archive's members as "\t\t". Symlink and +# hardlink targets are preserved verbatim so the gate can detect escapes. +record_members() { + local archive="$1" destination="$2" + case "$archive" in + *.whl | *.zip) + unzip -Z1 "$archive" | while IFS= read -r member; do + printf 'file\t%s\t\n' "$member" + done + ;; + *) + tar -tvf "$archive" | while IFS= read -r line; do + local permissions name link type + permissions="${line%% *}" + name="$(printf '%s' "$line" | sed -E 's/^.* [0-9]{2}:[0-9]{2} //')" + link="" + type="file" + case "$permissions" in + l*) type="symlink"; link="${name#* -> }"; name="${name%% -> *}" ;; + h*) type="hardlink"; link="${name#* link to }"; name="${name%% link to *}" ;; + d*) type="directory" ;; + esac + printf '%s\t%s\t%s\n' "$type" "$name" "$link" + done + ;; + esac >"$destination" +} + +# Record every bundled license-like file verbatim, flattened into one directory. +record_license_files() { + local root="$1" destination="$2" + mkdir -p "$destination" + find "$root" -maxdepth 4 -type f \ + \( -iname 'LICENSE*' -o -iname 'COPYING*' -o -iname 'NOTICE*' \) -print0 | + while IFS= read -r -d '' found; do + cp -- "$found" "$destination/$(printf '%s' "${found#"$root"/}" | tr '/' '_')" + done +} + +# Record install/build hook sources verbatim so the gate can inspect them. +record_hook_sources() { + local root="$1" destination="$2" + mkdir -p "$destination" + find "$root" -maxdepth 3 -type f \ + \( -name 'setup.py' -o -name 'build.rs' -o -name 'conanfile.py' \) -print0 | + while IFS= read -r -d '' found; do + cp -- "$found" "$destination/$(printf '%s' "${found#"$root"/}" | tr '/' '_')" + done +} + +# Record dynamic NEEDED entries and shipped static archives for native libraries. +record_native_libraries() { + local root="$1" destination="$2" + local entries="[]" + while IFS= read -r library; do + local needed + needed="$(readelf -d "$library" 2>/dev/null | + sed -n 's/.*(NEEDED).*\[\(.*\)\]/\1/p' | + jq -R . | jq -s .)" + entries="$(jq --arg path "${library#"$root"/}" --argjson needed "${needed:-[]}" \ + '. + [{"path": $path, "needed": $needed, "static_archives": []}]' <<<"$entries")" + done < <(find "$root" -type f \( -name '*.so' -o -name '*.so.*' -o -name '*.pyd' \)) + printf '%s\n' "$entries" >"$destination" +} + +capture_python() { + local lock="$1" + mkdir -p "$CAPTURE_ROOT/python" "$DOWNLOAD_ROOT" + cp -- "$lock" "$CAPTURE_ROOT/python/lock.txt" + + local plain_requirements + plain_requirements="$DOWNLOAD_ROOT/pins-without-hashes.txt" + # Fetch by exact pin with hash checking deliberately disabled, then hash the + # bytes here and compare against the lock in the gate. Downloading *with* + # --require-hashes would make pip itself reject a tampered distribution, so + # the gate could never observe SOURCE_HASH_MISMATCH. The install of these same + # bytes happens later, offline and *with* --require-hashes, in install_gated. + sed -E 's/\\$//' "$lock" | grep -oE '^[A-Za-z0-9._-]+==[^ ;]+' \ + >"$plain_requirements" + # The real lock may carry --index-url, --extra-index-url or --find-links, + # while this reconstructed plain file has none of them. Dropping them silently + # made collection resolve from a different source than install. The trusted + # gate therefore parses and *validates* those directives — allowed HTTPS + # origin, no userinfo, bounded relative path — and emits them one per line; + # anything unsupported or untrusted fails here rather than being dropped. + # mapfile keeps each value a single argv element, so no lock content is ever + # word-split or re-interpreted by this shell. This runs before the first + # network action, so a refused directive means nothing was ever fetched. + local -a source_options=() + if [ ! -f "$GATE_SCRIPT" ] || [ -L "$GATE_SCRIPT" ]; then + echo "ERROR: trusted gate script is missing beside this script." >&2 + exit 2 + fi + # Deliberately not `mapfile < <(python3 ...)`: inside process substitution the + # validator's exit status is discarded by set -e, so a refusal would be read as + # "no options" and collection would continue from the default index — the same + # silent drop this fix exists to remove. The status is checked explicitly. + local options_file="$DOWNLOAD_ROOT/validated-source-options.txt" + if ! python3 -I "$GATE_SCRIPT" lock-source-options \ + --lock "$lock" --permitted-root "$(dirname -- "$lock")" >"$options_file"; then + echo "ERROR: lock source directives failed validation; refusing to collect." >&2 + exit 2 + fi + mapfile -t source_options <"$options_file" + # --only-binary=:all: is not only a build-hook guard for the gate environment: + # `pip download` executes an sdist's build backend to get its metadata even + # with --no-deps, so a wheel-only collection is what keeps unadjudicated + # dependency code from running before the licence stage. + "${PIP[@]}" download --no-deps --only-binary=:all: \ + "${source_options[@]}" \ + --dest "$DOWNLOAD_ROOT" -r "$plain_requirements" >/dev/null + + # The enumeration and the licence fields both come from the *fetched + # distributions*, never from `pip inspect` of an installed environment: the + # closure is not installed yet at this point, and must not be until the + # licence stage has passed. The file keeps the `pip inspect` shape the gate + # already reconciles against the lock. + local installed="$CAPTURE_ROOT/python/installed.json" + printf '{"installed": []}\n' >"$installed" + while IFS= read -r pin; do + [ -n "$pin" ] || continue + local name version slug target distribution extracted + name="${pin%%==*}" + version="${pin#*==}" + slug="pypi__$(printf '%s' "$name" | tr '[:upper:]' '[:lower:]' | tr '._' '--')__$version" + target="$RAW_ROOT/$slug" + mkdir -p "$target" + distribution="$(find "$DOWNLOAD_ROOT" -maxdepth 1 -type f \ + -iname "$(printf '%s' "$name" | tr '.-' '__')-${version}*" | head -n 1)" + if [ -z "$distribution" ]; then + echo "ERROR: no fetched distribution for ${name}==${version}" >&2 + exit 2 + fi + cp -- "$distribution" "$target/source.archive" + sha256sum "$target/source.archive" | cut -d' ' -f1 >"$target/source.sha256" + record_members "$distribution" "$target/members.txt" + extracted="$(mktemp -d)" + case "$distribution" in + *.whl) unzip -qq -o "$distribution" -d "$extracted" ;; + *) tar -xf "$distribution" -C "$extracted" ;; + esac + record_license_files "$extracted" "$target/licenses" + record_hook_sources "$extracted" "$target/hooks" + record_native_libraries "$extracted" "$target/native.json" + find "$extracted" -maxdepth 3 -type f -name '*.py' -printf '%P\n' | + LC_ALL=C sort >"$target/parsed_inputs.txt" + printf '{}\n' >"$target/bundled_library_licenses.json" + # Licence metadata is read out of the distribution's own METADATA/PKG-INFO + # by the trusted gate, which also re-checks that the archive declares the + # pinned name and version. A file whose metadata names another project + # fails here instead of being adjudicated under the wrong identity. + python3 -I "$GATE_SCRIPT" distribution-metadata \ + --distribution "$distribution" --name "$name" --version "$version" \ + >"$target/metadata.json" + jq --slurpfile declared "$target/metadata.json" \ + '.installed += [{"metadata": $declared[0]}]' "$installed" \ + >"$installed.next" + mv -- "$installed.next" "$installed" + rm -rf "${extracted:?}" + done <"$plain_requirements" +} + +# Install exactly the distributions the licence stage already judged: offline, +# from the collected bytes, with --require-hashes so pip itself proves each file +# matches the lock. No index is consulted and nothing is re-resolved or +# re-downloaded, so the installed bytes are the inspected bytes by construction — +# which a second hash-less download could not establish for a multi-hash lock. +install_gated() { + local lock="$1" + if [ -z "$LICENSE_REPORT" ]; then + echo "ERROR: --install-gated requires --license-report." >&2 + exit 2 + fi + if [ ! -f "$GATE_SCRIPT" ] || [ -L "$GATE_SCRIPT" ]; then + echo "ERROR: trusted gate script is missing beside this script." >&2 + exit 2 + fi + if [ ! -d "$DOWNLOAD_ROOT" ]; then + echo "ERROR: no collected distributions to install from: $DOWNLOAD_ROOT" >&2 + exit 2 + fi + if [ -z "$CAPTURE_ROOT" ]; then + echo "ERROR: --install-gated requires --capture-root to bind the judged lock." >&2 + exit 2 + fi + # The report alone is not permission: bind-install refuses unless the lock still + # digests to what the verdict read, every judged artifact is present in the + # collected root by digest, and the root holds nothing else. It then pins each + # project to the single judged digest, so a lock recording several hashes for one + # project cannot admit an artifact whose licence and contents were never judged. + local bound_requirements="$DOWNLOAD_ROOT/gated-requirements.txt" + if ! python3 -I "$GATE_SCRIPT" bind-install \ + --report "$LICENSE_REPORT" \ + --capture "$CAPTURE_ROOT" \ + --download-root "$DOWNLOAD_ROOT" \ + --output "$bound_requirements" >/dev/null; then + echo "ERROR: the licence verdict does not authorize installing these bytes." >&2 + exit 2 + fi + "${PIP[@]}" "${PIP_TARGET_ARGS[@]}" install \ + --require-hashes --only-binary=:all: --no-index \ + --find-links "$DOWNLOAD_ROOT" \ + -r "$bound_requirements" +} + +capture_cargo() { + local manifest="$1" workspace_root + local cargo_root="$CAPTURE_ROOT/${2:-cargo}" + mkdir -p "$cargo_root" + cargo metadata --format-version 1 --locked --manifest-path "$manifest" \ + >"$cargo_root/metadata.json" + workspace_root="$(jq -er '.workspace_root | select(type == "string" and startswith("/"))' \ + "$cargo_root/metadata.json")" + cp -- "$workspace_root/Cargo.lock" "$cargo_root/Cargo.lock" + cargo fetch --locked --manifest-path "$manifest" >/dev/null + + while IFS=$'\t' read -r name version license; do + local slug target crate extracted + slug="cargo__${name}__${version}" + target="$RAW_ROOT/$slug" + mkdir -p "$target" + crate="$(find "${CARGO_HOME:-$HOME/.cargo}/registry/cache" -type f \ + -name "${name}-${version}.crate" | head -n 1)" + if [ -z "$crate" ]; then + echo "ERROR: no fetched crate for ${name} ${version}" >&2 + exit 2 + fi + cp -- "$crate" "$target/source.archive" + sha256sum "$target/source.archive" | cut -d' ' -f1 >"$target/source.sha256" + record_members "$crate" "$target/members.txt" + extracted="$(mktemp -d)" + tar -xf "$crate" -C "$extracted" + record_license_files "$extracted" "$target/licenses" + record_hook_sources "$extracted" "$target/hooks" + printf '[]\n' >"$target/native.json" + printf '{}\n' >"$target/bundled_library_licenses.json" + find "$extracted" -maxdepth 3 -type f -name '*.rs' -printf '%P\n' | + LC_ALL=C sort >"$target/parsed_inputs.txt" + local inclusion='["wheel"]' + if [ "${2:-cargo}" = "cargo-dev" ]; then + inclusion='["dev"]' + if [ -f "$target/metadata.json" ]; then + inclusion="$(jq -c '(.distribution_inclusion + ["dev"]) | unique' "$target/metadata.json")" + fi + fi + jq -n --arg name "$name" --arg version "$version" --arg license "$license" \ + --argjson inclusion "$inclusion" '{ + ecosystem: "cargo", + name: $name, + version: $version, + license_expression: $license, + license: "", + classifiers: [], + distribution_inclusion: $inclusion, + known_vulnerabilities: [] + }' >"$target/metadata.json" + rm -rf "${extracted:?}" + done < <(jq -r '.packages[] | select(.source != null) | [.name, .version, (.license // "")] | @tsv' \ + "$cargo_root/metadata.json") +} + +if [ "$MODE" = "install" ]; then + install_gated "$PYTHON_LOCK" + echo "Installed the prescreened release closure from collected bytes." + exit 0 +fi + +case ",${ECOSYSTEMS}," in +*,python,*) + if [ -z "$PYTHON_LOCK" ] || [ ! -f "$PYTHON_LOCK" ]; then + echo "ERROR: --python-lock must name the hash-pinned release lock." >&2 + exit 2 + fi + if [ -z "$DOWNLOAD_ROOT" ]; then + echo "ERROR: --download-root is required so the gated install reuses these bytes." >&2 + exit 2 + fi + capture_python "$PYTHON_LOCK" + ;; +esac + +case ",${ECOSYSTEMS}," in +*,cargo,*) + if [ -z "$CARGO_MANIFEST" ] || [ ! -f "$CARGO_MANIFEST" ]; then + echo "ERROR: --cargo-manifest must name the release Cargo.toml." >&2 + exit 2 + fi + capture_cargo "$CARGO_MANIFEST" + if [ -n "$CARGO_DEV_MANIFEST" ]; then + if [ ! -f "$CARGO_DEV_MANIFEST" ]; then + echo "ERROR: development Cargo manifest is absent." >&2 + exit 2 + fi + capture_cargo "$CARGO_DEV_MANIFEST" cargo-dev + fi + ;; +esac + +echo "Raw dependency capture complete: $(find "$RAW_ROOT" -mindepth 1 -maxdepth 1 -type d | wc -l) dependencies." diff --git a/scripts/ci/release_dependency_gate.py b/scripts/ci/release_dependency_gate.py new file mode 100644 index 0000000000..f11178f107 --- /dev/null +++ b/scripts/ci/release_dependency_gate.py @@ -0,0 +1,3092 @@ +#!/usr/bin/env python3 +"""Fail-closed pre-publish dependency gate for org releases (issue #2342). + +``scripts/ci/sbom_inventory_aggregator.py`` is a *scheduled, informational* org +SBOM roll-up: it flags GPL/AGPL/NOASSERTION components for governance, but it +is not per-dependency, not fail-closed, and not bound to a release head. This +module is the missing gate. It runs in +``.github/workflows/release-dependency-license-strix-gate.yml`` **before** a +release workflow publishes anything, and it either exits ``0`` or refuses the +release. There is no neutral outcome, no allow-failure, and no bypass. + +Design: the gate is a pure function over *captured* inputs. Workflow steps run +``pip inspect``, ``cargo metadata --locked``, archive listing, ``readelf -d``, +and Strix; each writes a file into a capture directory. This module only reads +files. That split keeps every deterministic decision unit-testable without a +runner and makes the Actions-only parts explicit instead of simulated. + +Capture layout (produced by the workflow, consumed here):: + + / + release.json source repository/SHA + artifact names + python/lock.txt the hash-pinned lock that was installed + python/installed.json `pip inspect` of the build environment + cargo/Cargo.lock the committed Cargo lock + cargo/metadata.json `cargo metadata --format-version 1 --locked` + evidence/.json per-dependency captured evidence + strix/bindings/.json per-dependency Strix structured binding + license-selections.json optional dual-license selections + +Every resolved dependency of both ecosystems must appear in the lock *and* in +the environment/build graph; any asymmetry fails ``LOCK_ENV_MISMATCH`` or +``CARGO_LOCK_GRAPH_MISMATCH``. No dependency is exempt: bootstrap tools such as +``pip`` are pinned in this organization's own ``*-hashes.txt`` files, so a lock +that omits an installed distribution is a defect, not a special case. + +Strix evidence is accepted **only** as a machine-readable binding. A textual +"0 findings" or "No exploitable vulnerabilities detected" is rejected +(``STRIX_TEXTUAL_PASS_REJECTED``), and a missing or malformed binding is a +failure rather than a neutral result. The binding's fail-closed shape and error +type follow ``scripts/ci/strix_evidence_binding.py``, which is imported from +this script's **own** directory so the gate behaves identically wherever the +trusted verifier is materialized. +""" + +from __future__ import annotations + +import argparse +import ast +import email.parser +import hashlib +import importlib +import io +import json +import os +import re +import stat +import subprocess +import sys +import tarfile +import urllib.parse +import uuid +import zipfile +from dataclasses import dataclass, field +from pathlib import Path, PurePosixPath +from typing import Any, Iterable, Mapping, Sequence + +def _import_toml_parser(): + """Return the stdlib TOML parser, or the declared Python 3.10 backport.""" + try: + return importlib.import_module("tomllib") + except ModuleNotFoundError: # Python 3.10; already declared in the dev group. + return importlib.import_module("tomli") + + +tomllib = _import_toml_parser() + +try: + from scripts.ci.spdx_license_policy import ( + LICENSE_MISSING, + LICENSE_SELECTION_INVALID, + LICENSE_TEXT_MISSING, + LICENSE_TEXT_UNVERIFIED, + LicenseDecision, + evaluate_license_expression, + recognize_license_text, + scan_license_text, + spdx_from_classifiers, + ) +except ImportError: # pragma: no cover - direct `python3 -I " + rendered = queue_health.render_html(report) + assert "<script>" in rendered + assert 'owner/repo' in rendered + assert "queue-age SLO: 900 seconds" in rendered + + empty = queue_health.build_report({"generated_at": "2026-08-19T11:00:00Z", "repositories": []}, now=NOW) + assert "No queued or in-progress jobs observed." in queue_health.render_html(empty) + + json_path = tmp_path / "nested" / "report.json" + html_path = tmp_path / "nested" / "report.html" + queue_health.write_reports(report, json_path, html_path) + assert json.loads(json_path.read_text(encoding="utf-8"))["schema_version"] == "actions.queue_health.v1" + assert " None: + """Exercise snapshot mode, allowlist mode, and bounded CLI failures.""" + args = queue_health.parse_args( + ["--snapshot", "snapshot.json", "--output-json", "out.json", "--output-html", "out.html"] + ) + assert args.snapshot == Path("snapshot.json") + args = queue_health.parse_args( + ["--allowlist", "allowlist.json", "--output-json", "out.json", "--output-html", "out.html"] + ) + assert args.allowlist == Path("allowlist.json") + with pytest.raises(SystemExit): + queue_health.parse_args(["--snapshot", "a", "--allowlist", "b", "--output-json", "o", "--output-html", "h"]) + + snapshot_path = tmp_path / "snapshot.json" + snapshot_path.write_text(json.dumps(report_snapshot()), encoding="utf-8") + json_path = tmp_path / "out.json" + html_path = tmp_path / "out.html" + assert queue_health.main( + [ + "--snapshot", + str(snapshot_path), + "--output-json", + str(json_path), + "--output-html", + str(html_path), + "--now", + "2026-08-19T12:00:00Z", + ] + ) == 0 + assert "QUEUE_HEALTH_RESULT=" in capsys.readouterr().out + + empty_snapshot_path = tmp_path / "empty-snapshot.json" + empty_snapshot_path.write_text( + json.dumps({"generated_at": "2026-08-19T11:00:00Z", "repositories": []}), + encoding="utf-8", + ) + assert queue_health.main( + [ + "--snapshot", + str(empty_snapshot_path), + "--output-json", + str(json_path), + "--output-html", + str(html_path), + "--now", + "2026-08-19T12:00:00Z", + ] + ) == 0 + assert "::warning::" not in capsys.readouterr().out + + error = io.StringIO() + assert queue_health.main( + ["--snapshot", str(tmp_path / "missing.json"), "--output-json", "o", "--output-html", "h"], + stderr=error, + ) == 2 + assert "ERROR:" in error.getvalue() + + allowlist_path = tmp_path / "allowlist.json" + allowlist_path.write_text(json.dumps(["owner/repo"]), encoding="utf-8") + original_collect = queue_health.collect_snapshot + queue_health.collect_snapshot = lambda repositories: report_snapshot() # type: ignore[assignment] + try: + assert queue_health.main( + ["--allowlist", str(allowlist_path), "--output-json", str(json_path), "--output-html", str(html_path)] + ) == 0 + finally: + queue_health.collect_snapshot = original_collect + assert "QUEUE_HEALTH_RESULT=" in capsys.readouterr().out diff --git a/tests/test_actions_queue_health_cancelled_before_runner.py b/tests/test_actions_queue_health_cancelled_before_runner.py new file mode 100644 index 0000000000..866280b4ac --- /dev/null +++ b/tests/test_actions_queue_health_cancelled_before_runner.py @@ -0,0 +1,324 @@ +"""Regression coverage for workflow cancellation before runner assignment.""" + +from __future__ import annotations + +import importlib.util +import json +from datetime import datetime, timezone +from pathlib import Path +from subprocess import CompletedProcess + +ROOT = Path(__file__).resolve().parents[1] +MODULE_PATH = ROOT / "scripts/ci/actions_queue_health.py" +SPEC = importlib.util.spec_from_file_location("actions_queue_health", MODULE_PATH) +assert SPEC and SPEC.loader +queue_health = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(queue_health) + + +def test_collect_snapshot_classifies_cancelled_job_before_runner_assignment(monkeypatch) -> None: + """A cancelled current-head job with no runner or steps stays explicit evidence.""" + repository_name = "owner/repo" + pull_request = { + "number": 17, + "state": "open", + "base": {"ref": "main", "repo": {"full_name": repository_name}}, + "head": {"sha": "exact-head"}, + "updated_at": "2026-09-02T13:15:00Z", + } + cancelled_run = { + "id": 1701, + "name": "Repository Metadata Reconcile", + "workflow_id": 9017, + "event": "pull_request", + "status": "completed", + "conclusion": "cancelled", + "head_sha": "exact-head", + "created_at": "2026-09-02T13:00:00Z", + "updated_at": "2026-09-02T13:08:00Z", + "run_attempt": 1, + "pull_requests": [{"number": 17, "head": {"sha": "exact-head"}}], + } + cancelled_job = { + "id": 17001, + "name": "validate", + "status": "completed", + "conclusion": "cancelled", + "runner_id": 0, + "runner_name": "", + "created_at": "2026-09-02T13:00:00Z", + "steps": [], + } + skipped_job = { + "id": 17002, + "name": "publish optional evidence", + "status": "completed", + "conclusion": "skipped", + "runner_id": 0, + "runner_name": "", + "created_at": "2026-09-02T13:00:00Z", + "steps": [], + } + missing_steps_job = { + "id": 17003, + "name": "cancelled without step evidence", + "status": "completed", + "conclusion": "cancelled", + "runner_id": 0, + "runner_name": "", + "created_at": "2026-09-02T13:00:00Z", + } + null_steps_job = { + **missing_steps_job, + "id": 17004, + "name": "cancelled with null step evidence", + "steps": None, + } + terminal_path = ( + f"repos/{repository_name}/actions/runs?status=completed" + "&head_sha=exact-head&per_page=50" + ) + + def runner(args: list[str], **_: object) -> CompletedProcess[str]: + """Return deterministic GitHub REST fixtures for the collector.""" + path = args[-1] + if path == f"repos/{repository_name}": + payload: object = {"default_branch": "main"} + elif path == f"repos/{repository_name}/pulls?state=open&per_page=100": + payload = [pull_request] + elif path == terminal_path: + payload = {"total_count": 1, "workflow_runs": [cancelled_run]} + elif path == f"repos/{repository_name}/actions/runs/1701/jobs?per_page=100": + payload = { + "total_count": 4, + "jobs": [ + cancelled_job, + skipped_job, + missing_steps_job, + null_steps_job, + ], + } + elif "status=startup_failure" in path: + raise AssertionError( + "GitHub workflow-run status filtering does not accept startup_failure" + ) + elif path.startswith(f"repos/{repository_name}/actions/runs?status="): + payload = {"total_count": 0, "workflow_runs": []} + else: # pragma: no cover - unexpected API expansion must fail loudly. + raise AssertionError(f"unexpected GitHub API path: {path}") + return CompletedProcess(args, 0, json.dumps(payload), "") + + snapshot = queue_health.collect_snapshot( + [repository_name], + runner=runner, + generated_at="2026-09-02T13:16:00Z", + ) + + assert snapshot["collection_errors"] == [] + assert [run["id"] for run in snapshot["repositories"][0]["runs"]] == [1701] + + report = queue_health.build_report( + snapshot, + now=datetime(2026, 9, 2, 13, 16, tzinfo=timezone.utc), + ) + cancelled_row = next(row for row in report["runs"] if row["job_id"] == 17001) + assert cancelled_row["identity_state"] == "current_head" + assert cancelled_row["run_conclusion"] == "CANCELLED" + assert cancelled_row["jobs_materialized"] is True + assert cancelled_row["runner_assigned"] is False + assert cancelled_row["admission_state"] == "cancelled_before_runner_assignment" + assert cancelled_row["blocker"] == "cancelled_before_runner_assignment" + assert cancelled_row["recommended_action"] == ( + "inspect_actions_control_plane_without_leaf_bypass" + ) + + skipped_row = next(row for row in report["runs"] if row["job_id"] == 17002) + assert skipped_row["run_conclusion"] == "CANCELLED" + assert skipped_row["admission_state"] != "cancelled_before_runner_assignment" + for unavailable_step_job_id in (17003, 17004): + unavailable_step_row = next( + row for row in report["runs"] if row["job_id"] == unavailable_step_job_id + ) + assert unavailable_step_row["admission_state"] != ( + "cancelled_before_runner_assignment" + ) + assert report["summary"]["cancelled_before_runner_assignment_count"] == 1 + actions = list(report["summary"]["external_actions"]) + monkeypatch.setattr(queue_health, "_CORE_BUILD_REPORT", lambda *_args, **_kwargs: report) + assert queue_health.build_report(snapshot)["summary"]["external_actions"] == actions + + +def test_collect_snapshot_retains_cancelled_pull_request_target_current_head() -> None: + """A target-triggered cancellation uses linked PR head identity, not base SHA.""" + repository_name = "owner/repo" + pull_request = { + "number": 23, + "state": "open", + "base": {"ref": "main", "repo": {"full_name": repository_name}}, + "head": {"sha": "exact-target-head"}, + "updated_at": "2026-09-02T13:20:00Z", + } + cancelled_run = { + "id": 2301, + "name": "Target Review", + "workflow_id": 9023, + "event": "pull_request_target", + "status": "completed", + "conclusion": "cancelled", + "head_sha": "base-commit-sha", + "created_at": "2026-09-02T13:00:00Z", + "updated_at": "2026-09-02T13:05:00Z", + "run_attempt": 1, + "pull_requests": [ + {"number": 23, "head": {"sha": "exact-target-head"}} + ], + } + cancelled_job = { + "id": 23001, + "name": "review", + "status": "completed", + "conclusion": "cancelled", + "runner_id": 0, + "runner_name": "", + "created_at": "2026-09-02T13:00:00Z", + "steps": [], + } + head_terminal_path = ( + f"repos/{repository_name}/actions/runs?status=completed" + "&head_sha=exact-target-head&per_page=50" + ) + target_cancelled_path = ( + f"repos/{repository_name}/actions/runs?status=cancelled" + "&event=pull_request_target&per_page=50" + ) + + def runner(args: list[str], **_: object) -> CompletedProcess[str]: + """Model GitHub target runs whose run-level SHA is the base commit.""" + path = args[-1] + if path == f"repos/{repository_name}": + payload: object = {"default_branch": "main"} + elif path == f"repos/{repository_name}/pulls?state=open&per_page=100": + payload = [pull_request] + elif path == head_terminal_path: + payload = {"total_count": 0, "workflow_runs": []} + elif path == target_cancelled_path: + payload = {"total_count": 1, "workflow_runs": [cancelled_run]} + elif "status=startup_failure" in path: + raise AssertionError( + "GitHub workflow-run status filtering does not accept startup_failure" + ) + elif path == f"repos/{repository_name}/actions/runs/2301/jobs?per_page=100": + payload = {"total_count": 1, "jobs": [cancelled_job]} + elif path.startswith(f"repos/{repository_name}/actions/runs?status="): + payload = {"total_count": 0, "workflow_runs": []} + else: # pragma: no cover - unexpected API expansion must fail loudly. + raise AssertionError(f"unexpected GitHub API path: {path}") + return CompletedProcess(args, 0, json.dumps(payload), "") + + snapshot = queue_health.collect_snapshot( + [repository_name], + runner=runner, + generated_at="2026-09-02T13:21:00Z", + ) + + assert snapshot["collection_errors"] == [] + assert [run["id"] for run in snapshot["repositories"][0]["runs"]] == [2301] + report = queue_health.build_report( + snapshot, + now=datetime(2026, 9, 2, 13, 21, tzinfo=timezone.utc), + ) + assert report["runs"][0]["identity_state"] == "current_head" + assert report["runs"][0]["admission_state"] == ( + "cancelled_before_runner_assignment" + ) + + +def test_collect_snapshot_rejects_head_change_after_target_evidence_read() -> None: + """Terminal evidence is rejected when its PR identity changes before completion.""" + repository_name = "owner/repo" + original_pull_request = { + "number": 29, + "state": "open", + "base": {"ref": "main", "repo": {"full_name": repository_name}}, + "head": {"sha": "original-head"}, + "updated_at": "2026-09-02T13:22:00Z", + } + changed_pull_request = { + **original_pull_request, + "head": {"sha": "replacement-head"}, + "updated_at": "2026-09-02T13:24:00Z", + } + cancelled_run = { + "id": 2901, + "name": "Target Review", + "workflow_id": 9029, + "event": "pull_request_target", + "status": "completed", + "conclusion": "cancelled", + "head_sha": "base-commit-sha", + "created_at": "2026-09-02T13:00:00Z", + "updated_at": "2026-09-02T13:05:00Z", + "run_attempt": 1, + "pull_requests": [{"number": 29, "head": {"sha": "original-head"}}], + } + cancelled_job = { + "id": 29001, + "name": "review", + "status": "completed", + "conclusion": "cancelled", + "runner_id": 0, + "runner_name": "", + "created_at": "2026-09-02T13:00:00Z", + "steps": [], + } + head_terminal_path = ( + f"repos/{repository_name}/actions/runs?status=completed" + "&head_sha=original-head&per_page=50" + ) + target_cancelled_path = ( + f"repos/{repository_name}/actions/runs?status=cancelled" + "&event=pull_request_target&per_page=50" + ) + pull_read_count = 0 + + def runner(args: list[str], **_: object) -> CompletedProcess[str]: + """Advance the PR head only after terminal/job evidence has been read.""" + nonlocal pull_read_count + path = args[-1] + if path == f"repos/{repository_name}": + payload: object = {"default_branch": "main"} + elif path == f"repos/{repository_name}/pulls?state=open&per_page=100": + pull_read_count += 1 + payload = [ + changed_pull_request if pull_read_count >= 3 else original_pull_request + ] + elif path == head_terminal_path: + payload = {"total_count": 0, "workflow_runs": []} + elif path == target_cancelled_path: + payload = {"total_count": 1, "workflow_runs": [cancelled_run]} + elif path == f"repos/{repository_name}/actions/runs/2901/jobs?per_page=100": + payload = {"total_count": 1, "jobs": [cancelled_job]} + elif "status=startup_failure" in path: + raise AssertionError( + "GitHub workflow-run status filtering does not accept startup_failure" + ) + elif path.startswith(f"repos/{repository_name}/actions/runs?status="): + payload = {"total_count": 0, "workflow_runs": []} + else: # pragma: no cover - unexpected API expansion must fail loudly. + raise AssertionError(f"unexpected GitHub API path: {path}") + return CompletedProcess(args, 0, json.dumps(payload), "") + + snapshot = queue_health.collect_snapshot( + [repository_name], + runner=runner, + generated_at="2026-09-02T13:25:00Z", + ) + + assert snapshot["repositories"] == [] + assert snapshot["collection_errors"] == [ + { + "repository": repository_name, + "error": "pull-request identity snapshot changed during evidence collection", + } + ] + assert pull_read_count == 3 diff --git a/tests/test_actions_queue_health_contract.py b/tests/test_actions_queue_health_contract.py new file mode 100644 index 0000000000..4958d18376 --- /dev/null +++ b/tests/test_actions_queue_health_contract.py @@ -0,0 +1,85 @@ +"""Contract tests for the scheduled read-only Actions queue report.""" + +import ast +import json +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def test_queue_health_workflow_is_scheduled_read_only_and_pinned() -> None: + """Keep the scheduled collector bounded, read-only, and supply-chain pinned.""" + workflow = (ROOT / ".github/workflows/actions-queue-health.yml").read_text(encoding="utf-8") + + assert 'cron: "7 * * * *"' in workflow + assert "workflow_dispatch:" not in workflow + assert "cancel-in-progress: false" in workflow + assert "timeout-minutes: 30" in workflow + assert "github.repository == 'ContextualWisdomLab/.github'" in workflow + assert "github.ref == 'refs/heads/main'" in workflow + assert "endsWith(github.workflow_ref, '@refs/heads/main')" in workflow + assert '"group":"CWL MCP remediation"' in workflow + assert '"labels":["self-hosted","linux","x64"]' in workflow + assert '"group":"CWL CI isolated"' in workflow + assert '"labels":["self-hosted","linux","x64","cwlab-ci-isolated"]' in workflow + assert "runs-on: ubuntu-24.04" not in workflow + assert "runs-on: ubuntu-latest" not in workflow + assert "actions: read" in workflow + assert "pull-requests: read" not in workflow + assert "contents: write" not in workflow + workflow_permissions = workflow.split("permissions:\n", 1)[1].split("\njobs:\n", 1)[0] + assert workflow_permissions == " contents: read\n actions: read\n" + collect_permissions = workflow.split(" collect:\n", 1)[1].split( + " permissions:\n", 1 + )[1].split(" steps:\n", 1)[0] + assert collect_permissions == " contents: read\n actions: read\n" + assert ( + "GH_TOKEN: ${{ secrets.PR_REVIEW_MERGE_TOKEN || secrets.OPENCODE_APPROVE_TOKEN }}" + in workflow + ) + assert "GH_TOKEN: ${{ github.token }}" not in workflow + assert "required for cross-repository queue reads" in workflow + assert "gh run cancel" not in workflow + assert "gh pr merge" not in workflow + assert "step-security/harden-runner@b09bb98e06d4d774595224525879c09bc6e98c40" in workflow + assert "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0" in workflow + assert "actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a" in workflow + assert "actions_queue_health.py" in workflow + assert "actions_queue_health_repositories.json" in workflow + + +def test_queue_health_allowlist_is_explicit_and_bounded() -> None: + """Keep the first product slice limited to its reviewed repositories.""" + payload = json.loads( + (ROOT / "config/actions_queue_health_repositories.json").read_text(encoding="utf-8") + ) + assert payload == { + "repositories": [ + "ContextualWisdomLab/.github", + "ContextualWisdomLab/ConceptWeave", + "ContextualWisdomLab/ELUNVERA", + "ContextualWisdomLab/LineageWeave", + "ContextualWisdomLab/OriginWeave", + "ContextualWisdomLab/TEPP", + "ContextualWisdomLab/contextual-orchestrator", + "ContextualWisdomLab/disksage", + "ContextualWisdomLab/fast-mlsirm", + "ContextualWisdomLab/mhtml-etl-gateway", + "ContextualWisdomLab/naruon", + "ContextualWisdomLab/noema", + "ContextualWisdomLab/pg-llm-batch", + "ContextualWisdomLab/quarantine-sandbox-runtime", + ] + } + + +def test_queue_health_core_does_not_duplicate_executable_entrypoints() -> None: + """Keep collection and CLI orchestration solely in the executable module.""" + core_path = ROOT / "scripts/ci/actions_queue_health_core.py" + tree = ast.parse(core_path.read_text(encoding="utf-8")) + top_level_functions = { + node.name for node in tree.body if isinstance(node, ast.FunctionDef) + } + + assert "collect_snapshot" not in top_level_functions + assert "main" not in top_level_functions diff --git a/tests/test_actions_queue_health_post_evidence_retry.py b/tests/test_actions_queue_health_post_evidence_retry.py new file mode 100644 index 0000000000..ae1c619cd0 --- /dev/null +++ b/tests/test_actions_queue_health_post_evidence_retry.py @@ -0,0 +1,171 @@ +"""Regression tests for post-evidence pull-request identity retry semantics.""" + +import importlib.util +import json +from pathlib import Path +from subprocess import CompletedProcess + +import pytest + + +ROOT = Path(__file__).resolve().parents[1] +MODULE_PATH = ROOT / "scripts/ci/actions_queue_health.py" +SPEC = importlib.util.spec_from_file_location("actions_queue_health_post_evidence_retry", MODULE_PATH) +assert SPEC and SPEC.loader +queue_health = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(queue_health) + + +def _pull(head_sha: str = "head") -> dict: + """Return one complete raw open-pull-request identity fixture.""" + return { + "number": 1, + "state": "open", + "base": {"ref": "main", "repo": {"full_name": "owner/repo"}}, + "head": {"sha": head_sha}, + "updated_at": "2026-09-02T14:00:00Z", + } + + +def _incomplete_pull() -> dict: + """Return a transiently incomplete identity fixture.""" + pull_request = _pull() + pull_request["head"] = {"sha": ""} + return pull_request + + +def _runner_with_post_evidence_identity_reads(*, persistent: bool): + """Return a runner that makes the post-evidence identity read incomplete.""" + pull_reads = 0 + + def runner(args: list[str], **kwargs: object) -> CompletedProcess[str]: + """Serve stable queue evidence with a transient or persistent final identity gap.""" + nonlocal pull_reads + path = args[-1] + if path == "repos/owner/repo": + payload: object = {"default_branch": "main"} + elif path == "repos/owner/repo/pulls?state=open&per_page=100": + pull_reads += 1 + if pull_reads == 3 or (persistent and pull_reads >= 3): + payload = [_incomplete_pull()] + else: + payload = [_pull()] + elif "/actions/runs?" in path: + payload = {"total_count": 0, "workflow_runs": []} + else: # pragma: no cover - any new endpoint must be explicitly governed. + raise AssertionError(f"unexpected endpoint: {path}") + return CompletedProcess(args, 0, json.dumps(payload), "") + + return runner + + +def test_post_evidence_identity_read_retries_one_transient_incomplete_snapshot(monkeypatch) -> None: + """A transient incomplete post-evidence identity read receives one bounded retry.""" + monkeypatch.setattr(queue_health.time, "sleep", lambda _: None) + snapshot = queue_health.collect_snapshot( + ["owner/repo"], + runner=_runner_with_post_evidence_identity_reads(persistent=False), + generated_at="2026-09-02T14:00:00Z", + ) + assert snapshot["collection_errors"] == [] + assert len(snapshot["repositories"]) == 1 + assert snapshot["repositories"][0]["pull_requests"][0]["head_sha"] == "head" + + +def test_post_evidence_identity_read_fails_closed_after_retry_remains_incomplete(monkeypatch) -> None: + """Persistent incomplete post-evidence identity is repository-scoped failure.""" + monkeypatch.setattr(queue_health.time, "sleep", lambda _: None) + snapshot = queue_health.collect_snapshot( + ["owner/repo"], + runner=_runner_with_post_evidence_identity_reads(persistent=True), + generated_at="2026-09-02T14:00:00Z", + ) + assert snapshot["repositories"] == [] + assert len(snapshot["collection_errors"]) == 1 + assert snapshot["collection_errors"][0]["repository"] == "owner/repo" + assert "pull-request identity validation failed" in snapshot["collection_errors"][0]["error"] + +def _run(run_id: int, workflow_id: int) -> dict: + """Return one linked run for terminal-filter boundary tests.""" + return { + "id": run_id, + "workflow_id": workflow_id, + "name": "required-check", + "event": "pull_request", + "status": "queued", + "head_sha": "head", + "pull_requests": [{"number": 1, "head": {"sha": "head"}}], + } + + +@pytest.mark.parametrize( + ("active_runs", "completed_runs", "target_runs", "expected_error"), + [ + ([{"id": 0, "status": "queued"}], [], [], "workflow run id"), + ([], [{**_run(8, 501), "status": "completed", "conclusion": "failure", "id": 0}], [], "workflow run id"), + ([], [{**_run(8, 501), "status": "completed", "conclusion": "success"}], [], None), + ([], [], [{**_run(8, 501), "status": "completed", "conclusion": "cancelled", "pull_requests": []}], None), + ], +) +def test_collector_rejects_invalid_run_ids_and_ignores_unrelated_terminal_runs( + active_runs: list[dict], completed_runs: list[dict], target_runs: list[dict], expected_error: str | None, +) -> None: + """Bad identities fail closed; unrelated terminal runs do not become current-head evidence.""" + def runner(args: list[str], **_kwargs: object) -> CompletedProcess[str]: + path = args[-1] + if path == "repos/owner/repo": + payload: object = {"default_branch": "main"} + elif path == "repos/owner/repo/pulls?state=open&per_page=100": + payload = [_pull()] + elif "status=completed&head_sha=" in path: + payload = completed_runs + elif "status=cancelled&event=pull_request_target" in path: + payload = target_runs + elif "status=queued" in path: + payload = active_runs + elif "/actions/runs?status=" in path: + payload = [] + else: + raise AssertionError(f"unexpected endpoint: {path}") + return CompletedProcess(args, 0, json.dumps(payload), "") + + snapshot = queue_health.collect_snapshot( + ["owner/repo"], runner=runner, generated_at="2026-09-02T00:00:00Z" + ) + if expected_error: + assert snapshot["repositories"] == [] + assert expected_error in snapshot["collection_errors"][0]["error"] + else: + assert snapshot["collection_errors"] == [] + assert snapshot["repositories"][0]["runs"] == [] + + +def test_final_pull_identity_retry_failure_is_bounded(monkeypatch: pytest.MonkeyPatch) -> None: + """A repeatedly incomplete final PR view cannot certify current-head evidence.""" + monkeypatch.setattr(queue_health.time, "sleep", lambda _seconds: None) + pull_reads = 0 + + def runner(args: list[str], **_kwargs: object) -> CompletedProcess[str]: + nonlocal pull_reads + path = args[-1] + if path == "repos/owner/repo": + payload: object = {"default_branch": "main"} + elif path == "repos/owner/repo/pulls?state=open&per_page=100": + pull_reads += 1 + payload = [_pull()] if pull_reads == 1 else [{**_pull(), "head": {"sha": ""}}] + elif "/actions/runs?status=" in path: + payload = [] + else: + raise AssertionError(f"unexpected endpoint: {path}") + return CompletedProcess(args, 0, json.dumps(payload), "") + + snapshot = queue_health.collect_snapshot(["owner/repo"], runner=runner) + assert pull_reads == 3 + assert snapshot["repositories"] == [] + assert "pull-request identity validation failed" in snapshot["collection_errors"][0]["error"] + + +def test_core_run_normalization_rejects_non_object() -> None: + """A malformed workflow-run payload cannot be classified as a real run.""" + with pytest.raises(queue_health.QueueHealthError, match="workflow run entry must be an object"): + queue_health._CORE_NORMALISE_RUN("owner/repo", None, []) diff --git a/tests/test_actions_queue_health_queued_job_evidence.py b/tests/test_actions_queue_health_queued_job_evidence.py new file mode 100644 index 0000000000..db526b3874 --- /dev/null +++ b/tests/test_actions_queue_health_queued_job_evidence.py @@ -0,0 +1,107 @@ +"""Regression contract for queued current-head jobs that materialize after run start.""" + +from datetime import datetime, timezone +import importlib.util +import json +from pathlib import Path +from subprocess import CompletedProcess + + +ROOT = Path(__file__).resolve().parents[1] +MODULE_PATH = ROOT / "scripts/ci/actions_queue_health.py" +SPEC = importlib.util.spec_from_file_location("actions_queue_health_queued_job", MODULE_PATH) +assert SPEC and SPEC.loader +queue_health = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(queue_health) + + +def _pull_request() -> dict: + """Return the open PR whose current head owns the queued run.""" + return { + "number": 1, + "state": "open", + "base": {"ref": "main", "repo": {"full_name": "owner/repo"}}, + "head": {"sha": "head"}, + "updated_at": "2026-09-17T02:55:00Z", + } + + +def _queued_run() -> dict: + """Return an old run whose downstream job only recently became eligible.""" + return { + "id": 910, + "workflow_id": 911, + "name": "required-check", + "event": "pull_request", + "status": "queued", + "conclusion": "", + "head_sha": "head", + "created_at": "2026-09-17T00:00:00Z", + "updated_at": "2026-09-17T02:58:00Z", + "run_attempt": 1, + "pull_requests": [{"number": 1, "head": {"sha": "head"}}], + } + + +def _queued_job() -> dict: + """Return the current downstream job with its own later queue timestamp.""" + return { + "id": 912, + "name": "dispatch-current-head", + "status": "queued", + "conclusion": None, + "runner_id": None, + "runner_name": None, + "created_at": "2026-09-17T02:58:00Z", + "steps": [], + } + + +def test_queued_current_head_fetches_job_evidence_and_uses_job_queue_start() -> None: + """Time a materialized queued job from its own eligibility, not the parent run.""" + queued_run = _queued_run() + queued_job = _queued_job() + responses: dict[str, object] = { + "repos/owner/repo": {"default_branch": "main"}, + "repos/owner/repo/pulls?state=open&per_page=100": [_pull_request()], + "repos/owner/repo/actions/runs?status=queued&per_page=50": [queued_run], + "repos/owner/repo/actions/runs?status=completed&head_sha=head&per_page=50": [], + "repos/owner/repo/actions/runs?status=cancelled&event=pull_request_target&per_page=50": [], + "repos/owner/repo/actions/runs/910/jobs?per_page=100": { + "total_count": 1, + "jobs": [queued_job], + }, + } + for status in ("in_progress", "pending", "requested", "waiting"): + responses[f"repos/owner/repo/actions/runs?status={status}&per_page=50"] = [] + + requested_paths: list[str] = [] + + def runner(args: list[str], **_: object) -> CompletedProcess[str]: + """Return deterministic REST payloads and retain the exact evidence reads.""" + path = args[-1] + requested_paths.append(path) + if path not in responses: + raise AssertionError(f"unexpected endpoint: {path}") + return CompletedProcess(args, 0, json.dumps(responses[path]), "") + + snapshot = queue_health.collect_snapshot( + ["owner/repo"], + runner=runner, + generated_at="2026-09-17T03:00:00Z", + ) + assert snapshot["collection_errors"] == [] + observed_run = snapshot["repositories"][0]["runs"][0] + assert "repos/owner/repo/actions/runs/910/jobs?per_page=100" in requested_paths + assert [job["id"] for job in observed_run["jobs"]] == [912] + assert observed_run["jobs"][0]["created_at"] == "2026-09-17T02:58:00Z" + + report = queue_health.build_report( + snapshot, + now=datetime(2026, 9, 17, 3, 0, tzinfo=timezone.utc), + ) + row = report["runs"][0] + assert row["job_id"] == 912 + assert row["queue_age_source"] == "job_created_at" + assert row["queue_age_started_at"] == "2026-09-17T02:58:00Z" + assert row["queue_age_seconds"] == 120 diff --git a/tests/test_actions_queue_health_snapshot_consistency.py b/tests/test_actions_queue_health_snapshot_consistency.py new file mode 100644 index 0000000000..f956eb1738 --- /dev/null +++ b/tests/test_actions_queue_health_snapshot_consistency.py @@ -0,0 +1,195 @@ +"""Regression tests for stable queue-health identity and audit evidence.""" + +import importlib.util +import json +from datetime import datetime, timezone +from pathlib import Path +from subprocess import CompletedProcess + +import pytest + +ROOT = Path(__file__).resolve().parents[1] +MODULE_PATH = ROOT / "scripts/ci/actions_queue_health.py" +SPEC = importlib.util.spec_from_file_location("actions_queue_health_consistency", MODULE_PATH) +assert SPEC and SPEC.loader +queue_health = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(queue_health) +NOW = datetime(2026, 9, 2, 0, 0, tzinfo=timezone.utc) + + +def _pull(head_sha: str = "head") -> dict: + """Return one complete open pull-request identity fixture.""" + return { + "number": 1, + "state": "open", + "base": {"ref": "main", "repo": {"full_name": "owner/repo"}}, + "head": {"sha": head_sha}, + "updated_at": "2026-09-01T23:00:00Z", + } + + +def _run(run_id: int, workflow_id: int, *, name: str = "shared-name") -> dict: + """Return one current-head queued workflow run with stable workflow identity.""" + return { + "id": run_id, + "workflow_id": workflow_id, + "name": name, + "event": "pull_request", + "status": "queued", + "conclusion": "", + "head_sha": "head", + "created_at": "2026-09-01T23:30:00Z", + "updated_at": "2026-09-01T23:30:00Z", + "run_attempt": 1, + "pull_requests": [{"number": 1, "head": {"sha": "head"}}], + "jobs": [], + } + + +def _runner_with_pull_transition(final_pulls: list[dict]): + """Return a runner whose final pull read differs from its initial read.""" + pull_reads = 0 + + def runner(args: list[str], **kwargs: object) -> CompletedProcess[str]: + """Serve metadata, pull identities, and empty active-run partitions.""" + nonlocal pull_reads + path = args[-1] + if path == "repos/owner/repo": + payload: object = {"default_branch": "main"} + elif path == "repos/owner/repo/pulls?state=open&per_page=100": + pull_reads += 1 + payload = [_pull()] if pull_reads == 1 else final_pulls + elif "/actions/runs?status=" in path: + payload = [] + else: # pragma: no cover - any new endpoint must be explicitly governed. + raise AssertionError(f"unexpected endpoint: {path}") + return CompletedProcess(args, 0, json.dumps(payload), "") + + return runner + + +@pytest.mark.parametrize("final_pulls", [[_pull("new-head")], []]) +def test_collect_snapshot_rejects_pull_identity_changes_during_run_sweep( + final_pulls: list[dict], +) -> None: + """A concurrent push or closure cannot corrupt current-head classification.""" + snapshot = queue_health.collect_snapshot( + ["owner/repo"], + runner=_runner_with_pull_transition(final_pulls), + generated_at="2026-09-02T00:00:00Z", + ) + assert snapshot["repositories"] == [] + assert snapshot["collection_errors"] == [ + { + "repository": "owner/repo", + "error": "pull-request identity snapshot changed during collection", + } + ] + + +def test_distinct_workflow_ids_with_same_display_name_are_not_duplicate_lanes() -> None: + """Duplicate-lane evidence groups by stable workflow identity, not display name.""" + snapshot = { + "generated_at": "2026-09-01T23:45:00Z", + "repositories": [ + { + "full_name": "owner/repo", + "pull_requests": [_pull()], + "runs": [_run(100, 501), _run(101, 502)], + } + ], + } + report = queue_health.build_report(snapshot, now=NOW) + assert report["summary"]["duplicate_pending_lane_count"] == 0 + assert {row["workflow_id"] for row in report["runs"]} == {501, 502} + assert {row["workflow_identity"] for row in report["runs"]} == { + "workflow_id:501", + "workflow_id:502", + } + + +def test_same_workflow_id_across_runs_is_one_duplicate_lane() -> None: + """Two pending runs of one workflow remain a true duplicate execution lane.""" + snapshot = { + "generated_at": "2026-09-01T23:45:00Z", + "repositories": [ + { + "full_name": "owner/repo", + "pull_requests": [_pull()], + "runs": [_run(100, 501), _run(101, 501)], + } + ], + } + report = queue_health.build_report(snapshot, now=NOW) + assert report["summary"]["duplicate_pending_lane_count"] == 1 + assert report["duplicate_pending_lanes"] == [ + { + "repository": "owner/repo", + "pull_request_number": 1, + "workflow_identity": "workflow_id:501", + "workflow_name": "shared-name", + "count": 2, + } + ] + + +def test_queue_age_exports_the_timestamp_and_source_used_for_calculation() -> None: + """Report consumers can reproduce queue age from exported evidence.""" + run = _run(100, 501) + run["status"] = "in_progress" + run["jobs"] = [ + { + "id": 1000, + "name": "second-stage", + "status": "queued", + "conclusion": None, + "runner_id": None, + "runner_name": None, + "created_at": "2026-09-01T23:55:00Z", + "steps": [], + } + ] + report = queue_health.build_report( + { + "generated_at": "2026-09-01T23:56:00Z", + "repositories": [ + { + "full_name": "owner/repo", + "pull_requests": [_pull()], + "runs": [run], + } + ], + }, + now=NOW, + ) + row = report["runs"][0] + assert row["queue_age_started_at"] == "2026-09-01T23:55:00Z" + assert row["queue_age_source"] == "job_created_at" + assert row["queue_age_seconds"] == 300 + + +def test_invalid_present_workflow_id_fails_closed() -> None: + """Malformed stable workflow identity cannot silently fall back to a display name.""" + run = _run(100, 501) + run["workflow_id"] = "501" + with pytest.raises(queue_health.QueueHealthError, match="workflow id"): + queue_health.build_report( + { + "generated_at": "2026-09-01T23:45:00Z", + "repositories": [ + { + "full_name": "owner/repo", + "pull_requests": [_pull()], + "runs": [run], + } + ], + }, + now=NOW, + ) + + +def test_queue_health_workflow_does_not_grant_unused_pull_request_permission() -> None: + """The scheduler token keeps only permissions used outside the cross-repository token.""" + workflow = (ROOT / ".github/workflows/actions-queue-health.yml").read_text(encoding="utf-8") + assert "\n pull-requests: read\n" not in workflow + assert "\n pull-requests: read\n" not in workflow diff --git a/tests/test_actions_queue_health_startup_failure.py b/tests/test_actions_queue_health_startup_failure.py new file mode 100644 index 0000000000..7bd9cba97e --- /dev/null +++ b/tests/test_actions_queue_health_startup_failure.py @@ -0,0 +1,165 @@ +"""Regression coverage for pre-job GitHub Actions startup failures.""" + +from __future__ import annotations + +import importlib.util +import json +from datetime import datetime, timezone +from pathlib import Path +from subprocess import CompletedProcess + + +ROOT = Path(__file__).resolve().parents[1] +MODULE_PATH = ROOT / "scripts/ci/actions_queue_health.py" +SPEC = importlib.util.spec_from_file_location("actions_queue_health", MODULE_PATH) +assert SPEC and SPEC.loader +queue_health = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(queue_health) + + +def test_collect_snapshot_preserves_current_head_startup_failure_without_jobs() -> None: + """A terminal startup failure with zero jobs must remain visible and explicit.""" + repository_name = "owner/repo" + pull_request = { + "number": 7, + "state": "open", + "base": {"ref": "main", "repo": {"full_name": repository_name}}, + "head": {"sha": "exact-head"}, + "updated_at": "2026-09-02T10:28:00Z", + } + startup_failure_run = { + "id": 701, + "name": "CodeQL PR", + "workflow_id": 9001, + "event": "pull_request", + "status": "completed", + "conclusion": "startup_failure", + "head_sha": "exact-head", + "created_at": "2026-09-02T10:28:00Z", + "updated_at": "2026-09-02T10:28:00Z", + "run_attempt": 1, + "pull_requests": [{"number": 7, "head": {"sha": "exact-head"}}], + } + requested_paths: list[str] = [] + terminal_path = ( + f"repos/{repository_name}/actions/runs?status=completed" + "&head_sha=exact-head&per_page=50" + ) + + def runner(args: list[str], **_: object) -> CompletedProcess[str]: + """Return deterministic GitHub REST fixtures for the collector.""" + path = args[-1] + requested_paths.append(path) + if path == f"repos/{repository_name}": + payload: object = {"default_branch": "main"} + elif path == f"repos/{repository_name}/pulls?state=open&per_page=100": + payload = [pull_request] + elif path == terminal_path: + payload = {"total_count": 1, "workflow_runs": [startup_failure_run]} + elif path == f"repos/{repository_name}/actions/runs/701/jobs?per_page=100": + payload = {"total_count": 0, "jobs": []} + elif path.startswith(f"repos/{repository_name}/actions/runs?status="): + payload = {"total_count": 0, "workflow_runs": []} + else: # pragma: no cover - unexpected API expansion must fail loudly. + raise AssertionError(f"unexpected GitHub API path: {path}") + return CompletedProcess(args, 0, json.dumps(payload), "") + + snapshot = queue_health.collect_snapshot( + [repository_name], + runner=runner, + generated_at="2026-09-02T10:30:00Z", + ) + + assert snapshot["collection_errors"] == [] + assert snapshot["repositories"][0]["runs"] == [ + { + "repository": repository_name, + "id": 701, + "workflow_name": "CodeQL PR", + "event": "pull_request", + "status": "COMPLETED", + "conclusion": "STARTUP_FAILURE", + "head_sha": "exact-head", + "created_at": "2026-09-02T10:28:00Z", + "updated_at": "2026-09-02T10:28:00Z", + "run_attempt": 1, + "concurrency_group": "unavailable_from_actions_api", + "pull_requests": [{"number": 7, "head_sha": "exact-head"}], + "jobs": [], + "workflow_id": 9001, + "workflow_identity": "workflow_id:9001", + } + ] + assert terminal_path in requested_paths + assert f"repos/{repository_name}/actions/runs/701/jobs?per_page=100" in requested_paths + + report = queue_health.build_report( + snapshot, + now=datetime(2026, 9, 2, 10, 30, tzinfo=timezone.utc), + ) + row = report["runs"][0] + assert row["identity_state"] == "current_head" + assert row["execution_state"] == "terminal" + assert row["run_conclusion"] == "STARTUP_FAILURE" + assert row["jobs_materialized"] is False + assert row["blocker"] == "startup_failure_before_job_materialization" + assert row["recommended_action"] == "inspect_actions_control_plane_without_leaf_bypass" + + +def test_collect_snapshot_retains_old_failure_for_unchanged_current_head() -> None: + """Current-head startup failures must not disappear merely because they are old.""" + repository_name = "owner/repo" + pull_request = { + "number": 8, + "state": "open", + "base": {"ref": "main", "repo": {"full_name": repository_name}}, + "head": {"sha": "unchanged-head"}, + "updated_at": "2026-09-02T10:29:00Z", + } + old_current_failure = { + "id": 801, + "name": "CodeQL PR", + "workflow_id": 9001, + "event": "pull_request", + "status": "completed", + "conclusion": "startup_failure", + "head_sha": "unchanged-head", + "created_at": "2026-08-01T10:00:00Z", + "updated_at": "2026-08-01T10:00:00Z", + "run_attempt": 1, + "pull_requests": [{"number": 8, "head": {"sha": "unchanged-head"}}], + } + terminal_path = ( + f"repos/{repository_name}/actions/runs?status=completed" + "&head_sha=unchanged-head&per_page=50" + ) + requested_paths: list[str] = [] + + def runner(args: list[str], **_: object) -> CompletedProcess[str]: + """Return an old but still current-head terminal failure by exact SHA.""" + path = args[-1] + requested_paths.append(path) + if path == f"repos/{repository_name}": + payload: object = {"default_branch": "main"} + elif path == f"repos/{repository_name}/pulls?state=open&per_page=100": + payload = [pull_request] + elif path == terminal_path: + payload = {"total_count": 1, "workflow_runs": [old_current_failure]} + elif path == f"repos/{repository_name}/actions/runs/801/jobs?per_page=100": + payload = {"total_count": 0, "jobs": []} + elif path.startswith(f"repos/{repository_name}/actions/runs?status="): + payload = {"total_count": 0, "workflow_runs": []} + else: # pragma: no cover - unexpected API expansion must fail loudly. + raise AssertionError(f"unexpected GitHub API path: {path}") + return CompletedProcess(args, 0, json.dumps(payload), "") + + snapshot = queue_health.collect_snapshot( + [repository_name], + runner=runner, + generated_at="2026-09-02T10:30:00Z", + ) + + assert snapshot["collection_errors"] == [] + assert [run["id"] for run in snapshot["repositories"][0]["runs"]] == [801] + assert terminal_path in requested_paths + assert not any("&created=" in path for path in requested_paths) diff --git a/tests/test_actions_queue_health_terminal_preexecution.py b/tests/test_actions_queue_health_terminal_preexecution.py new file mode 100644 index 0000000000..59311e3cfb --- /dev/null +++ b/tests/test_actions_queue_health_terminal_preexecution.py @@ -0,0 +1,110 @@ +"""Regression contracts for terminal failures that never obtained a runner.""" + +import importlib.util +import json +from datetime import datetime, timezone +from pathlib import Path +from subprocess import CompletedProcess + +ROOT = Path(__file__).resolve().parents[1] +MODULE_PATH = ROOT / "scripts/ci/actions_queue_health.py" +SPEC = importlib.util.spec_from_file_location("actions_queue_health_terminal", MODULE_PATH) +assert SPEC and SPEC.loader +queue_health = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(queue_health) + + +def _pull_request() -> dict: + """Return the exact open-PR identity used by the failed run.""" + return { + "number": 1, + "state": "open", + "base": {"ref": "main", "repo": {"full_name": "owner/repo"}}, + "head": {"sha": "head"}, + "updated_at": "2026-09-15T13:00:00Z", + } + + +def _terminal_failure_run() -> dict: + """Return a completed failure linked to the current pull-request head.""" + return { + "id": 900, + "workflow_id": 901, + "name": "required-check", + "event": "pull_request", + "status": "completed", + "conclusion": "failure", + "head_sha": "head", + "created_at": "2026-09-15T13:05:00Z", + "updated_at": "2026-09-15T13:06:00Z", + "run_attempt": 1, + "pull_requests": [{"number": 1, "head": {"sha": "head"}}], + } + + +def _terminal_failure_job() -> dict: + """Return a failed materialized job with no runner and no executed step.""" + return { + "id": 902, + "name": "required-check", + "status": "completed", + "conclusion": "failure", + "runner_id": None, + "runner_name": None, + "created_at": "2026-09-15T13:05:00Z", + "steps": [], + } + + +def test_terminal_preexecution_failure_survives_collection_and_is_not_product_failure(monkeypatch) -> None: + """Keep failed zero-step jobs as explicit non-passing admission evidence.""" + failed_run = _terminal_failure_run() + failed_job = _terminal_failure_job() + responses: dict[str, object] = { + "repos/owner/repo": {"default_branch": "main"}, + "repos/owner/repo/pulls?state=open&per_page=100": [_pull_request()], + "repos/owner/repo/actions/runs?status=completed&head_sha=head&per_page=50": [failed_run], + "repos/owner/repo/actions/runs?status=cancelled&event=pull_request_target&per_page=50": [], + "repos/owner/repo/actions/runs/900/jobs?per_page=100": { + "total_count": 1, + "jobs": [failed_job], + }, + } + for status in ("in_progress", "pending", "queued", "requested", "waiting"): + responses[f"repos/owner/repo/actions/runs?status={status}&per_page=50"] = [] + + def runner(args: list[str], **_: object) -> CompletedProcess[str]: + """Return deterministic GitHub REST payloads for the regression specimen.""" + path = args[-1] + if path not in responses: + raise AssertionError(f"unexpected endpoint: {path}") + return CompletedProcess(args, 0, json.dumps(responses[path]), "") + + snapshot = queue_health.collect_snapshot( + ["owner/repo"], + runner=runner, + generated_at="2026-09-15T13:10:00Z", + ) + observed_runs = snapshot["repositories"][0]["runs"] + assert [run["id"] for run in observed_runs] == [900] + assert observed_runs[0]["jobs"][0]["steps_count"] == 0 + assert observed_runs[0]["jobs"][0]["runner_id"] == 0 + + report = queue_health.build_report( + snapshot, + now=datetime(2026, 9, 15, 13, 10, tzinfo=timezone.utc), + ) + row = report["runs"][0] + assert row["identity_state"] == "current_head" + assert row["run_conclusion"] == "FAILURE" + assert row["execution_state"] == "terminal_pre_execution_failure" + assert row["admission_state"] == "terminal_pre_execution_failure" + assert row["is_pending"] is False + assert row["runner_assigned"] is False + assert row["blocker"] == "terminal_pre_execution_failure_before_runner_assignment" + assert row["recommended_action"] == "inspect_actions_control_plane_without_leaf_bypass" + assert report["summary"]["terminal_pre_execution_failure_count"] == 1 + assert report["summary"]["terminal_job_count"] == 1 + actions = list(report["summary"]["external_actions"]) + monkeypatch.setattr(queue_health, "_CORE_BUILD_REPORT", lambda *_args, **_kwargs: report) + assert queue_health.build_report(snapshot)["summary"]["external_actions"] == actions diff --git a/tests/test_adr_identifier_uniqueness.py b/tests/test_adr_identifier_uniqueness.py new file mode 100644 index 0000000000..c82662b79a --- /dev/null +++ b/tests/test_adr_identifier_uniqueness.py @@ -0,0 +1,30 @@ +"""Contracts for unambiguous architecture decision record identifiers.""" + +from __future__ import annotations + +from collections import defaultdict +from pathlib import Path +import re + + +ROOT = Path(__file__).resolve().parents[1] +ADR_DIRECTORY = ROOT / "docs" / "adr" +ADR_FILE_ID = re.compile(r"(?:^|[-_])(?P\d{4})(?:[-_]|$)") + + +def test_adr_file_identifiers_are_unique() -> None: + """Each numeric ADR identifier must name exactly one decision record.""" + records_by_identifier: dict[str, list[str]] = defaultdict(list) + + for path in sorted(ADR_DIRECTORY.glob("*.md")): + match = ADR_FILE_ID.search(path.stem) + if match is not None: + records_by_identifier[match.group("identifier")].append(path.name) + + duplicates = { + identifier: records + for identifier, records in records_by_identifier.items() + if len(records) > 1 + } + + assert duplicates == {}, f"duplicate ADR identifiers: {duplicates}" diff --git a/tests/test_agent_mention_downstream_idempotency.py b/tests/test_agent_mention_downstream_idempotency.py index c9b6ab86ea..2be8080c7f 100644 --- a/tests/test_agent_mention_downstream_idempotency.py +++ b/tests/test_agent_mention_downstream_idempotency.py @@ -1,5 +1,8 @@ """Static contracts for downstream review-agent invocation idempotency.""" +from tests.test_required_workflow_queue_contract import ( + workflow_level_cancels_in_progress, +) from pathlib import Path ROOT = Path(__file__).resolve().parents[1] @@ -33,10 +36,19 @@ def test_downstream_workflows_claim_artifacts_and_coalesce_by_pull_request() -> ): header = text.split("\npermissions:\n", 1)[0] job = text.split(" validate-and-forward:\n", 1)[1] - concurrency = job.split(" concurrency:\n", 1)[1].split( - "\n runs-on:", 1 - )[0] - assert "concurrency:" not in header + concurrency = header.split("\nconcurrency:\n", 1)[1] + # 109d79b7 ("replace unsupported queue concurrency") deleted a + # workflow-level block that used ``queue: max``, a key GitHub Actions + # does not support, and parked the group on the job while it was at it. + # What that commit pins is the absence of ``queue:``, not the level: the + # group is back at workflow level because a job-level group is never + # evaluated while the run waits behind the organization job ceiling, so a + # superseded mention held its queue slot until a runner freed up. Every + # other queue-bearing workflow here (strix.yml, noema-review.yml, + # opencode-review.yml, codeql-scan-dispatch.yml, + # opencode-review-dispatch.yml) keys its group at workflow level too. + assert "queue:" not in header + assert " concurrency:" not in job assert "github.event.client_payload.agent_invocation_key" in text assert "cwl-agent-invocation:" in text assert "source_comment_id" in text @@ -45,7 +57,7 @@ def test_downstream_workflows_claim_artifacts_and_coalesce_by_pull_request() -> f"group: {workflow_name}-${{{{ github.event.client_payload.target_repository }}}}-${{{{ github.event.client_payload.pr_number || github.run_id }}}}" in concurrency ) - assert "cancel-in-progress: true" in concurrency + assert workflow_level_cancels_in_progress(text) assert "queue: max" not in text assert "^[0-9a-f]{64}$" in text assert "^[1-9][0-9]*$" in text @@ -117,3 +129,16 @@ def test_quality_gate_runs_full_suite_for_docs_and_exact_diff() -> None: coverage_config = text.split("[run]\n", 1)[1].split("[report]\n", 1)[0] assert "scripts/ci/agent_mention_router.py" in coverage_config assert "scripts/ci/agent_mention_sweep.py" in coverage_config + + +def test_forwarders_restrict_self_hosted_admission_to_trusted_main() -> None: + """Branch workflows cannot select the main-only privileged runner group.""" + for path in (NOEMA_WORKFLOW, OPENCODE_WORKFLOW): + text = path.read_text(encoding="utf-8") + selector = next(line for line in text.splitlines() if line.startswith(" group:")) + assert "github.repository == 'ContextualWisdomLab/.github'" in selector + assert "endsWith(github.workflow_ref, '@refs/heads/main')" in selector + assert '"group":"CWL MCP remediation"' in selector + assert '"labels":["self-hosted","linux","x64"]' in selector + assert '|| fromJSON(\'{"group":"CWL CI isolated","labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}\')' in selector + assert "actions/checkout@" not in text diff --git a/tests/test_agent_mention_queue_isolation.py b/tests/test_agent_mention_queue_isolation.py index e93ae61aed..d751076661 100644 --- a/tests/test_agent_mention_queue_isolation.py +++ b/tests/test_agent_mention_queue_isolation.py @@ -2,6 +2,9 @@ from __future__ import annotations +import re + + from pathlib import Path ROOT = Path(__file__).resolve().parents[1] @@ -68,4 +71,7 @@ def test_interactive_queue_retires_older_requests_for_only_the_same_pr() -> None concurrency = _concurrency_block(local_job) assert "github.event.issue.number || github.run_id" in concurrency - assert "cancel-in-progress: true" in concurrency + # Anchored on the JOB block, not the workflow-level helper: this router + # declares no workflow-level concurrency, so the sibling helper would raise + # rather than read the block this test is about. + assert re.search(r"(?m)^[ \t]+cancel-in-progress:[ \t]+true[ \t]*$", concurrency) diff --git a/tests/test_agent_mention_workflow_contract.py b/tests/test_agent_mention_workflow_contract.py index c5fc4cae54..1cce19993e 100644 --- a/tests/test_agent_mention_workflow_contract.py +++ b/tests/test_agent_mention_workflow_contract.py @@ -20,7 +20,8 @@ def test_workflow_uses_local_event_and_central_sweep_with_job_scoped_writes() -> assert "workflow_dispatch:" not in header assert "permissions:\n contents: read" in header assert "contents: write" not in header - assert text.count("runs-on: ubuntu-24.04") == 2 + assert text.count("group: CWL central control") == 2 + assert text.count("labels: [self-hosted, linux, x64]") == 2 assert text.count(CHECKOUT_PIN) == 2 assert "ubuntu-latest" not in text assert "actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8" not in text @@ -51,6 +52,19 @@ def test_workflow_uses_local_event_and_central_sweep_with_job_scoped_writes() -> assert "agent_mention_sweep.py" in sweep +def test_full_suite_tooling_lock_includes_collection_dependencies() -> None: + """A quality install must provide both parsers imported during suite collection.""" + lock = (ROOT / "requirements-opencode-review-ci-hashes.txt").read_text( + encoding="utf-8" + ) + requirements = { + line.split("==", 1)[0].casefold() + for line in lock.splitlines() + if line and not line.startswith(("#", " ", "-")) and "==" in line + } + assert {"defusedxml", "pyyaml"} <= requirements + + def test_quality_workflow_measures_exact_files_without_module_name_warnings() -> None: """Coverage includes the two script paths instead of treating paths as modules.""" diff --git a/tests/test_agent_review_runtime_quality_consolidation.py b/tests/test_agent_review_runtime_quality_consolidation.py index b0c90eb707..4fecc93656 100644 --- a/tests/test_agent_review_runtime_quality_consolidation.py +++ b/tests/test_agent_review_runtime_quality_consolidation.py @@ -2,8 +2,14 @@ from __future__ import annotations +from tests.test_required_workflow_queue_contract import ( + workflow_level_cancels_in_progress, +) + +import os import re import subprocess +import textwrap from pathlib import Path import pytest @@ -56,7 +62,7 @@ def test_pr_concurrency_cancels_only_the_same_workflow_repository_and_pr() -> No "${{ github.repository }}-${{ github.event.pull_request.number }}" in concurrency_contract ) - assert "cancel-in-progress: true" in concurrency_contract + assert workflow_level_cancels_in_progress(workflow) assert "github.sha" not in concurrency_contract assert "head.sha" not in concurrency_contract assert "github.ref" not in concurrency_contract @@ -99,6 +105,7 @@ def test_consolidated_workflow_preserves_all_contract_suites() -> None: "tests/test_opencode_rust_coverage_toolchain_contract.py", "tests/test_docs_only_pr_runner_admission.py", "tests/test_strix_changed_path_policy.py", + "tests/test_strix_evidence_binding.py", "tests/test_strix_model_behavior_error.py", "tests/test_strix_nvidia_nim_not_found_fallback.py", "tests/test_strix_workflow_dependency_hashes.py", @@ -123,6 +130,116 @@ def test_consolidated_workflow_preserves_all_contract_suites() -> None: assert required_path in workflow +@pytest.mark.parametrize( + "changed_path", + ( + "scripts/ci/codeql_ghas_configuration_identity.py", + "tests/test_codeql_ghas_configuration_identity.py", + "tests/test_codeql_ghas_analyses_pagination.py", + "tests/test_github_api_url_boundary.py", + ), +) +def test_ghas_runtime_changes_start_the_quality_runner(changed_path: str) -> None: + """Admit the helper and each focused GHAS regression independently.""" + trigger = _workflow_text().split("on:\n", 1)[1].split("\nconcurrency:\n", 1)[0] + assert f' - "{changed_path}"' in trigger + + +@pytest.mark.parametrize( + ("changed_path", "expected_suites"), + ( + ("scripts/ci/codeql_ghas_configuration_identity.py", {"ghas"}), + ("tests/test_codeql_ghas_configuration_identity.py", {"ghas"}), + ("tests/test_codeql_ghas_analyses_pagination.py", {"ghas"}), + ("tests/test_github_api_url_boundary.py", {"ghas"}), + ("tests/test_agent_review_runtime_quality_consolidation.py", {"ghas"}), + ( + ".github/workflows/agent-review-runtime-quality-ci.yml", + {"noema", "opencode", "strix", "queue", "review_repair", + "commercial_readiness", "exact_artifact", "ghas"}, + ), + ("docs/doctoring/codeql-ghas-analyses-pagination.md", set()), + ), +) +def test_ghas_runtime_selector_executes_the_actual_shell( + changed_path: str, expected_suites: set[str], tmp_path: Path +) -> None: + """Execute the full selector with a synthetic diff and a real HEAD guard.""" + selector_step = _workflow_text().split( + "- name: Select affected contract suites", 1 + )[1].split(" - name:", 1)[0] + script = textwrap.dedent(selector_step.split(" run: |\n", 1)[1]) + output = tmp_path / "suites.txt" + head = subprocess.check_output( + ["git", "rev-parse", "HEAD"], cwd=REPOSITORY_ROOT, text=True + ).strip() + # Only changed-path input is synthetic; HEAD verification uses actual Git. + diff_fixture = ( + 'git() {\n' + ' if [[ "$1" == diff ]]; then\n' + ' printf "%s\\n" "$CHANGED_PATH"\n' + ' else\n' + ' command git "$@"\n' + ' fi\n' + '}\n' + ) + result = subprocess.run( + ["bash", "--noprofile", "--norc", "-e", "-o", "pipefail", "-c", + diff_fixture + script], + cwd=REPOSITORY_ROOT, + env={**os.environ, "HEAD_SHA": head, "BASE_SHA": head, + "CHANGED_PATH": changed_path, "GITHUB_OUTPUT": str(output)}, + text=True, capture_output=True, check=True, + ) + suites = dict(line.split("=", 1) for line in output.read_text().splitlines()) + assert suites == { + name: str(name in expected_suites).lower() + for name in ("noema", "opencode", "strix", "queue", "review_repair", + "commercial_readiness", "exact_artifact", "ghas") + } + assert result.stdout == result.stderr == "" + + +@pytest.mark.parametrize("pytest_exit", (0, 42)) +def test_ghas_runtime_step_runs_exact_tests_and_fails_closed( + pytest_exit: int, tmp_path: Path +) -> None: + """Run the actual step shell and capture its argv without invoking providers.""" + workflow = _workflow_text() + step_name = " - name: Verify GHAS identity, pagination, and API boundary contracts\n" + assert step_name in workflow, "focused GHAS test-command step is missing" + step = workflow.split(step_name, 1)[1].split(" - name:", 1)[0] + assert "if: steps.affected_suites.outputs.ghas == 'true'" in step + script = textwrap.dedent(step.split(" run: |\n", 1)[1]) + calls = tmp_path / "python-calls.txt" + recorder = ( + 'python() {\n' + ' printf "%s\\n" "$*" >> "$CALLS"\n' + ' if [[ "$*" == *pytest* ]]; then return "$PYTEST_EXIT"; fi\n' + '}\n' + ) + result = subprocess.run( + ["bash", "--noprofile", "--norc", "-e", "-o", "pipefail", "-c", + recorder + script], + cwd=REPOSITORY_ROOT, + env={**os.environ, "CALLS": str(calls), "PYTEST_EXIT": str(pytest_exit)}, + text=True, capture_output=True, check=False, + ) + tests = ( + "tests/test_codeql_ghas_configuration_identity.py " + "tests/test_codeql_ghas_analyses_pagination.py " + "tests/test_github_api_url_boundary.py" + ) + expected = [f"-m pytest -q {tests}"] + if pytest_exit == 0: + expected.append( + "-m compileall -q scripts/ci/codeql_ghas_configuration_identity.py " + tests + ) + assert calls.read_text().splitlines() == expected + assert result.returncode == pytest_exit + assert result.stdout == result.stderr == "" + + def test_exact_head_is_verified_before_selected_suites_run() -> None: """Reject a checkout that differs from the pull request's current head.""" @@ -149,6 +266,49 @@ def test_review_repair_suite_is_selected_and_conditionally_executed() -> None: assert workflow.count("runs-on:") == 1 +def test_review_launcher_runtime_is_owned_by_the_review_repair_suite() -> None: + """Trigger, execute, compile, and measure the launcher's runtime contracts.""" + + workflow = _workflow_text() + + assert workflow.count("tests/test_contextual_orchestrator_review_launcher.py") == 4 + assert workflow.count("tests/test_review_preflight_concurrency.py") == 4 + assert "--cov=scripts.ci.contextual_orchestrator_review_launcher" in workflow + + +@pytest.mark.parametrize( + "changed_path", + ( + "requirements-opencode-review-ci.txt", + "requirements-noema-document-ci.txt", + "scripts/ci/compile_opencode_review_lock.sh", + ), +) +def test_common_lock_sources_select_every_python_consumer_suite( + changed_path: str, +) -> None: + """A common-lock source change must execute both Python consumer suites.""" + + workflow = _workflow_text() + selector = workflow.split(' case "$changed_path" in\n', 1)[1].split( + " esac", 1 + )[0] + result = subprocess.run( + [ + "bash", "--noprofile", "--norc", "-e", "-o", "pipefail", "-c", + 'IFS= read -r changed_path\nnoema_suite=false\nopencode_suite=false\n' + 'case "$changed_path" in\n' + selector + + 'esac\nprintf "%s,%s" "$noema_suite" "$opencode_suite"\n', + ], + input=changed_path + "\n", + text=True, + capture_output=True, + check=True, + ) + assert result.stdout == "true,true" + assert result.stderr == "" + + @pytest.mark.parametrize( ("changed_path", "starts_runner", "review_repair", "queue"), ( diff --git a/tests/test_all_self_hosted_runner_contract.py b/tests/test_all_self_hosted_runner_contract.py new file mode 100644 index 0000000000..948968f3a7 --- /dev/null +++ b/tests/test_all_self_hosted_runner_contract.py @@ -0,0 +1,230 @@ +"""Require self-hosted CI without admitting PR code to privileged runner pools. + +The isolated label is an operator provisioning requirement, not evidence that an +isolated machine exists. Existing trusted control and review selectors retain +their main-ref predicates; their non-main fallback must never be GitHub-hosted. +""" +from pathlib import Path +import json +import re +import pytest +import yaml + + +STATIC_RUNNER_GROUP_LABELS = { + 'CWL central control': set(), + 'CWL central CodeQL': set(), + 'CWL central OpenCode': set(), + 'CWL CI isolated': {'cwlab-ci-isolated'}, + 'CWL law CI': {'law-ai-agent-ci'}, + 'CWL QSR hostile workload': {'cwl-hostile-workload'}, +} + + +def required_static_labels(group: object) -> set[str]: + """Return the exact additional labels admitted for a static runner group.""" + assert group in STATIC_RUNNER_GROUP_LABELS, ('unsupported static group', group) + return {'self-hosted', 'linux', 'x64'} | STATIC_RUNNER_GROUP_LABELS[group] + + +def test_every_runner_requires_self_hosted_and_no_hosted_fallback() -> None: + """Inspect every declaration, including expression and matrix selectors.""" + declarations = [] + for path in sorted(Path('.github/workflows').glob('*.yml')): + lines = path.read_text().splitlines() + for index, line in enumerate(lines): + if re.match(r'^ runs-on:', line): + block = line + if line.strip() == 'runs-on:': + block += '\n' + '\n'.join(lines[index + 1:index + 3]) + declarations.append((path, block)) + assert declarations + for path, block in declarations: + assert 'self-hosted' in block, (path, block) + assert not re.search(r'fromJSON\(\'\["ubuntu-|\|\| \'"ubuntu-', block), (path, block) + assert not re.search(r'runs-on: (?:ubuntu-|windows-|macos-)', block), (path, block) + + +def test_nonprivileged_runners_require_isolated_label() -> None: + """Check actual YAML selectors, including static labels and both extensions.""" + paths = sorted(set(Path('.github/workflows').glob('*.yml')) | + set(Path('.github/workflows').glob('*.yaml'))) + observed = 0 + groups = set(STATIC_RUNNER_GROUP_LABELS) + for path in paths: + for job_id, job in yaml.safe_load(path.read_text())['jobs'].items(): + if 'runs-on' not in job: + continue + observed += 1 + runner = job['runs-on'] + if isinstance(runner, dict): + group = runner.get('group') + if isinstance(group, str) and group.startswith('${{'): + # Dynamic predicates remain covered by the exact fallback, + # typed-pair and R-matrix contracts, not literal evaluation. + assert any(name in group for name in groups), (path, job_id, group) + continue + labels = runner.get('labels') + assert isinstance(labels, list) and all(isinstance(label, str) for label in labels), ( + path, job_id, 'static labels must be a string list', labels, + ) + required = required_static_labels(group) + assert required.issubset(label.lower() for label in labels), ( + path, job_id, 'missing required static labels', required.difference(labels), + ) + elif isinstance(runner, list): + # Preserve the inherited K-CSAP selector exactly; it is the only + # repository-scoped list admission and remains fail-closed. + assert (path.stem, job_id) == ('k-csap-skills-quality', 'quality'), ( + path, job_id, 'unsupported static label-only selector') + assert all(isinstance(label, str) for label in runner), (path, job_id, runner) + assert {label.lower() for label in runner} == { + 'self-hosted', 'linux', 'x64', 'k-csap-isolated', + }, (path, job_id, 'missing or unexpected repository-scoped labels', runner) + else: + assert isinstance(runner, str) and 'cwlab-ci-isolated' in runner, (path, job_id, runner) + assert observed, 'runner selector coverage is empty' + + +@pytest.mark.parametrize( + ('workflow_name', 'job_id', 'labels', 'accepted'), + [ + ('k-csap-skills-quality.yml', 'quality', + ['self-hosted', 'Linux', 'X64', 'k-csap-isolated'], True), + ('k-csap-skills-quality.yml', 'quality', + ['self-hosted', 'Linux', 'X64'], False), + ('k-csap-skills-quality.yml', 'quality', + ['self-hosted', 'Linux', 'k-csap-isolated'], False), + ('k-csap-skills-quality.yml', 'quality', + ['self-hosted', 'Linux', 'X64', 'cwlab-control'], False), + ('foreign-quality.yml', 'quality', + ['self-hosted', 'Linux', 'X64', 'k-csap-isolated'], False), + ('k-csap-skills-quality.yml', 'foreign-job', + ['self-hosted', 'Linux', 'X64', 'k-csap-isolated'], False), + ], +) +def test_existing_kcsap_list_selector_remains_exact( + tmp_path, monkeypatch, workflow_name, job_id, labels, accepted +): + """Admit one inherited label-only selector, not arbitrary runner lists.""" + directory = tmp_path / '.github/workflows' + directory.mkdir(parents=True) + (directory / workflow_name).write_text( + yaml.safe_dump({'jobs': {job_id: {'runs-on': labels}}}), encoding='utf-8' + ) + monkeypatch.chdir(tmp_path) + if accepted: + test_nonprivileged_runners_require_isolated_label() + else: + with pytest.raises(AssertionError): + test_nonprivileged_runners_require_isolated_label() + + +def test_static_runner_group_policy_is_explicit_and_fail_closed() -> None: + """Bind dedicated groups to their isolation labels and reject unknown groups.""" + assert required_static_labels('CWL law CI') == { + 'self-hosted', 'linux', 'x64', 'law-ai-agent-ci', + } + assert required_static_labels('CWL QSR hostile workload') == { + 'self-hosted', 'linux', 'x64', 'cwl-hostile-workload', + } + with pytest.raises(AssertionError, match='unsupported static group'): + required_static_labels('CWL ungoverned CI') + + +def test_isolated_fallback_never_selects_privileged_group() -> None: + """Inspect scalar and typed runner fallbacks, never an empty declaration set.""" + observed = 0 + expected = { + 'group': 'CWL CI isolated', + 'labels': ['self-hosted', 'linux', 'x64', 'cwlab-ci-isolated'], + } + paths = sorted(set(Path('.github/workflows').glob('*.yml')) | + set(Path('.github/workflows').glob('*.yaml'))) + for path in paths: + for job_id, job in yaml.safe_load(path.read_text())['jobs'].items(): + runner = job.get('runs-on') + fields = list(runner.values()) if isinstance(runner, dict) else [runner] + expressions = [field for field in fields if isinstance(field, str) and + field.startswith('${{') and '||' in field and + 'matrix.config.os' not in field] + if not expressions: + continue + observed += 1 + for expression in expressions: + fallback = expression.rsplit('||', 1)[1] + match = re.fullmatch( + r"\s*(?:fromJSON\(\s*)?'(\{[^']*\})'\s*\)*\s*" + r"(?:\.group|\.labels)?\s*\}\}", fallback, + ) + assert match is not None, (path, job_id, 'unsupported fallback', fallback) + assert json.loads(match.group(1)) == expected, ( + path, job_id, 'unsafe fallback', fallback, + ) + assert observed == 29, ('conditional runner fallback coverage', observed) + + +def test_isolated_label_is_scoped_to_dedicated_runner_group() -> None: + """A mutable label cannot replace the isolated runner access boundary.""" + for path in sorted(Path('.github/workflows').glob('*.yml')): + lines = path.read_text().splitlines() + for index, line in enumerate(lines): + if not re.match(r'^ runs-on:', line): + continue + block = line + if line.strip() == 'runs-on:': + block += '\n' + '\n'.join(lines[index + 1:index + 3]) + if 'cwlab-ci-isolated' in block: + assert 'CWL CI isolated' in block, (path, block) + + +TRUSTED_MAIN_UNION_SELECTORS = { + '.github/workflows/actions-queue-health.yml': {'collect'}, + '.github/workflows/audit-central-ruleset.yml': {'audit'}, + '.github/workflows/organization-commercial-readiness-loop.yml': {'coordinate'}, + '.github/workflows/pr-auto-rebase.yml': {'auto-rebase'}, + '.github/workflows/repository-metadata-reconcile.yml': {'validate', 'apply'}, + '.github/workflows/sbom-inventory-scheduler.yml': {'aggregate-sbom-inventory'}, +} + + +def test_trusted_main_union_selectors_preserve_control_and_isolated_fallback() -> None: + """Resolve main/head routing conflicts without restoring hosted fallback.""" + trusted_main = '{"group":"CWL MCP remediation","labels":["self-hosted","linux","x64"]}' + isolated_fallback = ( + '{"group":"CWL CI isolated",' + '"labels":["self-hosted","linux","x64","cwlab-ci-isolated"]}' + ) + for workflow_path, job_ids in TRUSTED_MAIN_UNION_SELECTORS.items(): + workflow = yaml.safe_load(Path(workflow_path).read_text()) + for job_id in job_ids: + mapping = workflow['jobs'][job_id]['runs-on'] + assert isinstance(mapping, dict), (workflow_path, job_id, mapping) + assert set(mapping) == {'group', 'labels'} + group = mapping['group'] + labels = mapping['labels'] + assert group.endswith(').group }}') + assert labels.endswith(').labels }}') + assert group.removesuffix(').group }}') == labels.removesuffix(').labels }}') + runner = '${{ ' + group[5:].removesuffix(').group }}') + ' }}' + expected = ( + "${{ github.repository == 'ContextualWisdomLab/.github' && " + "github.ref == 'refs/heads/main' && " + "endsWith(github.workflow_ref, '@refs/heads/main') && " + f"fromJSON('{trusted_main}') || fromJSON('{isolated_fallback}') }}" + "}" + ) + assert runner == expected, (workflow_path, job_id, runner) + condition = runner[4:].split(' && fromJSON(', 1)[0] + for repository in ('ContextualWisdomLab/.github', 'ContextualWisdomLab/example'): + for ref in ('refs/heads/main', 'refs/pull/2565/merge', 'refs/heads/candidate'): + for workflow_ref in ('main', 'candidate'): + terms = { + "github.repository == 'ContextualWisdomLab/.github'": repository == 'ContextualWisdomLab/.github', + "github.ref == 'refs/heads/main'": ref == 'refs/heads/main', + "endsWith(github.workflow_ref, '@refs/heads/main')": workflow_ref == 'main', + } + admitted = all(terms[term] for term in condition.split(' && ')) + wanted = repository == 'ContextualWisdomLab/.github' and ref == 'refs/heads/main' and workflow_ref == 'main' + assert admitted == wanted, (workflow_path, job_id, repository, ref, workflow_ref) + assert 'ubuntu-24.04' not in runner + assert 'ubuntu-latest' not in runner diff --git a/tests/test_audit_org_codeql_coverage.py b/tests/test_audit_org_codeql_coverage.py index ccd2cd9c42..8d95cdb0b1 100644 --- a/tests/test_audit_org_codeql_coverage.py +++ b/tests/test_audit_org_codeql_coverage.py @@ -14,6 +14,23 @@ def covered_by_default_setup(name: str) -> dict: "name": name, "archived": False, "default_setup_state": "configured", + "default_setup_languages": ["actions", "python"], + "latest_codeql_analysis": None, + } + + +def default_setup_scanning_nothing(name: str) -> dict: + """Return a repository whose default-setup is on but has no languages enabled. + + The live shape measured on 2026-09-07 for ``life-os``, ``aFIPC`` and + ``inkspan``: ``state`` is ``configured``, ``languages`` is empty and + ``schedule`` is null. ``life-os`` had zero CodeQL analyses of any language. + """ + return { + "name": name, + "archived": False, + "default_setup_state": "configured", + "default_setup_languages": [], "latest_codeql_analysis": None, } @@ -89,6 +106,60 @@ def test_default_setup_alone_counts_as_coverage() -> None: assert audit.audit_codeql_coverage(repositories, now=NOW) == [] +def test_default_setup_with_no_languages_enabled_is_not_coverage() -> None: + """A setup that scans nothing must not satisfy the configured-state check. + + Measured 2026-09-07: ``life-os`` reports ``configured`` with an empty + ``languages`` list and has zero CodeQL analyses of any language, while + ``codeql-pr.yml`` still runs on every pull request head. Before this check + the audit passed it on the state alone. + """ + repositories = [default_setup_scanning_nothing("life-os")] + + assert audit.audit_codeql_coverage(repositories, now=NOW) == [ + "life-os has CodeQL default-setup configured with no languages enabled, " + "so it scans nothing and produces no analyses" + ] + + +def test_default_setup_scanning_nothing_still_passes_on_a_fresh_analysis() -> None: + """The empty-language setup is only a gap when nothing else covers the repo. + + ``aFIPC`` and ``inkspan`` both report the empty-language shape yet receive + analyses from a repository-local ``codeql.yml``, so flagging them would be a + false alarm. + """ + repository = default_setup_scanning_nothing("aFIPC") + repository["latest_codeql_analysis"] = covered_by_recent_analysis("aFIPC")[ + "latest_codeql_analysis" + ] + + assert audit.audit_codeql_coverage([repository], now=NOW) == [] + + +def test_payload_without_the_languages_key_fails_closed() -> None: + """A payload predating the workflow change must not pass on state alone. + + Falling back to ``default_setup_state`` when the key is missing would + silently restore the gap this check exists to close. + """ + repository = covered_by_default_setup("PolicyWeave") + del repository["default_setup_languages"] + + assert audit.audit_codeql_coverage([repository], now=NOW) == [ + "PolicyWeave has CodeQL default-setup configured with no languages " + "enabled, so it scans nothing and produces no analyses" + ] + + +def test_non_list_languages_value_fails_closed() -> None: + """A malformed ``languages`` value is not evidence that anything is scanned.""" + repository = covered_by_default_setup("PolicyWeave") + repository["default_setup_languages"] = "python" + + assert len(audit.audit_codeql_coverage([repository], now=NOW)) == 1 + + def test_recent_analysis_alone_counts_as_coverage() -> None: repositories = [covered_by_recent_analysis("TEPP")] @@ -191,7 +262,14 @@ def test_null_latest_analysis_is_not_coverage() -> None: ] -def test_audit_codeql_coverage_defaults_now_to_current_time() -> None: +def test_audit_codeql_coverage_defaults_now_to_current_time(monkeypatch) -> None: + class FixedClock(datetime): + @classmethod + def now(cls, tz=None): + assert tz is timezone.utc + return NOW + + monkeypatch.setattr(audit, "datetime", FixedClock) repositories = [covered_by_recent_analysis("DefaultNowRepo", days_ago=0)] assert audit.audit_codeql_coverage(repositories) == [] @@ -228,7 +306,14 @@ def test_main_fail_path_reports_gaps_from_stdin(monkeypatch, capsys) -> None: assert "FAIL: 1 repositories have no CodeQL coverage" in captured.err -def test_main_pass_path_reports_from_file_arg(tmp_path, capsys) -> None: +def test_main_pass_path_reports_from_file_arg(tmp_path, capsys, monkeypatch) -> None: + class FixedClock(datetime): + @classmethod + def now(cls, tz=None): + assert tz is timezone.utc + return NOW + + monkeypatch.setattr(audit, "datetime", FixedClock) payload_path = tmp_path / "repositories.json" payload_path.write_text( json.dumps([covered_by_default_setup("ELUNVERA"), covered_by_recent_analysis("Orgmetra")]), @@ -269,3 +354,60 @@ def test_parse_args_defaults_to_none() -> None: args = audit.parse_args([]) assert args.repositories_json is None + + +def test_main_refuses_an_empty_payload_instead_of_passing_vacuously( + monkeypatch, capsys +) -> None: + """An audit that examined nothing must not print PASS. + + ``audit_codeql_coverage([])`` returning no gaps is correct -- there are no + repositories to have gaps. What is wrong is ``main`` turning that into + "PASS: all 0 repositories have real CodeQL coverage" and exiting 0, which + is the same vacuous-pass shape as a default setup that is configured with + no languages enabled. + """ + monkeypatch.setattr("sys.stdin", StringIO("[]")) + + assert audit.main([]) == 2 + captured = capsys.readouterr() + assert "audited nothing" in captured.err + assert "PASS" not in captured.out + + +def test_main_refuses_a_payload_of_only_archived_repositories(monkeypatch, capsys) -> None: + """Counting what was supplied, not what was examined, left the hole open. + + An archived-only payload is non-empty, so it passed the first version of + this guard, and archived repositories are then legitimately skipped -- the + run reported "PASS: all 1 repositories have real CodeQL coverage" having + examined none of them. Found in review, one layer out from the empty-payload + case it replaces. + """ + monkeypatch.setattr( + "sys.stdin", StringIO(json.dumps([uncovered("trivy-sarif-repro", archived=True)])) + ) + + assert audit.main([]) == 2 + captured = capsys.readouterr() + assert "0 of 1 repositories were eligible" in captured.err + assert "PASS" not in captured.out + + +def test_pass_line_counts_examined_repositories_not_supplied_ones( + tmp_path, capsys +) -> None: + """The PASS line must not credit archived repositories it never examined.""" + payload = tmp_path / "repositories.json" + payload.write_text( + json.dumps( + [ + covered_by_default_setup("PolicyWeave"), + uncovered("trivy-sarif-repro", archived=True), + ] + ), + encoding="utf-8", + ) + + assert audit.main([str(payload)]) == 0 + assert "PASS: all 1 repositories" in capsys.readouterr().out diff --git a/tests/test_bootstrap_codeql_pull_requests.py b/tests/test_bootstrap_codeql_pull_requests.py index eb20c3d1e0..12fd1d8b52 100644 --- a/tests/test_bootstrap_codeql_pull_requests.py +++ b/tests/test_bootstrap_codeql_pull_requests.py @@ -2,6 +2,10 @@ from __future__ import annotations +from tests.test_required_workflow_queue_contract import ( + workflow_level_cancels_in_progress, +) + from io import StringIO import json import subprocess @@ -67,7 +71,7 @@ def test_rendered_workflow_redetects_stacks_and_pins_every_action() -> None: assert "pull_request:" not in workflow assert "github.event.pull_request" not in workflow assert "github.event_name == 'push' && github.ref || github.event_name" in workflow - assert "cancel-in-progress: true" in workflow + assert workflow_level_cancels_in_progress(workflow) assert workflow.count("@cdf488f595d80d6e07e03d4674febd5ab45fa938 # v4.37.9") == 2 assert "actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0" in workflow diff --git a/tests/test_central_gh_first_use_contract.py b/tests/test_central_gh_first_use_contract.py new file mode 100644 index 0000000000..6a9ad2c17d --- /dev/null +++ b/tests/test_central_gh_first_use_contract.py @@ -0,0 +1,319 @@ +"""Require job-local GitHub CLI provisioning before the affected host calls.""" +from __future__ import annotations + +import copy +from pathlib import Path +import re + +import pytest +import yaml + +ROOT = Path(__file__).resolve().parents[1] +TARGETS = ( + ("strix.yml", "strix"), + ("codeql-pr.yml", "analyze-head"), + ("codeql-pr.yml", "dispatch-current-head"), +) +INSTALL = "Provision pinned job-local GitHub CLI" +VERIFY = "Verify pinned job-local GitHub CLI selection" + + +def _steps(filename: str, job: str) -> list[dict]: + """Read the actual executable steps without interpreting the event key.""" + return yaml.safe_load((ROOT / ".github/workflows" / filename).read_text())["jobs"][job]["steps"] + + +def _assert_order(steps: list[dict]) -> None: + """Require unconditional setup and selection before the first literal call.""" + installs = [i for i, step in enumerate(steps) if step.get("name") == INSTALL] + verifies = [i for i, step in enumerate(steps) if step.get("name") == VERIFY] + assert len(installs) == len(verifies) == 1, "missing gh provisioning/selection" + install, verify = installs[0], verifies[0] + calls = [i for i, step in enumerate(steps) if i not in (install, verify) + and re.search(r"(?:^|[\s;|&($])(?:/[^\s]+/)?gh\s+(?:api|auth|pr|run)\b", step.get("run", ""))] + assert calls and install < verify < min(calls), "gh provisioning is after first use" + for i in (install, verify): + # Conditional readiness must exactly follow every affected consumer. + consumer_guards = [steps[index].get("if") for index in calls] + expected = consumer_guards[0] if len(set(consumer_guards)) == 1 else None + assert steps[i].get("if") == expected + assert "continue-on-error" not in steps[i] + assert steps[i].get("shell") == "bash" + assert "sha256" in steps[install]["run"] + assert 'with open(os.environ["GITHUB_PATH"], "a", encoding="utf-8")' in steps[install]["run"] + assert "command -v gh" in steps[verify]["run"] + assert "gh --version" in steps[verify]["run"] + + +@pytest.mark.parametrize("filename,job", TARGETS) +def test_affected_job_provisions_gh_before_first_host_use(filename: str, job: str) -> None: + """Catch the original missing-prerequisite failure in each separate job.""" + _assert_order(_steps(filename, job)) + + +@pytest.mark.parametrize("mutation", ["delete", "late", "conditional", "tolerated", "comment"]) +def test_provisioning_order_rejects_mutated_executable_steps(mutation: str) -> None: + """A name/comment or optional/late setup cannot satisfy executable ordering.""" + steps = copy.deepcopy(_steps(*TARGETS[0])) + index = next(i for i, step in enumerate(steps) if step.get("name") == INSTALL) + if mutation == "delete": + del steps[index] + elif mutation == "late": + steps.append(steps.pop(index)) + elif mutation == "conditional": + steps[index]["if"] = "false" + elif mutation == "tolerated": + steps[index]["continue-on-error"] = True + else: + steps[index]["run"] = "# sha256 and $GITHUB_PATH are documentation only" + with pytest.raises(AssertionError): + _assert_order(steps) + + +def test_docs_only_shard_does_not_admit_network_provisioning() -> None: + """Preserve the docs-only check without downloading an unused host tool.""" + steps = _steps("codeql-pr.yml", "analyze-head") + guard = "needs.detect-languages.outputs.code == 'true'" + selected = [step for step in steps if step.get("name") in ( + "Set up Python for pinned GitHub CLI provisioning", INSTALL, VERIFY)] + assert len(selected) == 3 + assert all(step.get("if") == guard for step in selected) + consumer = next(step for step in steps if step.get("name") == "Read current-head CodeQL dispatch verdict") + assert consumer.get("if") == guard + + +def test_strix_bootstrap_does_not_introduce_fixed_network_deadline() -> None: + """Honor the existing Strix bootstrap DNS/TLS readiness policy.""" + import ast + + run = next(step["run"] for step in _steps("strix.yml", "strix") if step.get("name") == INSTALL) + payload = run.split("python3 - <<'PY'\n", 1)[1].rsplit("\nPY", 1)[0] + calls = [node for node in ast.walk(ast.parse(payload)) if isinstance(node, ast.Call) + and isinstance(node.func, ast.Attribute) and node.func.attr == "urlopen"] + assert len(calls) == 1 + assert not calls[0].keywords, "fixed network deadline introduced before Strix review" + + +@pytest.mark.parametrize("filename,job", TARGETS) +def test_owned_installation_is_cleaned_after_last_consumer(filename: str, job: str) -> None: + """Do not accumulate successful CLI copies on persistent runner temp storage.""" + steps = _steps(filename, job) + cleanup = steps[-1] + assert cleanup.get("name") == "Clean up this job's pinned GitHub CLI" + assert cleanup.get("if") == "always() && env.CWL_GH_ROOT != ''" + assert cleanup.get("shell") == "bash" + assert "CWL_GH_ROOT_INODE" in cleanup.get("run", "") + assert "shutil.rmtree(root)" in cleanup.get("run", "") + + +@pytest.mark.parametrize("mutation", ["owned", "identity-drift", "foreign-prefix", "symlink"]) +def test_actual_cleanup_preserves_other_job_directories(monkeypatch, tmp_path, mutation) -> None: + """Execute the exact job-end cleaner with real private directory identities.""" + parent = tmp_path / "runner" + parent.mkdir() + root = parent / ("foreign-job" if mutation == "foreign-prefix" else "cwl-gh-owned") + root.mkdir() + (root / "binary").write_bytes(b"owned synthetic content") + unrelated = parent / "cwl-gh-unrelated" + unrelated.mkdir() + (unrelated / "sentinel").write_bytes(b"untouched") + info = root.stat() + monkeypatch.setenv("RUNNER_TEMP", str(parent)) + monkeypatch.setenv("CWL_GH_ROOT", str(root)) + monkeypatch.setenv("CWL_GH_ROOT_DEVICE", str(info.st_dev)) + monkeypatch.setenv("CWL_GH_ROOT_INODE", str(info.st_ino + (mutation == "identity-drift"))) + if mutation == "symlink": + import shutil + shutil.rmtree(root) + root.symlink_to(unrelated, target_is_directory=True) + run = _steps(*TARGETS[0])[-1]["run"] + payload = run.split("python3 - <<'PY'\n", 1)[1].rsplit("\nPY", 1)[0] + if mutation == "owned": + exec(compile(payload, "", "exec"), {}) + assert not root.exists() + else: + with pytest.raises(RuntimeError, match="refusing"): + exec(compile(payload, "", "exec"), {}) + assert root.exists() + assert (unrelated / "sentinel").read_bytes() == b"untouched" + + +def _installer_tree(): + """Compile only the Python payload actually embedded in executable YAML.""" + import ast + + run = next(step["run"] for step in _steps(*TARGETS[0]) if step.get("name") == INSTALL) + payload = run.split("python3 - <<'PY'\n", 1)[1].rsplit("\nPY", 1)[0] + return ast.parse(payload) + + +def _fixture_archive(binary: bytes, kind: str = "regular") -> bytes: + """Make explicit synthetic supplier bytes for behavioral boundary controls.""" + import io + import tarfile + + result = io.BytesIO() + with tarfile.open(fileobj=result, mode="w:gz") as archive: + member = tarfile.TarInfo("gh_2.102.0_linux_amd64/bin/gh") + if kind == "linked": + member.type = tarfile.SYMTYPE + member.linkname = "/outside/gh" + archive.addfile(member) + else: + member.size = len(binary) + archive.addfile(member, io.BytesIO(binary)) + if kind == "duplicate": + archive.addfile(member, io.BytesIO(binary)) + return result.getvalue() + + +def _run_installer_fixture(monkeypatch, tmp_path, *, kind="regular", mismatch=None, fail_download=False): + """Execute real embedded installer with synthetic archive pins and no network.""" + import ast + import hashlib + import io + import platform + import sys + import urllib.request + + binary = b'#!/bin/sh\nprintf "gh version 2.102.0 (synthetic fixture)\\n"\n' + archive = _fixture_archive(binary, kind) + tree = _installer_tree() + pins = {"archive_sha256": hashlib.sha256(archive).hexdigest(), + "binary_sha256": hashlib.sha256(binary).hexdigest()} + if mismatch: + pins[mismatch] = "0" * 64 + for node in tree.body: + if isinstance(node, ast.Assign) and isinstance(node.targets[0], ast.Name): + key = node.targets[0].id + if key in pins: + node.value = ast.Constant(pins[key]) + ast.fix_missing_locations(tree) + runner = tmp_path / "runner" + runner.mkdir() + envfile = tmp_path / "env" + pathfile = tmp_path / "path" + monkeypatch.setenv("RUNNER_TEMP", str(runner)) + monkeypatch.setenv("GITHUB_ENV", str(envfile)) + monkeypatch.setenv("GITHUB_PATH", str(pathfile)) + # Platform patching admits only this synthetic, portable shell fixture. + monkeypatch.setattr(sys, "platform", "linux") + monkeypatch.setattr(platform, "machine", lambda: "x86_64") + + def download(url, timeout=None): + """Disallow external requests while reproducing a bounded response.""" + assert url == "https://github.com/cli/cli/releases/download/v2.102.0/gh_2.102.0_linux_amd64.tar.gz" + assert timeout is None + if fail_download: + raise OSError("synthetic download refusal") + return io.BytesIO(archive) + + monkeypatch.setattr(urllib.request, "urlopen", download) + try: + exec(compile(tree, "", "exec"), {}) + except BaseException: + assert not list(runner.iterdir()), "failed install must clean its exclusive directory" + assert not envfile.exists() and not pathfile.exists() + raise + installed = dict(line.split("=", 1) for line in envfile.read_text().splitlines()) + executable = Path(installed["CWL_GH_BIN"]) + assert installed["CWL_GH_ROOT"] == str(executable.parent.parent) + assert installed["CWL_GH_ROOT_DEVICE"] == str(executable.parent.parent.stat().st_dev) + assert installed["CWL_GH_ROOT_INODE"] == str(executable.parent.parent.stat().st_ino) + assert executable.read_bytes() == binary + assert pathfile.read_text() == str(executable.parent) + "\n" + assert not executable.is_symlink() + return executable + + +def test_embedded_installer_runs_synthetic_verified_supplier(monkeypatch, tmp_path) -> None: + """Exercise download/hash/copy/execution/path handoff, not real gh inference.""" + _run_installer_fixture(monkeypatch, tmp_path) + + +@pytest.mark.parametrize("mismatch", ["archive_sha256", "binary_sha256"]) +def test_embedded_installer_refuses_changed_supplier_bytes(monkeypatch, tmp_path, mismatch) -> None: + """Neither changed archive nor changed binary can reach publication.""" + with pytest.raises(RuntimeError, match="checksum mismatch"): + _run_installer_fixture(monkeypatch, tmp_path, mismatch=mismatch) + + +@pytest.mark.parametrize("kind", ["linked", "duplicate"]) +def test_embedded_installer_refuses_nonregular_or_duplicate_binary(monkeypatch, tmp_path, kind) -> None: + """Never follow archive links or choose ambiguously duplicated members.""" + with pytest.raises(RuntimeError, match="one regular binary"): + _run_installer_fixture(monkeypatch, tmp_path, kind=kind) + + +def test_embedded_installer_download_failure_cleans_exclusive_directory(monkeypatch, tmp_path) -> None: + """A transport failure propagates before path/environment publication.""" + with pytest.raises(OSError, match="synthetic download refusal"): + _run_installer_fixture(monkeypatch, tmp_path, fail_download=True) + + +@pytest.mark.parametrize("osname,architecture", [("darwin", "x86_64"), ("linux", "aarch64")]) +def test_embedded_installer_refuses_unapproved_platform_before_download(monkeypatch, osname, architecture) -> None: + """Do not execute the pinned x64 release on a different OS/architecture.""" + import platform + import sys + import urllib.request + + def forbidden(*args, **kwargs): + """Any downloader invocation in this negative control is a defect.""" + pytest.fail("download attempted before unsupported-platform refusal") + + monkeypatch.setattr(sys, "platform", osname) + monkeypatch.setattr(platform, "machine", lambda: architecture) + monkeypatch.setattr(urllib.request, "urlopen", forbidden) + with pytest.raises(RuntimeError, match="requires Linux x64"): + exec(compile(_installer_tree(), "", "exec"), {}) + + +def test_all_three_jobs_share_identical_supplier_and_verifier() -> None: + """Inline bootstraps must retain one exact distribution and selection contract.""" + for name in (INSTALL, VERIFY): + payloads = [next(step["run"] for step in _steps(*target) if step.get("name") == name) + for target in TARGETS] + # Only the documented Strix DNS/TLS no-timeout policy differs. + if name == INSTALL: + assert "urlopen(url)" in payloads[0] + assert all("urlopen(url, timeout=30)" in item for item in payloads[1:]) + payloads = [item.replace("urlopen(url, timeout=30)", "urlopen(url)") for item in payloads] + assert len(set(payloads)) == 1 + assert "7469124f706944133d6a169691dd1c6c3511b12e85878d255e044e2948df4c9b" in payloads[0] + + +@pytest.mark.parametrize("path_selection", ["approved-first", "forged-first", "changed-binary"]) +def test_actual_shell_verifier_checks_path_selection_and_bytes(tmp_path, path_selection) -> None: + """Run the real Bash verifier with explicitly pinned synthetic shell bytes.""" + import hashlib + import os + import shutil + import subprocess + + bindir = tmp_path / "approved" + forged = tmp_path / "ambient" + bindir.mkdir() + forged.mkdir() + executable = bindir / "gh" + binary = b'#!/bin/sh\nprintf "gh version 2.102.0 (synthetic fixture)\\n"\n' + executable.write_bytes(binary) + executable.chmod(0o700) + (forged / "gh").write_text('#!/bin/sh\nexit 99\n') + (forged / "gh").chmod(0o700) + verifier = next(step["run"] for step in _steps(*TARGETS[0]) if step.get("name") == VERIFY) + verifier = verifier.replace("7469124f706944133d6a169691dd1c6c3511b12e85878d255e044e2948df4c9b", hashlib.sha256(binary).hexdigest()) + if path_selection == "changed-binary": + executable.write_bytes(binary + b"# changed\n") + # sha256sum is a real host utility; no fake checksum executable is supplied. + checksum = shutil.which("gsha256sum") or shutil.which("sha256sum") + assert checksum, "fixture requires the actual checksum executable" + chosen = [forged, bindir] if path_selection == "forged-first" else [bindir, forged] + # macOS /sbin/sha256sum is BSD-compatible and lacks GNU --status; use GNU. + checksum_dir = Path(checksum).parent + environment = {"PATH": ":".join(map(str, chosen)) + ":" + str(checksum_dir) + ":/usr/bin:/bin", + "CWL_GH_BIN": str(executable), "HOME": str(tmp_path)} + verifier = verifier.replace("sha256sum --check --status", str(checksum) + " --check --status") + result = subprocess.run(["/bin/bash", "-e", "-c", verifier], env=environment, + capture_output=True, text=True, timeout=10) + assert (result.returncode == 0) == (path_selection == "approved-first"), result.stderr diff --git a/tests/test_central_required_workflow_ruleset_audit.py b/tests/test_central_required_workflow_ruleset_audit.py index 77bbf53305..cec0d2aead 100644 --- a/tests/test_central_required_workflow_ruleset_audit.py +++ b/tests/test_central_required_workflow_ruleset_audit.py @@ -517,13 +517,21 @@ def test_audit_organization_codeql_coverage_step_has_freshness_and_credential_gu " exit 1\n" " fi" ) in workflow + # This pinned `--jq .state` until 2026-09-07. What it protects is that the + # audit reads default-setup per repository, not that it reads only the + # state: `state == "configured"` with an empty `languages` list scans + # nothing and produces no analyses (live on life-os, aFIPC and inkspan), + # so the step now fetches the whole object and extracts both fields. + assert 'repos/${ORG_LOGIN}/${repository}/code-scanning/default-setup"' in workflow + assert """default_setup_state=$(jq '.state // null' "$default_setup_json")""" in workflow assert ( - 'repos/${ORG_LOGIN}/${repository}/code-scanning/default-setup" --jq .state' + """default_setup_languages=$(jq '.languages // []' "$default_setup_json")""" in workflow ) + assert "default_setup_languages: $default_setup_languages" in workflow assert ( 'if [ "$archived" != "true" ]; then\n' - ' default_setup_state_json="$RUNNER_TEMP/codeql-default-setup-' + ' default_setup_json="$RUNNER_TEMP/codeql-default-setup-' '${repository//[^A-Za-z0-9_.-]/_}.json"' ) in workflow assert ( @@ -540,6 +548,33 @@ def test_audit_organization_codeql_coverage_step_has_freshness_and_credential_gu assert "python3 scripts/ci/audit_org_codeql_coverage.py" in workflow +def test_codeql_coverage_audit_survives_a_ruleset_drift_failure() -> None: + """An owner-configured ruleset drift must not disable the coverage detector. + + Both audits live in one job, and the ruleset step exits 1 on governance + drift. It did on 2026-09-06 ("exactly two approving reviews are not + required", "last-push approval protection is disabled"), so every run since + 2026-09-04 failed before reaching the CodeQL coverage step. The subjects are + unrelated and the coverage step has no data dependency on the one above it, + so it is guarded by ``if: always()``. + + The bootstrap steps below it are deliberately *not* given the same guard: + they open pull requests, and running a mutation after an unexplained + upstream failure is a different decision from running a read-only detector. + """ + workflow = (REPO_ROOT / ".github/workflows/audit-central-ruleset.yml").read_text( + encoding="utf-8" + ) + coverage_step = workflow.split("- name: Audit organization CodeQL coverage\n", 1)[1] + before_next_step = coverage_step.split(" - name: ", 1)[0] + + assert "\n if: always()\n" in before_next_step + bootstrap_step = workflow.split( + "- name: Create missing CodeQL setup pull requests\n", 1 + )[1].split(" - name: ", 1)[0] + assert "if: always()" not in bootstrap_step + + def test_codeql_gap_bootstrap_uses_trusted_opencode_identity_without_pr_head_execution() -> None: """Backlog item 38 stays on trusted main and treats installation tokens as opaque.""" workflow = (REPO_ROOT / ".github/workflows/audit-central-ruleset.yml").read_text( diff --git a/tests/test_close_empty_pr_queue_pressure.py b/tests/test_close_empty_pr_queue_pressure.py index 331a604631..6da88f63f1 100644 --- a/tests/test_close_empty_pr_queue_pressure.py +++ b/tests/test_close_empty_pr_queue_pressure.py @@ -1,5 +1,6 @@ """Regression contracts for close-event runner admission pressure.""" +import re from pathlib import Path import pytest @@ -29,7 +30,7 @@ def test_closed_pull_request_does_not_allocate_a_noop_runner( assert "closed" in workflow assert "github.event.pull_request.number" in concurrency assert "github.event.pull_request.head.sha" not in concurrency - assert "cancel-in-progress:" in concurrency + assert re.search(r"(?m)^[ \t]+cancel-in-progress:[ \t]+\S", concurrency) assert "cancel-closed-pr-runs:" not in workflow assert "github.event.action != 'closed'" in workflow assert evidence_job in workflow diff --git a/tests/test_code_scanning_required_workflow_contract.py b/tests/test_code_scanning_required_workflow_contract.py index 19933303d8..dbabff9705 100644 --- a/tests/test_code_scanning_required_workflow_contract.py +++ b/tests/test_code_scanning_required_workflow_contract.py @@ -45,4 +45,6 @@ def test_ruleset_requires_dispatch_safe_codeql_pr() -> None: assert workflow_path in audit.REQUIRED_WORKFLOW_PATHS assert "uses: github/codeql-action" not in workflow - assert "event_type:\"codeql-scan\"" in workflow + assert "event_type:\"codeql-scan-v2\"" in workflow + assert 'pr_head:{schema:"1",ref:$pr_head_ref,sha:$pr_head_sha}' in workflow + assert "producer_source_sha:$producer_source_sha" in workflow diff --git a/tests/test_codeql_ghas_analyses_pagination.py b/tests/test_codeql_ghas_analyses_pagination.py new file mode 100644 index 0000000000..db906964d6 --- /dev/null +++ b/tests/test_codeql_ghas_analyses_pagination.py @@ -0,0 +1,443 @@ +"""Complete, fail-closed GHAS analyses traversal at the real HTTP boundary.""" + +from __future__ import annotations + +from email.message import Message +from io import BytesIO +import json +from urllib.parse import urlencode + +import pytest + +from scripts.ci import codeql_ghas_configuration_identity as identity + + +ENDPOINT = "https://api.github.com/repos/ContextualWisdomLab/example/code-scanning/analyses" + + +class _Page: + """Serve one synthetic HTTP page without contacting GitHub.""" + + def __init__(self, payload, link=""): + """Keep the page body and its actual Link response header.""" + self.body = json.dumps(payload).encode() + self.headers: dict[str, str] | Message = {"Link": link} + + def __enter__(self): + """Open the response context.""" + return self + + def __exit__(self, *_args): + """Close the response context without suppressing failures.""" + return None + + def read(self): + """Return the serialized body.""" + return self.body + + +def _url(page=None, ref: str | None = "refs/heads/main", per_page=100): + """Construct the documented analyses query, with an optional page number.""" + params = {"per_page": str(per_page), "tool_name": "CodeQL"} + if ref: + params["ref"] = ref + if page is not None: + params["page"] = str(page) + return ENDPOINT + "?" + urlencode(params) + + +def test_separate_link_headers_keep_next_after_current_last(monkeypatch): + """Every physical Link field contributes to the complete page traversal.""" + first = _Page([{"id": 1}]) + first.headers = Message() + first.headers["Link"] = f'<{_url(1)}>; rel="last"' + first.headers["link"] = f'<{_url(2)}>; rel="next"' + pages = {_url(): first, _url(2): _Page([{"id": 2}])} + calls = [] + + def open_page(request, timeout): + """Serve both pages and record their unchanged authenticated requests.""" + calls.append((request.full_url, request.get_header("Authorization"), timeout)) + return pages[request.full_url] + + monkeypatch.setattr(identity._GITHUB_API_OPENER, "open", open_page) + assert identity.list_codeql_analyses("ContextualWisdomLab/example", token="opaque", ref="refs/heads/main") == [ + {"id": 1}, {"id": 2} + ] + assert calls == [(url, "Bearer opaque", 30) for url in pages] + + +@pytest.mark.parametrize("links", [ + [f'<{_url(2)}>; rel="next"', f'<{_url(2)}>; rel="next"'], + [f'<{_url(2)}>; rel="next"', '; rel="next"'], + [f'<{_url(1)}>; rel="last"', f'<{_url(1)}>; rel="last"'], + [f'<{_url(2)}>; rel="next"', "garbage"], + [f'<{_url(2)}>; rel="next"', ""], + [f'<{_url(2)}>; rel="next"', f'<{_url(1)}>; rel="unknown"'], + [f'<{_url(1)}>; rel="last"', '; rel="next"'], + [f'<{_url(1)}>; rel="last"', f'<{_url(2, ref="refs/pull/7/head")}>; rel="next"'], +]) +def test_separate_link_headers_reject_invalid_later_field_before_request(monkeypatch, links): + """A later duplicate, malformed or unsafe field cannot hide behind the first.""" + response = _Page([{"id": 1}]) + response.headers = Message() + for link in links: + response.headers["Link"] = link + calls = [] + + def open_page(request, timeout): + """Fail if any combined invalid header reaches another bearer request.""" + calls.append((request.full_url, request.get_header("Authorization"))) + assert len(calls) == 1, "invalid later Link field reached authenticated transport" + return response + + monkeypatch.setattr(identity._GITHUB_API_OPENER, "open", open_page) + with pytest.raises(identity.ConfigurationIdentityError): + identity.list_codeql_analyses("ContextualWisdomLab/example", token="opaque", ref="refs/heads/main") + assert calls == [(_url(), "Bearer opaque")] + + +@pytest.mark.parametrize("header_type", ["message", "dict"]) +@pytest.mark.parametrize("relation", [None, "", "last", "next"]) +def test_message_and_dict_headers_preserve_absent_single_link_behavior(monkeypatch, header_type, relation): + """Absent, empty and single Link fields preserve Message and dict compatibility.""" + response = _Page([{"id": 1}]) + response.headers = Message() if header_type == "message" else {} + if relation is not None: + response.headers["Link"] = ( + f'<{_url(2 if relation == "next" else 1)}>; rel="{relation}"' if relation else "" + ) + terminal = _Page([{"id": 2}]) + terminal.headers = Message() if header_type == "message" else {} + calls = [] + + def open_page(request, timeout): + """Serve a single optional next page without inventing extra headers.""" + calls.append((request.full_url, request.get_header("Authorization"), timeout)) + assert len(calls) <= (2 if relation == "next" else 1) + return response if len(calls) == 1 else terminal + + monkeypatch.setattr(identity._GITHUB_API_OPENER, "open", open_page) + expected_urls = [_url(), _url(2)] if relation == "next" else [_url()] + assert identity.list_codeql_analyses("ContextualWisdomLab/example", token="opaque", ref="refs/heads/main") == [ + {"id": page} for page in range(1, len(expected_urls) + 1) + ] + assert calls == [(url, "Bearer opaque", 30) for url in expected_urls] + + +def test_complete_history_keeps_active_identity_after_first_hundred(monkeypatch): + """A base identity on page three must still block an unpaired head.""" + base_sha, head_sha = "a" * 40, "b" * 40 + active = {"commit_sha": base_sha, "analysis_key": "active-workflow:analyze", + "category": "/language:rust", "tool": {"name": "CodeQL"}} + historical = [{"id": n, "commit_sha": "c" * 40} for n in range(200)] + pages = { + _url(): _Page(historical[:100], f'<{_url(2)}>; rel="next", <{_url(3)}>; rel="last"'), + _url(2): _Page(historical[100:], f'<{_url(3)}>; rel="next", <{_url(1)}>; rel="prev"'), + _url(3): _Page([active], f'<{_url(1)}>; rel="first", <{_url(2)}>; rel="prev"'), + } + calls = [] + + def open_page(request, timeout): + """Record every authenticated request and serve its exact page.""" + calls.append((request.full_url, request.get_header("Authorization"), timeout)) + return pages[request.full_url] + + monkeypatch.setattr(identity._GITHUB_API_OPENER, "open", open_page) + rows = identity.list_codeql_analyses("ContextualWisdomLab/example", token="opaque", ref="refs/heads/main") + assert rows == historical + [active] + assert identity.pairing_ready(rows, [], base_sha=base_sha, head_sha=head_sha, language="rust") == ( + False, [("active-workflow:analyze", "/language:rust")] + ) + assert calls == [(url, "Bearer opaque", 30) for url in pages] + + +@pytest.mark.parametrize("target", [ + "https://evil.example/analyses?page=2", + _url(2).replace("api.github.com", "api.github.com.evil.example"), + _url(2).replace("https://", "http://"), + _url(2).replace("api.github.com", "api.github.com:443"), + _url(2).replace("api.github.com", "user@api.github.com"), + _url(2) + "#fragment", + _url(2).replace("/example/", "/other/"), + _url(2).replace("/analyses?", "/alerts?"), + _url(2, ref="refs/pull/7/head"), + _url(2, ref=None), + _url(2, per_page=50), + _url(2).replace("tool_name=CodeQL", "tool_name=Semgrep"), + _url(2) + "&ref=refs%2Fheads%2Fmain", + _url(2) + "&extra=unreviewed", + _url(2).replace("page=2", "page=4"), + _url(2).replace("page=2", "page=0"), + _url(2).replace("page=2", "page=two"), + _url(2).replace("page=2", "page=02"), + _url(2).replace("&page=2", ""), + _url(2) + "&broken", + "/repos/ContextualWisdomLab/example/code-scanning/analyses?page=2", +]) +def test_next_link_cannot_change_authority_endpoint_or_query(monkeypatch, target): + """Reject poisoned next URLs before constructing a second bearer request.""" + calls = [] + + def open_page(request, timeout): + """Refuse a second request even when its authority still appears valid.""" + calls.append((request.full_url, request.get_header("Authorization"))) + assert len(calls) == 1, "poisoned link reached authenticated transport" + return _Page([{"id": 1}], f'<{target}>; rel="next"') + + monkeypatch.setattr(identity._GITHUB_API_OPENER, "open", open_page) + with pytest.raises(identity.ConfigurationIdentityError): + identity.list_codeql_analyses("ContextualWisdomLab/example", token="opaque", ref="refs/heads/main") + assert calls == [(_url(), "Bearer opaque")] + + +@pytest.mark.parametrize("link", [ + "garbage", '