Skip to content

Commit 3ea7643

Browse files
SL-Marclaude
andcommitted
Add post-generation QC API linter with 8 lint rules
Static linter (regex+AST hybrid) catches and auto-fixes common LLM mistakes before code hits QC compilation: PascalCase methods/attrs/defs, wrong Resolution casing, Action() wrappers, len() on RollingWindow, .Values on RollingWindow. Warns on History DataFrame misuse, history() in on_data(), and indicator name shadowing. Integrated at Phase 1.5 in processor pipeline, after regeneration in fidelity loop, and in ValidateCodeTool. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1da6ba4 commit 3ea7643

5 files changed

Lines changed: 919 additions & 1 deletion

File tree

quantcoder/core/__init__.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Core modules for QuantCoder."""
22

33
# Lazy imports to avoid loading heavy dependencies at import time
4-
__all__ = ["ArticleProcessor", "LLMHandler", "SummaryStore"]
4+
__all__ = ["ArticleProcessor", "LLMHandler", "SummaryStore", "lint_qc_code", "LintResult"]
55

66

77
def __getattr__(name):
@@ -14,4 +14,10 @@ def __getattr__(name):
1414
if name == "SummaryStore":
1515
from .summary_store import SummaryStore
1616
return SummaryStore
17+
if name == "lint_qc_code":
18+
from .qc_linter import lint_qc_code
19+
return lint_qc_code
20+
if name == "LintResult":
21+
from .qc_linter import LintResult
22+
return LintResult
1723
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

quantcoder/core/processor.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,19 @@ def generate_code_from_summary(self, summary_text: str) -> Optional[str]:
580580
else:
581581
self.logger.info("No stubs detected — skipping Stage 2")
582582

583+
# -- Phase 1.5: QC API linting ----------------------------------------
584+
from .qc_linter import lint_qc_code
585+
586+
lint_result = lint_qc_code(qc_code)
587+
if lint_result.had_fixes and self._validate_code(lint_result.code):
588+
self.logger.info(
589+
"QC linter applied %d auto-fixes",
590+
sum(1 for i in lint_result.issues if i.fixed),
591+
)
592+
qc_code = lint_result.code
593+
for hint in lint_result.unfixable_hints:
594+
self.logger.warning("QC linter warning: %s", hint)
595+
583596
# -- Phase 2: fidelity assessment loop (unchanged) --------------------
584597
for fidelity_attempt in range(self.max_fidelity_attempts):
585598
self.logger.info(f"Fidelity assessment attempt {fidelity_attempt + 1}")
@@ -619,6 +632,10 @@ def generate_code_from_summary(self, summary_text: str) -> Optional[str]:
619632
syntax_attempt += 1
620633

621634
if self._validate_code(new_code):
635+
# Lint regenerated code too
636+
regen_lint = lint_qc_code(new_code)
637+
if regen_lint.had_fixes and self._validate_code(regen_lint.code):
638+
new_code = regen_lint.code
622639
qc_code = new_code
623640
else:
624641
self.logger.warning("Regenerated code failed syntax validation, keeping previous version")

0 commit comments

Comments
 (0)