Skip to content

harness compares every OCCURS occurrence; the whole table travels (Fixes #4765) - #4772

Merged
squid-protocol merged 4 commits into
mainfrom
fix/4765-occurs-compare
Oct 9, 2026
Merged

squid-protocol merged 4 commits into
mainfrom
fix/4765-occurs-compare

Conversation

@squid-protocol

@squid-protocol squid-protocol commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Fixes #4765. Part of #4270.

The bug (proof strength). The harness's record layout (equivalence_common.layout_fields) listed an OCCURS group's fields once, at occurrence 1, the same way the generated DTO does. So a COMMAREA's table was compared at its first occurrence only. Shown on cbsa-inqacccu: a mutant det port that never writes account 3's COMM-ACC-TYPE is PROVEN on origin/main. On this branch it is NOT PROVEN: commarea.COMM-ACC-TYPE(3): COBOL 'ISA', Java ''.

A second harness bug was hiding under that one. The scenario COMMAREA was cut at the end of the first occurrence. INQACCCU ran with 123 of its 1985 bytes (commarea.in was 123 bytes), so accounts 2..N were written past EIBCALEN.

What changed

  • layout_fields(..., occurrences=True): each occurrence is its own field, named by its subscripts (COMM-ACC-TYPE(3), nested CELL(2,4)), with its OCCURS DEPENDING ON constraints. The COMMAREA is read with this layout, and each side is filtered by its own ODO count (active_fields). So a count that differs is reported as a difference, and so is an occurrence that only one side has. A count that is not a number keeps every occurrence: none are dropped on a guess. The default layout (what records are built from, and what generated inputs use) is unchanged.
  • A scenario's COMMAREA is now the whole record (commarea_record), with every occurrence INITIALIZEd. A plain DTO-style name (COMM-EYE) means occurrence 1.
  • A LINKed task now also gets its COMMAREA's bytes (CicsTask.withLinkArea). If the port takes them (task.linkArea(), which det ports do), its result is read from those bytes, every occurrence included. A DTO's values map to occurrence 1 (first_occurrences). Occurrences the DTO lacks show up as absent and are compared, never assumed equal.
  • Batch / CICS file records name each occurrence. Before, those bytes were compared only as "bytes outside the layout". File records are compared whole, whatever the ODO count.
  • det (defect 2): a DTO whose OCCURS fields appear once now spans its declared record whether or not it has a POINTER. That covers the LINK span and EIBCALEN. RETURN and XCTL now pass its bytes (DetCics.commareaOut) instead of the first-occurrence DTO (Dto.extent vs Dto.size).

Tests: tests/cobol_mainframe/test_occurs_compare_4765.py (15 tests) covers OCCURS, ODO, nested and elementary OCCURS, an occurrence-3 difference caught end to end through outputs / _java_events / compare_events, ODO counts that differ, DTO first-occurrence mapping, batch naming, and the det LINK/XCTL span.

Re-proved

Not changed (follow-ups): equivalence_call.compare_dto still compares only DTO-mapped fields. Screens keep the old layout: no case's BMS map has an OCCURS. DTOs that differ from their declared length because of a trailing FILLER (CBSA CustctrlDfhcommarea, GenApp Lg*) keep their extent.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MQNoe4wPJgr7dbUvs3DG6D

… table travels (Fixes #4765)

The harness's record layout listed an OCCURS group's fields once (the first
occurrence), so a COMMAREA's table was compared at occurrence 1 only: a
cbsa-inqacccu port that never wrote account 3's type was PROVEN on main.

- layout_fields(..., occurrences=True): every occurrence a field, by name and
  subscript (COMM-ACC-TYPE(3), nested CELL(2,4)), with its OCCURS DEPENDING ON
  constraints; a COMMAREA is read by it, each side by its own ODO count
  (active_fields). A file's record names each occurrence (they were compared
  only as "bytes outside the layout").
- a scenario's COMMAREA is the whole record (it was cut at the first
  occurrence's end: INQACCCU's EIBCALEN was 123 of 1985 bytes, accounts 2..N
  written past it), every occurrence INITIALIZEd.
- a LINKed task gets its COMMAREA's bytes too (CicsTask.withLinkArea); a port
  that takes them (a det port) is read from them; a DTO's values are the
  first occurrence (first_occurrences), the rest absent -- compared.
- det: a DTO whose OCCURS fields appear once spans its declared record with or
  without a POINTER (LINK span, EIBCALEN), and RETURN / XCTL pass its bytes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MQNoe4wPJgr7dbUvs3DG6D
@squid-protocol squid-protocol added bug Unintended behavior or logic failure in the engine testing Unit, integration, and E2E pipeline verification legacy-modernization COBOL refractor, dead-code extraction, and JCL forging labels Oct 9, 2026
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

🐦‍⬛ Muninn Security Scan

✅ No security issues found.

🐦‍⬛ Powered by Muninn · Skald Lab

@squid-protocol squid-protocol changed the title WIP: harness compares every OCCURS occurrence; the whole table travels (Fixes #4765) harness compares every OCCURS occurrence; the whole table travels (Fixes #4765) Oct 9, 2026
@squid-protocol
squid-protocol marked this pull request as ready for review October 9, 2026 10:39
@squid-protocol
squid-protocol merged commit ec7f61c into main Oct 9, 2026
37 checks passed
@squid-protocol
squid-protocol deleted the fix/4765-occurs-compare branch October 9, 2026 10:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Unintended behavior or logic failure in the engine legacy-modernization COBOL refractor, dead-code extraction, and JCL forging testing Unit, integration, and E2E pipeline verification

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Equivalence: COMMAREA OCCURS fields are compared at the first occurrence only; det LINK of an OCCURS DTO passes the first occurrence's extent

1 participant