Skip to content

v0.17 β†’ v0.18: opt-in ForcedUpdate policy

What changed

The root command's background self-update check is now governed by a three-state update policy (disabled / prompt / enabled) instead of always prompting. The framework default is now disabled: a tool built on GTB logs that an update is available and continues, rather than prompting on every run. A new configurable update.check_interval replaces the hard-coded 24-hour throttle, and the latest version discovered by a check is remembered so a persistent out-of-date WARN is shown on subsequent runs.

This implements docs/development/specs/2026-06-16-forced-update-feature.md and remediates the keryx bug report (a background update could hijack an unrelated command, and a failed update could exit 0).

Impact

  • Downstream tools built on GTB: the default behaviour changes from "prompt to update" to "log only". If you relied on the old prompting/forcing, opt back in (below). No code change is required to keep building.
  • The gtb CLI itself now uses prompt (was effectively always-prompt), so its interactive UX is unchanged in practice.
  • A failed forced update now exits non-zero (previously it could exit 0). CI/scripts that swallowed that masked success may now correctly see a failure.

How to migrate

Keep the old prompting behaviour

Set the baseline on your Tool:

props.Tool{
    // ...
    UpdatePolicy:        props.UpdatePolicyPrompt, // or props.UpdatePolicyEnabled to block
    UpdateCheckInterval: 7 * 24 * time.Hour,       // optional; zero value = framework default (24h)
}

…or let your users (or your shipped config) set it:

update:
  policy: prompt        # disabled | prompt | enabled
  check_interval: 24h   # any Go duration; 0 = check every invocation

Resolution precedence

Policy: --ci / ci: true (bypass entirely) β†’ update.policy config β†’ tool baseline (props.Tool.UpdatePolicy, default disabled).

Check interval: a valid update.check_interval config value (where 0/0s means "check every run") β†’ tool baseline (props.Tool.UpdateCheckInterval, if positive) β†’ framework default (setup.DefaultCheckInterval, 24h). A zero-value tool baseline is treated as "unset" and falls through to 24h β€” runtime config is the only way to request the no-throttle "every run" behaviour.

Generating a tool with these baselines

gtb generate project exposes both baselines so a tool author can bake them in at scaffold time (persisted to the manifest, preserved across gtb regenerate):

gtb generate project -n mytool -r myorg/mytool \
  --update-policy prompt --update-check-interval 168h

Interactively, the Self-Update Policy and Update Check Interval wizard steps collect the same values. See generate.

Public API note (pre-1.0)

setup.SkipUpdateCheck gained a checkInterval time.Duration parameter; setup.ResolveCheckInterval gained a leading toolDefault time.Duration parameter (now ResolveCheckInterval(toolDefault, configValue)); props.Tool gained UpdatePolicy and UpdateCheckInterval fields; and new setup.SetCheckedVersion / setup.GetCheckedVersion plus props.UpdatePolicy / props.ResolveUpdatePolicy are added. These ship as a v0.x minor per the pre-1.0 policy.