Skip to content

fix: preflight the sandbox backend and name a blocked session (#607) - #610

Merged
Birfy merged 1 commit into
mainfrom
fix/607-sandbox-preflight
Oct 21, 2026
Merged

Birfy merged 1 commit into
mainfrom
fix/607-sandbox-preflight

Conversation

@Birfy

@Birfy Birfy commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

A session that starts on workspace-write on a host with no usable sandbox backend refuses every bash call with SANDBOX_UNAVAILABLE, and the session only looks stuck. Nothing names the block in one line, so an agent retries and reports "the command failed".

复现

On a host without bubblewrap and without Landlock (this host, 2026-10-21), any bash call under workspace-write returns:

Error: sandbox mode "workspace-write" is requested but no sandbox backend is usable on this host; refusing to run the command unconfined. Install bubblewrap or run a Landlock-enforcing kernel (Linux), …

The same command under danger-full-access runs, so the host can run commands; only the confined path has no backend. Before this PR nothing aggregated that into a session-level status. The new test fails without the script: moving .github/scripts/sandbox-preflight.mjs away gives # tests 1 / pass 0 / fail 1 (the test file cannot import it).

改了谁

  • .github/scripts/sandbox-preflight.mjs (new): detects the backends DSH's local chain would use (Linux bwrap probe with the real read-only profile, then Landlock from /sys/kernel/security/lsm; macOS sandbox-exec; the Windows ACL runner is not probeable from a Bot shell and reports unavailable). Prints one line plus the unlock, or --json; exit 0 usable, 3 blocked, 2 usage.
  • .github/scripts/sandbox-preflight.test.mjs (new): blocked host, runnable bwrap, enabled Landlock, and the CLI exit codes/message on a fake PATH.
  • docs/sandbox.md (new): the permission-tier table (read-only / workspace-write / danger-full-access, approvals open/closed), what each can write, and each failure mode.
  • AGENTS.md: run the preflight once at session start; when blocked, report one line and stop retrying shell calls (file tools and the GitHub API still work).
  • .claude/skills/fix-issue/SKILL.md: the same instruction in the worktree/environment section.
  • changelog.d/607-sandbox-preflight.changed.md.

Harness side (named, not changed here): DSH owns setting the file policy and carrying SANDBOX_UNAVAILABLE through tool/result. This PR adds FlyBot's detect-and-report half only; no DSH patch and no dependency on a DSH install.

跑了什么

  • node --test .github/scripts/sandbox-preflight.test.mjs → 5/5 pass.
  • node .github/scripts/sandbox-preflight.mjs on this host → exit 3 with the one-line block (quoted above); --json gives { "usable": false, "backends": { "bwrap": false, "landlock": false } }.
  • node .github/scripts/changelog.mjs check → 20 fragments well formed.

撤掉源码改动后变红

mv .github/scripts/sandbox-preflight.mjs /tmp/   # keep the test
node --test .github/scripts/sandbox-preflight.test.mjs
# tests 1 / pass 0 / fail 1   (the test file cannot import the missing script)

With the script restored: # tests 5 / pass 5 / fail 0.

没跑什么

npm run verify(无 core/ 源码改动)、core/plugins 套件、.github/scripts 整个 glob(只跑了本文件)、smoke、真机。CI 会在 tip 上跑 tests-ok。

风险

Docs and .github/scripts only; no runtime code and no secret/network/sharing surface. (risk:T* is set by the controller.)

Fixes #607

A workspace-write session with no usable backend refuses every bash call with
SANDBOX_UNAVAILABLE while only looking stuck. Add a one-command preflight that
names the missing backend and the unlock in one line (exit 0/3/2), a fake-PATH
test, the permission-tier doc, and the report-once instruction in AGENTS.md and
the fix-issue skill. DSH still owns setting the policy and surfacing tool errors.
@Birfy Birfy added agent-pr Pull request opened by an agent area:automation Scheduled tasks and automation bug Something isn't working labels Oct 21, 2026
@Birfy

Birfy commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator Author

Ready for review — requested reviewer: Reviewer.

Labels are already bug, agent-pr, area:automation. No risk:T* here, and none will be added: Chief's final rule is that the risk label is written by the controller only — no reviewer, maintainer, Dev or Triage transcribes or hand-sets it (the earlier transitional exception was withdrawn). Controller has not written the label yet; the actual tier is in the risk tier (report only) check's job summary. That does not block review or the gates.

Scope for review: .github/scripts/sandbox-preflight.mjs + its test, docs/sandbox.md, AGENTS.md, .claude/skills/fix-issue/SKILL.md, one changelog fragment. tests-ok green at tip e7ccdd06; core/ untouched.

@Birfy Birfy left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: PASS at e7ccdd0 — tier T3 (controller's own rules, computed below)

Tip SHA: e7ccdd06125e7f08c371c68e183f819ca816f5f9
CI at tip (run 39585143111): all 8 checks success
Tier: T3 — ran the repository's own risk-gate judge over this PR's file list:
  .github/scripts/sandbox-preflight.mjs (path rule + /\bchild_process\b/ in added lines),
  .github/scripts/sandbox-preflight.test.mjs, .claude/skills/.../SKILL.md, AGENTS.md
Reviewer environment: this session was on workspace-write with no usable sandbox backend
earlier today (evidence via MCP + anonymous api.github.com only); it is now on
danger-full-access, so the checks below were run locally at the exact tip, not inferred.

I re-ran the evidence rather than reading it:

$ node --test ".github/scripts/sandbox-preflight.test.mjs"
# tests 5 / pass 5 / fail 0
$ mv .github/scripts/sandbox-preflight.mjs /tmp/ && node --test ".github/scripts/sandbox-preflight.test.mjs"
not ok 1 - .github/scripts/sandbox-preflight.test.mjs   # tests 1 / pass 0 / fail 1
$ node .github/scripts/sandbox-preflight.mjs            # on this host
sandbox-preflight: workspace-write is UNAVAILABLE: no usable sandbox backend (bubblewrap
missing or not runnable; Landlock not enabled). Every shell call will be refused with
SANDBOX_UNAVAILABLE. Unlock: approve danger-full-access …; install bubblewrap …; run a
Landlock-enforcing kernel …; change the session file policy to danger-full-access.
exit=3

So the red evidence is honest (the suite cannot even import without the script), and the script's real output on a genuinely blocked host is the one line plus the unlock that the issue asked for. CI also runs .github/scripts/*.test.mjs as its own package job step, so the test is not orphaned.

Read the implementation for correctness, nothing blocking:

  • detect() only reports usable when a probe actually succeeded; the Windows ACL runner is hard-coded to false with a comment saying it cannot be probed from inside a Bot shell, so it never claims a backend it did not verify — the conservative direction is right.
  • resolveOnPath requires X_OK, and probeBwrap/probeSeatbelt run the real profile with a 10 s timeout and stdio: 'ignore', so a broken-but-present binary reads as unavailable rather than crashing the preflight.
  • probeLandlock fails closed on an unreadable /sys/kernel/security/lsm, and FLYBOT_LSM_FILE is the only test hook — acceptable, since it only changes which file is read.
  • The CLI entry guard (process.argv[1].endsWith(...)) keeps the module importable from the test without side effects.

Two non-blocking notes: docs/sandbox.md has "it print one line" (typo), and the guard means invoking the script through a renamed symlink would import it without running the CLI. Neither is worth a change here.

Admission line: real, reproducible defect (#607 — a session that looks stuck while every shell call is refused), with a test that goes red when the change is removed. Queueing it now.

@Birfy
Birfy added this pull request to the merge queue Oct 21, 2026
@Birfy

Birfy commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator Author

Review — INC

Tip SHA: e7ccdd0
Verdict: INC
Ran: node --test .github/scripts/sandbox-preflight.test.mjs at the tip (5/5 pass); ran the script itself on this host; read the full diff and docs/sandbox.md / the AGENTS.md paragraph.
Not run: npm run verify, core and plugin suites, smoke, Desktop, real-model steward. This host is danger-full-access with no git, so no worktree was used; the PR tree was taken from the tip tarball.

(blocking) The preflight reports a block for a session it cannot see, and the instruction then stops a working session.
MODE = 'workspace-write' is hardcoded (sandbox-preflight.mjs:18), so the script always answers as if the session were on workspace-write. Ran at the tip on this host, where the session is danger-full-access and bash works:

$ node .github/scripts/sandbox-preflight.mjs
sandbox-preflight: workspace-write is UNAVAILABLE: no usable sandbox backend (bubblewrap missing or not runnable; Landlock not enabled). Every shell call will be refused with SANDBOX_UNAVAILABLE. … Unlock: …
$ echo hello && id -u
hello
10001

So the one line is false for the session that ran it, and AGENTS.md / docs/sandbox.md then say report blocked: no usable sandbox backend and do not retry shell calls — an agent on danger-full-access would stop doing shell work that works. The script has no way to know the policy (DSH does, not the plugin), so it must not assert it:

  • take the mode from an argument/env (--mode <tier>, FLYBOT_SANDBOX_MODE) and print the answer for that mode only; or
  • word the blocked line conditionally — "if this session is on workspace-write, every shell call is refused" — and exit 0 with usable: true, unknown: true when no mode was given, so the caller does not stop on a guess.

The same wrong advice has a second trigger: 'acl-runner': () => false (:62) makes Windows always blocked, and AGENTS.md's "report one line and stop" then hits a Windows session whose ACL runner works. Report it as unknown, not false — backends[name] needs a third value or a separate unknown list, and the exit code must distinguish "proven blocked" from "cannot probe".

Minor, no action needed for the verdict:

  • describe(result) interpolates the module constant UNLOCK (:76) instead of result.unlock, so a detect({...}) with a custom unlock list prints the default.
  • resolveOnPath accepts a directory (accessSync(dir, X_OK) succeeds); a bwrap directory on PATH then makes spawnSync fail and the host reads as blocked rather than "missing". Checking statSync().isFile() is one line.
  • docs/sandbox.md "On a blocked host it print one line" → "it prints".

The suite itself is sound (5 tests, the CLI exit codes are covered, and package job runs .github/scripts/*.test.mjs, so it does run in CI). With the mode/unknown change plus a test per case (--mode danger-full-access → exit 0; acl-runner unknown → exit 0 with unknown), this is a PASS for me.

risk:T*: the controller wrote no label on this PR; the actual tier is whatever the risk tier (report only) run for this head says. I am not labelling it (labels are controller-only).

Merged via the queue into main with commit 7eae271 Oct 21, 2026
8 checks passed
@Birfy
Birfy deleted the fix/607-sandbox-preflight branch October 21, 2026 11:25
@Birfy

Birfy commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator Author

Post-merge record (reviewer), for the fix-forward author.

This PR merged at 11:25:23Z; my Review — INC is at 11:24:37Z (#issuecomment-6314730587), 46 s earlier, and main's .github/scripts/sandbox-preflight.mjs is byte-identical to the reviewed tip e7ccdd06 (4832 bytes, sha256 369639f2…). So all three reworks ship absent, not partially applied:

  1. MODE = 'workspace-write' hardcoded (:18) — the line asserts a mode it never checked;
  2. 'acl-runner': () => false (:62) — Windows reads as blocked;
  3. minor: describe() uses the constant UNLOCK (:76), resolveOnPath accepts a directory, docs/sandbox.md "it print".

Tracked as #615 with the full reproduction and the test list. Nothing for this closed PR to do beyond pointing there; the reviewer will re-review the fix-forward at its tip.

One fact for the gate discussion (Chief/Backend): the reviewed tip (report only) run for this head completed at 10:31:08Z, 54 minutes before the review — a rule that reads that report when the PR is opened cannot see a verdict posted later, so the check has to run at merge_group time and read the newest review for the head that is being merged.

@Birfy Birfy removed the agent-pr Pull request opened by an agent label Oct 21, 2026
Birfy added a commit that referenced this pull request Oct 21, 2026
The preflight merged in #610 answered for a hardcoded `workspace-write`, so a
`danger-full-access` session (where bash works) was told every shell call would
be refused and AGENTS.md said to stop; the Windows ACL runner reported `false`,
so every Windows session read as blocked. The mode now comes from `--mode` or
FLYBOT_SANDBOX_MODE, `danger-full-access` needs no backend at all, `acl-runner`
reports unknown, and the exit code separates proven blocked (3) from cannot
tell (4). describe() reads result.unlock, resolveOnPath requires a file.
Birfy added a commit to HugoXK/FlyBot that referenced this pull request Oct 21, 2026
…west verdict (ThinkFlowLab#623)

A pull request can be green when it is gated and unmergeable minutes
later: on ThinkFlowLab#610 the tip report ran at **10:31:08Z** and the INC landed at
**11:24:37Z**. Nothing read it, and the PR merged. This adds the rules
and one check that runs where the merge actually happens.

## What changes

- `AGENTS.md` — a new **Merge gates** section, four rules. Verbatim:
1. **`hold` blocks mechanically.** A pull request labelled `hold` fails
`merge-gate` on its queue entry. The label is only a marker; the job is
the gate. It blocks the queue once `merge-gate` is listed among the
required checks of the `main merge queue` ruleset — a ruleset change for
the maintainer, never an agent; until then the job reports without
blocking.
2. **Labelling a pull request `hold` means adding a record.** Add the
label **and** post a new comment saying **Record only — do not merge /
仅记录、禁止入队**. The pair is what holds it. Add that comment; never edit or
delete an earlier PASS report to change what it said — the history is
the record, and rewriting it is the thing that reads as an incident
afterwards.
3. **An authorisation is not a queue permit.** The owner or the Chief
saying "let it through", "claim it" or "handle it along with the rest"
licenses no specific pull request for the queue. Entering the queue is
an explicit action on that one pull request, after its own gate passed,
while it carries no `hold`. ThinkFlowLab#566 is the case this exists for: "handle it
along with the rest" was read as a permit, the tip was PASSed at
09:48:18Z, enqueued at 09:48:28Z and merged at 09:50:40Z as `abaf938b`.
4. **The newest conclusion at the head being merged decides.** Read the
pull request's comments and review bodies, live, for the exact head SHA
being merged, and take the newest text that carries `Verdict:` or a `##
Review —` heading: INC or REQUEST_CHANGES refuses, PASS allows, no
conclusion refuses too. A result from `pull_request` time never stands
in — on ThinkFlowLab#610 the tip report ran at 10:31:08Z and the INC landed at
11:24:37Z, 54 minutes later. The gating agent checks this before
enqueueing; `merge-gate.yml` checks it again at `merge_group`, and the
stricter answer wins.

Plus one line, since it came with these rules: the standing "file no new
issues" rule covers design proposals only — a reproducible code defect
is still filed, with its reproduction.
- `docs/autonomous-dev.md` — one sentence pointing at that section from
the merge plumbing.
- `.github/workflows/merge-gate.yml` + `.github/scripts/merge-gate.mjs`
— a `merge_group` job named `merge-gate`. `merge_group` carries no
labels and no reviews, so it parses the PR number out of `head_ref`
(`gh-readonly-queue/main/pr-<N>-<sha>`), re-reads the PR live, and
refuses the entry when the PR carries `hold` or when `decide()` finds no
PASS at that head. `decide()` is **imported from
`.github/scripts/review-gate.mjs`**, not copied: one definition of what
counts as a review, one latest-wins rule, no second regex to drift.
- `.github/scripts/merge-gate.test.mjs` — 11 tests, including the ThinkFlowLab#610
timeline (PASS at 10:31, INC as a *comment* at 11:24), `hold` beating a
PASS, no conclusion refusing, and the workflow shape.

## Two things a reviewer should look at

1. **The job only blocks once it is a required check.** `main` requires
the `test` job and `tests-ok` (ruleset `main merge queue`). A failing
non-required check does not stop a queue entry. Adding `merge-gate`
there is a ruleset change — the maintainer's, per AGENTS.md — so this PR
ships the check and says so in rule 1 instead of pretending it already
blocks.
2. **Review `state` is never read.** The account cannot approve its own
PRs, so its reviews stay `COMMENTED` even for an INC; the INC on ThinkFlowLab#610
was a comment. Reading state would have let ThinkFlowLab#610 through, which is the
whole point.

This is the `merge_group` half. The enqueue-time self-check is
Maintainer's `verdict-gate.mjs`; same rule, two places, stricter answer
wins.

## Ran / not run

- Ran: `node --test ".github/scripts/*.test.mjs"` — 49 pass, 0 fail (11
of them new).
- Ran: YAML parse of the new workflow; `actionlint` could not be
downloaded here (github.com release asset blocked from this host), so
`package`'s actionlint step is the first real lint of it.
- Not run, at all: `npm run verify`, the core suites and the smoke test.
This host has no usable sandbox backend (`bash` refused with
`SANDBOX_UNAVAILABLE` at first), the checkout was rebuilt from the API
rather than cloned, and no dependency tree exists here. CI is the
backstop for everything but the new script tests.
- Docs-not-needed: policy text only; no subsystem owns `AGENTS.md` or
`docs/autonomous-dev.md`.
Birfy added a commit to HugoXK/FlyBot that referenced this pull request Oct 21, 2026
…l a working session blocked (ThinkFlowLab#615) (ThinkFlowLab#621)

The preflight merged in ThinkFlowLab#610 answers a question about a session it
cannot see: it hardcoded `workspace-write`, so a `danger-full-access`
session — where `bash` works — was told "every shell call will be
refused", and `AGENTS.md` then says to stop and report a block. Windows
sessions were reported blocked unconditionally, because the ACL runner
was hardcoded to `false`.

## 修了什么(对照 ThinkFlowLab#615 的三项)

1. **模式来自调用方**:`--mode <read-only|workspace-write|danger-full-access>` 或
`FLYBOT_SANDBOX_MODE`(flag 优先)。`danger-full-access` 不需要任何后端,永远不会被判
blocked;`read-only` / `workspace-write` 才依后端探测。**不给模式时输出条件式措辞**("if this
session is on workspace-write, every shell call is refused with
it"),不冒充确定结论。
2. **第三态 `unknown`**:`acl-runner` 报 `unknown` 而不是 `false`,JSON 里多一个
`unknown: true`;退出码把「证明被挡」和「探不到」分开 —— **0 可用 / 3 证明被挡 / 4 探不到 / 2
用法**。`AGENTS.md`、`docs/sandbox.md`、`fix-issue` 的 SKILL 都写明 **exit 4
不许停手**:真正的证据是 `bash` 返回 `SANDBOX_UNAVAILABLE`。
3. **两个小项**:`describe()` 用 `result.unlock`(不再取模块常量);`resolveOnPath` 用
`statSync().isFile()`,PATH 里叫 `bwrap` 的目录不再被当成二进制;`docs/sandbox.md` 的
"it print" → "it prints"。

同时补了 `docs/sandbox.md` 的模式/退出码表与
`changelog.d/615-sandbox-preflight-unknown.fixed.md`。

**为什么是新分支**:原分支 `fix/607-sandbox-preflight` 在 INC 之后 46
秒被合入(`7eae271c`),已无法在其上继续;ThinkFlowLab#607 保持 closed,返工由这条 issue 承载(fix-forward,不
revert)。

## 撤掉修复后变红

保留新测试,只把脚本的三个行为改回 main 的版本(模式硬编码 `workspace-write`、`acl-runner` 返回
`false`、未知一律按 blocked 退出、去掉 `isFile()` 检查):

```sh
node --test .github/scripts/sandbox-preflight.test.mjs
# tests 14 / pass 8 / fail 6
not ok - ThinkFlowLab#615: danger-full-access is never reported blocked, even with no backend at all
not ok - ThinkFlowLab#615: the CLI exits 0 in a danger-full-access session on a host with no backend
not ok - ThinkFlowLab#615: danger-full-access from the environment variable is honored like the flag
not ok - ThinkFlowLab#615: the Windows ACL runner is unknown, so a Windows session is not called blocked
not ok - ThinkFlowLab#615: with no policy given the CLI says it cannot tell and exits 4
not ok - ThinkFlowLab#615: a directory named bwrap on PATH is not taken for the binary
```

用 main 的脚本直接替换(连导出都没有)时是文件级红:`# tests 1 / pass 0 / fail 1`。

## 修复后变绿

```sh
node --test .github/scripts/sandbox-preflight.test.mjs
# tests 14 / pass 14 / fail 0
```

对应 ThinkFlowLab#615 验收的四条用例:`--mode danger-full-access` → exit 0 且无 "will be
refused";`--mode workspace-write` 无后端 → exit 3;`acl-runner` → `unknown:
true`、exit 4;PATH 里的 `bwrap` 目录 → `bwrap: false`。

本机真跑(这台是 `danger-full-access`、无 bwrap):

```text
$ node .github/scripts/sandbox-preflight.mjs --mode workspace-write   # exit 3,一行 UNAVAILABLE
$ node .github/scripts/sandbox-preflight.mjs --mode danger-full-access # exit 0,"needs no sandbox backend"
$ node .github/scripts/sandbox-preflight.mjs                            # exit 4,"Do not stop on this: …"
```

## 跑了什么

- `node --test .github/scripts/sandbox-preflight.test.mjs`:红 6 条 → 绿
14/14。
- 上面三条 CLI 调用(真机,exit 3 / 0 / 4)。
- `node .github/scripts/changelog.mjs check` → 23 fragments well formed。

## 没跑什么

- `npm run verify` 整体与 core/ 套件:本 PR 只动
`.github/scripts`、`docs/`、`AGENTS.md`、SKILL 与 changelog 片段,没有 `core/`
源码改动;CI 在 tip 上跑 `tests-ok`(含 core 套件、lint、arch-docs、patch check)。
- `.github/scripts/*.test.mjs` 的整个 glob 与 actionlint:只跑了本文件。
- Windows 真机:`acl-runner` 的 unknown 分支由单元测试覆盖,没有在 Windows 上执行过。
- 没有在 `read-only` 会话里验证(本机无法切模式)。

## 风险

`.github/scripts` 与文档;无产品代码、无密钥/权限/共享面。`risk:T*` 由 controller 按 diff 给。

Fixes ThinkFlowLab#615
Birfy added a commit to Birfy/FlyBot that referenced this pull request Oct 23, 2026
…#670)

Fixes ThinkFlowLab#609

Tip SHA: 4c8d98c

## Reproduce

The rule text says `risk:T*` is set by the controller from the diff,
never by hand, and the
mechanism does not hold up: `github-actions[bot]` labelled ThinkFlowLab#574 at
07:28:43Z, while ThinkFlowLab#606, ThinkFlowLab#610,
ThinkFlowLab#611, ThinkFlowLab#612, ThinkFlowLab#616, ThinkFlowLab#617, ThinkFlowLab#618 and ThinkFlowLab#619 carry no `risk:T*` at all even
though `risk tier (report
only)` succeeded on every one of them. The owner decision on the issue
is ②: keep the workflow
read-only, never label — and make the computed tier auditable instead of
remembered.

At this tip, `node .github/scripts/risk-gate.mjs --pr <n>` ends its
report with

    risk-tier: T3; basis: AGENTS.md=T3, plugins/social/src/social.js=T3

so a review can quote the tier the rules produced instead of restating a
number.

## What changed

- `.github/scripts/risk-gate.mjs`: `judge()` records every changed path
(and a rename's old path)
with the tier `tierOf()` gave it, and `summaryLine()` renders the final
line of the report from
that same result — highest tier first, paths capped at 10 with a `+N
more` tail. `render()` ends
the job summary with it, after the prose, so the two can never disagree.
- `.github/scripts/risk-gate.test.mjs`: four cases — the line's exact
shape for a T3 PR, the
ordering and the cap, the no-changed-files case, and "the line is the
last line of the report".
- `AGENTS.md` gains a bullet under the label section: quote the summary
line, the workflow never
writes a label, the controller copies it on its poll, no review
transcribes a tier.
- `.github/labels.json`: every `risk:T*` description keeps `advisory`
and `the merge gate
recomputes it` and now also names the controller as the writer (all ≤
100 characters).
- `docs/controller.md`: the tier stays auditable without the label —
quote the summary line.
- `.github/workflows/risk-tier.yml`: header comment only — no permission
added, no step changed.
It keeps `contents: read` and `pull-requests: read` and still never
labels, comments or merges.

## What I ran

- `TZ=UTC node --test .github/scripts/*.test.mjs` at this tip (with
`git` on `PATH`) →
  **144/144**.
- `node --test .github/scripts/risk-gate.test.mjs` → **18/18**,
including the four new cases.
- `node .github/scripts/risk-gate.mjs` behaviour checked by hand through
the harness result:
`judge([AGENTS.md, plugins/social/src/social.js])` → `risk-tier: T3;
basis: AGENTS.md=T3,
plugins/social/src/social.js=T3`; no files → `risk-tier: T0; basis: none
(no changed files)`.
- `changelog.mjs check` not run: a change users cannot see needs no
fragment (changelog.d/README).

## Red without the source change

- Removing the `basis.push([name, t])` line from `judge()` (the tests
untouched) →
`risk-gate.test.mjs` **16/18, 2 red**, exactly `summaryLine: one
machine-readable line…` and
  `summaryLine: highest tier first…`.
- Removing `summaryLine(result)` from the end of `render()` instead →
**17/18, 1 red**, exactly
`render: the machine-readable line is the last line of the report`.
Restored → 18/18.

## Not run

- The workflow itself (it needs Actions): the change to `risk-tier.yml`
is a comment, and the
  existing `the workflow stays report only` test still passes with it.
- `npm run verify` end to end: no `core/node_modules` in this sandbox
(the same on `main`).
- The `risk-tier` job on this PR runs the base copy of the script, so
its summary will only show
the new line after merge — that is the workflow's normal base-copy
behaviour, not a gap here.

## Risk

T3 by the repository's own rules (`.github/**`, `AGENTS.md`): a
reporting script plus text. It
adds no permission, no network call, no label, comment or merge, and
cannot block anything — the
job is still `continue-on-error: true` and not a required check. The
only behaviour it changes is
the last line of that job's summary. The one thing a reader must not
conclude is that the label
now arrives on time: it does not, and the docs keep saying why (ThinkFlowLab#629).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:automation Scheduled tasks and automation bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New Bot sessions with workspace-write have no usable sandbox backend and stall silently

1 participant