Skip to content

docs: say what <spaceKey> is in the upgrade recipe - #1602

Merged
davidfarah2003 merged 1 commit into
mainfrom
followup/1578-spacekey-main
Sep 14, 2026
Merged

davidfarah2003 merged 1 commit into
mainfrom
followup/1578-spacekey-main

Conversation

@davidfarah2003

Copy link
Copy Markdown
Contributor

Follow-up to #1588, which added docs/UPGRADING.md. A reviewer on that PR flagged this as non-blocking; fixing it rather than noting it.

The defect

The 0.48.2 to 0.49.0 section tells an operator to watch the manager log at
.cotal/auth/<spaceKey>/manager.log. It never said what <spaceKey> is, and the
obvious substitution is wrong.

<spaceKey> is not the space name. It is lowercase hex of the name's UTF-8 bytes
(auth-paths.ts:47), which exists so alpha and Alpha cannot alias on a
case-insensitive filesystem.

What an operator hits today

Measured, not reasoned: with the file absent the offset capture reads 0 and the
wait burns its full 60 second timeout before saying anything.

So it fails loudly rather than passing falsely, which is why this is a follow-up
and not a blocker on the page itself. A minute of silence on a typo is still
avoidable.

The fix

Five lines in the existing step, plus the regenerated bundle.

The worked example was produced by calling the product's own spaceKey rather
than reimplementing it:

spaceKey("prod")  ->  70726f64

A second name was checked to confirm the reader discriminates rather than
returning a constant.

The page now also points at the launch line, because cotal up already prints
the real path (up.ts:1942). Reading it beats deriving it.

Scope

Two files: the page and the regenerated docs bundle. The bundle is generated,
never hand-edited; each added page line appears in it once, verified.

No other section changes. Nothing in the 0.49.0 answer set moves.

Refs #1578

…path

An operator substituting the plain space name gets a path that does not exist.
Measured: with the file absent the offset reads 0 and the wait burns its full
timeout before saying anything, so it fails loudly rather than false-passing,
but a 60 second silence on a typo is avoidable.

spaceKey is lowercase hex of the name's UTF-8 bytes (auth-paths.ts:47), which
exists so that `alpha` and `Alpha` cannot alias on a case-insensitive
filesystem. The worked example was produced by calling the product's own
spaceKey rather than by reimplementing it: prod -> 70726f64, with a second
name checked to confirm the reader discriminates.

`cotal up` already prints the real path on its launch line (up.ts:1942), so
the page now points there instead of leaving the reader to guess.

Flagged as non-blocking in review of #1588; fixed rather than noted.

Refs #1578
@davidfarah2003
davidfarah2003 force-pushed the followup/1578-spacekey-main branch from c2e1ea2 to 4910a8d Compare September 13, 2026 22:13
@davidfarah2003

Copy link
Copy Markdown
Contributor Author

This PR explains a placeholder that should not be in the recipe at all. Measured in source: the manager log has no <spaceKey> in its name, and .cotal/manager.<spaceKey>.log is a path the code never writes.

I merged #1588, so this defect is mine as much as anyone's. It is on main right now at docs/UPGRADING.md:284.

What the code does

The detached manager log is opened in exactly one place, with a constant name and no space segment:

implementations/cli/src/lib/manager-proc.ts:124
  const fd = openSync(cotalPath("manager.log"), "a", 0o600);

implementations/cli/src/lib/paths.ts:12
  export function cotalPath(...segments: string[]): string {
    return join(cotalRoot(), ".cotal", ...segments);
  }

cotalPath joins under .cotal/ and inserts nothing. So the file is .cotal/manager.log.

That is corroborated by the reserved-children list, which carries the literal name as a canonical sibling:

packages/workspace/src/space-segmentation.ts:48
  "manager.delivery-aware", "manager.log", "manager.pid", ...

Where spaceKey is real

The encoding this PR describes is correct, and it is used, just not here. Every spaceKey-suffixed filename in the source is an auth-service file:

implementations/cli/src/lib/auth-proc.ts:21   auth-service.${spaceKey(space)}.pid
implementations/cli/src/lib/auth-proc.ts:22   auth-service.${spaceKey(space)}.log
implementations/cli/src/commands/down.ts:745  auth-service.${spaceKey(space)}.pid

Enumerated rather than sampled: I grepped every spaceKey occurrence adjacent to .log or .pid across implementations/*/src and packages/*/src, and those three rows are the whole result. There is no manager row. The hex arithmetic in this PR is also right on its own terms (prod encodes to 70726f64), which is part of why the paragraph reads so convincingly.

Why this matters more than a typo

The recipe's step 3a waits on that path:

LOG=.cotal/manager.<spaceKey>.log
OFF=$( [ -f "$LOG" ] && wc -c < "$LOG" || echo 0 )
...
timeout 60 bash -c "until tail -c +$((OFF+1)) \"$LOG\" | grep -q '. manager up'; do sleep 1; done"

With a wrong $LOG, [ -f "$LOG" ] is false, OFF is 0, tail writes to stderr and produces nothing on stdout, and the loop spins until timeout returns 124. The recipe already documents 124 as meaning "the manager never came up, so STOP and look." An operator following it lands on the one instruction that tells them to halt a live upgrade and investigate a manager that is in fact healthy, in the middle of a scheduled outage window on a 30-agent fleet.

This PR's own new sentence describes that outcome accurately: "the wait below then burns its full timeout before telling you." The diagnosis of the failure mode is exactly right. The remedy points at the wrong file.

Suggested change

Replace the placeholder with the literal path rather than explaining how to compute it:

LOG=.cotal/manager.log

If a per-space manager log is wanted, that is a product change and not a docs change, and it should be raised as its own issue. I am not asking for it here.

Executed, not reasoned

I ran the recipe's own wait loop both ways against a real .cotal/manager.log carrying a manager up line, using the recipe's exact OFF/tail -c +$((OFF+1))/grep -q construction:

LOG=.cotal/manager.log            OFF=57   rc=0     wait satisfied
LOG=.cotal/manager.70726f64.log   OFF=0    rc=124   timeout

70726f64 is this PR's own worked example, the hex for space prod. On the placeholder path tail emits 0 bytes on stdout and writes tail: cannot open '.cotal/manager.70726f64.log' for reading: No such file or directory to stderr, which grep -q never sees, so the loop spins to the full timeout.

rc=124 is the value the recipe defines as "it never did, so STOP and look."

One thing I did not do: I did not boot a real 0.49.0 manager and observe the filename it creates, because this host runs 0.48.2 and starting a manager here is not something I will do to prove a docs point. The source read above is what establishes the name; this run establishes the consequence.

@davidfarah2003
davidfarah2003 merged commit aaff9d5 into main Sep 14, 2026
37 of 39 checks passed
@davidfarah2003

Copy link
Copy Markdown
Contributor Author

Correction to my comment above. It read openSync(cotalPath("manager.log")) at manager-proc.ts:124, and that line is not on main. Since 9021896 (per-space runtime pid and log namespace, 2026-08-30) the detached manager writer is managerLogPath, which expands MANAGER_LOGFILE (manager.{space}.log, packages/workspace/src/local-process.ts) through canonicalLocalProcessPath with the hex spaceKey. So cotal up --detach writes .cotal/manager.<spaceKey>.log, exactly as this PR says, and the launch line at implementations/cli/src/commands/up.ts:1942 prints that path. The comment above was measured against a stale checkout. The wait-loop measurement in it stands on its own terms but was run against the wrong file name.

One small thing left in the PR body, not in the diff: it says the section watches .cotal/auth/<spaceKey>/manager.log. The page and the code say .cotal/manager.<spaceKey>.log.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant