# Versioning and Release Tarball — Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Wire a single canonical version through the zyginit suite (zyginit + zygctl + sysv-wrapper), give every binary a `--version` flag, and add scripts to bump the version and produce a source release tarball for vendoring into Hammerhead.

**Architecture:** Root `reef.toml` `[package].version` is canonical. `scripts/bump-version.sh` rewrites it plus four derived files (`tools/zygctl/reef.toml`, `src/version.reef`, `tools/zygctl/src/version.reef`, `tools/sysv-wrapper/version.h`) in lock-step. The two Reef binaries import a `version` module that exposes `pub fn VERSION(): string`. The C wrapper includes `version.h` and prints `<argv0-basename> <ZYGINIT_VERSION>`. `scripts/make-release.sh` produces `releases/zyginit-<version>-source.tar.xz` plus a `.sha256` companion, modeled on Coral's `make-release.sh`.

**Tech Stack:** Reef 0.4.0+ (compiled via `reefc`), C (POSIX), POSIX shell (`/bin/sh`), Mercurial (`hg`) for VCS, GNU `tar` with `--transform`/`--exclude`, `sha256sum`.

**Spec:** `docs/superpowers/specs/2026-05-05-versioning-and-release-tarball-design.md`

**Working version:** Throughout this plan, examples use `0.1.0` as the **current** version (matching today's `reef.toml`). Bump examples use `0.2.0` as the **next** version. The actual version in source after these tasks is unchanged at `0.1.0` — bumping to `0.2.0` is a separate, post-implementation step the maintainer runs.

---

## File Structure

**New files:**
- `src/version.reef` — generated; defines `module version` with `pub fn VERSION(): string`
- `tools/zygctl/src/version.reef` — generated; same shape as above
- `tools/sysv-wrapper/version.h` — generated; defines `ZYGINIT_VERSION` macro
- `scripts/bump-version.sh` — rewrites all five version locations
- `scripts/make-release.sh` — creates source tarball + sha256

**Modified files:**
- `src/main.reef` — drop inline `VERSION()` (lines 67-69); `import version`; add early `--version` flag handling
- `src/socket.reef` — `import version`; replace literal `"zyginit 0.1.0\n"` (line 248)
- `tools/zygctl/src/main.reef` — `import version`; replace literal `"zygctl 0.1.0"` (line 55)
- `tools/sysv-wrapper/wrapper.c` — `#include "version.h"`; add `--version`/`-V` handling
- `tools/sysv-wrapper/Makefile` — add `version.h` to `wrapper.o` dependencies
- `tests/integration/run_tests.sh` — add `--version` assertions for both binaries

---

## Task 1: Add `version` module to zyginit core

**Files:**
- Create: `src/version.reef`
- Modify: `src/main.reef:35` (add import), `src/main.reef:67-69` (drop inline `VERSION()`), `src/main.reef:835-849` (add `--version` flag handling)
- Modify: `src/socket.reef:23` (add import after existing imports), `src/socket.reef:248` (replace literal)

- [ ] **Step 1: Create `src/version.reef` with the canonical version constant**

```reef
/******************************************************************************
                __               ____                __
               / /   ___  ____ _/ __/_____________ _/ /__
              / /   / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \
             / /___/  __/ /_/ / __(__  ) /__/ /_/ / /  __/
            /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/

    (C)opyright 2025-2026, Leafscale, LLC -  https://www.leafscale.com

    Project: zyginit
   Filename: version.reef
    Authors: Chris Tusa <chris.tusa@leafscale.com>
    License: <see LICENSE file included with this source code>
Description: Generated version constant. Do not edit by hand — regenerated
             by scripts/bump-version.sh.

******************************************************************************/

module version

pub fn VERSION(): string
    return "0.1.0"
end VERSION

end module
```

- [ ] **Step 2: Wire `src/main.reef` to use the module and handle `--version`**

Edit `src/main.reef`. Add `import version` after line 36 (after the `import core.str` line):

```reef
import core.str
import version
```

Delete lines 67-69 (the existing inline `VERSION()` definition):

```reef
fn VERSION(): string
    return "0.1.0"
end VERSION
```

Replace the call at line 843 (`println("zyginit v" + VERSION() + " starting")`) with:

```reef
println("zyginit v" + version.VERSION() + " starting")
```

Add `--version` flag handling at the top of `main()`. Find the current start of `proc main()` (around line 835) and insert this block as the **very first** statements in `main()`, before the `is_pid_1()` check:

```reef
proc main()
    // --version / -V short-circuits before any PID-1 setup. Manual invocations
    // (zyginit --version on a shell) have stdio fds; the kernel never passes
    // --version when exec'ing init as PID 1 (it passes -s, -m, etc. instead).
    if args.has_flag("version")
        println("zyginit " + version.VERSION())
        return
    end if

    // PID-1 fd setup MUST run before any println — when the kernel exec()s
    // init, stdin/stdout/stderr are not preopened. See setup_pid1_console
    // for the full explanation.
    if is_pid_1()
        let _ = setup_pid1_console()
    end if
    ...
```

- [ ] **Step 3: Wire `src/socket.reef` to use the module**

Edit `src/socket.reef`. Add `import version` after line 31 (after the existing `import core.str`):

```reef
import core.str
import version
```

Replace line 248:

```reef
        return "zyginit 0.1.0\n"
```

With:

```reef
        return "zyginit " + version.VERSION() + "\n"
```

- [ ] **Step 4: Build zyginit and confirm the new module compiles**

Run:
```
cd /home/ctusa/repos/zygaena-project/zyginit
clang -c src/helpers.c -o build/helpers.o
clang -c src/contract_linux_stubs.c -o build/contract_linux_stubs.o
reefc build --obj build/helpers.o --obj build/contract_linux_stubs.o
```

Expected: build succeeds, produces `build/zyginit`. If reefc reports `undefined symbol: VERSION`, search for stray callers of the old `VERSION()` (without the `version.` prefix); the only callers in-tree are the two we just edited.

- [ ] **Step 5: Verify `--version` works**

Run:
```
./build/zyginit --version
```

Expected output, exit 0:
```
zyginit 0.1.0
```

If the binary prints "zyginit v0.1.0 starting" instead of returning, the early-exit block in Step 2 wasn't placed before the PID-1 fd setup; move it to the top of `main()`.

- [ ] **Step 6: Verify socket version path**

There's no easy unit test for the socket dispatcher; instead, rebuild was the type check. Confirm with `grep`:

```
grep -n 'version.VERSION' src/socket.reef
```

Expected: one match on the line we edited (around line 248). The grep failing to match means the edit didn't land.

- [ ] **Step 7: Commit**

```
hg add src/version.reef
hg ci -m 'version: introduce version module; remove inline VERSION() and socket literal'
```

---

## Task 2: Add `version` module to zygctl

**Files:**
- Create: `tools/zygctl/src/version.reef`
- Modify: `tools/zygctl/src/main.reef:27` (add import), `tools/zygctl/src/main.reef:55` (replace literal)

- [ ] **Step 1: Create `tools/zygctl/src/version.reef`**

```reef
/******************************************************************************
                __               ____                __
               / /   ___  ____ _/ __/_____________ _/ /__
              / /   / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \
             / /___/  __/ /_/ / __(__  ) /__/ /_/ / /  __/
            /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/

    (C)opyright 2025-2026, Leafscale, LLC -  https://www.leafscale.com

    Project: zygctl
   Filename: version.reef
    Authors: Chris Tusa <chris.tusa@leafscale.com>
    License: <see LICENSE file included with this source code>
Description: Generated version constant. Do not edit by hand — regenerated
             by scripts/bump-version.sh.

******************************************************************************/

module version

pub fn VERSION(): string
    return "0.1.0"
end VERSION

end module
```

Note `Project: zygctl` — distinct from the zyginit-core copy.

- [ ] **Step 2: Wire `tools/zygctl/src/main.reef` to use it**

Edit `tools/zygctl/src/main.reef`. Add `import version` after line 26 (after `import core.str`):

```reef
import core.str
import version
```

Replace line 55:

```reef
        println("zygctl 0.1.0")
```

With:

```reef
        println("zygctl " + version.VERSION())
```

- [ ] **Step 3: Build zygctl**

Run:
```
cd /home/ctusa/repos/zygaena-project/zyginit/tools/zygctl
clang -c src/symlink_wrapper.c -o build/symlink_wrapper.o
reefc build --obj build/symlink_wrapper.o
```

Expected: build succeeds, produces `tools/zygctl/build/zygctl`.

- [ ] **Step 4: Verify `--version` works**

Run from `tools/zygctl/`:
```
./build/zygctl --version
```

Expected output, exit 0:
```
zygctl 0.1.0
```

Also confirm the bare `version` subcommand still works:
```
./build/zygctl version
```

Expected (same output):
```
zygctl 0.1.0
```

- [ ] **Step 5: Commit**

```
hg add tools/zygctl/src/version.reef
hg ci -m 'zygctl: use version module instead of literal'
```

---

## Task 3: Add `version.h` and `--version` to sysv-wrapper

**Files:**
- Create: `tools/sysv-wrapper/version.h`
- Modify: `tools/sysv-wrapper/wrapper.c` (add `#include`, add flag handling at top of `main()`)
- Modify: `tools/sysv-wrapper/Makefile` (depend on `version.h`)

- [ ] **Step 1: Create `tools/sysv-wrapper/version.h`**

```c
/******************************************************************************
                __               ____                __
               / /   ___  ____ _/ __/_____________ _/ /__
              / /   / _ \/ __ `/ /_/ ___/ ___/ __ `/ / _ \
             / /___/  __/ /_/ / __(__  ) /__/ /_/ / /  __/
            /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/

    (C)opyright 2025-2026, Leafscale, LLC -  https://www.leafscale.com

    Project: zyginit
   Filename: version.h
    Authors: Chris Tusa <chris.tusa@leafscale.com>
    License: <see LICENSE file included with this source code>
Description: Generated version constant. Do not edit by hand — regenerated
             by scripts/bump-version.sh.

******************************************************************************/

#ifndef ZYGINIT_VERSION_H
#define ZYGINIT_VERSION_H
#define ZYGINIT_VERSION "0.1.0"
#endif
```

- [ ] **Step 2: Edit `tools/sysv-wrapper/wrapper.c` to add `--version` handling**

Add the include after line 30 (after `#include <errno.h>`):

```c
#include <errno.h>
#include "version.h"
```

In `main()`, add a `--version` short-circuit **before** the `argv0_copy = strdup(argv[0])` line (currently line 84). The check has to come after computing `me` (the basename), so the printout can use the personality name.

The cleanest restructure: keep the existing basename computation, then add the flag check immediately after `me = basename(argv0_copy);`. Replace the block at lines 80-101 with:

```c
        const char *me;
        char *argv0_copy;

        /* basename(3) on illumos modifies its argument, so duplicate first */
        argv0_copy = strdup(argv[0]);
        if (argv0_copy == NULL) {
                (void) fprintf(stderr, "out of memory\n");
                return (1);
        }
        me = basename(argv0_copy);

        if (argc >= 2 &&
            (strcmp(argv[1], "--version") == 0 ||
             strcmp(argv[1], "-V") == 0)) {
                (void) printf("%s %s\n", me, ZYGINIT_VERSION);
                free(argv0_copy);
                return (0);
        }

        if (strcmp(me, "init") == 0 || strcmp(me, "telinit") == 0)
                return (init_dispatch(argc, argv));
        if (strcmp(me, "halt") == 0)
                return (run_zygctl("halt"));
        if (strcmp(me, "reboot") == 0)
                return (run_zygctl("reboot"));
        if (strcmp(me, "poweroff") == 0)
                return (run_zygctl("poweroff"));

        (void) fprintf(stderr, "sysv-wrapper: unknown invocation '%s'\n", me);
        return (1);
```

(Existing run_zygctl paths leak `argv0_copy` already — preserving that to minimize unrelated changes.)

- [ ] **Step 3: Edit `tools/sysv-wrapper/Makefile` to depend on `version.h`**

Replace the build rule:

```make
sysv-wrapper: wrapper.c
	$(CC) $(CFLAGS) -o $@ $<
```

With:

```make
sysv-wrapper: wrapper.c version.h
	$(CC) $(CFLAGS) -o $@ wrapper.c
```

(Switched `$<` to explicit `wrapper.c` because `$<` would only refer to `wrapper.c`, but adding a second prerequisite makes the explicit form clearer.)

- [ ] **Step 4: Build sysv-wrapper**

Run from `tools/sysv-wrapper/`:
```
make clean && make
```

Expected: produces `./sysv-wrapper`, no warnings.

- [ ] **Step 5: Verify `--version` for each personality**

Run:
```
./sysv-wrapper --version
ln -sf sysv-wrapper /tmp/halt && /tmp/halt --version
ln -sf sysv-wrapper /tmp/reboot && /tmp/reboot --version
ln -sf sysv-wrapper /tmp/poweroff && /tmp/poweroff --version
ln -sf sysv-wrapper /tmp/telinit && /tmp/telinit --version
rm -f /tmp/halt /tmp/reboot /tmp/poweroff /tmp/telinit
```

Expected (one line per invocation, exit 0 each):
```
sysv-wrapper 0.1.0
halt 0.1.0
reboot 0.1.0
poweroff 0.1.0
telinit 0.1.0
```

- [ ] **Step 6: Commit**

```
hg add tools/sysv-wrapper/version.h
hg ci -m 'sysv-wrapper: add --version (and -V) flag'
```

---

## Task 4: Write `scripts/bump-version.sh`

**Files:**
- Create: `scripts/bump-version.sh`
- Test (one-shot, not committed): `/tmp/bump-test.sh`

- [ ] **Step 1: Write a one-shot test harness for the bump script**

This isn't a permanent test — just a sanity check we run before committing the script. Create `/tmp/bump-test.sh`:

```sh
#!/bin/sh
# Sanity test for scripts/bump-version.sh.
# Snapshots all five version locations, runs the script, verifies all
# five rewrite to the new version, then restores from snapshot.
set -e

cd /home/ctusa/repos/zygaena-project/zyginit

# Snapshot
cp reef.toml /tmp/bump.snap.root.toml
cp tools/zygctl/reef.toml /tmp/bump.snap.zygctl.toml
cp src/version.reef /tmp/bump.snap.zyginit.version.reef
cp tools/zygctl/src/version.reef /tmp/bump.snap.zygctl.version.reef
cp tools/sysv-wrapper/version.h /tmp/bump.snap.sysv.h

# Run
./scripts/bump-version.sh 9.9.9

# Verify
fail=0
grep -q '^version = "9.9.9"' reef.toml || { echo FAIL: root reef.toml; fail=1; }
grep -q '^version = "9.9.9"' tools/zygctl/reef.toml || { echo FAIL: zygctl reef.toml; fail=1; }
grep -q 'return "9.9.9"' src/version.reef || { echo FAIL: src/version.reef; fail=1; }
grep -q 'return "9.9.9"' tools/zygctl/src/version.reef || { echo FAIL: zygctl version.reef; fail=1; }
grep -q '#define ZYGINIT_VERSION "9.9.9"' tools/sysv-wrapper/version.h || { echo FAIL: sysv version.h; fail=1; }

# Restore
cp /tmp/bump.snap.root.toml reef.toml
cp /tmp/bump.snap.zygctl.toml tools/zygctl/reef.toml
cp /tmp/bump.snap.zyginit.version.reef src/version.reef
cp /tmp/bump.snap.zygctl.version.reef tools/zygctl/src/version.reef
cp /tmp/bump.snap.sysv.h tools/sysv-wrapper/version.h
rm -f /tmp/bump.snap.*

if [ $fail -eq 0 ]; then
    echo "PASS: bump-version rewrote all five files"
else
    echo "FAIL"
    exit 1
fi
```

`chmod +x /tmp/bump-test.sh`. Don't run it yet — the script doesn't exist.

- [ ] **Step 2: Run the test to confirm it fails**

```
/tmp/bump-test.sh
```

Expected: error message that `./scripts/bump-version.sh` is not found.

- [ ] **Step 3: Write `scripts/bump-version.sh`**

Create `scripts/bump-version.sh` with mode 0755:

```sh
#!/bin/sh
#
# Bump the canonical zyginit version.
#
# Usage: ./scripts/bump-version.sh <new-version>
#
# Rewrites in lock-step:
#   - reef.toml                          (canonical)
#   - tools/zygctl/reef.toml
#   - src/version.reef                   (regenerated)
#   - tools/zygctl/src/version.reef      (regenerated)
#   - tools/sysv-wrapper/version.h       (regenerated)
#
# Does NOT auto-commit. Review with `hg diff` and commit by hand.
#

set -e

if [ $# -ne 1 ]; then
    echo "usage: $0 <new-version>" >&2
    exit 1
fi

NEW="$1"

# Validate semver shape (no pre-release suffix yet — see plan §10).
echo "$NEW" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || {
    echo "error: '$NEW' is not in MAJOR.MINOR.PATCH form" >&2
    exit 1
}

SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
ROOT=$(dirname "$SCRIPT_DIR")
cd "$ROOT"

CURRENT=$(grep '^version' reef.toml | head -1 | sed 's/.*"\([^"]*\)".*/\1/')
if [ -z "$CURRENT" ]; then
    echo "error: could not read current version from reef.toml" >&2
    exit 1
fi

if [ "$CURRENT" = "$NEW" ]; then
    echo "error: version is already $NEW" >&2
    exit 1
fi

echo "zyginit: bumping $CURRENT -> $NEW"

# 1. Root reef.toml — sed in place. Match only the first 'version = "..."'
#    in the [package] table (top of file).
echo "  rewriting reef.toml"
sed -i "0,/^version = \"[^\"]*\"/s//version = \"$NEW\"/" reef.toml

# 2. tools/zygctl/reef.toml
echo "  rewriting tools/zygctl/reef.toml"
sed -i "0,/^version = \"[^\"]*\"/s//version = \"$NEW\"/" tools/zygctl/reef.toml

# 3. src/version.reef — full regenerate.
echo "  regenerating src/version.reef"
gen_reef_module zyginit src/version.reef "$NEW"

# 4. tools/zygctl/src/version.reef — full regenerate.
echo "  regenerating tools/zygctl/src/version.reef"
gen_reef_module zygctl tools/zygctl/src/version.reef "$NEW"

# 5. tools/sysv-wrapper/version.h — full regenerate.
echo "  regenerating tools/sysv-wrapper/version.h"
gen_c_header tools/sysv-wrapper/version.h "$NEW"

echo "done. review with 'hg diff' and commit:"
echo "  hg ci -m 'Bump version to $NEW'"
```

Then add the helper functions ABOVE the `set -e` line (so they're defined before use). Insert this block right after the comment header, before `set -e`:

```sh
# ----------------------------------------------------------------------------
# Helpers for regenerating templated files.
# ----------------------------------------------------------------------------

gen_reef_module() {
    project="$1"
    path="$2"
    version="$3"
    cat > "$path" <<EOF
/******************************************************************************
                __               ____                __
               / /   ___  ____ _/ __/_____________ _/ /__
              / /   / _ \/ __ \`/ /_/ ___/ ___/ __ \`/ / _ \\
             / /___/  __/ /_/ / __(__  ) /__/ /_/ / /  __/
            /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/

    (C)opyright 2025-2026, Leafscale, LLC -  https://www.leafscale.com

    Project: $project
   Filename: version.reef
    Authors: Chris Tusa <chris.tusa@leafscale.com>
    License: <see LICENSE file included with this source code>
Description: Generated version constant. Do not edit by hand — regenerated
             by scripts/bump-version.sh.

******************************************************************************/

module version

pub fn VERSION(): string
    return "$version"
end VERSION

end module
EOF
}

gen_c_header() {
    path="$1"
    version="$2"
    cat > "$path" <<EOF
/******************************************************************************
                __               ____                __
               / /   ___  ____ _/ __/_____________ _/ /__
              / /   / _ \/ __ \`/ /_/ ___/ ___/ __ \`/ / _ \\
             / /___/  __/ /_/ / __(__  ) /__/ /_/ / /  __/
            /_____/\___/\__,_/_/ /____/\___/\__,_/_/\___/

    (C)opyright 2025-2026, Leafscale, LLC -  https://www.leafscale.com

    Project: zyginit
   Filename: version.h
    Authors: Chris Tusa <chris.tusa@leafscale.com>
    License: <see LICENSE file included with this source code>
Description: Generated version constant. Do not edit by hand — regenerated
             by scripts/bump-version.sh.

******************************************************************************/

#ifndef ZYGINIT_VERSION_H
#define ZYGINIT_VERSION_H
#define ZYGINIT_VERSION "$version"
#endif
EOF
}
```

`chmod +x scripts/bump-version.sh`.

- [ ] **Step 4: Run the test**

```
/tmp/bump-test.sh
```

Expected: `PASS: bump-version rewrote all five files`. The script also restored the snapshot, so `hg status` should show no changes.

If the test reports a `FAIL: <file>` line, inspect that file by hand: the `sed` substitution may not have matched (e.g., if reef.toml's `version` line has a different indentation), or the `cat <<EOF` heredoc had a quoting issue (backticks in the ASCII art are escaped as `\``).

- [ ] **Step 5: Negative tests**

```
./scripts/bump-version.sh                # missing arg
./scripts/bump-version.sh 1.2            # bad shape
./scripts/bump-version.sh 0.1.0          # same as current
```

Expected: each prints an error to stderr and exits non-zero. Run `hg status` after each — should show no changes.

- [ ] **Step 6: Commit**

```
hg add scripts/bump-version.sh
hg ci -m 'scripts: add bump-version.sh for in-lockstep version updates'
```

Delete the throwaway test:
```
rm -f /tmp/bump-test.sh
```

---

## Task 5: Write `scripts/make-release.sh`

**Files:**
- Create: `scripts/make-release.sh`
- Test (one-shot, not committed): inline at end of step 4

- [ ] **Step 1: Write `scripts/make-release.sh` (modeled on Coral's)**

Create `scripts/make-release.sh` with mode 0755:

```sh
#!/bin/sh
#
# Create a source release tarball for the zyginit suite.
#
# Usage: ./scripts/make-release.sh [version-override]
#
# Reads the canonical version from reef.toml unless an override is given.
# Produces:
#   releases/zyginit-<version>-source.tar.xz
#   releases/zyginit-<version>-source.tar.xz.sha256
#

set -e

SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
ROOT=$(dirname "$SCRIPT_DIR")
cd "$ROOT"

if [ -n "$1" ]; then
    VERSION="$1"
else
    VERSION=$(grep '^version' reef.toml | head -1 | sed 's/.*"\([^"]*\)".*/\1/')
fi

if [ -z "$VERSION" ]; then
    echo "error: could not determine version" >&2
    exit 1
fi

NAME="zyginit-${VERSION}-source"
RELEASES="releases"
TARBALL="${RELEASES}/${NAME}.tar.xz"

echo "Creating release: $NAME"
mkdir -p "$RELEASES"

tar \
    --exclude='build' \
    --exclude='build/*' \
    --exclude='tools/*/build' \
    --exclude='tools/*/build/*' \
    --exclude='releases' \
    --exclude='.hg' \
    --exclude='.hgignore' \
    --exclude='.hgtags' \
    --exclude='resume' \
    --exclude='ss' \
    --exclude='stub' \
    --exclude='*.o' \
    --exclude='*.a' \
    --exclude='*.swp' \
    --transform "s,^,${NAME}/," \
    -cJf "$TARBALL" \
    reef.toml \
    README.md \
    ROADMAP.md \
    CLAUDE.md \
    SRCHEADER.txt \
    src \
    docs \
    tests \
    services \
    tools \
    utils \
    scripts

echo "Created: $TARBALL"
ls -la "$TARBALL"

sha256sum "$TARBALL" > "${TARBALL}.sha256"
echo "Checksum: ${TARBALL}.sha256"
cat "${TARBALL}.sha256"
```

`chmod +x scripts/make-release.sh`.

- [ ] **Step 2: Run it**

```
./scripts/make-release.sh
```

Expected stdout (with `0.1.0` from current reef.toml):
```
Creating release: zyginit-0.1.0-source
Created: releases/zyginit-0.1.0-source.tar.xz
-rw-r--r-- 1 ctusa ctusa <size> <date> releases/zyginit-0.1.0-source.tar.xz
Checksum: releases/zyginit-0.1.0-source.tar.xz.sha256
<hash>  releases/zyginit-0.1.0-source.tar.xz
```

- [ ] **Step 3: Verify tarball contents**

```
tar tJf releases/zyginit-0.1.0-source.tar.xz | head -30
tar tJf releases/zyginit-0.1.0-source.tar.xz | grep -c '^zyginit-0.1.0-source/'
```

The first command shows the top of the listing — every entry MUST start with `zyginit-0.1.0-source/`. The second prints the total entry count.

Also confirm the excludes worked:

```
tar tJf releases/zyginit-0.1.0-source.tar.xz | grep -E '\.hg|/build/|/releases/|/resume/|/ss/|/stub/|\.o$|\.a$' && echo FAIL || echo OK
```

Expected: `OK`. Any line printed by `grep` indicates an exclude that didn't take.

- [ ] **Step 4: Verify the tarball is buildable as-is**

```
mkdir -p /tmp/release-check
tar xJf releases/zyginit-0.1.0-source.tar.xz -C /tmp/release-check
cd /tmp/release-check/zyginit-0.1.0-source
clang -c src/helpers.c -o build/helpers.o 2>/dev/null || \
    (mkdir -p build && clang -c src/helpers.c -o build/helpers.o)
clang -c src/contract_linux_stubs.c -o build/contract_linux_stubs.o
reefc build --obj build/helpers.o --obj build/contract_linux_stubs.o
./build/zyginit --version
cd /home/ctusa/repos/zygaena-project/zyginit
rm -rf /tmp/release-check
```

Expected: `zyginit 0.1.0`, exit 0. This is the smoke test that the tarball is self-sufficient.

- [ ] **Step 5: Verify checksum file is well-formed**

```
sha256sum -c releases/zyginit-0.1.0-source.tar.xz.sha256
```

Expected: `releases/zyginit-0.1.0-source.tar.xz: OK`.

- [ ] **Step 6: Add `releases/` to `.hgignore`**

Check whether `releases/` is already ignored:

```
hg status releases/
```

If `hg status` lists the new tarball as untracked (`?`), add it to `.hgignore`. The existing `.hgignore` uses `syntax: glob` (already declared at the top), so append the line:

```
releases/
```

Verify by re-running `hg status releases/` — it should now show no output (the directory is ignored).

- [ ] **Step 7: Commit (script and `.hgignore` only — not the tarball)**

```
hg status
hg add scripts/make-release.sh
# Only `hg add .hgignore` if it's not already tracked.
hg ci -m 'scripts: add make-release.sh for source tarball generation'
```

`hg status` after commit should show only the untracked `releases/` directory if `.hgignore` worked, or nothing if `releases/` is empty/absent.

---

## Task 6: Add `--version` integration tests

**Files:**
- Modify: `tests/integration/run_tests.sh` (add new section)

- [ ] **Step 1: Add a `version` test section near the bottom of `run_tests.sh`**

Find the last `section "..."` block in `tests/integration/run_tests.sh`. Add a new section just before the final summary print. Search for a stable anchor like `printf "\n=== Summary ===\n"` or wherever the final `PASS=`/`FAIL=` rollup happens, and add this block immediately before it:

```sh
# ============================================================================
section "Version reporting"
# ============================================================================

# Both binaries should respond to --version (and `version` subcommand for zygctl)
# with a single line "<binary> <version>" matching reef.toml.

EXPECTED_VERSION=$(grep '^version' "$PROJECT_DIR/reef.toml" | head -1 | sed 's/.*"\([^"]*\)".*/\1/')

OUT="$("$ZYGINIT" --version 2>&1)"
assert_equals "$OUT" "zyginit $EXPECTED_VERSION" "zyginit --version output"

OUT="$(zygctl --version)"
assert_equals "$OUT" "zygctl $EXPECTED_VERSION" "zygctl --version output"

OUT="$(zygctl version)"
assert_equals "$OUT" "zygctl $EXPECTED_VERSION" "zygctl version subcommand output"
```

(The script already has `EXPECTED_VERSION` as a fresh variable name; if there's a collision, rename to `_VERSION`. The `zygctl()` shell function on line 109 already wraps the binary call, so `zygctl --version` here uses that wrapper.)

- [ ] **Step 2: Run the integration tests**

```
./tests/integration/run_tests.sh
```

Expected: total test count increased by 3, all passing. The tail of the output should include:

```
=== Version reporting ===
  PASS: zyginit --version output
  PASS: zygctl --version output
  PASS: zygctl version subcommand output
```

If `zyginit --version` fails by printing the startup banner instead of returning, Task 1 Step 2's flag short-circuit was placed in the wrong spot — re-check that it's the first thing in `main()`.

- [ ] **Step 3: Commit**

```
hg ci -m 'tests/integration: assert --version output for zyginit and zygctl'
```

---

## Task 7: End-to-end bump rehearsal (no commit)

This task verifies the whole pipeline works together: bump → build → release → install-check. It's a rehearsal — we don't commit the bumped version. Use `9.9.9` so it's obviously a test value.

**Files:** No persistent changes.

- [ ] **Step 1: Snapshot current state**

```
hg status   # should be clean before starting
```

If not clean, stop and resolve before proceeding.

- [ ] **Step 2: Bump to a sentinel version**

```
./scripts/bump-version.sh 9.9.9
hg diff --stat
```

Expected diff: 5 files changed (`reef.toml`, `tools/zygctl/reef.toml`, `src/version.reef`, `tools/zygctl/src/version.reef`, `tools/sysv-wrapper/version.h`), each with a small change.

- [ ] **Step 3: Build all three binaries with the new version**

```
clang -c src/helpers.c -o build/helpers.o
clang -c src/contract_linux_stubs.c -o build/contract_linux_stubs.o
reefc build --obj build/helpers.o --obj build/contract_linux_stubs.o
(cd tools/zygctl && clang -c src/symlink_wrapper.c -o build/symlink_wrapper.o && reefc build --obj build/symlink_wrapper.o)
(cd tools/sysv-wrapper && make clean && make)
```

Expected: all three succeed.

- [ ] **Step 4: Verify all three report 9.9.9**

```
./build/zyginit --version
./tools/zygctl/build/zygctl --version
./tools/sysv-wrapper/sysv-wrapper --version
```

Expected:
```
zyginit 9.9.9
zygctl 9.9.9
sysv-wrapper 9.9.9
```

- [ ] **Step 5: Make a release tarball at 9.9.9**

```
./scripts/make-release.sh
ls -la releases/zyginit-9.9.9-source.tar.xz releases/zyginit-9.9.9-source.tar.xz.sha256
```

Expected: both files present. Filename includes `9.9.9`.

- [ ] **Step 6: Roll back the bump**

```
hg revert reef.toml tools/zygctl/reef.toml src/version.reef tools/zygctl/src/version.reef tools/sysv-wrapper/version.h
rm -f releases/zyginit-9.9.9-source.tar.xz releases/zyginit-9.9.9-source.tar.xz.sha256
hg status
```

Expected: `hg status` clean, releases dir contains no `9.9.9` artifacts.

- [ ] **Step 7: No commit**

This is a rehearsal. The actual first version bump is left to the maintainer to perform when they're ready to cut a real release.

---

## Final Verification Checklist

- [ ] `./build/zyginit --version` prints `zyginit 0.1.0`
- [ ] `./tools/zygctl/build/zygctl --version` prints `zygctl 0.1.0`
- [ ] `./tools/zygctl/build/zygctl version` prints `zygctl 0.1.0`
- [ ] `./tools/sysv-wrapper/sysv-wrapper --version` prints `sysv-wrapper 0.1.0`
- [ ] `halt --version` (via symlink) prints `halt 0.1.0`
- [ ] `./scripts/bump-version.sh 9.9.9` rewrites all five files; rehearsal then reverted
- [ ] `./scripts/make-release.sh` produces `releases/zyginit-0.1.0-source.tar.xz` + `.sha256`
- [ ] Extracted tarball builds cleanly with `reefc build` and yields a working `zyginit --version`
- [ ] `tests/integration/run_tests.sh` passes with three new assertions
- [ ] `hg log --limit 6` shows six commits in the order: version module, zygctl, sysv-wrapper, bump-version, make-release, integration tests
