Stone forge crossroads at twilight with two diverging glowing amber paths, GitHub upstream vs sibling clone
Appendix G

Update Source Modes

Where pforge update pulls template bytes from, and why the default changed in v2.56.0.

The Problem

Before v2.56.0, pforge update had a single hard-coded source-selection rule: use the sibling clone at ../plan-forge if one existed, otherwise fail and ask for --from-github. This was optimized for contributors on their primary machine, the sibling is always on master, which is always freshly built, so contributors dogfood every change.

The trouble showed up on secondary machines: users who happened to have cloned the Plan Forge repo earlier (say, to browse the source) would later run pforge update on an unrelated project and get surprise -dev bytes from a stale master checkout. The second PC behaved differently from the first, for reasons that weren't obvious.

The Three Modes

.forge.json now accepts an updateSource key with three values. The default, auto, picks the right thing for most people; the other two give you explicit control.

ModeBehaviorWhen to use
auto (default) Picks the newer of your sibling clone and the latest GitHub tag. If the sibling is on a -dev build, GitHub wins. Users on any machine. Teams. Anyone who isn't actively contributing patches back to Plan Forge.
github-tags Always downloads the latest tagged release from GitHub. Ignores any sibling clone even if present. Teams that want reproducible, audited updates. CI pipelines. Pinned-dependency shops.
local-sibling Always uses the sibling clone at ../plan-forge. Errors if one is missing. Contributors working on Plan Forge itself. You run git pull in the sibling to pick up changes.
Auto mode in detail. It calls the GitHub Releases API (cached 24h in .forge/update-check.json) to resolve the latest tag, reads the sibling's VERSION file, and compares the two with semver precedence, any -dev pre-release loses to a clean tag. If the sibling wins or there's no network, it uses the sibling. If GitHub wins or there's no sibling, it uses the tag.

How to Change Your Mode

Three ways, all equivalent, they all write .forge.json.

1. CLI

Terminal
# Read current value
pforge config get update-source

# Set it
pforge config set update-source github-tags
pforge config set update-source local-sibling
pforge config set update-source auto

# List all settable keys
pforge config list

2. Dashboard

Open the dashboard (localhost:3100/dashboard), switch to the Config tab, find the Update Source select. Your choice saves immediately, no Save button required. The hint text below the dropdown reminds you what each mode does.

3. Hand-edit .forge.json

.forge.json
{
  "preset": "dotnet",
  "templateVersion": "2.56.0",
  "updateSource": "auto"
}

FAQ

Will auto ever install -dev bytes over my clean release?

No. The -dev refusal guard from v2.53.2 is still in place: if the selected source is a -dev build and your current install is clean, the update aborts with a helpful message. auto mode short-circuits this earlier by preferring the tagged release. If you explicitly set local-sibling and the sibling is -dev, you'll hit the refusal unless you pass --allow-dev.

What happens offline in auto mode?

If the GitHub tag lookup fails (timeout, no network, rate-limit), auto falls back to the sibling if one exists. If there's no sibling and no network, you'll get the same error you would have gotten pre-v2.56.0, run --from-github when you're back online, or set a sibling clone.

pforge self-update — does this affect it?

No. self-update is a separate command that always pulls from GitHub releases (it's designed to heal a corrupted install). updateSource only controls pforge update.

Should CI pipelines set a mode?

Yes, set updateSource to github-tags in your CI's .forge.json. This guarantees every CI run pulls from a specific tagged release and ignores whatever happens to be checked out in adjacent directories.

Do I need to migrate my existing .forge.json?

No. Projects with no updateSource key default to auto, which is the recommended behavior anyway. The change is additive.

See Also