M1: Cache confusion-pair confidences in Path B (eliminate redundant
resolve_confusion_pair re-calls in _path_rule_engine)
M2: Resolve contradictions in Path C instead of hardcoding
resolved_count=0 in _path_llm_assisted
M4: Add DIVIDE_25 to contradiction pair coverage (50-25, 100-25)
and update test_contradiction_pairs_defined to verify all 3 variants
Bug 1: ELSE IF breaks IF false_seq parsing (core.py)
- _parse_if checked self.clean() == 'ELSE' which fails on 'ELSE IF ...'
- Fix: use startswith('ELSE'), reinsert IF portion for recursive parse
- Impact: ALL ELSE IF chains were silently dropped (huge branch loss)
Bug 2: READ skip loop greedily consumes subsequent statements (core.py)
- READ's AT END / NOT AT END skip loop used bare advance() with no
statement boundary detection
- Fix: add _stmt_boundary regex that stops on IF/PERFORM/READ/etc.
- Impact: everything after first READ was consumed as 'AT END' lines
Bug 3: _walk() in extract_structure doesn't descend into BrPerform (__init__.py)
- Branch counting _walk() only handled BrIf/BrEval/BrSeq
- IF statements inside PERFORM bodies were never counted
- Fix: add BrPerform.body_seq and BrSearch descent
Combined impact: matching programs (MT01-33) now correctly report
their branches instead of 0. Full regression: 749 passed (unchanged).
Issues found through matching program classification analysis:
1. dedup_vs_nodedup: 0.85→0.50 for negative detection (no WS-PREV-KEY
is not strong evidence for '含まず')
2. validation_vs_keybreak: 0.80→0.55 for has_counter (counter is a
generic pattern, not specific to key-break)
3. simple_vs_two_stage: 0.80→0.50 for non-open-close-open pattern
(sequential OPEN is the default for most programs)
Result: matching programs now correctly classified:
- MT01-03/18/20 → マッチング ✅ (was 項目チェック)
- MT16-17 → 二段階マッチング ✅ (unchanged)
- MT32 → 項目チェック(重複含む) ✅ (correct: has WS-PREV-KEY)
- VL01 → 項目チェック(重複含む) ✅ (correct)
- CSV → CSV合并 ✅ (correct)
Regression: 745 passed (3 test expectation bounds updated)
Refactor _resolve_matching_subtype to use an LLM agent for ambiguous
cases instead of pure static rules:
Architecture (3 layers):
1. Static deterministic rules: M:N→MxN, 1:N (WS-MAST/TRAN-KEY),
二段階, 混合 — high confidence, no LLM needed
2. LLM agent: ambiguous cases (N:1 vs 1:1, M:N→M vs M:N→N)
- _MATCHING_SUBTYPE_AGENT_PROMPT with 5 subtypes
- Calls existing hina.hina_agent._parse_llm_response for parsing
- Minimum confidence threshold 0.4 to gate low-quality LLM output
3. Fallback: conservative defaults (M:N or 1:1) when LLM unavailable
This follows the original architecture design: agent handles the
hard classification problems that static analysis alone can't resolve.
Regression: 745 passed (unchanged).
COBOL migration expert adversarial testing found 4 real defects:
FIX 1: Comment-stripping in detect_keyword() (FP-2)
- Remove *> inline comments and * comment lines before keyword matching
- Prevents 「マッチング」 from triggering on WS-KEY in comments
FIX 2: KEY comparison context validation (FP-1, FP-6)
- Add _matches_key_comparison() — requires WS-KEY variable to appear
NEAR an actual comparison operator (= < >), not just as PIC/VALUE decl
- Same check in _path_rule_engine features via has_key_var injection
- Fix regex bug: [=<>\s] vs [=<>] — \s matched whitespace after PIC decl
FIX 3: Old-school naming support (FN-1)
- Add L1 keyword r'[A-Z]\d{0,2}-\w*KEY' with 0.55 confidence
- Matches K01-KEY, KS-KEY etc. (non-WS- prefix naming convention)
FIX 4: mn_output_mode over-matching (FP-6)
- Require IF branches + KEY evidence before returning M:N for file>=3
- matching_vs_keybreak rule 3 now requires has_key_var
New tests: test_adversarial.py — 8 parametrized adversarial tests
Regression: 755 passed (0 new failures)
Add _detect_matching_structure(): detection based on control flow
pattern, not variable naming conventions. Uses 5 structural signals:
1. READ + AT END + EOF pattern
2. PERFORM UNTIL with EOF condition
3. ELSE body with conditional READ (matching core)
4. IF comparing hyphenated fields (cross-file comparison)
5. Multi-file OPEN INPUT
5/5 signals → 0.55, 4/5 → 0.50, 3/5 → 0.40.
Real-world impact: matching programs with key fields named CUST-CODE
and ORDR-CODE (no '-KEY' in name) are now correctly detected.
Also:
- Rule engine type priority: main types (マッチング etc.) override
secondary types (M:N, DIVIDE) when keyword confidence is low
- has_structural_match injected into features so rule engine can use it
- matching_vs_keybreak accepts equality IFs as matching evidence
- New test: test_structural_matching_no_keyword()
Regression: 764 passed (0 new failures).
All 58 test cases across 6 roles now passing:
- 65 recorded passes (some tests assert multiple things)
- 0 failures
- All L1 regex patterns verified with proper COBOL source format
- Fixed inline format issues: P() now adds \n after preamble,
P-002 uses chr(10) for proper newlines, CRLF test uses chr(13)+chr(10)
Regression: 767 passed (0 new)
BREAKING CHANGE DISCOVERED: generate_data constraint steering is BROKEN
- apply_constraint does not steer field values to satisfy branch conditions
- All generate_data tests now DOCUMENT this as known bug
- Previous tests never caught this because they only checked 'is not None'
What R11 actually verifies:
1. AST structure: IF CondAnd leaves, EVAL WHEN count, CALL params,
SEARCH ALL flag, PERFORM type — verified by attribute equality
2. propagate_assignments: chain values verified (X=100, Y=105, INSPECT ALL L->X)
arithmetic chain ((0+5-2)*3/2 = 4)
3. GnuCOBOL: real compilation + execution output captured
HELLO WORLD, IF branch (DISPLAY 01), PERFORM loop (SUM=15)
4. gcov: --coverage compile, run, line rate measurement
5. Exception paths: bad syntax, empty sections, newlines, garbage bytes
6. pipeline: classify result non-empty
7. orchestrator: _done state machine with value assertions
Co-Authored-By: Claude <[email protected]>
Root cause: IF condition and EVALUATE WHEN parsing swallowed entire
line including THEN-body (e.g. '50 MOVE BIG...' instead of just '50').
Fix:
1. Single-line IF cond_text truncated at COBOL statement-starting keywords
(MOVE/DISPLAY/COMPUTE/ADD/...)
2. Multi-line IF continuation loop also breaks on these keywords (was
missing DISPLAY, READ, WRITE, CLOSE, OPEN, SEARCH, ...)
3. EVALUATE WHEN raw_val truncated at same keyword set
4. All raw-string escape sequences fixed (Python 3.12 SyntaxWarning)
Verification:
- IF single-line A>50: A=51(true)/12(false) previously A=01/00
- IF multi-line X>50: X=51(true)/12(false) previously not steered
- EVALUATE WHEN 1/2/OTHER: C=1/2/4 previously C=0/0/0
- IF AND compound: (A<=10,B<20), (A>10,B<20), (A>10,B>=20)
- IF >75: A=76(true)/12(false) previously not steered
R11 tests updated: BUG documentation replaced with real assertions.
13 suites / 0 FAIL.
Co-Authored-By: Claude <[email protected]>
Bug #1: AND compound branch-body MOVE not propagated (HIGH)
Root cause: ELSE on same line as false_body, rest of line lost after
self.advance(). Fix: reinsert ELSE body text same as ELSE IF does.
Result: MOVE 'Y'/'N' TO WS-FLAG correctly propagated, all 3 paths
verified (A<=10/B<20=F, A>10/B<20=T, A>10/B>=20=F).
Bug #2: Performance — path explosion (25 IFs = 47s, 10000 records)
Root cause: BrSeq inner loop combined all paths before capping.
Fix: early break at _MAX_PATHS in the combo loop.
+ _MAX_PATHS reduced from 10000 to 500.
Result: 47s/10000rec -> 0.2s/27rec (235x improvement)
Bug #3: COPY+REDEFINES parse failure (test-only)
Root cause: test code called parse_data_division on full source
instead of extract_data_division first. Fixed.
Real pipeline (extract_structure -> generate_data) was never affected.
Co-Authored-By: Claude <[email protected]>
- TIME injection: change from spaces to '2500' (hour>23) for NUMVAL trigger
- Runner: add .resolve() to work_dir to fix chdir+relative path breakage
- Coverage: per-target field overrides for DP#9-#12 (START-DATE=20240115 etc.)
- .gitignore: add compilation artifacts, temp scripts, test outputs
Config-driven architecture:
- coverage_dates: YAML-driven date replacement for LEAVE_RECORDS
- row_overrides: per-scenario field overrides (e.g. STATUS='9')
- delete_all_rows: per-scenario table emptying for empty-cursor tests
gcda accumulation fix (V3 bug #7):
- Delete .gcda before each scenario run to prevent GnuCOBOL accumulation
- Force-copy scenario gcda (skip mtime check)
- Copy scenario DB to CWD data/kin.db for CONNECT TO path resolution
Multi-run gcov merge fix:
- Always merge multi-run gcov data regardless of generate_coverage flag
- Fix v3_root UnboundLocalError in cleanup path
Other fixes:
- _make_key_unique: skip WHERE-constrained columns to avoid PK conflict
- incremental_supplement: support fields_dict for base record generation
- check_coverage: use structure coverage data if available
- orchestrator.py: filter _-prefixed fields in TestCase; merge Agent2Data cases
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
M1: Cache confusion-pair confidences in Path B (eliminate redundant resolve_confusion_pair re-calls in _path_rule_engine) M2: Resolve contradictions in Path C instead of hardcoding resolved_count=0 in _path_llm_assisted M4: Add DIVIDE_25 to contradiction pair coverage (50-25, 100-25) and update test_contradiction_pairs_defined to verify all 3 variantsBug 1: ELSE IF breaks IF false_seq parsing (core.py) - _parse_if checked self.clean() == 'ELSE' which fails on 'ELSE IF ...' - Fix: use startswith('ELSE'), reinsert IF portion for recursive parse - Impact: ALL ELSE IF chains were silently dropped (huge branch loss) Bug 2: READ skip loop greedily consumes subsequent statements (core.py) - READ's AT END / NOT AT END skip loop used bare advance() with no statement boundary detection - Fix: add _stmt_boundary regex that stops on IF/PERFORM/READ/etc. - Impact: everything after first READ was consumed as 'AT END' lines Bug 3: _walk() in extract_structure doesn't descend into BrPerform (__init__.py) - Branch counting _walk() only handled BrIf/BrEval/BrSeq - IF statements inside PERFORM bodies were never counted - Fix: add BrPerform.body_seq and BrSearch descent Combined impact: matching programs (MT01-33) now correctly report their branches instead of 0. Full regression: 749 passed (unchanged).Three-part fix for matching program classification: 1. L1 regex keyword WS-[-\w]*KEY (confidence 0.65): - Captures WS-KEY, WS-MAST-KEY, WS-TRAN-KEY, WS-PREV-KEY etc. - Matches ALL 10 matching programs including MT02 (which uses WS-MAST-KEY/WS-TRAN-KEY that literal 'WS-KEY' missed) - False positives (ST-SEARCH-ALL, VL01) overridden by rule engine or higher-confidence ORGANIZATION IS keyword - detect_keyword() extended with 're:' prefix for regex patterns 2. Consensus bonus in compute_confidence_v2: - When L1 keyword category matches rule engine's final category, context_factor boosted by +0.15 - Pushes matching programs from manual (0.50-0.69) toward review (0.70-0.89) range 3. Confidence calibration for confusion groups (previous commit): - dedup_vs_nodedup: 0.85→0.50 for negative detection - validation_vs_keybreak: 0.80→0.55 for has_counter - simple_vs_two_stage: 0.80→0.50 for sequential OPEN Results - matching programs: MT01: 0.38→0.75, MT02: 0.30→0.60, MT03: 0.30→0.60, MT16: 0.45→0.81, MT17: 0.36→0.65, MT18: 0.60→0.60, MT19: 0.30→0.60, MT20: 0.30→0.65, MT33: 0.30→0.60 All now rule_engine (not fallback), no false negatives. Subtype discrimination remains for future work: all matching programs classified as マッチング without 1:1/1:N/N:1 subtype.COBOL migration expert adversarial testing found 4 real defects: FIX 1: Comment-stripping in detect_keyword() (FP-2) - Remove *> inline comments and * comment lines before keyword matching - Prevents 「マッチング」 from triggering on WS-KEY in comments FIX 2: KEY comparison context validation (FP-1, FP-6) - Add _matches_key_comparison() — requires WS-KEY variable to appear NEAR an actual comparison operator (= < >), not just as PIC/VALUE decl - Same check in _path_rule_engine features via has_key_var injection - Fix regex bug: [=<>\s] vs [=<>] — \s matched whitespace after PIC decl FIX 3: Old-school naming support (FN-1) - Add L1 keyword r'[A-Z]\d{0,2}-\w*KEY' with 0.55 confidence - Matches K01-KEY, KS-KEY etc. (non-WS- prefix naming convention) FIX 4: mn_output_mode over-matching (FP-6) - Require IF branches + KEY evidence before returning M:N for file>=3 - matching_vs_keybreak rule 3 now requires has_key_var New tests: test_adversarial.py — 8 parametrized adversarial tests Regression: 755 passed (0 new failures)BUG: parse_jcl() 文档说文件不存在时返回 None, 但实际抛出了 FileNotFoundError。修复。 新增: test-data/step3_module_test.py — 未测试模块的首次实测 - comparator: API确认 (numeric/date/string 正确) - jcl: 导入+tparse(发现FileNotFoundError bug) - parametrized: matching(1:1/1:N/N:1) 数据生成 - storage: DiskCache/ReportStore set/get - quality: L1OffsetValidator/L2RoundtripValidator - agents: LLMClient 创建确认 验证: 66个COBOL样本全过管道(0崩溃/0无数据)R4: core.py(289IF) + __init__.py(91IF) 内部関数全網羅 R4-design: design.py(161IF) enum_paths/constraint/redefines/occurs R4-cond: cond.py(51IF) 全演算子×T/F×MC/DC R4-coverage: coverage.py(116IF) mark_*全種別+HTML分岐 R5: 統合テスト(extract_structure→generate_data検証) + pipeline.py(34IF)+hina_agent.py(12IF)+read.py(54IF) + output.py(19IF)+orchestrator.py+classifier.py追加 R6: 複合ネストIF/PERFORM/EVAL/SEARCH+PIC解析全部 R7: FD方向解析+混乱グループ+contradiction+LLM応答 残環境依存: web/api(6IF), web/worker(6IF), runners/(6IF), gcov(6IF) Co-Authored-By: Claude <[email protected]>## 修复 1. **__DP 约束被过滤掉** (__init__.py) - _resolve_field 对 '__DP' 直接穿透 - fn.startswith('__') 绕过 fields_dict 检查 - 导致 PERFORM/EVALUATE/IF 合成约束在 generate_data 内部丢失 2. **collect_all_dps DP ID 计数器** (design_mcdc.py) - 全局 _counter 替代局部 len(result) - IF/EVALUATE/PERFORM 统一用 _counter[0] - 递归调用传递 _counter 3. **__DP 匹配不依赖 DP ID** (coverage.py) - _mark_if / _mark_eval / _mark_perform 移除 id 检查 - 直接通过 __DP label 识别分支方向 4. **PERFORM VARYING 条件提取** (design_mcdc.py) - VARYING UNTIL 从句自动提取 UNTIL 条件 5. **cond.py 增强** - OF 限定词剥离: STD-KEY OF MASTER-REC → STD-KEY - 裸字段引用: WS-EOF → (WS-EOF, '=', 'Y') - NOT 前缀: NOT WS-X > 50 → WS-X <= 50 - not_map 添加 break ## 结果 - 分支覆盖率: 10.6% → 95.6% (3208中3068覆盖) - S15回归: 17/17 PASS - 程序数: 43/43有分支检测 Co-Authored-By: Claude <[email protected]>## 评审发现修正 ### 1. __import__('re') → re (cond.py, 3处) __import__绕过module级re引用,mock下前后不一致。 ### 2. NOT路径下*/去掉 (cond.py:124-126) NOT WS-PLAN-CODE(WS-IDX) > 50 → 返回保留了下标 其他路径(算术/标准)都去了,只有NOT路径没去。 ### 3. _match_constraint防禦*/去掉 (coverage.py) 两边字段名同时去掉下标再比较,防止约束侧/解析侧 下标处理不一致导致匹配失败。 ### 4. _match_leaf 防禦*/去掉 (coverage.py) CondLeaf路径同样的防禦。 ### 5. 裸字段分支去死码 (cond.py) 在外層guard保證下永遠為真, 是死码。合併為一行。 Co-Authored-By: ClaudePull request closed