bylazora 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,15 +1,29 @@
1
1
  # bylazora
2
2
 
3
3
  **Proof, not percentages.** Bylazora is the byte-exact mainframe migration
4
- standard: a migrated workload's outputs must equal the legacy COBOL reference
5
- byte for byte, or the gate fails it.
4
+ standard: a migrated workload's outputs must equal the legacy COBOL
5
+ reference byte for byte, or the gate fails it.
6
6
 
7
- This package reserves the `bylazora` name on npm. It will carry the MCP client
8
- and the agent rule packs: your coding assistant can operate the engine,
9
- propose, and measure, but it cannot mark a job proven. Only the validator can.
7
+ This package is the agent-facing companion to the bylazora-core engine. It
8
+ ships:
10
9
 
11
- The engine itself is a Rust crate, developed and released at
12
- **https://github.com/bylazora/bylazora** under AGPL-3.0-or-later (engine) and
10
+ - **rules/**: the canon rule pack for coding agents (rules/CLAUDE.md for
11
+ Claude-family assistants, rules/README.md for everyone). The pack covers
12
+ the migration loop: read the contract, change the target, run
13
+ `bylazora-core migrate verify`, read the diff, repeat. Only the
14
+ validator marks a run proven.
15
+ - **template/**: the shape `bylazora-core migrate new` generates for one
16
+ job, worked: SPEC.json, the reference capture script, verify.sh, and the
17
+ migrated Rust target from the public batch-txn sample. Copy it as a
18
+ reference while your agent writes the real logic.
19
+ - **index.js**: exports the version and the absolute paths `rulesPath` and
20
+ `templatePath`, so an agent harness can locate the bundled files
21
+ directly.
22
+
23
+ The engine itself, the validator, the bench runtime, the migrate routine,
24
+ and the MCP server (`bylazora-core mcp`), is the Rust crate
25
+ **bylazora-core**: https://crates.io/crates/bylazora-core, developed at
26
+ https://github.com/bylazora/bylazora under AGPL-3.0-or-later (engine) and
13
27
  Apache-2.0 (the copybook and DB2 parsers).
14
28
 
15
29
  ## The engine in one paragraph
@@ -18,8 +32,11 @@ One Rust binary runs the equivalence gate and the production runtime across
18
32
  three compute tiers: CPU, any GPU through vendor-neutral wgpu, and CUDA.
19
33
  Measured at one billion rows against the compiled COBOL reference: 28.1x on
20
34
  CPU, 61.3x on wgpu, 68.8x on CUDA, every run byte-identical. Money stays
21
- integer cents and is never a float.
35
+ integer cents and is never a float. The engine is free and unlimited under
36
+ AGPL: a licence key, when installed, is a licensee record for the run
37
+ record and the audit, never a gate.
22
38
 
23
39
  - Homepage: https://bylazora.com
24
40
  - Repository: https://github.com/bylazora/bylazora
25
41
  - Evidence annex: https://bylazora.com/evidence.html
42
+
package/index.js CHANGED
@@ -1,17 +1,25 @@
1
1
  "use strict";
2
2
 
3
- // Reserves the `bylazora` package name on npm.
3
+ // The `bylazora` package: the agent-facing companion to the bylazora-core
4
+ // engine. It ships the canon rule packs and the migrate workspace template,
5
+ // so a coding assistant can find them without scraping the repository.
4
6
  //
5
7
  // Bylazora is the byte-exact mainframe migration standard: a migrated
6
8
  // workload's outputs must equal the legacy COBOL reference byte for byte, or
7
- // the gate fails it. The engine itself is a Rust crate, developed and released
8
- // at https://github.com/bylazora/bylazora . This package will carry the MCP
9
- // client and the agent rule packs that let a coding assistant drive the engine
10
- // while the validator, and only the validator, decides what is proven.
9
+ // the gate fails it. The engine (validator, bench runtime, migrate routine,
10
+ // MCP server) is the Rust crate bylazora-core, developed and released at
11
+ // https://github.com/bylazora/bylazora . The validator, and only the
12
+ // validator, decides what is proven.
13
+
14
+ const path = require("path");
11
15
 
12
16
  module.exports = {
13
- version: "0.4.1",
17
+ version: "0.5.1",
14
18
  repository: "https://github.com/bylazora/bylazora",
15
19
  homepage: "https://bylazora.com",
16
- note: "Name reservation. The MCP client and rule packs ship here."
20
+ // Absolute paths to the bundled assets, for agents that want to copy
21
+ // them into a job workspace or a repository's agent configuration.
22
+ rulesPath: path.join(__dirname, "rules"),
23
+ templatePath: path.join(__dirname, "template")
17
24
  };
25
+
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bylazora",
3
- "version": "0.4.1",
4
- "description": "Byte-exact mainframe migration: MCP client and agent rule packs for the open-source Bylazora engine and gate.",
3
+ "version": "0.5.1",
4
+ "description": "Byte-exact mainframe migration: agent rule packs and the migrate workspace template for the open-source Bylazora engine and gate.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "repository": {
7
7
  "type": "git",
@@ -19,9 +19,12 @@
19
19
  "main": "index.js",
20
20
  "files": [
21
21
  "index.js",
22
- "README.md"
22
+ "README.md",
23
+ "rules",
24
+ "template"
23
25
  ],
24
26
  "engines": {
25
27
  "node": ">=18"
26
28
  }
27
29
  }
30
+
@@ -0,0 +1,51 @@
1
+ # Bylazora: rules for agents working on migrations
2
+
3
+ You are working on a migration that will be certified by the Bylazora gate.
4
+ The gate is absolute. These rules are not guidance; violating them fails the job.
5
+
6
+ ## The invariants
7
+
8
+ 1. The validator is the only authority that marks a run proven. A run is
9
+ proven when and only when every declared output file is byte-identical to
10
+ the legacy reference. No tool parameter, script, prompt, or confidence
11
+ score can mark a run proven.
12
+ 2. Money is integer cents, end to end. Never float, never double, never
13
+ rounding beyond the declared fixed-point precision. If a path cannot hold
14
+ exact integer arithmetic, stop and say so.
15
+ 3. Byte-exact means byte-exact: headers, field widths, ordering, totals rows,
16
+ line endings. A single differing byte is a failure with a diff.
17
+ 4. Reproducible or rejected: fixed seeds, same input yields the same output,
18
+ warm and cold runs agree.
19
+ 5. Never weaken the gate to make a run pass. No tolerance flags, no skipped
20
+ files, no partial comparisons. A missing reference means unvalidated -
21
+ never proven.
22
+
23
+ ## How you work
24
+
25
+ - Draft, then run, then read the verdict. Use the MCP tools: bench (cpu, gpu,
26
+ cuda) and validate.
27
+ - A MISMATCH is a diff to fix, never a threshold to tune.
28
+ - Propose measurements, run them, report them with the run record. Do not
29
+ report a performance number without a recorded run.
30
+
31
+ ## The migration loop
32
+
33
+ A migration is a workspace generated by `bylazora-core migrate new`. Inside it:
34
+
35
+ - SPEC.json is the contract: declared inputs and outputs.
36
+ - reference/ is sealed. Never edit it; the seal hash sits in SPEC.json, and
37
+ the verify step compares against the sealed copy.
38
+ - target/rust/src/main.rs is the only file the logic belongs in. It takes
39
+ (input_dir, output_dir), reads the declared inputs, writes the declared
40
+ outputs. Everything else is scaffolding; leave it alone.
41
+ - Run `bylazora-core migrate verify <job>` to build, run and gate. The
42
+ verdict record lands in runs/ and is never overwritten, which is what makes
43
+ the trail an audit artefact.
44
+ - A MISMATCH is a diff to fix, never a threshold to tune.
45
+ - The MCP tools migrate_verify and migrate_status drive the same loop from
46
+ inside your session.
47
+
48
+ ## The licence boundary
49
+
50
+ The engine is AGPL-3.0-or-later; the parsers are Apache-2.0. Do not claim the
51
+ gate passed when it did not; the run record is the audit trail.
@@ -0,0 +1,63 @@
1
+ # agents/
2
+
3
+ The rule pack for AI coding agents that work on a Bylazora migration. Give an
4
+ agent these rules and it knows the invariants; the gate still disposes of
5
+ whatever it drafts. The agent proposes; the gate disposes.
6
+
7
+ ## What is here
8
+
9
+ `CLAUDE.md` is the canonical pack. Deliberately one file of plain markdown, not
10
+ one file per tool.
11
+
12
+ ## Installing it into your migration repository
13
+
14
+ Every agent tool reads its instructions from a different path, and those paths
15
+ move: `.cursorrules` was Cursor's legacy format and is now `.cursor/rules/*.mdc`,
16
+ Copilot reads `.github/copilot-instructions.md`, and `AGENTS.md` has become the
17
+ cross-tool convention. Shipping a copy per tool would mean shipping files that
18
+ are wrong within a year and disagree with each other in the meantime.
19
+
20
+ So copy the contents of `CLAUDE.md` into whichever file your tool reads:
21
+
22
+ | Tool | Commonly read | Notes |
23
+ |---|---|---|
24
+ | Claude Code | `CLAUDE.md` | at the repository root |
25
+ | Most agents | `AGENTS.md` | the emerging cross-tool convention |
26
+ | Cursor | `.cursor/rules/*.mdc` | rules can be scoped to a glob; `.cursorrules` is legacy |
27
+ | GitHub Copilot | `.github/copilot-instructions.md` | repository-wide, with path-specific instructions also supported |
28
+
29
+ Check your tool's current documentation rather than trusting this table: it is
30
+ the part of this repository most likely to age badly, which is exactly why the
31
+ pack is not split per tool.
32
+
33
+ **If your repository already has one of those files, merge the invariants into
34
+ it. Do not replace it.** Your file knows things about your estate that this pack
35
+ does not.
36
+
37
+ ## If you are writing your own
38
+
39
+ The five invariants are the non-negotiable part. Copy them verbatim, because
40
+ each one is enforced by the gate rather than being a matter of taste:
41
+
42
+ 1. the validator is the only authority that marks a run proven;
43
+ 2. money is integer cents, end to end, never float;
44
+ 3. byte-exact means headers, field widths, ordering, totals rows and line endings;
45
+ 4. reproducible, or rejected;
46
+ 5. never weaken the gate to make a run pass.
47
+
48
+ Everything after that - how the agent works, which tools it calls, the licence
49
+ boundary - is yours to fit to the estate. Keep the rule about run records: a
50
+ performance number without a recorded run is not a number.
51
+
52
+ ## The migration loop
53
+
54
+ The pack pairs with the migration routine: `bylazora-core migrate new`
55
+ scaffolds a gated workspace, the agent writes the logic into
56
+ `target/rust/src/main.rs`, and `bylazora-core migrate verify` is the only
57
+ thing that can mark the job proven. The same loop runs over MCP
58
+ (migrate_verify, migrate_status).
59
+
60
+ ## Keeping it current
61
+
62
+ The pack names the engine's CLI and MCP tools. If you pin a version, check that
63
+ list against `bylazora-core --help` when you upgrade.
@@ -0,0 +1,20 @@
1
+ # The migrate workspace template
2
+
3
+ This directory mirrors what `bylazora-core migrate new` scaffolds for one
4
+ job, worked with the public batch-txn sample. The inputs and the sealed
5
+ reference outputs are not bundled here; the complete, proven workspace
6
+ lives in the repository at sample/, where the verdict trail shows the gate
7
+ passing.
8
+
9
+ Copy this shape, then:
10
+
11
+ 1. Read SPEC.json: the job name, the declared inputs and outputs, the
12
+ copybook schema, and the sealed reference hash.
13
+ 2. Write the migrated logic in target/rust/src/main.rs. The contract is
14
+ argv[1] = input directory, argv[2] = output directory; read the declared
15
+ inputs, write the declared outputs.
16
+ 3. Seal the legacy reference: `bylazora-core migrate reference <job>`
17
+ (this runs reference/capture.sh and refuses to reseal without --force).
18
+ 4. Run the gate: `bylazora-core migrate verify <job>`. Read the diff, fix
19
+ the mismatch, repeat. Only the validator marks a run proven.
20
+
@@ -0,0 +1,107 @@
1
+ {
2
+ "name": "batch-txn",
3
+ "language": "rust",
4
+ "inputs": [
5
+ {
6
+ "path": "transactions.csv",
7
+ "format": "csv"
8
+ },
9
+ {
10
+ "path": "balances.csv",
11
+ "format": "csv"
12
+ }
13
+ ],
14
+ "outputs": [
15
+ "final_balances.csv",
16
+ "summary_report.csv"
17
+ ],
18
+ "schema": [
19
+ {
20
+ "children": [
21
+ {
22
+ "children": [],
23
+ "fixed_point": false,
24
+ "kind": "display",
25
+ "length": 7,
26
+ "level": 5,
27
+ "name": "TXN-ACCOUNT",
28
+ "occurs": 1,
29
+ "offset": 0,
30
+ "scale": 0,
31
+ "signed": false
32
+ },
33
+ {
34
+ "children": [],
35
+ "fixed_point": false,
36
+ "kind": "display",
37
+ "length": 7,
38
+ "level": 5,
39
+ "name": "TXN-AMOUNT",
40
+ "occurs": 1,
41
+ "offset": 7,
42
+ "scale": 0,
43
+ "signed": false
44
+ },
45
+ {
46
+ "children": [],
47
+ "fixed_point": false,
48
+ "kind": "display",
49
+ "length": 1,
50
+ "level": 5,
51
+ "name": "TXN-TYPE",
52
+ "occurs": 1,
53
+ "offset": 14,
54
+ "scale": 0,
55
+ "signed": false
56
+ }
57
+ ],
58
+ "fixed_point": false,
59
+ "kind": "group",
60
+ "length": 15,
61
+ "level": 1,
62
+ "name": "TXN-REC",
63
+ "occurs": 1,
64
+ "offset": 0,
65
+ "scale": 0,
66
+ "signed": false
67
+ },
68
+ {
69
+ "children": [
70
+ {
71
+ "children": [],
72
+ "fixed_point": false,
73
+ "kind": "display",
74
+ "length": 7,
75
+ "level": 5,
76
+ "name": "BAL-ACCOUNT",
77
+ "occurs": 1,
78
+ "offset": 0,
79
+ "scale": 0,
80
+ "signed": false
81
+ },
82
+ {
83
+ "children": [],
84
+ "fixed_point": false,
85
+ "kind": "display",
86
+ "length": 12,
87
+ "level": 5,
88
+ "name": "BAL-AMOUNT",
89
+ "occurs": 1,
90
+ "offset": 7,
91
+ "scale": 0,
92
+ "signed": true
93
+ }
94
+ ],
95
+ "fixed_point": false,
96
+ "kind": "group",
97
+ "length": 19,
98
+ "level": 1,
99
+ "name": "BAL-REC",
100
+ "occurs": 1,
101
+ "offset": 15,
102
+ "scale": 0,
103
+ "signed": false
104
+ }
105
+ ],
106
+ "sealed_reference_sha256": "1d2a70cf0770cff8e49e9a3b363bd640e8239a9b36be9d70a9a894c159df1ffd"
107
+ }
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+ # The reference capture: compile the legacy COBOL with GnuCOBOL and run it
3
+ # on the declared inputs. `bylazora-core migrate reference` executes this
4
+ # script and seals whatever it leaves in output/ as the byte-level contract.
5
+ set -euo pipefail
6
+ cd "$(dirname "$0")"
7
+ mkdir -p output
8
+ rm -f output/*
9
+ cobc -free -x -O2 -o /tmp/batch-txn-reference ../legacy/BATCHTXN.cob
10
+ /tmp/batch-txn-reference ../input/transactions.csv ../input/balances.csv output
@@ -0,0 +1,8 @@
1
+ [package]
2
+ name = "batch-txn"
3
+ version = "0.1.0"
4
+ edition = "2021"
5
+
6
+ [[bin]]
7
+ name = "batch-txn"
8
+ path = "src/main.rs"
@@ -0,0 +1,6 @@
1
+ # batch-txn: rules for whoever writes the logic
2
+
3
+ The contract is SPEC.json. The gate is absolute: only
4
+ `bylazora-core migrate verify` (which calls the validator) marks a run
5
+ proven. Run it often; read the diff; a MISMATCH is a diff to fix, never a
6
+ threshold to tune. The canon rules live in the agents/ rule pack.
@@ -0,0 +1,105 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // The migrated BATCHTXN application, in Rust. The workspace skeleton was
3
+ // generated by `bylazora-core migrate new`; this file is the migration
4
+ // work. Contract: argv[1] = input directory, argv[2] = output directory.
5
+ // Read the declared inputs from argv[1], write the declared outputs to
6
+ // argv[2]. The gate compares the outputs against the sealed COBOL
7
+ // reference byte for byte, so one differing byte is a failure with a diff.
8
+ //
9
+ // Semantics mirror the legacy program exactly:
10
+ // - balances.csv defines the account domain (ACCOUNT_ID,BALANCE_CENTS)
11
+ // - transactions.csv applies deposits (D) and withdrawals (anything else)
12
+ // in integer cents
13
+ // - final_balances.csv: ACCOUNT_ID,BALANCE_CENTS, one row per account
14
+ // - summary_report.csv: per-account TOTAL_DEPOSITS, TOTAL_WITHDRAWALS,
15
+ // TXN_COUNT, ENDING_BALANCE, then one grand-totals row with account id -1
16
+ use std::env;
17
+ use std::fs;
18
+
19
+ fn parse_i64(field: &str) -> i64 {
20
+ let mut v: i64 = 0;
21
+ for c in field.bytes() {
22
+ v = v * 10 + (c - b'0') as i64;
23
+ }
24
+ v
25
+ }
26
+
27
+ fn main() {
28
+ let args: Vec<String> = env::args().collect();
29
+ let input_dir = &args[1];
30
+ let output_dir = &args[2];
31
+ fs::create_dir_all(output_dir).expect("output dir");
32
+
33
+ // Balances define the account domain.
34
+ let mut starting: Vec<i64> = Vec::new();
35
+ let balances = fs::read_to_string(format!("{}/balances.csv", input_dir))
36
+ .expect("balances.csv");
37
+ for line in balances.lines().skip(1) {
38
+ let line = line.trim_end_matches('\r');
39
+ if line.is_empty() {
40
+ continue;
41
+ }
42
+ let f: Vec<&str> = line.split(',').collect();
43
+ let id = parse_i64(f[0]) as usize;
44
+ let balance = parse_i64(f[1]);
45
+ if starting.len() <= id {
46
+ starting.resize(id + 1, 0);
47
+ }
48
+ starting[id] = balance;
49
+ }
50
+ let n_acct = starting.len();
51
+
52
+ // Transactions: D deposits, anything else withdraws. Integer cents.
53
+ let mut deposits = vec![0i64; n_acct];
54
+ let mut withdrawals = vec![0i64; n_acct];
55
+ let mut counts = vec![0i64; n_acct];
56
+ let transactions = fs::read_to_string(format!("{}/transactions.csv", input_dir))
57
+ .expect("transactions.csv");
58
+ for line in transactions.lines().skip(1) {
59
+ let line = line.trim_end_matches('\r');
60
+ if line.is_empty() {
61
+ continue;
62
+ }
63
+ let f: Vec<&str> = line.split(',').collect();
64
+ let id = parse_i64(f[0]) as usize;
65
+ let amount = parse_i64(f[1]);
66
+ if f[2].as_bytes()[0] == b'D' {
67
+ deposits[id] += amount;
68
+ } else {
69
+ withdrawals[id] += amount;
70
+ }
71
+ counts[id] += 1;
72
+ }
73
+
74
+ // Write the two reports in the legacy program's exact shape.
75
+ let mut final_balances = String::from("ACCOUNT_ID,BALANCE_CENTS\n");
76
+ let mut summary = String::from(
77
+ "ACCOUNT_ID,TOTAL_DEPOSITS,TOTAL_WITHDRAWALS,TXN_COUNT,ENDING_BALANCE\n",
78
+ );
79
+ let (mut total_dep, mut total_wd, mut total_cnt, mut total_end) =
80
+ (0i64, 0i64, 0i64, 0i64);
81
+ for i in 0..n_acct {
82
+ let d = deposits[i];
83
+ let w = withdrawals[i];
84
+ let c = counts[i];
85
+ let e = starting[i] + d - w;
86
+ final_balances.push_str(&format!("{},{}\n", i, e));
87
+ summary.push_str(&format!("{},{},{},{},{}\n", i, d, w, c, e));
88
+ total_dep += d;
89
+ total_wd += w;
90
+ total_cnt += c;
91
+ total_end += e;
92
+ }
93
+ summary.push_str(&format!(
94
+ "-1,{},{},{},{}\n",
95
+ total_dep, total_wd, total_cnt, total_end
96
+ ));
97
+
98
+ fs::write(
99
+ format!("{}/final_balances.csv", output_dir),
100
+ final_balances,
101
+ )
102
+ .expect("write final_balances.csv");
103
+ fs::write(format!("{}/summary_report.csv", output_dir), summary)
104
+ .expect("write summary_report.csv");
105
+ }
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env bash
2
+ # Runs the full gate for this job: build the target, run it on the declared
3
+ # inputs, and byte-compare its outputs against the sealed reference.
4
+ set -euo pipefail
5
+ exec bylazora-core migrate verify "$(cd "$(dirname "$0")" && pwd)"