Skip to content

fix(host): record the host at create, read a token without host.json as unshared, freeze again - #303

Merged
ThomasK33 merged 13 commits into
mainfrom
fix/host-record-at-create
Nov 4, 2026
Merged

ThomasK33 merged 13 commits into
mainfrom
fix/host-record-at-create

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Nov 3, 2026 •

Copy link
Copy Markdown
Member

Why

Three gaps remained after #274, #283 and #291 in how a CLI judges a session's host and program group when it does not share the host's pid space. One of them let list in another pid namespace record a starting session as failed and make its create fail.

Follow-ups of #274 (verifier note N4, refs #262), #291 (the start-up window in the lifecycle skill's Scope) and #283 (advisor follow-ups, refs #244).

What changes for users

  1. Start-up window. create now writes host.json for the new host right after it launches the host, before session.json names the host. The host writes it again when it starts. Before, host.json appeared only once the host had read its manifest. A list, inspect or gc in another pid namespace that read the session before that point found no process with the host's pid and recorded the live session failed. Then create itself failed. Now such a CLI reads the host as unknown (running) from the first manifest on.

  2. A token without host.json (N4). A program.json with an autogroup token but no host.json now counts as out of the CLI's pid space. Its group reads as running and gets no signal. Before, a CLI in another pid namespace read such a group as gone, and destroy recorded destroyed while a process that the program left still ran. Only unreleased development builds between fix(host): prove the program's group before every destroy kill #221 and fix(host): prove the host pid with a start-up record before reconcile or destroy --force trusts it #260 wrote this state: no tag up to v0.5.0 contains programGroup.ts or hostRecord.ts.

  3. Freeze (from fix(host): freeze a program group that no scan shows before the proven kill #283).

    • stopProgramGroup now sends SIGSTOP again at each judgement that reads the group gone. A member that a SIGCONT from elsewhere resumed inside the window stops again.
    • It sends no SIGSTOP when /proc/self does not name this process's pid. Then /proc belongs to another pid namespace, for example after unshare --pid --fork without --mount-proc, and the group id can name the CLI's own group.
  4. Job-control side effect (declined, documented). A SIGSTOP to an unrelated group that is a job of an interactive shell makes the shell report the job as stopped. After the SIGCONT the job runs in the background. The session's own group never has a job-control parent, because its parent is the host. A freeze without this effect needs the cgroup freezer or ptrace, which costs more than the case is worth. docs/USAGE.md Limits item 3 now says it.

  5. A host that cannot record itself records no token (verifier N1). A host that cannot read /proc/<pid>/ns/pid but can read the autogroup used to write a token without host.json. Then destroy of its exited session failed with HOST_TIMEOUT. runHost now records the token only when it wrote host.json, so a token always comes with a record of its pid space.

  6. A /proc of another pid namespace (verifier N3). Main had this gap too. A CLI in the host's pid namespace, but with the /proc of another one (nsenter --pid without --mount), scanned the wrong /proc and recorded destroyed while a leftover ran. inPidNamespace now also requires /proc/self to name the CLI's own pid. That view reads the group as unshared, so destroy fails with HOST_TIMEOUT and sends no signal.

No CLI JSON shape changes.

What changes for maintainers

  • sharesHostPids(sessionDirectory, sharedWithoutRecord): token readers pass false, the childPid fallback passes true. Released sessions have no token and no host.json, so they keep the old answer.
  • The model gets the guard CreateRecordsHost and CliHostAlive, the CLI's view of the host. In another pid space, a host that host.json names reads alive, and one that host.json does not name reads dead. destroy --force kills no host there.
  • New run mutant-CreateRecordsHost-otherspace, which must violate R2. witness-WitnessUnrecordedHostMeetsKill now runs without the guard, because a live host without a record is out of reach with it. With the main merges (fix(host): read a terminal session next to the destroy marker as destroyed #284 and fix: create stops its host and names the session on every error of its readiness wait #294), that makes 72 runs and thirty-two guards.
  • facts.mjs reads the host record at create, the new freeze statement and procShowsOwnPidSpace. It also reads the new sharesHostPids text. RecordsProgramSync now also reads that runHost records the token only beside host.json.

Validation

  • Red first: 8d8d515c adds 4 unit tests, and all 4 fail on main's source.
    • records the host before the first manifest names it: a real child as host, then a reconcile from another pid namespace.
    • reads alive and gets no kill without a host record.
    • stops a group that reads gone again at each of its 5 judgements, then continues it.
    • sends no SIGSTOP when /proc belongs to another pid namespace.
  • Fact mutations: I removed writeHostRecord, restored frozen ||=, stubbed procShowsOwnPidSpace, and restored record === null ||. Each one flips its guard to FALSE.
  • check.sh --update-coverage gave 72/72 runs as expected on fdbc191, the merge with fix: create stops its host and names the session on every error of its readiness wait #294. current has 24,686,944 distinct states and current-no-token has 24,467,356.
  • I did not rerun TLC after the later main merges or after the N1 and N3 fixes. Those changed only covered-code fingerprints. The guard facts, the model and check.sh equal those of that run, so its results still hold, and I wrote coverage.json from facts.mjs.
  • Further red tests:
    • destroy of an exited session whose host could not read its pid namespace ends ok (integration, preload test/fixtures/denied-pid-namespace.ts): on 5c3b329 the host recorded a token without host.json.
    • reads alive in the host's pid namespace when /proc belongs to another one (unit).
    • records the host before the first manifest names it now checks that host.json exists when session.json is first renamed into place. It fails when I move the write after writeManifest.
  • tsc --noEmit, oxlint and oxfmt --check pass. Unit: 140 files, 1945 tests. Integration: lifecycle, gc, non-reaping-init and idle-timeout, 75 tests. Under load averages of 45 to 210, two of them hit time-outs in the full run: gc collects exited, failed, and destroyed sessions and destroy ends a program whose killed background process stays a zombie. Both passed when I reran them alone.
  • Live, main vs this branch: the host is held 4 s before it reads its manifest, and list --all runs in sudo unshare --pid --fork --mount-proc as the same user:
main this branch
host.json when session.json first appears missing present
list in another pid namespace failed running
create fails ok
inspect afterwards failed, "host process died unexpectedly" running

Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: xhigh

…as unshared, freeze again

- allocateSession writes host.json for the launched host before the first
  manifest names it, so a CLI in another pid namespace reads a starting
  host as unknown instead of dead.
- A token program.json without host.json reads as the unshared group: no
  record names the host's pid space.
- stopProgramGroup sends SIGSTOP again at each judgement that reads gone,
  and sends none when /proc/self does not name this process.
… it by host.json

The lifecycle model gains the CreateRecordsHost guard and CliHostAlive,
the CLI's view of the host: in another pid space a host that host.json
names reads alive, and one that it does not name reads dead.
mutant-CreateRecordsHost-otherspace must violate R2. facts.mjs reads
the new freeze, the /proc/self check and the host record at create. The
docs describe the repeated SIGSTOP and the job-control effect.
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-04T01:59:02.033492Z e937e91 Manual request
🔒 Security Review ✅ Completed 2026-11-04T01:59:58.611312Z e937e91 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: 3ca9ead30c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 774f105b17

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…eate

# Conflicts:
#	.agents/skills/verify-session-lifecycle/SKILL.md
#	.agents/skills/verify-session-lifecycle/coverage.json
#	.agents/skills/verify-session-lifecycle/model/SessionLifecycle.tla
#	.agents/skills/verify-session-lifecycle/scripts/check.sh
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: 1a6307428c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 1a6307428c

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…eate

# Conflicts:
#	.agents/skills/verify-session-lifecycle/coverage.json
…eate

# Conflicts:
#	.agents/skills/verify-session-lifecycle/coverage.json
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: 5c3b329990

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 5c3b329990

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…another pid namespace as unshared

#303 verifier:
- N1: a host that could not read /proc/<pid>/ns/pid wrote a token without
  host.json, so destroy of its exited session failed with HOST_TIMEOUT.
  runHost now records the token only when it wrote host.json.
- N3: a CLI in the host's pid namespace whose /proc belongs to another one
  scanned the wrong /proc and recorded destroyed over a live leftover.
  inPidNamespace now also needs /proc/self to name this pid, so the group
  reads unshared there.
- N4: src/host/README.md and docs/USAGE.md describe both, the repeated
  SIGSTOP and the host.json write at create.
The guard facts, the model and check.sh equal those of the last full
check (72/72 as expected), so the TLC results do not change.
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: e937e91ea5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: e937e91ea5

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@ThomasK33
ThomasK33 added this pull request to the merge queue Nov 4, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Nov 4, 2026
@ThomasK33
ThomasK33 added this pull request to the merge queue Nov 4, 2026
Merged via the queue into main with commit 3262f06 Nov 4, 2026
17 of 18 checks passed
@ThomasK33
ThomasK33 deleted the fix/host-record-at-create branch November 4, 2026 03:11
@github-actions github-actions Bot mentioned this pull request Nov 4, 2026
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