Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/language_status/blockers_history.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@
{"pr": 4608, "main_sha": "f54d746d56dd1223eb2db7d365e3e2f90a5c3127", "date": "2026-10-07T19:57:59Z", "whole": 76, "total": 125, "whole_burned": 71, "total_burned": 104, "whole_non_burned": 5, "total_non_burned": 21, "refused_whole": 12, "top_gaps": [{"gap": "refused: LayoutError: source defect: source text past column N (…) cuts a literal open: fixed-form COBOL reads columns N", "only": 7, "only_burned": 0, "only_non_burned": 7, "refuses_program": true}, {"gap": "missing copybook CMQGMOV", "only": 2, "only_burned": 2, "only_non_burned": 0, "refuses_program": true}, {"gap": "EXEC CICS FORMATTIME RESP", "only": 1, "only_burned": 0, "only_non_burned": 1, "refuses_program": false}, {"gap": "missing copybook BAQRINFO", "only": 1, "only_burned": 0, "only_non_burned": 1, "refuses_program": true}, {"gap": "EXEC CICS (no reason given)", "only": 1, "only_burned": 1, "only_non_burned": 0, "refuses_program": false}], "scope": "burned + local + census corpora"}
{"pr": 4627, "main_sha": "f8408bf6e4b3490fee2b0b9bda3de72ec07fb4fb", "date": "2026-10-07T19:58:16Z", "whole": 76, "total": 125, "whole_burned": 71, "total_burned": 104, "whole_non_burned": 5, "total_non_burned": 21, "refused_whole": 12, "top_gaps": [{"gap": "refused: LayoutError: source defect: source text past column N (…) cuts a literal open: fixed-form COBOL reads columns N", "only": 7, "only_burned": 0, "only_non_burned": 7, "refuses_program": true}, {"gap": "missing copybook CMQGMOV", "only": 2, "only_burned": 2, "only_non_burned": 0, "refuses_program": true}, {"gap": "EXEC CICS FORMATTIME RESP", "only": 1, "only_burned": 0, "only_non_burned": 1, "refuses_program": false}, {"gap": "missing copybook BAQRINFO", "only": 1, "only_burned": 0, "only_non_burned": 1, "refuses_program": true}, {"gap": "EXEC CICS (no reason given)", "only": 1, "only_burned": 1, "only_non_burned": 0, "refuses_program": false}], "scope": "burned + local + census corpora"}
{"pr": 4636, "main_sha": "b72819c4099b8aafac4242f82a19385a7f43225b", "date": "2026-10-07T20:08:37Z", "whole": 78, "total": 125, "whole_burned": 73, "total_burned": 104, "whole_non_burned": 5, "total_non_burned": 21, "refused_whole": 12, "top_gaps": [{"gap": "refused: LayoutError: source defect: source text past column N (…) cuts a literal open: fixed-form COBOL reads columns N", "only": 7, "only_burned": 0, "only_non_burned": 7, "refuses_program": true}, {"gap": "missing copybook CMQGMOV", "only": 2, "only_burned": 2, "only_non_burned": 0, "refuses_program": true}, {"gap": "EXEC CICS FORMATTIME RESP", "only": 1, "only_burned": 0, "only_non_burned": 1, "refuses_program": false}, {"gap": "missing copybook BAQRINFO", "only": 1, "only_burned": 0, "only_non_burned": 1, "refuses_program": true}, {"gap": "EXEC CICS (no reason given)", "only": 1, "only_burned": 1, "only_non_burned": 0, "refuses_program": false}], "scope": "burned + local + census corpora"}
{"pr": 7901, "main_sha": "4174b5ef9d28735ccf226e84f36345337ff5f70f", "date": "2026-11-06T17:34:09Z", "whole": 113, "total": 127, "whole_burned": 105, "total_burned": 106, "whole_non_burned": 8, "total_non_burned": 21, "refused_whole": 8, "top_gaps": [{"gap": "refused: LayoutError: source defect: source text past column N (…) cuts a literal open: fixed-form COBOL reads columns N", "only": 7, "only_burned": 0, "only_non_burned": 7, "refuses_program": true}, {"gap": "EXEC CICS WEB CONVERSE POST", "only": 1, "only_burned": 0, "only_non_burned": 1, "refuses_program": false}, {"gap": "missing copybook BAQRINFO", "only": 1, "only_burned": 0, "only_non_burned": 1, "refuses_program": true}, {"gap": "MOVE an alphanumeric literal holds a character beyond the single-byte code page (…): its bytes and length depend on how the source reached the compiler", "only": 0, "only_burned": 0, "only_non_burned": 0, "refuses_program": false}, {"gap": "STRING sender <name>, PIC <name>(…)<name>: a numeric item with a P or a decimal place is a compile error in Enterprise COBOL", "only": 0, "only_burned": 0, "only_non_burned": 0, "refuses_program": false}], "scope": "burned + local + census corpora", "copy_members": 1, "note": "T9 re-survey; census basis 40 -> 39 (copy member listed, not counted; owner ruling 2026-11-06, #7709)"}
13 changes: 13 additions & 0 deletions tests/tools/cics_census.py
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,12 @@ def blockers(rows: dict[tuple[str, str], dict[str, Any]], only: set[tuple[str, s
}


def note_copy_members(res: dict[str, Any], n: int) -> None:
"""#7901: name the copy members the inventory listed and did not count (added only when there are some)."""
if n:
res["copy_members"] = n


def non_burned_text(res: dict[str, Any]) -> str:
"""#6064: a --no-census run that counted no non-burned program (no local ones either) says `non-burned: not
surveyed`, never `non-burned 0 / 0`."""
Expand All @@ -634,6 +640,9 @@ def non_burned_text(res: dict[str, Any]) -> str:
def format_blockers(res: dict[str, Any], where: str, top: int = 0) -> str:
out = [f"# blockers in {where}: {res['scope']}",
f"translated whole: {res['whole']} / {res['programs']} ({non_burned_text(res)}); refused whole (no statements translated): {res['refused']}"] # fmt: skip
if res.get("copy_members"):
out.append(f"basis: {res['programs']} programs; {res['copy_members']} copy member(s) listed, not counted "
f"({COPY_MEMBER_REASON})") # fmt: skip
if res.get("cics_programs_not_surveyed"):
out.append(
f"CICS programs with no survey row: {res['cics_programs_not_surveyed']} (a corpus left out of the run?)"
Expand Down Expand Up @@ -681,6 +690,7 @@ def cmd_blockers(args: argparse.Namespace) -> int:
res["census_surveyed"] = not args.no_census
if only is not None:
res["cics_programs_not_surveyed"] = len(only - set(rows))
note_copy_members(res, len(load_copy_members(args.dir, args.label)))
where = f"{args.dir}/{args.label}-*"
if args.json:
print(json.dumps(res, indent=1))
Expand Down Expand Up @@ -879,6 +889,7 @@ def baseline_blockers(d: Path, all_programs: bool = False, census: bool | None =
res["census_surveyed"] = census
if only is not None:
res["cics_programs_not_surveyed"] = len(only - set(rows))
note_copy_members(res, len(load_copy_members(d, "before")))
return res


Expand Down Expand Up @@ -1062,6 +1073,8 @@ def history_entry(res: dict[str, Any], sha: str, date: str, pr: int, note: str |
"refused_whole": res["refused"], "top_gaps": top, "scope": scope} # fmt: skip
if res.get("census_surveyed") is False and not nb: # #6064: not 0 / 0 -- the census was not surveyed
entry |= {"whole_non_burned": None, "total_non_burned": None, "census_surveyed": False}
if res.get("copy_members"):
entry["copy_members"] = res["copy_members"]
if note:
entry["note"] = note
return entry
Expand Down
27 changes: 27 additions & 0 deletions tests/tools/test_cics_census_copy_member_7901.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,30 @@ def test_program_without_program_id_keeps_a_program_row():
rows = {"c": [{"program": "B.cbl", "error": "UnitRefused: no PROGRAM-ID"}]}
assert cc.copy_member_reason(NO_PROGRAM_ID) is None
assert "| c | 1 | 0 | 0 | 1 |" in ds.markdown(rows)


def _copy_survey(tmp_path, with_copy=True):
d = tmp_path / "before-b"
d.mkdir()
rows = [{"program": "A.cbl", "statements": 1}]
if with_copy:
rows.append({"program": "REC.cbl", "copy_member": cc.COPY_MEMBER_REASON})
(d / "survey.json").write_text(json.dumps({"c": rows}), encoding="utf-8")
return cc.blockers(cc.load_surveys(tmp_path, "before"))


def test_blockers_name_the_copy_member_basis(tmp_path):
res = _copy_survey(tmp_path)
cc.note_copy_members(res, len(cc.load_copy_members(tmp_path, "before")))
res["scope"] = "every surveyed program"
assert res["programs"] == 1 and res["copy_members"] == 1
assert "basis: 1 programs; 1 copy member(s) listed, not counted" in cc.format_blockers(res, "x")
assert cc.history_entry(res, "abc", "2026-11-06", 1, None, "s")["copy_members"] == 1


def test_blockers_have_no_copy_member_key_when_none(tmp_path):
res = _copy_survey(tmp_path, with_copy=False)
cc.note_copy_members(res, len(cc.load_copy_members(tmp_path, "before")))
res["scope"] = "every surveyed program"
assert "copy_members" not in res and "basis:" not in cc.format_blockers(res, "x")
assert "copy_members" not in cc.history_entry(res, "abc", "2026-11-06", 1, None, "s")
Loading