SlimeRESCUE-AS400 — 検証資料と監査記録
IBM i 実機で何を測ったか、どう測っているか、そして何がまだ実証されていないか。このページの元になった監査で、以前の照合経路が主張どおりのものを比べていなかったことが判明したため、欠陥の記録も修正後の結果と並べて残しています。
1. 以前のハーネスが実際に比べていたもの
最初のハーネス (rescue/run_rescue_diff.sh) は、両辺の stdout と stderr を
結合したものに grep -oE '[0-9]{8,}' | head -1 をかけ、取れた整数どうしを比べていました。
この経路のどこでもバイト列は比較されておらず、8 桁の数字が出ない run では比較対象自体が生じません。
さらに 2 つのステップが黙って失敗していました。出力を捨てるのをやめて初めて見えたものです。
- DB2 クロスチェックはインライン
RUNSQL形式を使っており、本機はこれを拒否します (SQL0104・戻り値 255・メッセージ ID なし)。出力は/dev/nullに捨てられ、 値は PASS 条件にも入っていないため、この照合はどの run でも値を出していなかった可能性があります。 - トランスパイラは DDS がソースの隣に無いと別のプログラムを出力します (レコード項目の型が失われる)。 それを暴くコンパイル失敗も同様に捨てられていました。これは製品側の欠陥であり、3 節に欠陥として記録しています。
2. 修正版ハーネスがしていること
rescue/run_rescue_diff_v2.sh。旧版は変更していません — 監査の対象そのものであり、
上書きは証拠の破壊にあたるためです。
- 宣言された観測を三層。いずれも数字の grep ではありません: 完了メッセージの全文 / データメンバの生バイト (CCSID 変換なし・run 前後) / 回収したソースメンバの生バイト。
- ソース属性はオブジェクトから読む —
QSYS2.PROGRAM_INFOであり、 RTVCLSRC が出力冒頭に印字するテキストではありません。列名は記憶ではなく、毎 runQSYS2.SYSCOLUMNSから取り直します。 - DB2 は PASS 条件の中にあります。横に置いてあるのではありません。
- どのステップの失敗でも run が止まります。 戻り値を検査し、非ゼロが許されるのは そのステップの出したメッセージ ID が全て明示の benign 一覧にある場合だけです。未知の ID は失敗します。
- env が無ければ PASS しません。 ジョブの CCSID・日付書式・ライブラリリストを 最初に取得し、取れなければ結果を報告することを拒否します。
3. 検証済みの数値 Open
この節の数字はすべて claims/emit_claims.py が実測から生成しています。手で書いた数字はありません。生成されなかった項目は空欄になります(古い数字が残ることはありません)。
exists-over-tested = 測定した範囲で成立した、という意味であり、全入力に対する主張ではありません。
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,'] |
| 保証レベル | S |
| 量化 | exists-over-tested |
| 測定域 | programs=3, constructs=zoned-decimal file summation, packed decimal with a fraction, MONMSG on a real message, library=BARON1 |
| 観測対象 | 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 |
| オラクル | IBM i on PUB400 IBM i V7R5M0 execution |
| 期待値の出所 | 実機で確定 |
| 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 |
| 保証レベル | S |
| 量化 | exists-over-tested |
| 測定域 | programs=3 |
| 観測対象 | the completion message text in full |
| オラクル | IBM i on PUB400 IBM i V7R5M0 execution |
| 期待値の出所 | 実機で確定 |
| 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 |
| 保証レベル | B |
| 量化 | for-all-emissions |
| 測定域 | form=OsContext(kind, id), instances=VOS3 svc(n) / IBM i msg(msgid), cl_sources_in_repo=43, refused=1 |
| 観測対象 | what the emitter does with a MONMSG whose message is not in the table |
| オラクル | the registry and the emitter source static |
| 期待値の出所 | リポジトリとの整合確認 |
| 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 |
| 保証レベル | S |
| 量化 | exists-over-tested |
| 測定域 | programs=1, records=3, expected_sum=425, program=P5DRV (CLLE, reads a PF and sums a zoned field) |
| 観測対象 | 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 |
| オラクル | IBM i on PUB400 IBM i V7R5M0 execution |
| 期待値の出所 | 実機で確定 |
| 未モデル | 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 |
| 保証レベル | pending |
| 量化 | exists-over-tested |
| 測定域 | programs=1, records=3, expected_sum=425 |
| 観測対象 | the first 8+ digit run in the combined stdout and stderr |
| オラクル | IBM i 7.5 on PUB400 execution |
| 期待値の出所 | phantom-caught |
| 未モデル | 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 |
| 保証レベル | S |
| 量化 | exists-over-tested |
| 測定域 | files=1, records=4, bytes=88, types=CHAR(6) and DECFLOAT(34) as decimal128 DPD |
| 観測対象 | the whole member's bytes, copied with no CCSID conversion, compared as bytes rather than as values |
| オラクル | IBM i on PUB400 IBM i V7R5M0 execution |
| 期待値の出所 | 実機で確定 |
| 未モデル | 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 |
| 保証レベル | pending |
| 量化 | exists-over-tested |
| 測定域 | harness=p19/run_p19dg_diff.sh |
| 観測対象 | mem[whole member] as hex |
| オラクル | IBM i 7.5 on PUB400 execution |
| 期待値の出所 | 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 |
| 保証レベル | B |
| 量化 | for-all-lines-read |
| 測定域 | files=['run_rescue_diff.sh', 'run_p19dg_diff.sh'] |
| 観測対象 | the source of the harness |
| オラクル | the harness source static reading |
| 期待値の出所 | リポジトリとの整合確認 |
| 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 |
| 保証レベル | B |
| 量化 | for-all-emissions-with-unresolved-dclf |
| 測定域 | file=src/dds.rs, function=resolve_dclf, observed_case=rescue/out_v2 without CUSTF.PF beside the source |
| 観測対象 | the emitted Rust's declaration of the DCLF fields |
| オラクル | the repository source static |
| 期待値の出所 | リポジトリとの整合確認 |
| 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 |
| 保証レベル | B |
| 量化 | exists-over-tested |
| 測定域 | case=CHGVAR of *DEC(9 2) into *CHAR, job DECFMT=J, sources_needing_declaration=4, sources_unaffected=39 |
| 観測対象 | the character rendering of a packed decimal |
| オラクル | IBM i on PUB400 execution |
| 期待値の出所 | 実機で確定 |
| 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 |
生成: 2026-07-27T04:11:52Z — bitexact/_AS400_AUDIT_2026-07-26/emit_claims_as400.py
4. 主張していること・していないこと
ソース喪失実験は実施しました (2026-07-27)。3 本のプログラムをソースメンバからコンパイルし、
メンバを RMVM で削除し、削除前に成功していた読み取りを再度試みて
3 本とも CPFA0A9 で失敗することを確認しています — この負の対照があって初めて、
削除は主張ではなく事実になります。そのうえで各オブジェクトからソースを回収し、
3 本すべてで回収された CL がコンパイルしたものと同じコマンド列であることを確認しました。
回収したプログラムを走らせた結果は別の問いです。最初の測定は 3 本中 1 本、現在は 3 本中 3 本
— そして読む価値があるのはこの 2 つの数字の間の経路です。どちらの不一致も救済の失敗ではありません。
ソースは最初から 3 本とも正しく回収されており、失敗していたのはトランスパイラが実機の環境を
再現できていなかった点です。ジョブは DECFMT J なので IBM i は 350,80 と書きますが、
トランスパイラは 350.80 と書いていました — 分離子を既定値ではなく宣言された入力に変え、
ハーネスが測定した書式をそのまま渡すようにしました。存在しないオブジェクトへの CHKOBJ は
CPF9801 を上げ MONMSG が捕まえますが、トランスパイラはこれを何もしないものとして扱っていました
— CPF9801 は登録された OS 効果になり、登録に無い効果は emit 時に拒否され、コメントとして
書き出されることはなくなりました。
数値は宣言された観測の下での値の一致です。CL 経路ではバイト一致ではありません — 実機側のメッセージは CCSID 変換を経ており、候補側はネイティブバイナリです。回収ソースは RTVCLSRC 自身の書式を正規化したうえでコマンド列として比較しています。 バイト一致はこの経路では得られず、主張もしていません。
訂正 (2026-07-27): 本ページの以前の版は、QSYS2.PROGRAM_INFO の
ソースファイル列が空であることを、コンパイル方法についての証拠として挙げていました。
この列は ILE プログラムではコンパイル方法によらず空であり、出所は束ねられたモジュール側に記録されます。
QSYS2.BOUND_MODULE_INFO で読むと、ストリームファイルからのコンパイルはそのパスを、
メンバからのコンパイルはライブラリ・ファイル・メンバを記録しています。結論は変わらず、
むしろ強くなりますが、最初に挙げた根拠は誤ったビューから読んだものでした。
救済はソース保持付きで作成されたプログラムであることに依存します。それ無しでコンパイルされた *PGM はこの方法では回収できず、その場合は未検証です。N=3 本。
5. 先行性について — 正確な scope での「世界初」
ソースメンバが実際に削除された IBM i の *PGM から、削除を負の対照で立証したうえで ソースを復元してみせた公開実証を、我々は他に知りません。 これが主張であり、その scope は 測定した範囲そのものです: ソース層・3 本・削除は事後に同じ読み取りを試みて 3 本とも CPFA0A9 を 得ることで立証。
この文の性質について 2 点。本ページの他の記述は測定から組み立てられていますが、これは違います。
- 先行性はここでは測定できません。 本ハーネスが立てられるのはこの機械で 何が起きたかであって、他の誰が何をしていないかは決して立てられません。上の一文は 我々の知る限りの公表記録についての言明であり、その性質のものとして書いています。 先行する実証を示されたら正しい対応は撤回であり、他を書き換えずに撤回できる形にしてあります。
- scope はソース層に限られます。 回収したプログラムが元と同じ動きをする、 という主張ではありません — 3 本中 1 本がそうで、そうでない 2 本は理由とともに上に記録しています。 以前の本サイトは救済経路全体に「world-first」を付けていましたが、その文言は自社監査により その裏付けが 3 桁整数の値一致であったと判明したため 2026-07-26 に取り下げており、復帰させません。
6. 関連
- 製品ページ: SlimeRESCUE-AS400 / SlimeRESCUE ファミリ
- 同ファミリ: SlimeRESCUE-VOS3 検証資料
- 索引: リソース
