Skip to content

feat: config-driven contract + concurrent-writer safety#189

Open
labgadget015-dotcom wants to merge 2 commits into
mainfrom
feat/config-driven-contract
Open

feat: config-driven contract + concurrent-writer safety#189
labgadget015-dotcom wants to merge 2 commits into
mainfrom
feat/config-driven-contract

Conversation

@labgadget015-dotcom

Copy link
Copy Markdown
Owner

Deferred review fixes from #180 / #188

Closes out the major/minor findings the dual code review flagged but were deferred from #188.

1. Config drift eliminated (major)

config.yaml blocks were declared but never read — ledger.py/recommendation_contract.py/message_formatter.py hardcoded everything. New config_loader.py reads config.yaml via PyYAML (with stdlib fallback if PyYAML isn't installed), so debounce windows, status tags, max_step_len, and the require_* severity sets are now config-driven. Operators can tune behaviour without code changes.

2. Concurrent-writer safety (major)

The recommender (record) and executor (transition) agents write to the same ledger. Previously _next_seq raced and large appends could interleave, losing entries or colliding seqs. New _append_with_seq() holds an exclusive fcntl.flock across seq-assignment + write, with fsync per write. POSIX-only; no-op fallback on non-fcntl platforms.

3. Status validation (minor)

transition() now raises ValueError on unknown statuses (e.g. assiged typo) via VALID_STATUSES, so a typo can't silently break de-dup by creating an unmatchable ledger entry.

Tests

14/14 passing. New tests:

  • test_config_overrides_max_step_len — custom config lowers the step cap
  • test_config_overrides_debounce_window — custom config widens the suppress window
  • test_transition_rejects_unknown_status — typo raises ValueError
  • test_concurrent_writers_do_not_lose_entries — 8 threads × 25 writes, no lost entries, no seq collisions
PYTHONPATH=autopilot python3 autopilot/tests/test_recommendation_contract.py

Files

  • autopilot/config_loader.py (new) — config reader with stdlib fallback
  • autopilot/decisions/ledger.py — fcntl locking, config-driven debounce, status validation
  • autopilot/recommendation_contract.py — config-driven validate()
  • autopilot/message_formatter.py — config-driven status tags
  • autopilot/tests/test_recommendation_contract.py — 14 tests
  • autopilot/README.md — updated file listing

Refs #180 #188

Addresses the deferred review findings from PR #180/#188:

1. [MAJOR] config.yaml blocks are now read (were declared but never used).
   New config_loader.py reads config.yaml via PyYAML with stdlib fallback,
   so debounce windows, status tags, max_step_len, and require_* sets are
   config-driven instead of hardcoded. Silent config drift eliminated.

2. [MAJOR] Concurrent-writer safety: ledger _append_with_seq() now holds an
   exclusive fcntl.flock across seq-assignment + write, preventing the
   _next_seq race and large-entry interleaving between the recommender
   (record) and executor (transition) agents. fsync per write. POSIX-only;
   no-op fallback on non-fcntl platforms.

3. [MINOR] Status validation: transition() now raises ValueError on unknown
   statuses (e.g. 'assiged' typo) via VALID_STATUSES, so a typo can't
   silently break de-dup.

Tests: 14/14 passing (added 4: config-overrides-max_step_len,
config-overrides-debounce, transition-rejects-unknown-status,
concurrent-writers-do-not-lose-entries with 8x25 parallel writes).
Copilot AI review requested due to automatic review settings July 6, 2026 04:04
@github-actions github-actions Bot added the autopilot Changes to autopilot/ label Jul 6, 2026
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

📊 Code Complexity Analysis

Summary:

  • Total Functions Analyzed: 821
  • Average Complexity: 3.51
  • High Complexity Functions: 27
  • Low Maintainability Files: 59

⚠️ High Complexity Functions

These functions exceed the complexity threshold and should be refactored:

File Function Complexity Line
core/risk_scorer.py score_pull_request 35 141
autopilot/autopilot.py generate_summary 24 195
autopilot/staleness_engine.py process_stale_prs 16 281
autopilot/ai_optimization/performance_monitor.py get_benchmark_stats 15 184
.github/scripts/weekly_digest.py build_blocks 15 38
autopilot/recommendation_contract.py validate 14 53
.github/scripts/metrics_collector.py parse_workflow_metrics 14 148
.github/scripts/setup_branch_protection.py main 14 240
.github/scripts/self_healing_system.py analyze_failure_patterns 14 256
.github/scripts/ai_code_suggestor.py _check_import_organization 14 113

... and 17 more

Recommendations:

  • Break down large functions into smaller, focused units
  • Extract complex conditional logic into separate functions
  • Use early returns to reduce nesting

🔧 Low Maintainability Files

These files have low maintainability scores and may need refactoring:

File Score Status
.github/scripts/health_dashboard_generator.py 28.14 🔴
.github/scripts/workflow_monitor.py 33.73 🔴
.github/scripts/ai_code_suggestor.py 33.76 🔴
.github/scripts/ai_workflow_optimizer.py 35.51 🔴
.github/scripts/performance_benchmark.py 39.46 🔴
.github/scripts/self_healing_system.py 40.27 🔴
.github/scripts/threshold_monitor.py 41.13 🔴
.github/scripts/parallel_code_analyzer_optimized.py 41.16 🔴
autopilot/tests/test_recommendation_contract.py 42.02 🔴
autopilot/autopilot.py 42.45 🔴
autopilot/ai_optimization/anomaly_detector.py 42.56 🔴
agents/triage_agent.py 42.79 🔴
.github/scripts/refactoring_assistant.py 43.03 🔴
autopilot/ai_optimization/intelligent_cache.py 43.28 🔴
autopilot/ai_optimization/commit_summarizer.py 44.05 🔴
.github/scripts/async_parallel_analyzer.py 44.47 🔴
autopilot/ai_optimization/performance_monitor.py 44.69 🔴
.github/scripts/badge_generator.py 45.28 🔴
.github/scripts/copilot_integration.py 45.37 🔴
.github/scripts/distributed_monitoring.py 45.53 🔴
.github/scripts/elite_copilot.py 45.69 🔴
agents/dependency_agent.py 45.76 🔴
.github/scripts/issue_auto_creator.py 46.39 🔴
.github/scripts/cost_calculator.py 46.4 🔴
.github/scripts/inline_pr_commenter.py 46.63 🔴
.github/scripts/complexity_reporter.py 46.78 🔴
.github/scripts/pr_triage.py 47.13 🔴
core/risk_scorer.py 48.15 🔴
autopilot/ai_optimization/nlp_relevance_filter.py 48.43 🔴
.github/scripts/pr_inline_commenter.py 48.47 🔴
autopilot/staleness_engine.py 48.73 🔴
.github/scripts/metrics_collector.py 48.91 🔴
.github/scripts/dependency_updater.py 48.91 🔴
autopilot/ai_optimization/ml_priority_scorer.py 49.53 🔴
.github/scripts/changelog_generator.py 49.75 🔴
.github/scripts/parallel_code_analyzer.py 49.96 🔴
autopilot/ai_optimization/api_optimizer.py 50.46 🟡
agents/security_scan_agent.py 51.04 🟡
.github/scripts/workflow_optimizer.py 51.67 🟡
.github/scripts/cot_selector.py 51.73 🟡
.github/scripts/release_manager.py 51.92 🟡
.github/scripts/llm_router.py 52.35 🟡
.github/scripts/auto_pr.py 52.72 🟡
.github/scripts/notification_manager.py 53.58 🟡
.github/scripts/prometheus_exporter.py 54.96 🟡
.github/scripts/weekly_digest.py 55.02 🟡
core/audit_logger.py 55.6 🟡
.github/scripts/gather_context.py 56.0 🟡
core/llm_provider.py 56.32 🟡
.github/scripts/streaming_results.py 56.64 🟡
.github/scripts/setup_branch_protection.py 57.0 🟡
.github/scripts/optimized_github_client.py 58.27 🟡
agents/orchestrator_agent.py 59.02 🟡
agents/code_review_agent.py 60.45 🟡
core/github_client.py 61.96 🟡
autopilot/decisions/ledger.py 62.87 🟡
core/message_queue.py 63.22 🟡
core/agent_config.py 63.86 🟡
core/idempotency.py 64.45 🟡

Maintainability Index Guide:

  • 🟢 85-100: Excellent maintainability
  • 🟡 65-84: Good maintainability
  • 🟠 50-64: Moderate maintainability (consider refactoring)
  • 🔴 0-49: Poor maintainability (needs refactoring)

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🟢 Risk Assessment: LOW (1.5/10)

Analysed 6 files, 346+ / 70− lines. Test coverage unchanged or improved.

Scoring breakdown

Factor Score
Change volume — 416 lines changed +1.5

✅ Eligible for auto-merge (subject to CI passing).

try:
r = _good()
assert should_post(r, path)[0] is True # first raise allowed
assert should_post(r, path)[0] is True # first raise allowed
r = _good()
r.steps[0] = "x" * 25 # over the custom 20 limit, under default 120
ok, errs = validate(r)
assert not ok
r.steps[0] = "x" * 25 # over the custom 20 limit, under default 120
ok, errs = validate(r)
assert not ok
assert any("step 1" in e and "20" in e for e in errs)
from decisions.ledger import record, should_post
record(r, ledger.name)
allow, reason = should_post(r, ledger.name)
assert allow is False, reason # 100000h window always suppresses
record(r, ledger.name)
try:
transition(r.signature(), "assiged", path=ledger.name) # typo
assert False, "expected ValueError for unknown status"
transition(r.signature(), "assiged", path=ledger.name) # typo
assert False, "expected ValueError for unknown status"
except ValueError as e:
assert "assiged" in str(e)
for t in threads:
t.join()

assert errors == [], errors
assert errors == [], errors
from decisions.ledger import _load
entries = _load(ledger.name)
assert len(entries) == n_writers * n_each, (
)
# seq values should be unique 0..N-1 (no collisions from the race)
seqs = sorted(e.get("seq", -1) for e in entries)
assert seqs == list(range(n_writers * n_each)), "seq collisions detected"
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🤖 Elite AI Copilot Analysis

Elite AI Copilot Analysis Report

Generated: 2026-07-06 04:04:48
Session ID: copilot_1783310688
Repository: .

🎯 Health Score: 100.0/100

🚀 Top Recommendations

  1. ✅ Repository is in excellent shape - continue current practices

📊 Detailed Insights

Code Quality Baseline Established

  • Category: code_quality
  • Severity: info
  • Description: Repository code quality metrics captured
  • Suggested Action: Continue monitoring for regressions
  • Confidence: 90%

Security Scan Initiated

  • Category: security
  • Severity: info
  • Description: No critical vulnerabilities detected in initial scan
  • Suggested Action: Enable continuous security monitoring
  • Confidence: 85%

Repository Structure Analyzed

  • Category: architecture
  • Severity: info
  • Description: Well-organized modular structure detected
  • Suggested Action: Maintain separation of concerns
  • Confidence: 80%

Performance Baseline Captured

  • Category: performance
  • Severity: info
  • Description: Repository performance metrics recorded
  • Suggested Action: Monitor for performance regressions
  • Confidence: 75%

Documentation Structure Good

  • Category: documentation
  • Severity: info
  • Description: Comprehensive documentation files present
  • Suggested Action: Keep documentation in sync with code changes
  • Confidence: 90%

Powered by Elite AI Copilot v1.0

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Code Quality Analysis ❌ FAILED

Duration: 0.02s
Total Issues: 10

Tool Results

  • pylint: ❌
  • flake8: ❌
  • bandit: ❌
  • radon_cc: ❌
  • radon_mi: ❌
View detailed results
{
  "timestamp": "2026-07-06 04:04:50",
  "elapsed_seconds": 0.02,
  "summary": {
    "total_issues": 10,
    "critical": 0,
    "high": 0,
    "medium": 0,
    "low": 0
  },
  "tools": {
    "pylint": {
      "status": "failed",
      "output": "",
      "errors": "Pylint error: [Errno 2] No such file or directory: 'pylint'"
    },
    "flake8": {
      "status": "failed",
      "output": "",
      "errors": "Flake8 error: [Errno 2] No such file or directory: 'flake8'"
    },
    "bandit": {
      "status": "failed",
      "output": "",
      "errors": "Bandit error: [Errno 2] No such file or directory: 'bandit'"
    },
    "radon_cc": {
      "status": "failed",
      "output": "",
      "errors": "Radon error: [Errno 2] No such file or directory: 'radon'"
    },
    "radon_mi": {
      "status": "failed",
      "output": "",
      "errors": "Radon MI error: [Errno 2] No such file or directory: 'radon'"
    }
  },
  "passed": false
}

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🔍 Pre-commit Checks

🔧 Pre-commit issues were automatically fixed and committed.

Please pull the latest changes before pushing again:

git pull origin feat/config-driven-contract

Pre-commit hooks help maintain code quality and consistency.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

🔒 Security Scan Results

🛡️ Bandit Security Scan

  • 🔴 HIGH: 0
  • 🟡 MEDIUM: 9
  • 🟢 LOW: 108

📦 Dependency Vulnerabilities

  • Total vulnerable dependencies: 61

Vulnerable Dependencies:

  • pygithub 2.9.1
  • aiohttp 3.14.1
  • multidict 6.7.1
  • yarl 1.24.2
  • pyyaml 6.0.3
  • ... and 56 more

Security scans run automatically on every PR. View detailed reports in the Actions tab.

@labgadget015-dotcom

Copy link
Copy Markdown
Owner Author

🤖 DRC Agent Analysis

Recommendation: 🔴 P0 CRITICAL

Summary: Realist recommended Solution 1: Merge As-Is with Hardened CI Gate

Next steps:

  1. Step 1: Merge PR feat: config-driven contract + concurrent-writer safety #189 into main immediately with no code changes — captures all existing gains and closes issues feat: add DRC recommendation contract + de-dup ledger #180 and fix: debounce assigned/inflight items + minor review fixes #188
  2. Step 2: Add autopilot/config.schema.json defining required fields (debounce_window as integer >=1, max_step_len as integer >=1, require_* as arrays, status_tags as array) with type and minimum constraints
  3. Step 3: Create .github/workflows/config-validation.yml triggered on pull_request paths autopilot/** running jsonschema validation against config.schema.json and the concurrent-writer pytest canary with --timeout=30
  4. Step 4: Pin PyYAML>=6.0, jsonschema>=4.0, and pytest-timeout>=0.5 in requirements.txt and requirements-dev.txt respectively with inline comments documenting the POSIX-only locking caveat
  5. Step 5: Update README.md with a Config section documenting each config.yaml field, valid ranges, and the POSIX-only locking caveat

Strategic fit: Consulting: high · Product: medium · Tech debt: reduces


Analysed by GadgetLab DRC Agent (Dreamer → Realist → Critic) · Run run_1783310661875

Copilot AI left a comment

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.

Pull request overview

This PR follows up on #180/#188 to make the DRC recommendation contract + ledger behavior config-driven and to harden the ledger against concurrent writers by adding file locking and atomic seq assignment.

Changes:

  • Introduces autopilot/config_loader.py to load autopilot/config.yaml (with defaults when PyYAML/config are unavailable) and wires contract/debounce/status tags to it.
  • Updates the decisions ledger to append entries with a lock-held seq assignment + fsync, and validates lifecycle statuses on transition.
  • Adds tests for config overrides, unknown-status rejection, and concurrent writers; updates docs.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
autopilot/config_loader.py New config loader with cached, merged defaults + helpers for debounce/status tags/contract/ledger path.
autopilot/decisions/ledger.py Adds flock-based locking + atomic append-with-seq, config-driven debounce window lookup, and status validation on transitions.
autopilot/recommendation_contract.py Makes validation rules (max step length + required fields by severity) config-driven and exports valid statuses.
autopilot/message_formatter.py Makes status tags config-driven (with fallback).
autopilot/tests/test_recommendation_contract.py Adds tests for config overrides, status validation, and concurrent writers; small formatting tweaks.
autopilot/README.md Updates the autopilot module file list to reflect new config-driven/locked behavior.
Comments suppressed due to low confidence (1)

autopilot/tests/test_recommendation_contract.py:5

  • These tests live under autopilot/tests/, but pytest in this repo is configured with testpaths = ["tests"] (pyproject.toml), so CI/default pytest runs will not collect them. That means the new config-driven + concurrency behavior may not actually be covered in the main test suite unless tests are moved under tests/ (or pytest config is updated).
"""Smoke tests for recommendation_contract + ledger + formatter.

Run:  python -m pytest autopilot/tests/test_recommendation_contract.py -q
  or:  python autopilot/tests/test_recommendation_contract.py
"""

Comment on lines 49 to +53
with open(path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if line:
try:
out.append(json.loads(line))
except json.JSONDecodeError:
continue
if not line:
continue
Comment on lines +81 to +86
"""Append a JSON line under an exclusive file lock.

Holds the lock across _next_seq + write when called via _append_locked
so concurrent writers can't collide on sequence numbers or interleave
large entries.
"""
Comment on lines +146 to +163
import config_loader
config_loader.reset_cache()
cfg = tempfile.NamedTemporaryFile(
mode="w", suffix=".yaml", delete=False, encoding="utf-8")
cfg.write("message_contract:\n max_step_len: 20\n")
cfg.close()
try:
config_loader.DEFAULT_CONFIG_PATH = cfg.name
config_loader.reset_cache()
r = _good()
r.steps[0] = "x" * 25 # over the custom 20 limit, under default 120
ok, errs = validate(r)
assert not ok
assert any("step 1" in e and "20" in e for e in errs)
finally:
config_loader.reset_cache()
os.unlink(cfg.name)

Comment on lines +167 to +191
import config_loader
config_loader.reset_cache()
cfg = tempfile.NamedTemporaryFile(
mode="w", suffix=".yaml", delete=False, encoding="utf-8")
cfg.write(
"recommendation_debounce:\n"
" repost_policy:\n"
" open: {min_hours_between: 100000, never_repost: false}\n"
)
cfg.close()
ledger = tempfile.NamedTemporaryFile(suffix=".jsonl", delete=False)
ledger.close()
try:
config_loader.DEFAULT_CONFIG_PATH = cfg.name
config_loader.reset_cache()
r = _good()
from decisions.ledger import record, should_post
record(r, ledger.name)
allow, reason = should_post(r, ledger.name)
assert allow is False, reason # 100000h window always suppresses
finally:
config_loader.reset_cache()
os.unlink(cfg.name)
os.unlink(ledger.name)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autopilot Changes to autopilot/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants