SlimeRESCUE-AS400 — verification materials and audit trail
What was measured on real IBM i hardware, how the measurement is made, and what is still unproven. The audit that produced this page found that the earlier comparison path did not compare what it claimed to, so the record of the defect is kept here alongside the corrected result.
1. What the earlier harness actually compared
The first harness (rescue/run_rescue_diff.sh) took
grep -oE '[0-9]{8,}' | head -1 over the combined stdout and stderr of both
sides and compared the two integers. No bytes were compared anywhere on that path, and
a run that produced no eight-digit number simply produced nothing to compare.
Two further steps were failing silently on that path, which only became visible once the output stopped being discarded:
- the DB2 cross-check used an inline
RUNSQLform that this machine rejects (SQL0104, return code 255, no message id). Its output went to/dev/nulland its value was not part of the pass condition, so the cross-check may never have produced a figure on any run. - the transpiler emits a different program when the DDS is not beside the source — the record fields lose their types — and the compiler failure that reveals this was also discarded. That is a product defect, and it is recorded as one in section 3.
2. What the corrected harness does
rescue/run_rescue_diff_v2.sh. The earlier script is kept unchanged: it is
the object of the audit, and overwriting it would destroy the evidence.
- Three declared observation layers, none of them a grep for a number: the completion message in full; the data member's raw bytes, copied with no CCSID conversion, before and after the run; the recovered source member's raw bytes.
- The source attributes are read from the object through
QSYS2.PROGRAM_INFO, not from the text RTVCLSRC prints at the top of its output. The column names are re-derived fromQSYS2.SYSCOLUMNSon every run rather than remembered. - DB2 is inside the pass condition, not beside it.
- Any step's failure stops the run. Return codes are checked, and a non-zero one is accepted only when every message id it produced is on an explicit benign list. An unknown id fails.
- No environment, no pass. The job's CCSID, date format and library list are captured before anything else, and the run refuses to report a result without them.
3. Verified figures Open
Every figure in this section is generated from a live measurement by claims/emit_claims.py. None of them is typed by hand. An item that was not generated is left empty — a stale number is never carried forward.
exists-over-tested means it held over the range that was measured. It is not a claim about all inputs.
source deleted, then recovered from the object: the recovered CL is the same command stream that was compiled as400-source-loss-recovery-20260727
| programs | 3 |
|---|---|
| negative_controls_failed | True |
| read_after_delete_message | CPFA0A9 Object not found |
| source_recovered | 3 |
| command_stream_equal_after_normalisation | 3/3 |
| compiled_from | source member (CRTBNDCL SRCFILE/SRCMBR) |
| module_attributes | ['RM1SUM,RM1SUM,BARON1,QCLSRC,RM1SUM,', 'RM2DEC,RM2DEC,BARON1,QCLSRC,RM2DEC,', 'RM3MSG,RM3MSG,BARON1,QCLSRC,RM3MSG,'] |
| Guarantee level | S |
| Quantifier | exists-over-tested |
| Measured over | programs=3, constructs=zoned-decimal file summation, packed decimal with a fraction, MONMSG on a real message, library=BARON1 |
| Observed | the source member read before deletion, the same read attempted after RMVM (required to fail), the member's absence in the file system, the recovered source's command stream after normalising RTVCLSRC formatting |
| Oracle | IBM i on PUB400 IBM i V7R5M0 execution |
| Provenance of the expected value | established on real hardware |
| finding | this is the first run in which the source was actually gone. The negative control is the load-bearing part: after RMVM the same read fails with CPFA0A9 and the member is absent from the file system, so the recovery cannot have come from the source file. The programs were compiled the traditional way, so QSYS2.BOUND_MODULE_INFO records SOURCE_FILE_LIBRARY/FILE/MEMBER pointing at a member that no longer exists — 'recorded but gone', which is a different state from the stream-file case's 'recorded as a path'. |
| caveat | equality is of the command stream after normalising RTVCLSRC's formatting, not of bytes: RTVCLSRC re-emits its own header, column positions and line wrapping, so byte equality is not available on this path and is not claimed. Recovery depends on the program having been created with its source retained; a *PGM compiled without that cannot be rescued this way, and that case was not tested. N=3 programs. Whether the recovered program then behaves identically is a separate row — it does not, for two of the three. |
| audit_hash | sha256:2c1e3692d8081b4d5275e3895cec13f3293ebb52eaec16375a019f4f35b2dbae |
the recovered programs, transpiled and run: 3 of 3 match the live result as400-loss-execution-20260727
| programs | 3 |
|---|---|
| agreeing | 3 |
| job_decimal_format | J |
| Guarantee level | S |
| Quantifier | exists-over-tested |
| Measured over | programs=3 |
| Observed | the completion message text in full |
| Oracle | IBM i on PUB400 IBM i V7R5M0 execution |
| Provenance of the expected value | established on real hardware |
| finding | first measured at 1 of 3, and the two failures were transpiler gaps rather than rescue failures — the source had recovered correctly in all three cases. Both gaps were then closed and the run repeated: the job's decimal format became a declared input (RM2DEC), and CPF9801 became a modelled OS effect so that CHKOBJ raises it and MONMSG catches it (RM3MSG). The figure here is from the repeat. Keeping the intermediate state visible matters more than the final number: a row that had only ever said 3 of 3 would not show that the environment had to be declared to the tool before the tool could reproduce the machine. |
| caveat | three programs, one job, one machine. Agreement here is under the declared observation — the completion message text — and under a declared environment: CCSID 273, DECFMT J. Change the job and the same programs may not agree; that is a property of the claim, not a defect. This row and the recovery row are kept separate because they answer different questions: joined into one figure, a transpiler gap would read as a failed rescue. |
| audit_hash | sha256:0a0d48d5f47e6dd3083492579f3853f49d55e13c1b49b73a29d74e31eeed88ad |
one effect lattice across SKUs; an unmodelled OS effect is declared, not dropped os-effect-lattice-20260727
| registry | claims/os_effects.json |
|---|---|
| effects_registered | 4 |
| ibm_i_modelled | ['CPF0864', 'CPF9801'] |
| vos3_declared_unmodelled | ['PR'] |
| registry_matches_emitter | True |
| drift | [] |
| unmodelled_effect_behaviour | refused at emit time, exit 6 |
| Guarantee level | B |
| Quantifier | for-all-emissions |
| Measured over | form=OsContext(kind, id), instances=VOS3 svc(n) / IBM i msg(msgid), cl_sources_in_repo=43, refused=1 |
| Observed | what the emitter does with a MONMSG whose message is not in the table |
| Oracle | the registry and the emitter source static |
| Provenance of the expected value | a consistency check against the repo |
| finding | the third fail-open found in this emitter. An unmodelled MONMSG was written out as a comment, so a program whose control flow depends on catching an escape message ran as if the message never happened — RM3MSG said NOTHING-CAUGHT where the machine said CPF9801-CAUGHT. The pattern across all three (DCLF, DECFMT, MONMSG) is the same: an undeclared part of the environment gets a default and execution continues. The lattice replaces the default with a registry, and absence from the registry with a refusal. |
| caveat | the table holds one measured IBM i effect (CPF9801) plus the two older paths (CPF0864, and CPF0000 in the RUNSQL position only). That is deliberately small: an entry requires a measurement, not a recollection. Scope for MONMSG itself was cut to a command-level monitor with a single message id and a non-branching EXEC; branching monitors, generic class monitors and message replacement variables are refused, and one source in the repository (DBGWRAP.clp, a bare CPF0000) is refused as a result. |
| audit_hash | sha256:22ba28f06b5f0bf525e4ce660038ae1796316f7dd0ce0a23a9d18a8c2cf523d4 |
a *PGM's CL source, recovered from the object by RTVCLSRC, reproduces the live program's result as400-cl-rescue-20260727
| live_message | 00000000425 |
|---|---|
| candidate_message | 00000000425 |
| db2_sum | 425 |
| equal | True |
| custf_member_sha256 | 0000f587b1dc1caef13cd85689b0e909 |
| custf_unchanged_by_run | True |
| recovered_member_sha256 | 3ecfd78752d3b0ac1a83face50d384f1 |
| source_file_attributes_on_object | ['', '', '', ''] |
| n_programs | 1 |
| n_records | 3 |
| Guarantee level | S |
| Quantifier | exists-over-tested |
| Measured over | programs=1, records=3, expected_sum=425, program=P5DRV (CLLE, reads a PF and sums a zoned field) |
| Observed | the completion message text in full, compared as a string, the CUSTF member's raw bytes before and after the run (no CCSID conversion), the recovered source member's raw bytes |
| Oracle | IBM i on PUB400 IBM i V7R5M0 execution |
| Provenance of the expected value | established on real hardware |
| Unmodeled | the job log beyond the completion messagespoolMSGID |
| finding | CORRECTED 2026-07-27: the source attributes were read from QSYS2.PROGRAM_INFO, and those columns are empty for an ILE *PGM no matter how it was compiled — a control compiled the traditional way (SRCFILE/SRCMBR) shows them empty too. The origin of an ILE program is recorded on the bound module: in QSYS2.BOUND_MODULE_INFO this program carries SOURCE_STREAM_FILE_PATH = the .clp it was compiled from, while the control carries SOURCE_FILE_LIBRARY/FILE/MEMBER instead. So the audit's reading holds and is in fact stronger — the object records where its source came from either way, and the empty member columns say 'compiled from a stream file', not 'source lost' — but the evidence first published for it was read from the wrong view. Two defects in the old path also showed up under measurement: the inline RUNSQL form v1 used for the DB2 cross-check fails on this machine (SQL0104, no message id, rc=255 discarded to /dev/null), so that cross-check may never have produced a value; and SlimeCL silently emits a different program when the DDS is not beside the source, which only surfaces because rustc refuses to compile it — v1 discarded rustc's output too. |
| caveat | value equality under the declared observation, not byte-exactness: the live side's message crosses a CCSID conversion and the candidate is a native binary, so the two are not byte-comparable and this claim does not pretend otherwise. N=1 program, 3 records. The source member was present for the whole run: nothing here shows a lost source can be recovered. The member hash pins this run's input; it is not stable across runs, because deleted-record space makes the member grow. |
| supersedes | as400-cl-rescue-20260726 |
| audit_hash | sha256:cb5bb38685b61a14be08e828dba9130c158a9f13543212536c639ba74cba2a5f |
CL rescue: *PGM -> RTVCLSRC -> SlimeCL -> Rust vs the live CALL as400-cl-rescue-20260726
| comparison | value equality of a 3-digit integer (425) |
|---|---|
| both_sides_via | grep -oE '[0-9]{8,}' | head -1 |
| candidate_normalised_by | CR and trailing-whitespace strip |
| bytes_compared | 0 |
| db2_cross_check_in_pass_condition | False |
| n | 1 |
| Guarantee level | pending |
| Quantifier | exists-over-tested |
| Measured over | programs=1, records=3, expected_sum=425 |
| Observed | the first 8+ digit run in the combined stdout and stderr |
| Oracle | IBM i 7.5 on PUB400 execution |
| Provenance of the expected value | phantom-caught |
| Unmodeled | job logMSGIDexceptionsspool |
| blocked by | kept as the record of what the v1 path measured, which is one integer pulled out of a text stream by grep. It is superseded rather than deleted because the defect is the point: the figure it produced was never worth publishing, and two of its steps are now known to have been failing silently. Replaced by as400-cl-rescue-20260727, measured through rescue/run_rescue_diff_v2.sh on 2026-07-27. What neither row shows is a lost source being recovered — that needs the RMVM experiment, with the failed source lookup as a negative control, and it has not been run. |
| finding | the empty 'original source file' slot that the world-first wording rested on is produced by compiling from a stream file (SRCSTMF) in this same script. It is a consequence of the compile method, not evidence that a source was lost. |
| audit_hash | sha256:b6ec96fa2c4dc16c772fdfd326e9faec2306a6dcdc6018201a310a150bc5c13a |
DECFLOAT(34) data rescue: the candidate's record bytes equal the live member's, byte for byte as400-data-rescue-p19-20260727
| bytes_compared | 88 |
|---|---|
| identical | True |
| ibm_i_member_sha256 | 7d28c344ff862107c55480e171b3a7c7 |
| candidate_sha256 | 7d28c344ff862107c55480e171b3a7c7 |
| member_changed_by_live_run | True |
| records | 4 |
| files | 1 |
| Guarantee level | S |
| Quantifier | exists-over-tested |
| Measured over | files=1, records=4, bytes=88, types=CHAR(6) and DECFLOAT(34) as decimal128 DPD |
| Observed | the whole member's bytes, copied with no CCSID conversion, compared as bytes rather than as values |
| Oracle | IBM i on PUB400 IBM i V7R5M0 execution |
| Provenance of the expected value | established on real hardware |
| Unmodeled | records beyond this memberother DECFLOAT encodings |
| finding | comparing DECFLOAT as DPD bytes rather than as decimal values is what makes this the strict path: a candidate that is value-equal but byte-unequal cannot pass. The harness now also refuses to report a result if the live program left the member unchanged, which would have made the comparison vacuous. |
| caveat | the comparison is byte-level; the guarantee level is not. S means a differential test against an oracle over the domain that was tested — one file, four records, one job whose CCSID is 273. It is not a statement about all DECFLOAT values or all members. 'byte-exact' describes what was compared, not how far it generalises. |
| supersedes | as400-data-rescue-p19-20260726 |
| audit_hash | sha256:e6239c55b296fc9ecabc88adcee28859d4a766a6a8cfecdc1b1a1ce147d56b76 |
data rescue: DECFLOAT(34) member bytes vs the live machine as400-data-rescue-p19-20260726
| comparison | hex of the whole physical-file member |
|---|---|
| member_obtained_by | cp from /qsys.lib (no CCSID conversion) |
| decfloat_compared_as | DPD bytes, not values |
| Guarantee level | pending |
| Quantifier | exists-over-tested |
| Measured over | harness=p19/run_p19dg_diff.sh |
| Observed | mem[whole member] as hex |
| Oracle | IBM i 7.5 on PUB400 execution |
| Provenance of the expected value | measured-on-disk |
| blocked by | the method is sound — raw member bytes, no conversion, DECFLOAT compared as DPD bytes rather than values, so value-equal/byte-unequal cannot slip through — but it has not been re-run under audit. The row carries the method, not a result. |
| audit_hash | sha256:e6830c60447a9ffb1dde01d0a67800a67783e8865460ef589bdeb7ffd5ad0d8f |
audit of the comparison path, from the source of the harnesses as400-harness-audit-20260726
| scripts_read | 2 |
|---|---|
| defects_in_cl_harness | 9 |
| errors_discarded | every remote system call redirects to /dev/null |
| set_e | False |
| library_hardcoded_at | run_rescue_diff.sh:56 |
| gate_read_from | English header string in CCSID-1208 text |
| Guarantee level | B |
| Quantifier | for-all-lines-read |
| Measured over | files=['run_rescue_diff.sh', 'run_p19dg_diff.sh'] |
| Observed | the source of the harness |
| Oracle | the harness source static reading |
| Provenance of the expected value | a consistency check against the repo |
| finding | the strongest wording in the product sits on the weakest comparison in the repository, and the strongest comparison already exists a few directories away in p19. |
| audit_hash | sha256:ea2516e469f4f950cb7ad859778cae200f120ccdb3fa7f36bef09ed4e725de9b |
SlimeCL emitted a different program without a diagnostic when the DCLF's DDS was missing — removed 2026-07-27 slimecl-silent-dclf-degradation-20260727
| function | dds::resolve_dclf |
|---|---|
| signals_on_unresolved_dds | True |
| caller_refuses_to_emit | True |
| exit_code_on_refusal | 4 |
| regression_tests | 3 |
| detected_by | rustc rejecting i64 + String, once the harness stopped discarding compiler output |
| fixed | True |
| Guarantee level | B |
| Quantifier | for-all-emissions-with-unresolved-dclf |
| Measured over | file=src/dds.rs, function=resolve_dclf, observed_case=rescue/out_v2 without CUSTF.PF beside the source |
| Observed | the emitted Rust's declaration of the DCLF fields |
| Oracle | the repository source static |
| Provenance of the expected value | a consistency check against the repo |
| finding | a defect row, not a result. It is here because a fail-open path in the product is worth more attention than a passing figure: every Slime-family claim that runs through this transpiler inherits it, SlimeCOBOL included. |
| caveat | the check now has two outcomes and no third: the DDS was found and read, or the file is named in a refusal that exits 4. Three regression tests hold that shape, because a fallback is easy to reintroduce as a convenience. An explicit --allow-unresolved-dclf remains for the case where untyped fields are genuinely intended; it has to be typed, which is the difference between a decision and a default. The harness-side defences stay in place — layered, because the product fix and the harness check fail differently. The first version of the check counted fields rather than files and refused two valid programs whose display file holds only constants; resolution now means the DDS was found. |
| audit_hash | sha256:03b6eae4fd5b2c09197fca31cf951a93abdb476db8416f0882ed7e142b49518f |
SlimeCL rendered decimals with a period regardless of the job's DECFMT — now a declaration, refused when absent slimecl-decfmt-unmodeled-20260727
| job_decfmt | J |
|---|---|
| ibm_i | 000000350,80 |
| slimecl | 000000350,80 |
| agrees_now | True |
| before_the_fix | IBM i 000000350,80 vs SlimeCL 000000350.80 |
| declaration_required | True |
| exit_code_when_undeclared | 5 |
| sources_in_repo_needing_a_declaration | 4 |
| fixed | True |
| Guarantee level | B |
| Quantifier | exists-over-tested |
| Measured over | case=CHGVAR of *DEC(9 2) into *CHAR, job DECFMT=J, sources_needing_declaration=4, sources_unaffected=39 |
| Observed | the character rendering of a packed decimal |
| Oracle | IBM i on PUB400 execution |
| Provenance of the expected value | established on real hardware |
| finding | the second fail-open found in this emitter, after the DCLF one: both are an undeclared environment being given a default and carried on with. Two of a kind is a reason to look for a third, so an inventory of every environment-dependent default in SlimeCL is queued behind the MONMSG work. The job on this machine has DECFMT J, so CL writes 350,80 while the emitter wrote 350.80 and did not fail. The separator is now a declared model commitment: --decfmt J maps to a comma because that is what was measured here, comma and period are direct declarations by the caller, and an IBM i DECFMT value that has not been measured is refused rather than guessed. A program that renders a fractional decimal without a declaration does not compile — exit 5. With the harness now passing the job DECFMT it measured, this case agrees: the loop from 'environment observed on the machine' to 'environment declared to the tool' is closed. |
| caveat | only J was established on hardware. period and comma are declarations, not measurements, and are labelled that way in the source. The first version of the refusal matched the helper's definition rather than its call sites and rejected all 43 CL sources in the repository; the check now looks at call sites, and 4 sources genuinely render a fractional decimal while 39 are untouched. |
| audit_hash | sha256:0fec29184dda4d80526e20fa65f34695f1389732dd265639616dd713128a3a30 |
Generated: 2026-07-27T04:11:52Z — bitexact/_AS400_AUDIT_2026-07-26/emit_claims_as400.py
4. What is and is not claimed
The source-loss experiment has now been run (2026-07-27). Three programs were
compiled from source members, the members were deleted with RMVM, and the
same read that succeeded before deletion was attempted again and failed with
CPFA0A9 for all three — the negative control is what makes the deletion a
fact rather than an assertion. The source was then recovered out of each object, and in
all three cases the recovered CL is the same command stream that was compiled.
Running those recovered programs is a separate question. It was one out of
three when first measured, and it is three out of three now — and the
route between those two numbers is the part worth reading. Neither failure was a rescue
failure: the source had recovered correctly in all three cases from the start. Both were
the transpiler failing to reproduce the machine's environment. The job runs with
DECFMT J, so IBM i writes 350,80 where the transpiler wrote
350.80; the separator is now a declared input rather than a default, and the
harness passes the format it measured. CHKOBJ against a missing object
raises CPF9801 for MONMSG to catch, which the transpiler modelled as
nothing at all; CPF9801 is now a registered OS effect, and an effect that is not in the
registry is refused at emit time instead of being written out as a comment.
The figures are value equality under a declared observation. They are not byte-exactness for the CL path: the live program's message crosses a CCSID conversion and the candidate is a native binary. Recovered source is compared as a command stream after normalising RTVCLSRC's own formatting — byte-exactness is not available there and is not claimed.
Correction (2026-07-27): an earlier version of this page cited
QSYS2.PROGRAM_INFO's empty source-file columns as evidence about how a
program had been compiled. Those columns are empty for any ILE program regardless of
compile method; the origin is recorded on the bound module. Read from
QSYS2.BOUND_MODULE_INFO, a stream-file compile records the path it was
compiled from and a member compile records the library, file and member. The conclusion
stands and is stronger than before; the evidence first given for it was read from the
wrong view.
Rescue depends on the program having been created with its source retained. A *PGM compiled without that cannot be recovered this way, and that case has not been tested. N=3 programs.
5. On priority — world-first, stated in its exact scope
We know of no earlier public demonstration of recovering the source of an IBM i *PGM whose source member had actually been deleted, with the deletion established by a negative control rather than asserted. That is the claim, in the scope it was measured in: the source layer, three programs, the deletion proved by attempting the same read afterwards and getting CPFA0A9 all three times.
Two things about the form of this statement, because the rest of this page is built out of measurements and this one is not:
- Priority is not measurable here. This harness can establish what happened on this machine; it can never establish what nobody else has done. The sentence above is a statement about the published record as we know it, and it is made in that spirit. If someone shows us an earlier demonstration, the correct response is to withdraw it, which is why it is phrased so that withdrawal is possible without rewriting anything else.
- The scope is the source layer only. It is not a claim that the recovered programs behave identically to the originals — one of three does, and the two that do not are recorded above with the reasons. An earlier version of this site carried "world-first" over the whole rescue path; that wording was withdrawn on 2026-07-26 after our own audit found the comparison behind it was a value match on a three-digit integer, and it is not being restored.
6. Related
- Product page: SlimeRESCUE-AS400 / SlimeRESCUE family
- Same family: SlimeRESCUE-VOS3 materials
- Index: Resources
