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
9 changes: 0 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1305,14 +1305,6 @@ jobs:
timeout-minutes: 90
permissions:
contents: read
# Required by mikepenz/action-junit-report to create check-run
# annotations surfaced in the PR Checks tab.
checks: write
# Required by ctrf-io/github-test-reporter to post the test-result
# summary comment on the PR that triggered the run. The action no-
# ops gracefully on push events; the workflow-level Step Summary
# is always populated.
pull-requests: write
env:
# Single source of truth for the shard count. The `--shard i/N`
# denominator below and the per-shard slice both key off this; bump
Expand Down Expand Up @@ -1531,7 +1523,6 @@ jobs:
timeout-minutes: 90
permissions:
contents: read
checks: write
env:
# Per-push macOS runs only shard 1 of this many - a deterministic
# ~1/4 subset of the file list. ci-macos-nightly.yml runs the full
Expand Down
2 changes: 1 addition & 1 deletion docs/operations/ci-topology.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ This report lists the workflow graph surfaces reviewers need to inspect when CI
| .github/workflows/ci-gate-stub.yml | workflow: {"contents": "read"}<br>ci-gate: {"contents": "read"} | - |
| .github/workflows/ci-macos-nightly.yml | workflow: {"contents": "read"}<br>open-failure-issue: {"contents": "read", "issues": "write"}<br>test-macos-nightly: {"checks": "write", "contents": "read"} | GITHUB_TOKEN |
| .github/workflows/ci-weekly-digest.yml | digest: {"contents": "read", "issues": "write"} | - |
| .github/workflows/ci.yml | workflow: {"contents": "read"}<br>actionlint: {"contents": "read"}<br>adapter-integration: {"contents": "read"}<br>adapter-integration-macos: {"contents": "read"}<br>autofix: {"contents": "write"}<br>bandit: {"contents": "read"}<br>beartype: {"contents": "read"}<br>ci-gate: {"contents": "read"}<br>close-ci-issues: {"contents": "read", "issues": "write"}<br>coverage-report: {"contents": "read"}<br>dead-code: {"contents": "read"}<br>determine-changes: {"contents": "read"}<br>diff-coverage: {"contents": "read"}<br>dist-size: {"contents": "read"}<br>install-smoke-pipx: {"contents": "read"}<br>install-smoke-uv: {"contents": "read"}<br>lineage-gate: {"contents": "read"}<br>lint: {"contents": "read"}<br>mutmut-diff: {"contents": "read"}<br>pip-audit: {"contents": "read"}<br>pr-summary: {"pull-requests": "write"}<br>property-tests: {"contents": "read"}<br>pyright-strict-zone: {"contents": "read"}<br>repo-hygiene: {"contents": "read"}<br>schemathesis-smoke: {"contents": "read"}<br>semgrep: {"contents": "read"}<br>snapshot-tests: {"contents": "read"}<br>spelling: {"contents": "read"}<br>test: {"checks": "write", "contents": "read", "pull-requests": "write"}<br>test-macos: {"checks": "write", "contents": "read"}<br>typecheck: {"contents": "read"} | CODECOV_TOKEN, GITHUB_TOKEN |
| .github/workflows/ci.yml | workflow: {"contents": "read"}<br>actionlint: {"contents": "read"}<br>adapter-integration: {"contents": "read"}<br>adapter-integration-macos: {"contents": "read"}<br>autofix: {"contents": "write"}<br>bandit: {"contents": "read"}<br>beartype: {"contents": "read"}<br>ci-gate: {"contents": "read"}<br>close-ci-issues: {"contents": "read", "issues": "write"}<br>coverage-report: {"contents": "read"}<br>dead-code: {"contents": "read"}<br>determine-changes: {"contents": "read"}<br>diff-coverage: {"contents": "read"}<br>dist-size: {"contents": "read"}<br>install-smoke-pipx: {"contents": "read"}<br>install-smoke-uv: {"contents": "read"}<br>lineage-gate: {"contents": "read"}<br>lint: {"contents": "read"}<br>mutmut-diff: {"contents": "read"}<br>pip-audit: {"contents": "read"}<br>pr-summary: {"pull-requests": "write"}<br>property-tests: {"contents": "read"}<br>pyright-strict-zone: {"contents": "read"}<br>repo-hygiene: {"contents": "read"}<br>schemathesis-smoke: {"contents": "read"}<br>semgrep: {"contents": "read"}<br>snapshot-tests: {"contents": "read"}<br>spelling: {"contents": "read"}<br>test: {"contents": "read"}<br>test-macos: {"contents": "read"}<br>typecheck: {"contents": "read"} | CODECOV_TOKEN, GITHUB_TOKEN |
| .github/workflows/cifuzz-pr.yml | workflow: {"contents": "read"}<br>cifuzz: {"contents": "read"} | GITHUB_TOKEN |
| .github/workflows/cleanup-runs.yml | workflow: {"contents": "read"}<br>cleanup: {"actions": "write"} | GITHUB_TOKEN |
| .github/workflows/cluster-e2e.yml | workflow: {"contents": "read"} | - |
Expand Down
239 changes: 131 additions & 108 deletions packages/vscode/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/bernstein/cli/commands/cost.py
Original file line number Diff line number Diff line change
Expand Up @@ -935,7 +935,7 @@ def _read_envelopes_from_yaml(yaml_path: Path) -> dict[str, dict[str, Any]]:
for name, payload in envelopes_block.items():
if isinstance(payload, dict):
payload_d = cast("dict[str, Any]", payload)
out[name] = dict(payload_d)
out[name] = payload_d.copy()
return out


Expand Down
2 changes: 1 addition & 1 deletion src/bernstein/cli/commands/telemetry_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ def telemetry_status(home: Path | None) -> None:
dsn = os.environ.get(sidechannel.DSN_ENV) or "(unset)"
from bernstein.core.telemetry.share import resolve_share_endpoint

share_endpoint_configured = resolve_share_endpoint(dict(os.environ)) is not None
share_endpoint_configured = resolve_share_endpoint(os.environ.copy()) is not None
lines: list[str] = [
f"enabled: {str(state.enabled).lower()}",
f"source: {state.source.value}",
Expand Down
2 changes: 1 addition & 1 deletion src/bernstein/cli/commands/verify_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ def _verify_determinism(

fingerprint = fp.compute()

if expect is None and baseline_run_id is None:
if expect is None is baseline_run_id:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The new condition expect is None is baseline_run_id is almost certainly a logic bug and changes semantics from the original check.

The previous condition if expect is None and baseline_run_id is None clearly required both to be None. The new expect is None is baseline_run_id is parsed as (expect is None) is baseline_run_id, i.e. an identity comparison between a boolean and baseline_run_id, which changes behavior and is likely incorrect. If the intent is still to require both values to be None, please restore the original condition; if the intent differs (e.g., tri‑state logic), it should be expressed more explicitly.

# Bare mode: observe-only, byte-identical to the original surface.
# The entry count is only displayed in this branch, so the second WAL
# scan stays scoped here rather than running for every gated call.
Expand Down
2 changes: 1 addition & 1 deletion src/bernstein/cli/commands/worktrees_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ def gc_cmd(workdir: Path, yes: bool, dry_run: bool, force_unsaved: bool) -> None
console.print("[green]No reapable worktrees - nothing to do.[/green]")
return

targets = list(reapable)
targets = reapable.copy()
if force_unsaved:
targets.extend(unsaved)

Expand Down
2 changes: 1 addition & 1 deletion src/bernstein/cli/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def from_dict(cls, data: str | dict[str, Any]) -> AgentInfo:
role=str(data.get("role", "")),
model=str(data.get("model", "")),
status=str(data.get("status", "idle")),
task_ids=list(cast("list[str]", data.get("task_ids") or [])),
task_ids=cast("list[str]", data.get("task_ids") or []).copy(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Restore iterable-safe conversion for task_ids.

Line 159 now assumes task_ids has a .copy() method; if upstream passes a non-list iterable, from_dict will raise at parse time. Use list(...) to keep the parser tolerant.

Proposed fix
-            task_ids=cast("list[str]", data.get("task_ids") or []).copy(),
+            task_ids=list(cast("Iterable[str]", data.get("task_ids") or [])),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
task_ids=cast("list[str]", data.get("task_ids") or []).copy(),
task_ids=list(cast("Iterable[str]", data.get("task_ids") or [])),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/bernstein/cli/ui.py` at line 159, The task_ids conversion in from_dict is
no longer iterable-safe because it assumes data.get("task_ids") returns a list
with .copy(); update the task_ids assignment to build a new list from whatever
iterable is provided so parsing stays tolerant. Use the existing from_dict
method and the task_ids field as the fix location, and avoid relying on
list-specific methods on the incoming value.

runtime_s=float(data.get("runtime_s", 0.0)),
abort_reason=str(data.get("abort_reason", "")),
abort_detail=str(data.get("abort_detail", "")),
Expand Down
3 changes: 1 addition & 2 deletions src/bernstein/core/approval/gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ def load_approval_config(workdir: Path | None = None) -> ApprovalConfig:
return ApprovalConfig()
if not isinstance(raw, dict):
return ApprovalConfig()
raw_mapping = cast("dict[str, Any]", raw)
raw_section: object = raw_mapping.get("approvals")
raw_section: object = cast("dict[str, Any]", raw).get("approvals")
if not isinstance(raw_section, dict):
return ApprovalConfig()
section = cast("dict[str, Any]", raw_section)
Expand Down
3 changes: 1 addition & 2 deletions src/bernstein/core/config/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,7 @@ def _load(self) -> dict[str, Any]:

def load_raw(self) -> dict[str, object]:
"""Return raw persisted global settings without default expansion."""
data = self._load()
return dict(data)
return self._load().copy()

def _save(self, data: dict[str, Any]) -> None:
"""Persist data to config.yaml, creating home dir if needed."""
Expand Down
2 changes: 1 addition & 1 deletion src/bernstein/core/cost/retry_budget.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ def _is_criterion_name(name: str) -> bool:
first = name[0]
if not (first == "_" or "A" <= first <= "Z" or "a" <= first <= "z"):
return False
return all(ch == "_" or ch == "-" or "0" <= ch <= "9" or "A" <= ch <= "Z" or "a" <= ch <= "z" for ch in name[1:])
return all(ch in ("_", "-") or "0" <= ch <= "9" or "A" <= ch <= "Z" or "a" <= ch <= "z" for ch in name[1:])


def _split_retry_budget_spec(spec: str) -> tuple[int, str | None]:
Expand Down
40 changes: 19 additions & 21 deletions src/bernstein/core/orchestration/schedule_supervisor.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ def _read_schedule_fire_entries(sdd_dir: Path) -> dict[tuple[str, int], dict[str
key = (str(details["schedule_id"]), int(details["fire_time"]))
except (KeyError, TypeError, ValueError):
continue
enriched: dict[str, Any] = dict(details)
enriched: dict[str, Any] = details.copy()
enriched["__hmac__"] = str(entry.get("hmac", ""))
# Last writer wins: later daily segments override earlier ones
# for the same (id, fire_time), which cannot legitimately
Expand Down Expand Up @@ -704,26 +704,24 @@ def ok(self) -> bool:

def to_json(self) -> list[dict[str, Any]]:
"""Return a JSON-safe per-receipt view for ``--json`` output."""
out: list[dict[str, Any]] = []
for r in self.results:
out.append(
{
"schedule_id": r.schedule_id,
"fire_time": r.fire_time,
"counterfactual": r.counterfactual,
"rev": r.rev,
"recorded_projection_hash": r.recorded_projection_hash,
"recomputed_projection_hash": r.recomputed_projection_hash,
"projection_match": r.projection_match,
"chain_match": r.chain_match,
"rev_skipped": r.rev_skipped,
"skipped": r.skipped,
"verified": r.verified,
"mismatch": r.mismatch,
"reasons": list(r.reasons),
},
)
return out
return [
{
"schedule_id": r.schedule_id,
"fire_time": r.fire_time,
"counterfactual": r.counterfactual,
"rev": r.rev,
"recorded_projection_hash": r.recorded_projection_hash,
"recomputed_projection_hash": r.recomputed_projection_hash,
"projection_match": r.projection_match,
"chain_match": r.chain_match,
"rev_skipped": r.rev_skipped,
"skipped": r.skipped,
"verified": r.verified,
"mismatch": r.mismatch,
"reasons": list(r.reasons),
}
for r in self.results
]


def verify_receipts(sdd_dir: Path) -> AuditReport:
Expand Down
3 changes: 2 additions & 1 deletion src/bernstein/core/replay/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import json
import logging
import operator
import os
import threading
import time
Expand Down Expand Up @@ -319,7 +320,7 @@ def _load_fixtures(self) -> None:

# Sort by (seq, file_pos) so the per-kind lists are in recorded order
# even if the log was written or stitched out of strict line order.
rows.sort(key=lambda r: (r[0], r[1]))
rows.sort(key=operator.itemgetter(0, 1))
for _seq, _pos, kind, key, response in rows:
ordered = self._ordered_by_kind.setdefault(kind, [])
fixture = _Fixture(response=response)
Expand Down
2 changes: 1 addition & 1 deletion src/bernstein/core/routing/bandit_router.py
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ def _load_arm_matrices(
if not _is_matrix(raw_matrix, FEATURE_DIM) or not _is_vector(raw_vector, FEATURE_DIM):
logger.info("BanditPolicy: resetting %s because arm %s has incompatible dimensions", path, arm)
return None
matrix = [list(row) for row in raw_matrix]
matrix = [row.copy() for row in raw_matrix]
loaded_inv[arm] = matrix if arm in raw_inv_by_arm else _inv(matrix)
loaded_vec[arm] = list(raw_vector)

Expand Down
3 changes: 1 addition & 2 deletions src/bernstein/core/security/permission_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,7 @@ def _load_yaml_permissions(workdir: Path) -> Mapping[str, Any] | None:
return None
if not isinstance(raw, dict):
return None
raw_mapping = cast("Mapping[str, Any]", raw)
section = raw_mapping.get("permissions")
section = cast("Mapping[str, Any]", raw).get("permissions")
if not isinstance(section, dict):
return None
return cast("Mapping[str, Any]", section)
Expand Down
3 changes: 1 addition & 2 deletions src/bernstein/core/skills/authoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,7 @@ def load_trigger_suite(path: Path) -> tuple[TriggerCase, ...]:
raise SkillAuthoringError(f"{path}: failed to read trigger suite: {exc}") from exc
if not isinstance(loaded, dict):
raise SkillAuthoringError(f"{path}: suite must be a YAML mapping")
suite_data = cast("dict[str, object]", loaded)
raw_cases = suite_data.get("cases")
raw_cases = cast("dict[str, object]", loaded).get("cases")
if not isinstance(raw_cases, list):
raise SkillAuthoringError(f"{path}: suite must contain a cases list")

Expand Down
2 changes: 1 addition & 1 deletion src/bernstein/core/skills/routing.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def _cosine(left: dict[str, float], right: dict[str, float]) -> float:
return 0.0
left_norm = math.sqrt(sum(weight * weight for weight in left.values()))
right_norm = math.sqrt(sum(weight * weight for weight in right.values()))
if left_norm == 0 or right_norm == 0:
if 0 in (left_norm, right_norm):
return 0.0
return numerator / (left_norm * right_norm)

Expand Down
3 changes: 1 addition & 2 deletions src/bernstein/sdd/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,7 @@ def load_schema(version: str = "v1") -> dict[str, Any]:
raise SchemaNotFoundError(f"schema {filename} is not valid JSON: {exc}") from exc
if not isinstance(loaded_raw, dict):
raise SchemaNotFoundError(f"schema {filename} is not a JSON object")
loaded_dict = cast("dict[str, Any]", loaded_raw)
loaded: dict[str, Any] = dict(loaded_dict)
loaded: dict[str, Any] = cast("dict[str, Any]", loaded_raw).copy()
# Sanity-check it really is Draft-07 compatible.
jsonschema.Draft7Validator.check_schema(loaded)
_SCHEMA_CACHE[canonical] = loaded
Expand Down
Loading