Skip to content

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 13 Sep 06:50
· 34 commits to main since this release
440f96d

Added

  • [[render]] on a resource substitutes per-instance values into a file the
    project commits (#10).

    [ports] reached commands as {{ports.x}} and as an env var, which assumes
    the tool takes its port on argv. Most do not: Supabase reads
    supabase/config.toml, Expo reads .env, Compose reads compose.yaml.
    Everyone who hit this wrote the same section-aware config rewriter inside
    their prepare hook.

    [[render]]
    path = "supabase/config.toml"
    replace = [
      { find = 'project_id = "faretable"', with = 'project_id = "faretable-{{branch.slug}}"' },
      { find = "port = 54321",             with = "port = {{ports.api}}" },
    ]

    There is no template file. port = 54321 is the project's working default,
    so a clone without newgit still starts on it; newgit substitutes into the
    committed content and writes the result into one workspace. find is a
    literal string, never a regex, and must match exactly once — which is
    also the drift detector: when the default changes upstream, the bind fails
    naming the file and the string instead of quietly doing nothing. A
    multi-line find disambiguates two sections sharing a value, and count = N
    declares a genuine repeat.

    A render reads committed content — HEAD, or the bound lane rev for a
    tracker-owned path — never the working file, so it is idempotent: undo
    and tracker pull re-render off the binding record and values never
    compound.

    Instance values stay out of everything downstream. Source-owned targets are
    marked --skip-worktree, so they never show in git status and git add -A
    cannot commit them; newgit export and a checkpoint's uncommitted-state
    capture both take them from HEAD; and newgit tracker capture reverses
    the substitution for tracker-owned targets, so a key you add to .env.local
    reaches the shared lane and this instance's port does not.

    On a source-owned path, skip-worktree also means real edits to that file in
    this workspace do not survive it. newgit reports that precisely rather
    than as a caveat
    : a render is a pure function of committed content and the
    binding record, so the expected bytes are recomputable, and newgit compares
    against them at checkpoint and before every re-render — naming the file and
    how many lines a re-render will discard, and saying nothing when the file is
    what the render produced.

    Replacements are simultaneous: every find is located in the committed
    content and the whole batch applies in one pass, so a replacement's output
    is never a match target and reordering the replace array cannot change the
    result. Two rules claiming overlapping text are refused by name.

  • newgit reference prints the definition format — every tracker and resource
    key with its type and default, the checkpoint/restore/ownership tables, and
    which template variables are in scope for which hook
    (#13).

    All of it was documented in newgit-v1-mvp.md, and none of it travelled:
    cargo install newgit leaves a binary and a README on disk, and the
    README's relative link to that file resolved to nothing on crates.io,
    docs.rs, or in the registry directory. Reading the crate source — or running
    strings on the binary to enumerate {{...}} variables — was the only way
    to answer what checkpoint.mode accepts. The reference now ships inside the
    binary, which is the one copy guaranteed to be wherever the definitions are,
    and init, tracker create, and resource add each name it. The README's
    links to the design documents are absolute, and say they live on GitHub.

  • newgit status <instance> --path prints that instance's workspace path and
    nothing else (#12).

    A bootstrap script, a README snippet, or an editor integration wanting the
    path had to parse table output or read .newgit/branches/<name>.toml, which
    makes the store layout someone else's API. Now:
    W=$(newgit status auth-refactor --path). The instance is inferred inside a
    workspace, and warnings stay on stderr so stdout is a path.

  • newgit remove <name> --purge and newgit cleanup --purge-archived release
    the snapshot revs an archived instance's checkpoints were pinning
    (#12).

    Cleanup never prunes a rev a checkpoint points at, which is right for a live
    instance and a dead end for a removed one: undo needs a binding record, so
    those checkpoints are unreachable while their revs are permanent. Purging
    drops the checkpoint log and its refs/newgit/checkpoints/<slug>/* store
    refs, and the same pass reclaims what they held; a purging --dry-run
    reports exactly that. It stays opt-in and never touches a live instance's
    checkpoints. Ordinary cleanup now reports how many of its retained revs
    are held only by archived instances, and remove says how many checkpoints
    it kept.

  • newgit tracker capture <tracker> --from-store seeds a lane from the store
    repo's working tree and sets the lane head, so the first spawn comes up
    with the content (#9).

    A lane starts empty and capture reads from an instance workspace, so
    adopting newgit for a tracker carrying .env files meant spawning an
    instance guaranteed to come up without them, copying the files in, capturing,
    merging, and re-running the resource action that had already failed. The
    content was in the store repo at the same relative paths the whole time.
    That bootstrap is now two commands: tracker track, then
    tracker capture --from-store.

    Declared paths with nothing behind them are reported as warnings rather than
    silently seeding a partial lane; a tracker with nothing at all on disk is an
    error, not an empty lane head. newgit tracker track now suggests
    --from-store when the paths it just added already have content.

  • .newgit/scripts/ and a {{scripts}} template variable, so a resource's
    script lives under the same rule as the definition that calls it
    (#8).

    A resource definition is read from the store, but anything its commands
    shelled out to was read from the workspace, where it is subject to source
    materialization. The two halves of one definition lived under different
    rules and only the TOML half was editable in place, so iterating on a
    prepare meant committing every attempt or copying the script into the
    workspace by hand between runs.

    {{scripts}} resolves to .newgit/scripts/ in the store. Edit a script
    and the next newgit action runs it — nothing to commit, nothing to copy,
    and it works on the first spawn. The directory is control plane and is
    listed in init's "commit these" output; init writes a README.md there
    because Git will not track an empty directory. Project-owned scripts are
    unaffected: they stay in the project tree, are read from the workspace, and
    must still be committed before a spawn that calls them.

Changed

  • The command-snapshot template's [actions.migrate] says what it is: a
    convenience command you invoke with newgit action <resource>.migrate, not
    a lifecycle hook. No stage ever ran it, and sitting beside prepare — which
    spawn and a recompute restore do run — it read like one
    (#12).

Fixed

  • A declared captures name that never appeared in an action's stdout failed
    in total silence: the command exited 0, newgit reported prepare: ok,
    marked the resource ready, and published an empty handle that went
    unnoticed until an API call returned 401
    (#7).

    Each missing capture is now warned for by name, with the log to look in and
    the convention that explains nearly every occurrence — when captures is
    set, stdout belongs to newgit, so everything else the command prints should
    go to stderr. Warnings appear at spawn, on newgit action, and during a
    recompute restore. A missing name is still not an error: a resource may
    legitimately publish a handle only on some runs. The external template's
    comments now state the stdout convention too.

  • An undo where a resource restore failed reported Restored on its first
    line and FAILED on its fourth, describing one operation two ways
    (#11). A restore command
    is not transactional — one that rebuilds a schema and then fails to load
    the rows leaves its resource in neither the pre-undo state nor the
    checkpoint state — so the summary no longer implies the instance is in a
    known state:

    Undo of `smoke` to ckpt_001 ("before agent") INCOMPLETE: 0 of 1 resources restored
      `db` may be in a partial state — a failed restore command is not rolled back
    

    newgit undo now exits non-zero when the undo was incomplete.

  • A failed undo left a pre-undo checkpoint indistinguishable from one a human
    named, so three failed attempts left three of them, each pinning its
    tracker revs. The safety checkpoint now records undo_completed once the
    undo it preceded finishes, and newgit checkpoints shows those entries as
    failed-undo rather than before-undo — they are not redo points. The
    record is annotated rather than deleted: newgit cannot know at save time
    whether the undo will succeed, and discarding the only record of a state is
    what checkpoints exist to prevent. Releasing the revs those entries pin is
    a cleanup concern, tracked in
    #12.

  • A resource whose depends_on named something that did not exist yet made
    every newgit command fail, including tracker create and tracker track
    — the commands that create the missing name. The only way out was to hand-
    edit the depends_on line, run the command, and put the line back
    (#6).

    The dependency graph is now resolved leniently at load and its problems
    reported rather than raised. Commands that act on the graph — spawn,
    run, action, checkpoint, undo — still refuse, with the same error
    naming the missing dependency. Commands that build it (tracker create,
    tracker track, resource add) and commands that inspect it (status,
    tracker list, resource list) now run and print the problem as a warning.
    remove stays reachable too, so teardown never depends on the graph holding
    together. Dependency cycles are handled the same way.

Documentation

  • Say what a package manager costs under newgit. Every instance installs its
    own dependencies — that independence is the point, and a shared installed
    tree across branches with different lockfiles is the thing that would be
    wrong — but the size of that cost is the package manager's call, and
    nothing said so. pnpm hardlinks from one content-addressed store, so
    instance ten adds directory entries; npm ci expands a full copy per
    instance, which on a monorepo is gigabytes each. Documented in the README's
    adoption section, next to the install resource in newgit-v1-mvp.md, and
    in the pnpm template's own comments, since that is what someone reads
    when they hand-edit the definition.