Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ac87506
docs(auth): expand AuthService class docstring with async/sync + thre…
Aureliolo May 10, 2026
c9e69bb
refactor(core): add compare_ci helper, migrate inline .lower()== site…
Aureliolo May 10, 2026
08a066a
refactor(escalation): collapse Winner+Hybrid processors into HumanDec…
Aureliolo May 10, 2026
6eaaf02
build(web): drop openapi-typescript; migrate enums.ts + reports.ts to…
Aureliolo May 10, 2026
d5a1ac2
docs: protocols audit (Refs #1850 I)
Aureliolo May 10, 2026
f723654
chore: bump line-shift baselines after compare_ci import additions (R…
Aureliolo May 10, 2026
8b36246
refactor: apply pre-PR review feedback for issue 1850 (Refs #1850)
Aureliolo May 10, 2026
b83267a
chore: bump line-shift baselines after pre-PR review fixes (Refs #1850)
Aureliolo May 10, 2026
7a05a51
fix: address PR #1859 reviewer feedback (round 1)
Aureliolo May 10, 2026
65399fc
chore: bump line-shift baseline after registry.py import expansion (R…
Aureliolo May 10, 2026
963e08f
fix: address PR #1859 reviewer feedback (round 2)
Aureliolo May 10, 2026
af7d681
chore: bump line-shift baseline after dispatcher start() expansion (R…
Aureliolo May 10, 2026
b1cff10
fix: address PR #1859 reviewer feedback (round 3)
Aureliolo May 10, 2026
02140f7
chore: bump line-shift baseline after dispatcher start() unsubscribe …
Aureliolo May 10, 2026
fccb22f
fix: address PR #1859 reviewer feedback (round 4)
Aureliolo May 10, 2026
3f73e9d
fix: address PR #1859 reviewer feedback (round 5)
Aureliolo May 10, 2026
477dc94
fix: address PR #1859 reviewer feedback (round 6) + proper kill switc…
Aureliolo May 10, 2026
3ab673b
fix: cast _FakeConfigResolver to ConfigResolver for mypy strict (Refs…
Aureliolo May 10, 2026
979eb1f
fix: round 7 reviewer feedback + tests for round-6 code
Aureliolo May 10, 2026
00c503a
fix: round 8 dispatcher recovery edge cases (PR #1859)
Aureliolo May 10, 2026
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
4 changes: 0 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,6 @@ _site/
docs/openapi/reference.html
docs/openapi/openapi.json

# Generated TS types from OpenAPI (built by `npm --prefix web
# run generate-types`, invoked as a pre-hook before type-check/build).
web/src/api/types/generated.d.ts

# Intermediate i18n catalog built by scripts/extract_web_strings.py
# (hand-off artefact for issue #1417; regenerate locally as needed).
web/src/i18n/_extracted_catalog.json
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ PYTHONPATH=. uv run zensical build # docs
- [docs/reference/claude-reference.md](docs/reference/claude-reference.md): Doc layout, Docker, releasing, CI, dependencies, Hypothesis deep-dive
- [docs/reference/conventions.md](docs/reference/conventions.md): repository CRUD, lifecycle, response wrapping, validators, event imports, domain errors, file structure, frozen ConfigDict, args models, Pydantic v2, async, Clock seam, observability event-name inventory, repository CRUD method names, MCP handler logging centralisation, repository file structure, registering MANDATORY rules, `activate_*` / `deactivate_*` lifecycle naming
- [docs/reference/convention-gates.md](docs/reference/convention-gates.md): gate inventory (35 enforcement gates + meta-gate)
- [docs/reference/regional-defaults.md](docs/reference/regional-defaults.md), [persistence-boundary.md](docs/reference/persistence-boundary.md), [configuration-precedence.md](docs/reference/configuration-precedence.md), [errors.md](docs/reference/errors.md), [sec-prompt-safety.md](docs/reference/sec-prompt-safety.md), [lifecycle-sync.md](docs/reference/lifecycle-sync.md), [mcp-handler-contract.md](docs/reference/mcp-handler-contract.md), [typed-boundaries.md](docs/reference/typed-boundaries.md), [retry-patterns.md](docs/reference/retry-patterns.md), [scaffolding.md](docs/reference/scaffolding.md), [audit-category-gate-coverage.md](docs/reference/audit-category-gate-coverage.md), [dead-api-endpoints.md](docs/reference/dead-api-endpoints.md), [pluggable-subsystems.md](docs/reference/pluggable-subsystems.md), [telemetry.md](docs/reference/telemetry.md)
- [docs/reference/regional-defaults.md](docs/reference/regional-defaults.md), [persistence-boundary.md](docs/reference/persistence-boundary.md), [configuration-precedence.md](docs/reference/configuration-precedence.md), [errors.md](docs/reference/errors.md), [sec-prompt-safety.md](docs/reference/sec-prompt-safety.md), [lifecycle-sync.md](docs/reference/lifecycle-sync.md), [mcp-handler-contract.md](docs/reference/mcp-handler-contract.md), [typed-boundaries.md](docs/reference/typed-boundaries.md), [retry-patterns.md](docs/reference/retry-patterns.md), [scaffolding.md](docs/reference/scaffolding.md), [audit-category-gate-coverage.md](docs/reference/audit-category-gate-coverage.md), [dead-api-endpoints.md](docs/reference/dead-api-endpoints.md), [pluggable-subsystems.md](docs/reference/pluggable-subsystems.md), [protocols-audit.md](docs/reference/protocols-audit.md), [telemetry.md](docs/reference/telemetry.md)

## Diagrams

Expand Down
395 changes: 395 additions & 0 deletions docs/reference/protocols-audit.md

Large diffs are not rendered by default.

5 changes: 1 addition & 4 deletions scripts/long_running_loops_kill_switch_baseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@
# New violations not on this list fail the gate; entries here
# that no longer match the scan emit a stale-baseline warning
# (gate still passes; regenerate via --update-baseline).
src/synthorg/api/bus_bridge.py:MessageBusBridge:_poll_channel:528
src/synthorg/api/controllers/events.py:_sse_event_stream:433
src/synthorg/api/controllers/ws.py:_outbound_consumer:341
src/synthorg/api/controllers/ws.py:_receive_loop:735
src/synthorg/api/controllers/ws_revalidation.py:_periodic_revalidate:112
src/synthorg/api/services/idempotency_service.py:IdempotencyService:run_idempotent:185
src/synthorg/backup/scheduler.py:BackupScheduler:_run_loop:279
src/synthorg/budget/quota_poller.py:QuotaPoller:_poll_loop:137
src/synthorg/client/continuous.py:ContinuousMode:start:118
src/synthorg/communication/bus/_nats_history.py:collect_history_batches:89
src/synthorg/communication/bus/_nats_receive.py:receive_blocking:388
src/synthorg/communication/bus/_nats_receive.py:receive_with_timeout:428
Expand All @@ -31,7 +28,7 @@ src/synthorg/persistence/sqlite/escalation_repo.py:SQLiteEscalationRepository:_n
src/synthorg/providers/resilience/rate_limiter.py:RateLimiter:_wait_for_rpm_slot:148
src/synthorg/providers/resilience/rate_limiter.py:RateLimiter:acquire:71
src/synthorg/security/timeout/scheduler.py:ApprovalTimeoutScheduler:_run_loop:288
src/synthorg/settings/dispatcher.py:SettingsChangeDispatcher:_poll_loop:337
src/synthorg/settings/dispatcher.py:SettingsChangeDispatcher:_poll_loop:414
src/synthorg/telemetry/collector.py:TelemetryCollector:_heartbeat_loop:777
src/synthorg/tools/sandbox/lifecycle/per_agent.py:PerAgentStrategy:_idle_expire:277
src/synthorg/workers/worker.py:Worker:run:115
7 changes: 1 addition & 6 deletions scripts/loop_bound_init_baseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,12 @@
src/synthorg/api/bus_bridge.py:102:MessageBusBridge:_lifecycle_lock:Lock
src/synthorg/backup/service.py:75:BackupService:_backup_lock:Lock
src/synthorg/budget/quota_poller.py:64:QuotaPoller:_lifecycle_lock:Lock
src/synthorg/client/continuous.py:65:ContinuousMode:_stop_event:Event
src/synthorg/client/continuous.py:71:ContinuousMode:_lifecycle_lock:Lock
src/synthorg/communication/bus/memory.py:110:InMemoryMessageBus:_lock:Lock
src/synthorg/communication/bus/memory.py:125:InMemoryMessageBus:_shutdown_event:Event
src/synthorg/engine/task_engine.py:104:TaskEngine:_lifecycle_lock:Lock
src/synthorg/engine/task_engine.py:112:TaskEngine:_admission_lock:Lock
src/synthorg/engine/task_engine.py:114:TaskEngine:_observer_queue:Queue
src/synthorg/engine/task_engine.py:96:TaskEngine:_queue:Queue
src/synthorg/engine/workflow/webhook_bridge.py:64:WebhookEventBridge:_lifecycle_lock:Lock
src/synthorg/hr/pruning/service.py:103:PruningService:_lifecycle_lock:Lock
src/synthorg/hr/pruning/service.py:129:PruningService:_processing_lock:Lock
src/synthorg/hr/pruning/service.py:97:PruningService:_wake_event:Event
Expand All @@ -34,9 +31,7 @@ src/synthorg/notifications/adapters/ntfy.py:116:NtfyNotificationSink:_lifecycle_
src/synthorg/notifications/adapters/slack.py:104:SlackNotificationSink:_lifecycle_lock:Lock
src/synthorg/notifications/dispatcher.py:113:NotificationDispatcher:_dispatch_idle:Event
src/synthorg/notifications/dispatcher.py:92:NotificationDispatcher:_lifecycle_lock:Lock
src/synthorg/providers/health_prober.py:184:ProviderHealthProber:_stop_event:Event
src/synthorg/providers/health_prober.py:190:ProviderHealthProber:_lifecycle_lock:Lock
src/synthorg/settings/dispatcher.py:104:SettingsChangeDispatcher:_lifecycle_lock:Lock
src/synthorg/settings/dispatcher.py:105:SettingsChangeDispatcher:_lifecycle_lock:Lock
src/synthorg/telemetry/collector.py:321:TelemetryCollector:_lifecycle_lock:Lock
src/synthorg/workers/worker.py:83:Worker:_stop_event:Event
src/synthorg/workers/worker.py:92:Worker:_lifecycle_lock:Lock
47 changes: 11 additions & 36 deletions scripts/no_magic_numbers_baseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ src/synthorg/a2a/models.py:134:28
src/synthorg/a2a/models.py:135:24
src/synthorg/a2a/models.py:136:29
src/synthorg/a2a/push_verifier.py:22:30
src/synthorg/api/app.py:144:42
src/synthorg/api/auth/middleware.py:47:16
src/synthorg/api/auth/middleware.py:48:16
src/synthorg/api/auth/ticket_store.py:64:29
src/synthorg/api/auth/ticket_store.py:111:29
src/synthorg/api/auth/token_size.py:22:33
Expand Down Expand Up @@ -69,17 +68,8 @@ src/synthorg/api/controllers/meetings.py:36:28
src/synthorg/api/controllers/meetings.py:37:28
src/synthorg/api/controllers/meetings.py:38:31
src/synthorg/api/controllers/meetings.py:244:29
src/synthorg/api/controllers/memory.py:67:34
src/synthorg/api/controllers/memory.py:145:33
src/synthorg/api/controllers/memory.py:146:36
src/synthorg/api/controllers/memory.py:377:21
src/synthorg/api/controllers/memory.py:632:21
src/synthorg/api/controllers/messages.py:39:29
src/synthorg/api/controllers/messages.py:133:29
src/synthorg/api/controllers/meta.py:84:29
src/synthorg/api/controllers/meta.py:137:29
src/synthorg/api/controllers/meta.py:179:29
src/synthorg/api/controllers/meta.py:229:29
src/synthorg/api/controllers/meta_analytics.py:37:30
src/synthorg/api/controllers/meta_analytics.py:44:33
src/synthorg/api/controllers/meta_analytics.py:115:29
Expand All @@ -93,19 +83,14 @@ src/synthorg/api/controllers/ontology.py:359:29
src/synthorg/api/controllers/ontology.py:450:29
src/synthorg/api/controllers/personalities.py:76:29
src/synthorg/api/controllers/projects.py:71:29
src/synthorg/api/controllers/providers.py:1414:29
src/synthorg/api/controllers/reports.py:144:29
src/synthorg/api/controllers/requests.py:89:29
src/synthorg/api/controllers/role_versions.py:44:29
src/synthorg/api/controllers/scaling.py:214:29
src/synthorg/api/controllers/settings.py:320:29
src/synthorg/api/controllers/simulations.py:291:29
src/synthorg/api/controllers/subworkflows.py:128:29
src/synthorg/api/controllers/tasks.py:172:29
src/synthorg/api/controllers/users.py:255:29
src/synthorg/api/controllers/webhooks.py:57:32
src/synthorg/api/controllers/workflow_versions.py:197:29
src/synthorg/api/controllers/workflows.py:109:29
src/synthorg/api/controllers/ws.py:61:29
src/synthorg/api/controllers/ws.py:64:33
src/synthorg/api/controllers/ws.py:66:29
Expand Down Expand Up @@ -133,7 +118,7 @@ src/synthorg/api/path_params.py:64:24
src/synthorg/api/rate_limits/inflight_middleware.py:41:29
src/synthorg/api/services/idempotency_service.py:47:41
src/synthorg/api/services/idempotency_service.py:48:49
src/synthorg/api/services/org_mutations.py:43:22
src/synthorg/api/services/org_mutations.py:44:22
src/synthorg/api/services/ssrf_violation_service.py:141:21
src/synthorg/api/state_services.py:115:26
src/synthorg/backup/service_archive.py:38:23
Expand Down Expand Up @@ -348,7 +333,6 @@ src/synthorg/engine/workflow/strategies/throughput_adaptive.py:85:28
src/synthorg/engine/workflow/validate_edges.py:18:22
src/synthorg/engine/workflow/validation_types.py:10:22
src/synthorg/engine/workflow/version_service.py:43:21
src/synthorg/engine/workflow/webhook_bridge.py:37:38
src/synthorg/engine/workspace/disk_quota.py:26:16
src/synthorg/engine/workspace/semantic_checks.py:74:25
src/synthorg/engine/workspace/semantic_llm.py:181:27
Expand Down Expand Up @@ -389,7 +373,7 @@ src/synthorg/hr/performance/theil_sen_strategy.py:62:37
src/synthorg/hr/performance/theil_sen_strategy.py:63:37
src/synthorg/hr/performance/tracker.py:73:41
src/synthorg/hr/persistence_protocol.py:42:21
src/synthorg/hr/registry.py:45:37
src/synthorg/hr/registry.py:50:37
src/synthorg/hr/scaling/guards/approval_gate.py:49:27
src/synthorg/hr/scaling/guards/conflict_resolver.py:30:19
src/synthorg/hr/scaling/guards/cooldown.py:34:32
Expand All @@ -413,9 +397,9 @@ src/synthorg/hr/training/extractors/tool_patterns.py:29:25
src/synthorg/hr/training/guards/review_gate.py:31:25
src/synthorg/hr/training/guards/sanitization.py:25:22
src/synthorg/hr/training/service.py:65:26
src/synthorg/hr/training/source_selectors/department_diversity.py:28:31
src/synthorg/hr/training/source_selectors/department_diversity.py:29:31
src/synthorg/hr/training/source_selectors/role_top_performers.py:28:17
src/synthorg/hr/training/source_selectors/department_diversity.py:30:31
src/synthorg/hr/training/source_selectors/department_diversity.py:31:31
src/synthorg/hr/training/source_selectors/role_top_performers.py:30:17
src/synthorg/infrastructure/services.py:917:21
src/synthorg/infrastructure/services.py:957:21
src/synthorg/integrations/health/checks/generic_http.py:21:11
Expand Down Expand Up @@ -505,8 +489,6 @@ src/synthorg/memory/ranking.py:332:23
src/synthorg/memory/ranking.py:410:32
src/synthorg/memory/ranking.py:454:30
src/synthorg/memory/retrieval/hierarchical/supervisor.py:80:29
src/synthorg/memory/retrieval/hierarchical/supervisor.py:102:37
src/synthorg/memory/retrieval/hierarchical/supervisor.py:104:31
src/synthorg/memory/retrieval/hierarchical/workers.py:42:33
src/synthorg/memory/retrieval/reranking/cache.py:26:23
src/synthorg/memory/retrieval/reranking/cache.py:27:20
Expand Down Expand Up @@ -688,7 +670,6 @@ src/synthorg/persistence/ontology_protocol.py:77:21
src/synthorg/persistence/ontology_protocol.py:107:21
src/synthorg/persistence/ontology_protocol.py:115:21
src/synthorg/persistence/postgres/approval_repo.py:41:23
src/synthorg/persistence/postgres/approval_repo.py:390:21
src/synthorg/persistence/postgres/artifact_repo.py:37:22
src/synthorg/persistence/postgres/artifact_repo.py:202:21
src/synthorg/persistence/postgres/audit_repository.py:102:21
Expand Down Expand Up @@ -729,7 +710,6 @@ src/synthorg/persistence/postgres/workflow_execution_repo.py:55:22
src/synthorg/persistence/provider_audit_protocol.py:62:21
src/synthorg/persistence/settings_protocol.py:56:21
src/synthorg/persistence/sqlite/approval_repo.py:30:23
src/synthorg/persistence/sqlite/approval_repo.py:450:21
src/synthorg/persistence/sqlite/artifact_repo.py:27:22
src/synthorg/persistence/sqlite/artifact_repo.py:269:21
src/synthorg/persistence/sqlite/audit_repository.py:127:21
Expand Down Expand Up @@ -769,16 +749,11 @@ src/synthorg/persistence/user_protocol.py:189:21
src/synthorg/persistence/version_protocol.py:107:21
src/synthorg/providers/cost_recording.py:103:45
src/synthorg/providers/discovery.py:41:43
src/synthorg/providers/drivers/litellm_driver.py:96:24
src/synthorg/providers/drivers/litellm_driver.py:97:24
src/synthorg/providers/health.py:37:23
src/synthorg/providers/health.py:38:22
src/synthorg/providers/health.py:39:18
src/synthorg/providers/health.py:40:24
src/synthorg/providers/health_prober.py:46:40
src/synthorg/providers/health_prober.py:47:39
src/synthorg/providers/health_prober.py:48:43
src/synthorg/providers/health_prober.py:49:40
src/synthorg/providers/health_prober.py:50:35
src/synthorg/providers/management/_capability_helpers.py:29:26
src/synthorg/providers/management/_helpers.py:29:35
src/synthorg/providers/management/audit_service.py:74:21
Expand All @@ -802,8 +777,8 @@ src/synthorg/security/trust/config.py:18:39
src/synthorg/security/trust/milestone_strategy.py:34:39
src/synthorg/security/trust/weighted_strategy.py:27:40
src/synthorg/settings/bridge_configs.py:34:44
src/synthorg/settings/dispatcher.py:39:48
src/synthorg/settings/dispatcher.py:42:44
src/synthorg/settings/dispatcher.py:40:48
src/synthorg/settings/dispatcher.py:43:44
src/synthorg/settings/subscribers/per_op_rate_limit_subscriber.py:50:33
src/synthorg/telemetry/collector.py:85:45
src/synthorg/telemetry/collector.py:95:33
Expand Down Expand Up @@ -851,7 +826,7 @@ src/synthorg/tools/sandbox/docker_sandbox_sidecar.py:34:36
src/synthorg/tools/sandbox/runtime_resolver.py:25:47
src/synthorg/tools/sandbox/subprocess_sandbox.py:49:44
src/synthorg/tools/web/base_web_tool.py:41:33
src/synthorg/tools/web/http_request.py:64:34
src/synthorg/tools/web/http_request.py:65:33
src/synthorg/tools/web/http_request.py:65:34
src/synthorg/tools/web/http_request.py:66:33
src/synthorg/tools/web/web_search.py:55:27
src/synthorg/workers/__main__.py:48:24
158 changes: 158 additions & 0 deletions scripts/protocol_audit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
"""Audit-time helper: enumerate Protocol classes and collect usage counts.

Output: a CSV-shaped markdown table (one row per Protocol class):

| path | line | name | rc | impl | typeuse | testuse |

Where:
rc - 1 if ``@runtime_checkable`` decorator on the class, else 0.
impl - count of explicit ``class X(<Name>...)`` matches in src/.
typeuse - count of ``: <Name> | -> <Name> | <Name> |`` matches in src/.
testuse - count of any ``<Name>`` token in tests/.

Run from the repo root:

uv run python scripts/protocol_audit.py

The output is consumed by ``docs/reference/protocols-audit.md``; that
page's date pin in the title records when the snapshot was last
regenerated. Re-run when revisiting the audit.
"""

import re
import subprocess
from dataclasses import dataclass
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
SRC = ROOT / "src" / "synthorg"
TESTS = ROOT / "tests"


@dataclass(frozen=True)
class ProtocolEntry:
"""One Protocol-class definition discovered during the SRC walk."""

path: str
line: int
name: str
runtime_checkable: bool


def _enumerate_protocols() -> list[ProtocolEntry]:
"""Walk SRC and yield every class declared as ``class X(... Protocol ...):``."""
pattern = re.compile(
r"^class (\w+)(?:\[[^\]]+\])?\((?:[\w\s,\.]*\b)Protocol\b",
)
rc_pattern = re.compile(r"^@runtime_checkable\s*$")
entries: list[ProtocolEntry] = []
for py_file in sorted(SRC.rglob("*.py")):
try:
text = py_file.read_text(encoding="utf-8")
except UnicodeDecodeError:
continue
lines = text.splitlines()
rel = py_file.relative_to(ROOT).as_posix()
for i, line in enumerate(lines):
m = pattern.match(line)
if not m:
continue
name = m.group(1)
# Walk backwards through decorators / blank lines to detect
# @runtime_checkable.
j = i - 1
rc = False
while j >= 0 and lines[j].strip().startswith("@"):
if rc_pattern.match(lines[j].strip()):
rc = True
break
j -= 1
entries.append(
ProtocolEntry(
path=rel,
line=i + 1,
name=name,
runtime_checkable=rc,
),
)
return entries


def _count(pattern: str, root: Path) -> int:
"""Count matches via system ``grep -rEo``.

The ``-o`` flag emits one line per match (rather than one line
per matched file-line), so the splitlines-based counting below
sees actual occurrence counts. Without it the helper would
silently undercount any source line that contains multiple
references to the same protocol (common in ``: <Name> | ->
<Name>`` annotations).

Raises ``RuntimeError`` on a missing grep binary or a non-zero
grep failure exit code (>1). Silent-zero fallback would
misclassify protocols as unused and quietly taint the audit
table; the script is invoked manually so failing loudly is the
right default.
"""
try:
result = subprocess.run(
["grep", "-rEo", "--include=*.py", pattern, str(root)],
check=False,
capture_output=True,
text=True,
)
except FileNotFoundError as exc:
msg = (
"grep binary not found on PATH; install GNU grep or run "
"the audit on a system that ships it."
)
raise RuntimeError(msg) from exc
if result.returncode > 1:
msg = (
f"grep exited with code {result.returncode} for pattern "
f"{pattern!r} under {root}: {result.stderr.strip()}"
)
raise RuntimeError(msg)
return sum(1 for line in result.stdout.splitlines() if line.strip())
Comment on lines +81 to +116
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify current error-handling behaviour in _count.
nl -ba scripts/protocol_audit.py | sed -n '79,92p'

Repository: Aureliolo/synthorg

Length of output: 103


🏁 Script executed:

cat -n scripts/protocol_audit.py | sed -n '75,95p'

Repository: Aureliolo/synthorg

Length of output: 762


🏁 Script executed:

head -100 scripts/protocol_audit.py

Repository: Aureliolo/synthorg

Length of output: 3187


🏁 Script executed:

grep -n "_count" scripts/protocol_audit.py

Repository: Aureliolo/synthorg

Length of output: 517


🏁 Script executed:

find . -type f -name "*.py" -exec grep -l "class DomainError" {} \;

Repository: Aureliolo/synthorg

Length of output: 157


🏁 Script executed:

grep -r "DomainError" --include="*.py" | head -20

Repository: Aureliolo/synthorg

Length of output: 1998


🏁 Script executed:

grep -h "raise\|Error" scripts/protocol_audit.py

Repository: Aureliolo/synthorg

Length of output: 127


Fail fast on grep errors instead of returning zero counts; use proper error hierarchy.

Returning 0 on grep execution failures silently misclassifies protocols as unused and taints the audit table. Raise on command errors and missing binary so bad audits fail loudly. However, the proposed fix must use an appropriate DomainError subclass (e.g., ProtocolAuditError) per the mandatory error hierarchy constraint—never RuntimeError.

🔧 Corrected approach

Define a ProtocolAuditError class inheriting from DomainError in the same file or import an existing audit-related error class. Then update _count() to raise it instead of returning 0.

🤖 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 `@scripts/protocol_audit.py` around lines 79 - 92, The _count function
currently swallows grep failures and returns 0; instead define or import a
ProtocolAuditError subclass of DomainError and modify _count to raise
ProtocolAuditError when subprocess.run raises FileNotFoundError (missing grep)
or when result.returncode > 1 (grep error), leaving normal zero/positive counts
only when grep succeeds (returncode 0 or 1); reference _count,
ProtocolAuditError (subclass of DomainError), and subprocess.run to locate and
implement the change.



def _impl_count(name: str) -> int:
"""Concrete classes inheriting from this Protocol (direct subclass)."""
return _count(rf"^class \w+\(.*\b{name}\b", SRC)


def _typeuse_count(name: str) -> int:
"""Type-annotation use of the protocol name across SRC.

Counts ``: <Name>``, ``-> <Name>``, and union-pipe ``| <Name>``
occurrences. The defining ``class <Name>(`` line is already
excluded by the pattern.
"""
return _count(rf"(:|->|\|)\s*\b{name}\b", SRC)


def _testuse_count(name: str) -> int:
"""Count occurrences of the protocol name anywhere under tests/."""
return _count(rf"\b{name}\b", TESTS)
Comment thread
coderabbitai[bot] marked this conversation as resolved.


def main() -> None:
"""Emit the Protocol-class audit table to stdout."""
entries = _enumerate_protocols()
print(f"# {len(entries)} Protocol classes")
print()
print("| path | line | name | rc | impl | typeuse | testuse |")
print("|---|---|---|---|---|---|---|")
for e in entries:
impl = _impl_count(e.name)
typeuse = _typeuse_count(e.name)
testuse = _testuse_count(e.name)
rc = "1" if e.runtime_checkable else "0"
print(
f"| {e.path} | {e.line} | {e.name} | {rc} | "
f"{impl} | {typeuse} | {testuse} |",
)


if __name__ == "__main__":
main()
Loading
Loading