# Safe-Change Map — Agent Repair Loop, Testing, and Invariants to Preserve

> The closing synthesis: the complete agent edit loop (read → change → check --json → explain → fix --plan → patch → test), the conformance fixture contract (pass/ vs fail/), xfail test markers, the no-legacy-C invariant to guard in every build output, safe-change rules for the pre-1 unstable surface, and the sandbox requirement that keeps native test artifacts off developer machines by default.

- Repository: vercel-labs/zerolang
- GitHub: https://github.com/vercel-labs/zerolang
- Human wiki: https://grok-wiki.com/public/wiki/vercel-labs-zerolang-9ab46b2a38e0
- Complete Markdown: https://grok-wiki.com/public/wiki/vercel-labs-zerolang-9ab46b2a38e0/llms-full.txt

## Source Files

- `skill-data/zero-testing.md`
- `skill-data/zero-agent.md`
- `scripts/agent-repair-demo.mts`
- `conformance/run.mjs`
- `conformance/native`
- `AGENTS.md`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:

- [skill-data/zero-testing.md](skill-data/zero-testing.md)
- [skill-data/zero-agent.md](skill-data/zero-agent.md)
- [scripts/agent-repair-demo.mts](scripts/agent-repair-demo.mts)
- [conformance/run.mjs](conformance/run.mjs)
- [conformance/native/pass/test-blocks.0](conformance/native/pass/test-blocks.0)
- [conformance/native/pass/test-expected-fail.0](conformance/native/pass/test-expected-fail.0)
- [conformance/native/fail/test-unexpected-pass.0](conformance/native/fail/test-unexpected-pass.0)
- [examples/agent-repair-demo/broken.0](examples/agent-repair-demo/broken.0)
- [AGENTS.md](AGENTS.md)
- [package.json](package.json)
- [scripts/native-test-sandbox.mts](scripts/native-test-sandbox.mts)
</details>

# Safe-Change Map — Agent Repair Loop, Testing, and Invariants to Preserve

This page is the operational synthesis for contributors and agents working inside the Zero repository. It documents the precise edit loop agents must follow, how conformance fixtures are structured and interpreted, which runtime signals constitute a hard invariant violation, the policy for evolving a pre-1 unstable surface, and the sandbox guardrail that prevents native test artifacts from escaping onto developer machines.

Understanding these rules together is critical: a change that individually passes a type check, satisfies one conformance fixture, and emits no C—yet leaves an `xfail` marker on a bug that is now fixed—is still a bad change. The rules are a system. Breaking any one of them leaves the repository in a state that misleads the next agent or contributor.

---

## The Agent Repair Loop

The repair loop is a deterministic, observable cycle built around machine-readable CLI output. It is not a prose-driven workflow; every step produces structured JSON that the next step consumes.

```text
┌─────────────┐
│  Read files │  .0 sources, zero.json, tests, examples
└──────┬──────┘
       │
       ▼
┌─────────────┐
│  Edit       │  Smallest change that satisfies the request
└──────┬──────┘
       │
       ▼
┌──────────────────────────┐
│  zero check --json <file> │  Structured diagnostics, repair hints
└──────┬───────────────────┘
       │ diagnostic present?
       ├─── yes ──►  zero explain <code>        (structured explanation)
       │              zero fix --plan --json     (repair plan, no writes)
       │              apply patch manually
       │              loop back to check
       │
       └─── no  ──►
┌───────────────────────────────┐
│  zero test --json <file-or-pkg>│  Verify behavior is correct
└───────────────────────────────┘
```

Sources: [skill-data/zero-agent.md:26-45](), [scripts/agent-repair-demo.mts:23-60]()

### Step-by-Step Details

| Step | Command | What to look for |
|------|---------|-----------------|
| Check | `zero check --json <input>` | `ok`, `diagnostics[].code`, `diagnostics[].repair.id` |
| Explain | `zero explain --json <code>` | `code`, `repair.id`, human description |
| Plan | `zero fix --plan --json <input>` | `mode: "plan"`, `appliesEdits: false`, `fixes[].id` |
| Patch | Edit source manually per plan | N/A — fix is applied by the agent, not the CLI |
| Test | `zero test --json <input>` | `ok`, `passedTests`, `failedTests`, `unexpectedPasses` |

**Critical constraint:** `zero fix --plan` returns a plan object but never writes files itself (`appliesEdits: false`). The agent is responsible for applying the indicated patch. This is intentional: Zero's design keeps the compiler as an oracle, not an autonomous mutator.

The live demo encodes this contract exactly:

```typescript
// scripts/agent-repair-demo.mts:32-37
const plan = zeroJson(["fix", "--plan", "--json", workFile]);
assert.equal(plan.mode, "plan");
assert.equal(plan.appliesEdits, false);
assert.equal(plan.fixes[0].id, "make-binding-mutable");

const fixedSource = brokenSource.replace("    let dst: [4]u8", "    let mut dst: [4]u8");
writeFileSync(workFile, fixedSource);
```

The broken source (`examples/agent-repair-demo/broken.0`) assigns through an immutable binding—triggering diagnostic `TYP009`. The repair id `make-binding-mutable` names the fix without performing it.

### Focused Commands for Narrow Loops

Use `--filter` during an edit loop to avoid running unrelated tests:

```sh
zero test --json --filter addition conformance/native/pass/test-blocks.0
```

The filter matches test names by substring. When the surface under change is wider, run the containing package or fixture after narrowing work is done. Sources: [skill-data/zero-testing.md:31-36]()

---

## Conformance Fixture Contract: `pass/` vs `fail/`

Conformance fixtures live under `conformance/native/` and are split into two directories with opposing semantics.

```text
conformance/native/
├── pass/   ← fixtures that MUST compile, check, or test-pass successfully
└── fail/   ← fixtures that MUST produce a diagnostic or test-failure
```

### `pass/` Fixtures

A fixture in `conformance/native/pass/` is a language-level contract that:
- `zero check --json` returns `ok: true`
- `zero test --json` returns `ok: true`
- No legacy C is emitted (see no-legacy-C invariant below)

Example — the canonical test-block fixture:

```zero
// conformance/native/pass/test-blocks.0
fun add(a: i32, b: i32) -> i32 {
    return a + b
}

test "addition works" {
    expect(add(2, 3) == 5)
}
```

The conformance runner asserts:

```javascript
// conformance/run.mjs:2404-2415
const zeroTestRun = await execFileAsync(zero, ["test", "conformance/native/pass/test-blocks.0"]);
assert.equal(zeroTestRun.stdout, "1 test(s) ok\n");

const zeroTestJsonBody = JSON.parse(zeroTestJsonRun.stdout);
assert.equal(zeroTestJsonBody.ok, true);
assert.equal(zeroTestJsonBody.fixtures.snapshotKey, "zero-test-direct-frontend-v1");
assert.equal(zeroTestJsonBody.results[0].status, "passed");
```

### `fail/` Fixtures

A fixture in `conformance/native/fail/` is a negative contract — the compiler or test runner **must** reject it. Placing a fixture in `fail/` is a commitment that it should never silently succeed.

The conformance harness verifies this by asserting a non-zero exit code or a specific diagnostic code. For example, the `assertCheckTimeoutOrDiagnostic` helper asserts that either the check times out or one of the expected diagnostic codes appears in output:

```javascript
// conformance/run.mjs:102-113
async function assertCheckTimeoutOrDiagnostic(fixture, expectedCodes) {
  const result = await execFileAsync(zero, ["check", "--json", fixture], { timeout: checkTimeoutMs }).catch((error) => error);
  if (result.killed || result.signal) {
    assert.equal(result.killed, true);
    return { timedOut: true };
  }
  assert.notEqual(result.code, 0);
  const body = JSON.parse(result.stdout);
  const code = body.diagnostics?.[0]?.code;
  assert(expectedCodes.includes(code), `expected one of ${expectedCodes.join(", ")}, got ${code}`);
}
```

---

## `xfail` Test Markers

Expected-failure markers let the test suite document known incomplete behavior without blocking CI. They are a contract, not a workaround—and they require active maintenance.

### Marker Forms

Any of the following prefixes in a test name activates xfail semantics:

| Marker | Example |
|--------|---------|
| `xfail:` | `test "xfail: pending parser edge case" { ... }` |
| `expected fail:` | `test "expected fail: borrow edge" { ... }` |
| `[xfail]` | `test "[xfail] unresolved variant" { ... }` |

Sources: [skill-data/zero-testing.md:54-68]()

### Semantics

An `xfail` test must **fail** its `expect(...)` assertion for the overall `zero test` command to succeed. If the underlying code is fixed and the test starts passing, the command fails with `unexpectedPasses`.

```javascript
// conformance/run.mjs:2426-2438
const zeroExpectedFailBody = JSON.parse(zeroExpectedFailJsonRun.stdout);
assert.equal(zeroExpectedFailBody.ok, true);
assert.equal(zeroExpectedFailBody.expectedFailures, 1);
assert.equal(zeroExpectedFailBody.results[0].status, "expected-fail");

const zeroUnexpectedPassBody = JSON.parse(zeroUnexpectedPassJsonRun.stdout);
assert.equal(zeroUnexpectedPassBody.ok, false);
assert.equal(zeroUnexpectedPassBody.unexpectedPasses, 1);
assert.equal(zeroUnexpectedPassBody.results[0].status, "unexpected-pass");
```

The fixtures that encode these two behaviors live at:
- `conformance/native/pass/test-expected-fail.0` — `expect(false)` under `xfail:`, command succeeds
- `conformance/native/fail/test-unexpected-pass.0` — `expect(true)` under `xfail:`, command fails

### Agent Rule

> Do not leave an `xfail` marker on a fixed bug.

When a fix causes an xfail test to pass, the marker must be removed in the same change. Leaving it causes the opposite failure mode—the test suite now rejects the fix as an `unexpectedPasses` violation. Sources: [skill-data/zero-testing.md:74-76]()

---

## The No-Legacy-C Invariant

Every build output—check, test, size, graph, doc, mem, build—must demonstrate that Zero's native frontend is in use, not a C code generation fallback. This is enforced uniformly as three JSON field assertions.

### The `assertNoC` Contract

The agent repair demo codifies this as a reusable assertion:

```typescript
// scripts/agent-repair-demo.mts:48-52
function assertNoC(body) {
  assert.equal(body.generatedCBytes ?? 0, 0);
  assert.equal(body.cBridgeFallback ?? body.selfHostRouting?.cBridge?.required ?? false, false);
  assert.notEqual(body.legacy, true);
}
```

| JSON Field | Assertion | Meaning |
|------------|-----------|---------|
| `generatedCBytes` | `=== 0` | No C source was emitted during compilation |
| `cBridgeFallback` | `=== false` | The C bridge codepath was not activated |
| `legacy` | `!== true` | Legacy backend was not selected |

### Applied to Every Output Mode

The demo applies `assertNoC` to every output surface after any significant change:

```typescript
// scripts/agent-repair-demo.mts:54-84
assertNoC(projectCheck);   // zero check --json
assertNoC(projectTest);    // zero test --json
assertNoC(projectDoc);     // zero doc --json
assertNoC(projectSize);    // zero size --json
assertNoC(projectMem);     // zero mem --json
assertNoC(projectRelease); // zero build --json --release
```

Additionally, the conformance runner's native fixture helpers each assert `generatedCBytes === 0` and `legacy === false` independently for build outputs:

```javascript
// conformance/run.mjs:54-55
assert.equal(body.generatedCBytes, 0);
assert.equal(body.legacy, false);
```

A failing `assertNoC` in any output indicates a regression: some code path activated the C bridge or legacy backend that should no longer be reachable. This is a hard blocker, not a warning.

---

## Safe-Change Rules for the Pre-1 Unstable Surface

Zero is explicitly pre-1.0 and agent-first. The contributor charter (`AGENTS.md`) establishes specific policies for what kinds of changes are safe and expected.

### Breaking Changes Are Acceptable

> Breaking changes are acceptable when they move the language, standard library, compiler, or tooling closer to [agent-first] goals.
>
> — [AGENTS.md:9-11]()

This means contributors must not defend backward compatibility as a first principle. Compatibility shims, migration layers, and legacy paths are explicitly out of scope unless they are required for the current agent-facing design.

### What Counts as Safe

A change is safe if it satisfies all of:

1. **Conformance fixtures pass** — both `pass/` (must succeed) and `fail/` (must fail) hold their contracts.
2. **No C emitted** — `assertNoC` passes across check, test, build, and related outputs.
3. **xfail markers are current** — no marker left on a now-passing test; no unmarked test that should be expected-fail.
4. **Examples remain runnable** — `examples/` stay copyable and executable from the repository root.
5. **Docs describe current behavior** — no release-planning language, internal diary details, or stale API descriptions.

### What Is Not Safe

| Pattern | Why It Fails |
|---------|--------------|
| Adding a compatibility shim for removed syntax | Contradicts "prefer the clearer agent-facing design" |
| Leaving an `xfail` on a fixed test | Causes `unexpectedPasses` failure in CI |
| Writing examples that require a legacy flag | Violates "keep examples runnable" |
| Emitting generated C in build output | Hard invariant (`generatedCBytes > 0`) |
| Publishing docs with validation-report language | Violates public docs policy |

Sources: [AGENTS.md:6-16](), [AGENTS.md:41-43]()

---

## The Sandbox Requirement

Native test artifacts (compiled binaries, object files, ELF executables) must not land on developer machines by default. The conformance runner enforces this with a hard startup guard.

### The Gate

```javascript
// conformance/run.mjs:7-10
if (process.env.ZERO_NATIVE_TEST_SANDBOX !== "1" && process.env.ZERO_NATIVE_TEST_ALLOW_LOCAL !== "1") {
  console.error("conformance emits native test artifacts; run `pnpm run conformance` for Vercel Sandbox execution or set ZERO_NATIVE_TEST_ALLOW_LOCAL=1 to opt into local artifacts.");
  process.exit(1);
}
```

The script exits immediately unless one of two environment variables is set. This is not a warning—it is an exit(1) with no recovery path inside the script.

### The Two Modes

| Mode | How to activate | What runs |
|------|----------------|-----------|
| **Sandbox (default)** | `pnpm run conformance` | Uploads a source archive to Vercel Sandbox; native binaries never touch the local machine |
| **Local (explicit opt-in)** | `ZERO_NATIVE_TEST_ALLOW_LOCAL=1 pnpm run conformance:local` | Runs on the local machine; developer accepts artifact presence |

The `package.json` scripts make sandbox the default for all three related test commands:

```json
// package.json (lines 16, 22-25, 38-39)
"conformance":          "pnpm run conformance:sandbox",
"conformance:sandbox":  "node ... scripts/native-test-sandbox.mts -- pnpm run conformance:local",
"command-contracts":    "pnpm run command-contracts:sandbox",
"native:test":          "pnpm run native:test:sandbox",
"native:test:local":    "ZERO_NATIVE_TEST_ALLOW_LOCAL=1 bash scripts/test-native.sh",
```

The sandbox script (`scripts/native-test-sandbox.mts`) documents explicitly that it "never copies native test binaries back to the local machine."

### Why This Matters

Zero's security posture in `AGENTS.md` calls out that compiler crashes, malformed output, and unsafe runtime behavior should never reach production state. The sandbox requirement extends that philosophy to the test artifacts themselves—executables that might crash or misbehave are contained in ephemeral compute rather than persisted locally.

Sources: [conformance/run.mjs:7-10](), [package.json scripts](), [scripts/native-test-sandbox.mts:1-35](), [AGENTS.md:23-30]()

---

## Summary

The agent edit loop (`read → check --json → explain → fix --plan → patch → test`) is a closed, observable cycle where every transition produces structured JSON. The conformance fixture contract (`pass/` must succeed, `fail/` must fail) is a binary invariant—fixtures do not have gradations. The `xfail` marker system handles known incomplete behavior but demands active cleanup: a marker left on a fixed test breaks CI as surely as a broken test would. Every compiler output must satisfy `assertNoC` (`generatedCBytes === 0`, `cBridgeFallback === false`, `legacy !== true`) across all output modes; this is the no-legacy-C invariant that guards Zero's native frontend progress. The pre-1 unstable surface policy accepts breaking changes that advance agent-first goals and explicitly rejects compatibility shims. Finally, the sandbox gate in `conformance/run.mjs:7-10` ensures native test binaries stay in ephemeral compute by default, protecting local machines from experimental artifacts until the developer consciously opts in with `ZERO_NATIVE_TEST_ALLOW_LOCAL=1`.
