Skip to content
Closed
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
20 changes: 19 additions & 1 deletion .github/scripts/agents_pr_meta_update_body.js
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,25 @@ function upsertBlock(body, marker, replacement) {
const startIndex = body.indexOf(start);
const endIndex = body.indexOf(end);
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
return `${body.slice(0, startIndex)}${replacement}${body.slice(endIndex + end.length)}`;
// Replace the first marker pair in place
let result = `${body.slice(0, startIndex)}${replacement}${body.slice(endIndex + end.length)}`;

// Remove any duplicate marker pairs left by concurrent writers
let hadDuplicates = false;
let limit = 10;
while (limit-- > 0) {
const dupStart = result.indexOf(start, startIndex + replacement.length);
const dupEnd = result.indexOf(end, dupStart + start.length);
if (dupStart !== -1 && dupEnd !== -1 && dupEnd > dupStart) {
result = `${result.slice(0, dupStart)}${result.slice(dupEnd + end.length)}`;
hadDuplicates = true;
} else {
break;
}
}
Comment on lines +715 to +724

Copilot AI Mar 1, 2026

Copy link

Choose a reason for hiding this comment

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

The duplicate-removal loop can miss duplicates after the first deletion: removing a later block shifts subsequent duplicates left, potentially to an index before startIndex + replacement.length, so the next indexOf(start, ...) search may skip them. Consider restarting the search from just after the first block’s start marker (e.g., startIndex + 1) each iteration (or otherwise re-scanning until no more matches) so all duplicates are reliably removed.

Copilot uses AI. Check for mistakes.

// Collapse triple+ newlines left by removed blocks
return hadDuplicates ? result.replace(/\n{3,}/g, '\n\n') : result;
}

const trimmed = body.trimEnd();
Expand Down
26 changes: 0 additions & 26 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,32 +87,6 @@ See `docs/INTEGRATION_GUIDE.md` in Workflows repo.
- Fix in Workflows repo → sync will propagate here
- Or request manual sync: `gh workflow run maint-68-sync-consumer-repos.yml --repo stranske/Workflows`

## Slow Test Strategy

This repo uses a `slow` pytest marker to keep PR Gate fast (~4 min) while
ensuring heavyweight tests still run on Main CI.

**How it works:**

- `pyproject.toml` registers the `slow` marker.
- `pr-00-gate.yml` passes `pytest_markers: "not release and not slow"` to
exclude slow tests from PR feedback.
- `ci.yml` (Main CI) runs the **full** suite including slow tests.
- Locally, `make test` also excludes slow tests for fast iteration.

**Currently marked slow:**

- `tests/pipeline/test_run_pipeline.py` — auto-marked by
`tests/pipeline/conftest.py` (each test runs the full pipeline with
real Excel fixtures, 100-170 s each).
- `tests/spec/test_macro_spec_fixtures.py` — module-level
`pytestmark = pytest.mark.slow` (session fixture parses three NISA
workbooks, ~85 s).

**Rule for agents:** any new test that parses real workbooks or calls
`run_pipeline()` **must** be marked `@pytest.mark.slow`. Omitting the
marker will silently regress Gate duration for every open PR.

## Reference Implementation

**Travel-Plan-Permission** is the reference consumer repo. When debugging:
Expand Down
4 changes: 1 addition & 3 deletions scripts/langchain/capability_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,7 @@ def _build_llm_config(
issue_or_pr = (
str(issue_number)
if issue_number is not None
else env_issue
if env_issue.isdigit()
else "unknown"
else env_issue if env_issue.isdigit() else "unknown"
)
metadata = {
"repo": repo,
Expand Down
4 changes: 1 addition & 3 deletions scripts/langchain/issue_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,7 @@ def _build_llm_config(
issue_or_pr = (
str(issue_number)
if issue_number is not None
else env_issue
if env_issue.isdigit()
else "unknown"
else env_issue if env_issue.isdigit() else "unknown"
)
metadata = {
"repo": repo,
Expand Down
23 changes: 0 additions & 23 deletions tools/langchain_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,29 +148,6 @@ def _resolve_slots() -> list[SlotDefinition]:
return _apply_slot_env_overrides(_load_slot_config())


Copilot AI Mar 1, 2026

Copy link

Choose a reason for hiding this comment

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

get_provider_model_catalog() was removed, but it is still imported/used by src/counter_risk/chat/providers/base.py (and patched in tests/test_chat_provider_clients.py). This will cause an ImportError at runtime/test time. Either restore get_provider_model_catalog() (and keep its behavior consistent with slot resolution/defaults) or update all call sites to use the new intended API.

Suggested change
def get_provider_model_catalog() -> list[SlotDefinition]:
"""Return the resolved provider/model slot catalog.
This applies the same configuration loading and environment overrides as used
by the LangChain client helpers, ensuring consistent defaults and slot order.
"""
return _resolve_slots()

Copilot uses AI. Check for mistakes.
def list_slot_definitions() -> list[SlotDefinition]:
"""Return configured LLM slot definitions after env overrides."""

return list(_resolve_slots())


def get_provider_model_catalog() -> dict[str, set[str]]:
"""Return provider->model mappings from configured slots."""

catalog: dict[str, set[str]] = {
PROVIDER_OPENAI: set(),
PROVIDER_ANTHROPIC: set(),
PROVIDER_GITHUB: set(),
}
for slot in _resolve_slots():
catalog.setdefault(slot.provider, set()).add(slot.model)

if not any(catalog.values()):
for slot in _default_slots():
catalog.setdefault(slot.provider, set()).add(slot.model)
return catalog


def _is_reasoning_model(model: str) -> bool:
"""Return True if the model is an OpenAI reasoning model that rejects temperature.

Expand Down
Loading