| index b68b7089e..775efbad1 100644 |
| --- a/internal/registry/registry.go |
| +++ b/internal/registry/registry.go |
| @@ -656,12 +656,13 @@ func (r *Registry) Delete(host string) error { |
| return err |
| } |
| |
| -// Upsert inserts or updates a route by host. Seven columns are deliberately |
| +// Upsert inserts or updates a route by host. Ten columns are deliberately |
| // not plain overwrites — `environment`, `caps`, `tunnel`, `provisioned`, |
| -// `seed`, `unit` and `fleet_box` all keep the existing row's value when the |
| -// incoming Route carries the zero one. |
| +// `seed`, `unit`, `fleet_box`, `args`, `idle_seconds` and `max_run_seconds` |
| +// all keep the existing row's value when the incoming Route carries the |
| +// zero one. |
| // |
| -// The reason is the same for all seven, and it is structural: a full-row |
| +// The reason is the same for all ten, and it is structural: a full-row |
| // Upsert caller (`carlos add`, the domains reconciler's two mint paths, the |
| // provision pass's mint) builds its Route from what IT knows, and none of |
| // them knows about the columns another feature owns. `environment` was the |
| @@ -686,12 +687,23 @@ func (r *Registry) Delete(host string) error { |
| // the next promote would stamp X-Carlos-Version onto a console still serving |
| // the old build, which is issue #110 returning silently. |
| // |
| +// `args`, `idle_seconds` and `max_run_seconds` are the same shape as `seed` |
| +// and for the same reason (2026-08-17 fix round 1): they are materialized |
| +// from a sidecar's manifest by the reconcile pass, and a full-row Upsert |
| +// from a caller that knows nothing about sidecars (`carlos add`) must not |
| +// blank a live argv or reset a worker's ceilings to the platform default. |
| +// Omitting them from this clause entirely — the original shape of this |
| +// change — was tried and rejected: omission is not preserve-if-empty, it |
| +// silently ignores even a genuine NEW value, so a sidecar whose manifest |
| +// argv changed would keep stale argv on the row until a narrow SetArgs |
| +// happened to run. |
| +// |
| // An explicit non-zero value still wins in every case; clearing keeps |
| // exactly one door per column, none of which goes through Upsert: |
| // SetEnvironment (`carlos route -environment -`), SetCaps (`carlos route |
| // -revoke`), SetTunnel (`carlos route -tunnel -`), SetUnit (`carlos route |
| -// -unit -`), SetSeed, SetFleetBox, and for `provisioned` the row's own |
| -// deletion. |
| +// -unit -`), SetSeed, SetFleetBox, SetArgs, SetWorkerLimits, and for |
| +// `provisioned` the row's own deletion. |
| // |
| // `addrs` and `health` (multi-upstream routing, issue #152) are, by |
| // contrast, PLAIN overwrites — see their ON CONFLICT clause below — same as |
| @@ -751,7 +763,10 @@ ON CONFLICT(host) DO UPDATE SET |
| seed=CASE WHEN excluded.seed = '' THEN routes.seed ELSE excluded.seed END, |
| unit=CASE WHEN excluded.unit = '' THEN routes.unit ELSE excluded.unit END, |
| fleet_box=CASE WHEN excluded.fleet_box = '' THEN routes.fleet_box ELSE excluded.fleet_box END, |
| - addrs=excluded.addrs, health=excluded.health`, |
| + addrs=excluded.addrs, health=excluded.health, |
| + args=CASE WHEN excluded.args = '' THEN routes.args ELSE excluded.args END, |
| + idle_seconds=CASE WHEN excluded.idle_seconds = 0 THEN routes.idle_seconds ELSE excluded.idle_seconds END, |
| + max_run_seconds=CASE WHEN excluded.max_run_seconds = 0 THEN routes.max_run_seconds ELSE excluded.max_run_seconds END`, |
| t.Host, t.Slug, t.Kind, t.Socket, t.Addr, t.Status, t.LastError, |
| t.DBPath, t.Version, t.PrevVersion, t.App, t.Pool, t.Region, t.Channel, |
| b2i(t.Hibernate), b2i(t.Canary), t.Replication, t.NextDue, t.Backing, |