crafting-table/crafting_table/parsers/generic.py
Kayos d467b2f5be v0.1 wave 2A (steps 5+6): per-language parsers + findings extraction
- parsers/ package: rust / python / go / typescript / generic
- parser registry with language+recipe -> fallback resolution
- fingerprint hash (kind+file+line+code) for cross-run dedup
- runner.py post-exec hook: parse log, persist findings, count on job row
  (extraction runs before mark_job_finished so callers polling on terminal
  status see findings_count populated atomically)
- db.insert_finding / list_findings / increment_findings_count DAOs already
  shipped in wave 1; wired here
- GET /jobs/{id}/findings now returns real data (server route already
  shipped; was returning empty list because nothing populated the table)
- tests/test_parsers/: 6 modules + 11 fixtures (rust/python/go/typescript)
- tests/test_runner_findings.py: 3 integration tests
- README: tick steps 2-6, add Findings section

Suite: 108 passing (62 wave-1 + 46 new).
Spec: memory/spec-crafting-table.md
2026-04-29 08:36:16 -07:00

34 lines
1.1 KiB
Python

"""Fallback parser — used when no language-specific parser claims the recipe.
Behavior is intentionally minimal. We don't try to extract anything from
arbitrary stdout for unknown (language, recipe) combos — that's a v0.2
problem. All we do is: if the recipe exited non-zero, emit a single
``recipe_fail`` finding so the digest can flag "something went wrong" without
hand-grepping the log. exit_code 0 = no findings.
"""
from __future__ import annotations
from .base import Finding
class GenericParser:
@classmethod
def matches(cls, language: str, recipe: str) -> bool:
# Always true — the registry uses this as the last-resort fallback.
return True
@classmethod
def parse(cls, raw_log: str, exit_code: int, recipe: str) -> list[Finding]:
if exit_code == 0:
return []
return [
Finding(
kind="recipe_fail",
severity="warn",
code=f"exit_{exit_code}",
message=(
f"recipe '{recipe}' exited with status {exit_code}; "
f"see job log for details"
),
)
]