Two reports, one cause: the script's fetch had no .catch. When the server had exited the promise rejected, the chain never ran, and not even the status line moved — so a dead server and a click that did nothing were indistinguishable. play again read as a dead button, and the linger timeout was invisible. Now a rejection says the session is gone and seals the page, and a 5-second heartbeat against a new /alive notices it without needing a click, which is what the timeout case requires. The beat carries the token, and does not extend the linger: that deadline is absolute. The harness had the same hole. jsrun's fetch stub had no .catch, so the branch that notices a dead server would have been unreachable in every test — the very defect the stub's own comment records from CB-WP-0024. Teaching it __failing, .catch and a recorded setInterval was the fix; writing the script defensively would have repeated the trap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
c2a7e40b67
commit
82aead6cea
9 changed files with 448 additions and 7 deletions
|
|
@ -351,6 +351,7 @@ impl Server {
|
|||
cb_render_html::Endpoints {
|
||||
command: &self.guard.endpoint(),
|
||||
note: &self.guard.note_endpoint(),
|
||||
alive: &self.guard.alive_endpoint(),
|
||||
},
|
||||
Some(seat),
|
||||
may_pass,
|
||||
|
|
@ -365,6 +366,10 @@ impl Server {
|
|||
);
|
||||
respond(&mut stream, 200, "text/html; charset=utf-8", &page);
|
||||
}
|
||||
// CB-WP-0035. Cheap proof the session is still here. It
|
||||
// advances nothing and decides nothing -- the loop keeps
|
||||
// waiting for the seat's actual move.
|
||||
("GET", "/alive") => respond(&mut stream, 200, "text/plain", "here"),
|
||||
("POST", "/command") => {
|
||||
let fact = match PointerFact::parse(&req.body) {
|
||||
Ok(f) => f,
|
||||
|
|
@ -490,6 +495,7 @@ impl Server {
|
|||
message,
|
||||
&self.guard.endpoint(),
|
||||
note_to.as_deref(),
|
||||
&self.guard.alive_endpoint(),
|
||||
cb_render_html::doc::Account {
|
||||
lines: &self.log_lines(),
|
||||
notes: &self.log_notes(),
|
||||
|
|
@ -525,6 +531,10 @@ impl Server {
|
|||
Err(why) => respond(&mut stream, 400, "text/plain", &why),
|
||||
}
|
||||
}
|
||||
// CB-WP-0035. Answered here too, and it does NOT extend
|
||||
// the linger: the deadline is absolute, so a tab left
|
||||
// open still cannot hold the process forever.
|
||||
("GET", "/alive") => respond(&mut stream, 200, "text/plain", "here"),
|
||||
("POST", "/command") => {
|
||||
// CB-WP-0020 T05: the browser could not start a second
|
||||
// game without going back to a terminal, which made it
|
||||
|
|
@ -1049,6 +1059,7 @@ mod tests {
|
|||
"it broke",
|
||||
"/c",
|
||||
None,
|
||||
"/alive?t=x",
|
||||
cb_render_html::doc::Account::of(&[]),
|
||||
&[],
|
||||
);
|
||||
|
|
@ -1266,6 +1277,7 @@ mod tests {
|
|||
"P1 ran out of input",
|
||||
"/command?t=x",
|
||||
None,
|
||||
"/alive?t=x",
|
||||
cb_render_html::doc::Account::of(&[]),
|
||||
&[],
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue