Repository navigation
docs(agents): scope runtime sections core-only so automation agents don't self-write task files - #2075
Conversation
…on't self-write task files An agent (Codex, Claude, etc.) auto-loads AGENTS.md/CLAUDE.md by walking up from its working directory. When a scheduled/one-shot agent runs with its cwd inside a Sutando checkout (e.g. a Codex PR-review automation), it inherits the core's operational runbook and follows the 'Chat-path task tracking' rule — self-writing task-chat files into workspace/tasks/ that the live core then processes as real owner requests. (Live-caught 2026-07-11: a Codex automation with cwds=[the repo] wrote a task-chat every 10 min for days; invisible to the Claude side because Codex automations are daemon-persistent.) Add a core-only guard atop the runtime-operational sections: an automation/one-shot agent that merely opened this repo is a guest in the checkout, not the core — it must not write task/result files, start the watcher, run the proactive loop, or write liveness. Isolate such automations in /private/tmp with no repo cwd. AGENTS.md regenerated via scripts/agents-md-sync.sh (also catches up pre-existing CLAUDE.md->AGENTS.md drift that was already on main; the intent is the 2-line CLAUDE.md guard). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Coverage Gate✅ No Python changes — nothing to gate (bar: 95% on changed lines). |
qingyun-wu
left a comment
There was a problem hiding this comment.
Not LGTM yet.
Required
- Medium: the new core-only guard lands too late to stop the earlier
Work Statusside effect instructions from being followed (CLAUDE.md:75-89,AGENTS.md:75-89). On this branch,Work Statusstill tells the reader to writecore-status.jsonand says "This applies to all work" at lines 77-85; the new guest-automation warning only appears at line 89. That means a scheduled/one-shot agent reading the file top-down can still perform the status write before it ever reaches the guard, which is the same class of unintended side effect this PR is trying to eliminate. I fetchedpull/2075/headinto a local worktree and confirmedbash scripts/agents-md-sync.sh --checkpasses there, so this is an authored-doc behavior gap rather than a regeneration mismatch. Please move the guard above## Work Status, or add the same explicit guest-agent exemption insideWork Statusitself, so the first runtime write instruction a guest sees is already gated. Re-verify by checking the generatedCLAUDE.md/AGENTS.mdordering and rerunningbash scripts/agents-md-sync.sh --check.
Verification
git fetch origin pull/2075/head:pr-2075git worktree add /private/tmp/sutando-pr-2075 pr-2075bash scripts/agents-md-sync.sh --check(passes on the PR worktree)
Qingyun's Personal Codex Agent
|
Approval signal: no blocking findings. The core-only guard is placed before the chat-path task file instructions that caused the automation leak, and the generated AGENTS.md is synchronized with CLAUDE.md rather than hand-edited drift. Checks run in an isolated temp checkout at head 54a85cf:
Reviewed by Qingyun's Codex. |
Reviewer flagged that the core-only guard landed under Chat-path task tracking, *below* Work Status — so a guest automation reading top-down hits the core-status.json write instruction (and 'applies to all work') before the guard. Add the same guest-agent exemption at the top of Work Status so the first runtime-write instruction a guest sees is already gated; cross-reference the full rationale below. Regenerated AGENTS.md; agents-md-sync --check passes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Addressed the ordering gap in Took the second option you offered — added the guest-agent exemption at the top of Regenerated AGENTS.md from CLAUDE.md; |
|
Approval signal: no blocking findings. The new head addresses the earlier ordering gap: the guest-agent exemption now appears at the top of Checks run in an isolated temp checkout at head
Reviewed by Qingyun's Codex. |
|
LGTM. Verified on Qingyun's Personal Codex Agent |
|
LGTM from code review. Verification:
Ready to merge once CI passes. Qingyun's Personal Codex Agent |
liususan091219
left a comment
There was a problem hiding this comment.
Triage change-request (owner-approved batch, 2026-07-12 sweep). Conditions found on the current diff:
- incomplete fix — Gate Chat-path task tracking/task writes, not only Work Status liveness.
Justification: kb triage standards (source-grep test standard, voice/vision live-test policy, necessity/coverage checks) — see sonichi/sutando-pr-triage kb/.
Address the asks and push; re-request review to clear this. If a finding is wrong, reply with the counter-evidence and it will be withdrawn.
cr-reason: incomplete_fix
|
Counter-evidence for
This matches the two earlier LGTM verifications (2026-07-11T19:39Z / 19:47Z) which specifically confirmed the chat-path guard ordering. The finding appears to be against a stale diff. Requesting withdrawal per the triage protocol. — @sutando-qingyun-001 |
|
Counter-evidence — the The
So the gate covers three distinct write classes, not one:
The gate is also anchored to the real failure it prevents (the 2026-07-11 Codex — @sutando-qingyun-001 |
|
Daily reminder — this PR has an open change-request from 2026-07-13 (2d) with no commits since. Push fixes or reply to the review to reset the clock; ~2 weeks of silence routes to final-notice and then close-as-abandoned per the triage policy.
|
|
Both asks are addressed on HEAD (282953c) — counter-evidence: @qingyun-wu (Medium: Work Status guard lands too late). Fixed. The core-only guard now sits at the TOP of the Work Status section, BEFORE the @liususan091219 (incomplete_fix: gate Chat-path task tracking/task writes). The Chat-path task tracking section IS gated, and before its task-write recipe: CLAUDE.md line 91 (guard) precedes the Both files stay mirrored: |
|
Daily reminder — this PR has an open change-request from 2026-07-13 (3d) with no commits since. Push fixes or reply to the review to reset the clock; ~2 weeks of silence routes to final-notice and then close-as-abandoned per the triage policy.
|
|
Re-verified: resolved by |
|
Blocking finding on the new head
Validation run in
Reviewed by Qingyun's Codex. |
liususan091219
left a comment
There was a problem hiding this comment.
Auto re-review (author pushed after the change-request):
- ask 1: ADDRESSED — The diff adds a core-only/guest-skip gate to Work Status and explicitly extends the rationale to “Ch
All asks addressed — the stale CHANGES_REQUESTED will be dismissed in the next owner-approved batch.
john-the-dev
left a comment
There was a problem hiding this comment.
The core of this — the "Core-only — guests skip this" callouts on Work Status + Chat-path task tracking — is exactly right and worth landing: it directly closes the 2026-07-11 hole where a codex exec automation with cwds=[this repo] auto-loaded the runbook and self-wrote a task-chat every 10 min into the live core's queue. The scoping language (guest-in-checkout vs the single live core) is clear and unambiguous. The rest is a faithful AGENTS.md ↔ CLAUDE.md sync.
One should-fix before merge — the per-host label recipe. This PR newly introduces per-host resolution to AGENTS.md using:
<hostname>=hostname | sed 's/\..*//'
for both PERSONAL_AGENTS.md and pending-questions.md. But that diverges from the canonical recipe everywhere else:
- CLAUDE.md resolves the same two paths via
bash scripts/sutando-config.sh host-label. - The code reader
personal_path()→_host_label()(src/util_paths.py) resolves scutilLocalHostNamefirst, then shorthostname— its docstring cites the DHCP-drift incident (2026-06-22 / #1745): on a Comcast leasehostnamereturnsChis-MBP.hsd1.wa.comcast.net→Chis-MBP, while the stable Bonjour name isChis-MacBook-Pro.
So on any machine where scutil LocalHostName ≠ short hostname, a guest that follows AGENTS.md would write hosts/<hostname-sed>/pending-questions.md, but personal_path() reads hosts/<scutil-label>/… — the reader silently misses the write (split-brain per-host paths, the exact failure #1745 documents). Since AGENTS.md is the Codex-facing runbook, this would propagate the anti-pattern the schedule-crons skill explicitly warns against.
Fix: replace both hostname | sed 's/\..*//' occurrences with bash scripts/sutando-config.sh host-label (matching CLAUDE.md + the code readers). One-line change; happy to push it if useful.
Not blocking the guest-scoping value — flagging so the runbook doesn't ship a host-label recipe that the code won't honor.
liususan091219
left a comment
There was a problem hiding this comment.
Auto re-review (author pushed after the change-request):
- ask 1: REMAINS — Current diff only adds a Core-only guest skip for Work Status/liveness writes (
core-status.json/
Remaining asks above — the change-request stays until they land.
|
Daily reminder — commits landed after the 2026-07-13 change-request, but the auto re-review found asks still open (see the re-review comment above for exactly what remains). Pushing those closes this out; ~2 weeks without them routes to final-notice per the triage policy.
|
|
Conflicts resolved by merging current main and regenerating AGENTS.md via |
|
Flagging before re-reviewing further: this PR's diff has ballooned to 172 files / +15490 -449 (was a 2-file, ~52-line AGENTS.md+CLAUDE.md docs change when I approved it earlier today). Not re-reviewing the full 15K-line diff since it doesn't represent this PR's actual intended change. Recommend rebasing this branch cleanly onto current |
|
Reviewed head c5902c1. No blocking findings. This refresh is back down to the intended docs-only guard: Focused checks passed:
Approval signal from me. Since the PR is still draft, treat this as ready from my review lane once the author is ready to undraft. Reviewed by Qingyun's Personal Codex. |
bassilkhilo-ag2
left a comment
There was a problem hiding this comment.
Draft, so this is a scoping comment rather than a verdict — but I think it needs restructuring before it leaves draft.
The label and the diff disagree by a wide margin
Titled docs(agents): and described as scoping runtime sections so automation agents don't self-write task files. The actual diff is +15490/-449 across 172 files:
src/ 31 files
skills/ 29 files
packages/ 14 files
.github/ 7 files
hooks/ 5 files
scripts/ 5 files
+ package.json, package-lock.json, eslint.config.mjs, ruff.toml,
CLAUDE.md, AGENTS.md, CONTRIBUTING.md, TELEMETRY.md
package-lock.json, eslint.config.mjs and ruff.toml are not docs, and 31 files under src/ is not a documentation change. Whatever the dependency/lint/config churn is, it's riding along invisibly under a docs(...) label — and package-lock.json alone probably accounts for a large share of the +15490, which is exactly the kind of bulk that makes the real change impossible to see.
Per CONTRIBUTING's single-concern rule, I'd split this into at least:
- The actual docs change — the AGENTS.md / CLAUDE.md / CONTRIBUTING.md scoping edit that the title describes. That's the part with the interesting semantics (which sections automation agents may act on), and it deserves careful reading on its own.
- The lockfile / lint-config bump — mechanical, reviewable in thirty seconds, but only if it isn't buried.
- The
src/+skills/+packages/edits — 74 files across three trees needs its own statement of intent; right now a reviewer cannot tell whether these are mechanical doc-header updates (plausible, givendocs/src-map.mdregeneration is a thing in this repo) or behavior changes.
If (3) is mechanical — e.g. every touched file got a header-comment tweak that regenerates src-map.md — say so explicitly in the body, because that single sentence turns 74 unreviewable files into 74 skimmable ones.
Why this matters beyond tidiness
The stated purpose is a guardrail: stopping automation agents from self-writing task files. That's a security-adjacent behavioral boundary, and it's the kind of change where a reviewer needs to read every line of the rule and think about how an agent might route around it. That reading will not happen inside a 172-file diff — it will get an approval based on the title. A guardrail approved by title is not a guardrail.
I'd rather review a 60-line PR on this than skim a 15,000-line one, and I suspect you would rather have that too.
What I did not do
I did not review the content — no verdict on the scoping rule itself, which is the part I actually want to read once it's separable.
bassilkhilo-ag2
left a comment
There was a problem hiding this comment.
My APPROVED from 2026-07-24T14:53 is stale and should not be counted for the current head. Please dismiss it, or treat this comment as the qualification. Flagging rather than silently leaving a vouch on content I have not read.
What changed after I approved. Seven substantive commits landed the same evening, none of them documentation:
16:56 b87f5264 feat(observe): /observe policy core — Policy Interaction Layer MVP
16:57 d23e9ce4 feat(hooks): activity-emitter — durable activity journal
17:31 cd5c5db1 fix(slack-bridge): durable on-disk backup for access allowlist
17:45 c6ece1b2 feat(dashboard): editable schedules — add / edit / delete crons
17:49 f79f590a fix(bridges): allowlist additions default to read-only, not owner
17:57 ba493beb docs(claude): explicit ambient-tier sandbox mapping
18:06 08024406 feat(agent-room-ops): events client (subscribe/pull/SSE stream)
The PR now stands at 172 changed files, +15,490/-449, 55 commits, and of the first 100 files only 10 are .md — the rest are CI workflows, eslint config, hooks, and source. Whatever I reviewed under the title "docs(agents): scope runtime sections core-only", my approval does not cover this.
Two of those commits are ones I would want to read carefully rather than wave through, since they touch authorization rather than presentation: f79f590a (allowlist additions default to read-only, not owner) and cd5c5db1 (durable on-disk backup for the access allowlist). A tier-default change is exactly the class where a stale approval is worst — it looks reviewed and it governs who can do what.
I am not requesting changes and not claiming a defect. I have no finding here; I simply cannot vouch for 172 files I did not read, and an approval that implies otherwise weakens the gate. Given the base is qingyun-dev rather than main, blocking a dev integration branch over my own review hygiene would be disproportionate — hence a comment plus a dismissal request rather than a CHANGES_REQUESTED.
Happy to do a real review of the current head if that is useful; say the word and I will scope it to the authorization-touching commits first.
Context: I audited my own review states tonight and found 13 stale ones — 10 standing approvals and 3 blocks. The blocks are cleared (#2324, #2180 approved after re-verification; #2197 corrected). This is the first of the approvals, and the largest.
|
Addressed the diff-balloon @bassilkhilo flagged — the 172-file / +15490 −449 diff was an ancestry artifact of the base, not real content. Root cause: this PR was targeting Fix (no history rewrite): re-targeted the base The 8 inserted lines are the two mirrored "Core-only — guests skip this" callouts on |
|
Approval signal for current head No blocking findings from the current-base re-review. Against Focused checks passed in an isolated
The remaining Reviewed by Qingyun's Personal Codex. |
john-the-dev
left a comment
There was a problem hiding this comment.
Re-reviewed exact head c5902c193fcb8d28e6d6421fe4042cc17db3069a; replacing the stale old-head blocker with two current merge gates.
The guard implementation itself is sound. The current server diff is now only CLAUDE.md and generated AGENTS.md, +4 lines each (two guard paragraphs plus spacing). Both place the guest exemption before the first status/task write, explicitly cover task/result files, watcher, proactive loop, and liveness, and bash scripts/agents-md-sync.sh --check plus git diff --check pass. Added-line host-path scan is clean. No code/semantics blocker found.
Blocking documentation gate: the PR body still describes an obsolete comparison: it says CLAUDE.md is +2 and AGENTS.md has a much larger 78-line drift catch-up. Against current main, GitHub reports exactly two files and the local exact-head diff is +4/+4. Update the body to describe the current diff so reviewers are not asked to reason about scope that no longer exists.
Blocking freshness gate: the branch is behind current main, remains draft, and this exact head exposes only smoke, CLA recheck, and CLA status—today’s clean-install/coverage/lint gate set is absent. Refresh from main, rerun the complete current required checks, then request exact-head review.
The earlier 172-file concern is not a current-diff blocker: after fetching current main, gh pr diff --name-only and git diff origin/main...HEAD both show only AGENTS.md and CLAUDE.md.
Resolved on current head c5902c1: both Work Status and Chat-path task/result writes are explicitly guarded before their write instructions; exact sync/order verification passes. Current remaining gates are documented in John’s exact-head review.
qingyun-wu
left a comment
There was a problem hiding this comment.
Approval signal for current head da38a861f89c6b1d93614a19c9b0efbece0eaeac (regular comment because the authenticated account owns this PR).
No blockers found. The current diff is the intended docs-only scope: AGENTS.md and CLAUDE.md each add the mirrored core-only guard before the Work Status liveness recipe and before the Chat-path task tracking task/result write recipe. The PR body now describes the current +4/+4 diff and explicitly supersedes the stale drift-catch-up wording, and the branch is no longer draft with the current check set green. That clears the two current gates from john-the-dev's previous review from my lane.
Focused verification passed in an isolated /private/tmp checkout:
bash scripts/agents-md-sync.sh --checkgit diff --check origin/main...HEADgit diff --stat origin/main...HEAD->2 files changed, 8 insertions(+)
GitHub checks and CLA are green on this head.
Reviewed by Qingyun's Personal Codex.
|
Both merge gates addressed at exact head
Verified locally at |
john-the-dev
left a comment
There was a problem hiding this comment.
Re-reviewed exact head 504ec2ad; this replaces my stale scope/freshness blocker on c5902c19. Both requested gates are resolved on the current head: the PR body now accurately describes the two-file AGENTS.md/CLAUDE.md +4/+4 diff, the branch is out of draft, and every exact-head CI/lint/coverage/CLA check is green. An independent conflict-free cumulative merge with current main passes agents-md-sync --check, diff hygiene, hardcoded-path policy, and explicit parity/order assertions: the liveness exemption precedes the first core-status.json write, and the broader guest-agent exemption precedes the first task/result write in both files. The guard still permits explicitly orchestrated core/delegate work while preventing checkout-guest automations from self-injecting runtime tasks.
Worst-case disruption is an actual core being misclassified as a guest; the wording avoids that by limiting the exemption to agents that merely inherit the repo cwd and repeatedly identifies the single live Sutando core as the owner of these mechanics. APPROVE for this exact head. Not merge-ready yet: it is 13 commits behind current main and needs refresh, fresh exact-head gates/review, and a second qualifying maintainer approval.
qingyun-wu
left a comment
There was a problem hiding this comment.
Current-head approval signal for 0fae73ffd492b73c5a321a014f2345a534934cfd (formal self-approval is unavailable because the authenticated account owns this PR).
This is a clean current-main merge refresh. The cumulative PR diff remains only the mirrored AGENTS.md/CLAUDE.md core-only guard, four inserted lines in each; the three topic commits are unchanged. Reviewer-run agents-md-sync 5/5, sync check, diff hygiene, and REVIEW.md path checks pass, and GitHub now reports all required checks green with an APPROVED aggregate decision. The worst-case misclassification concern remains mitigated by wording that exempts only checkout-guest automations while leaving the single live Sutando core responsible for status/task writes. Code-ready; repository merge gates still apply.
Reviewed by Qingyun's Personal Codex.
bassilkhilo-ag2
left a comment
There was a problem hiding this comment.
Verified independently: diff is exactly 2 files, +8/-0 (AGENTS.md, CLAUDE.md), both additions byte-identical — confirmed via gh pr diff + gh api .../pulls/2075/files (additions:8, deletions:0, changedFiles:2), so the earlier 172-file ancestry-balloon I flagged on 2026-07-24 is gone.
Confirmed the bug is real on current main (fetched contents/CLAUDE.md?ref=main directly): no "Core-only" guard exists today under ## Work Status or ## Chat-path task tracking — a scheduled/one-shot automation that cd's into this checkout would inherit the unqualified task-write instructions as claimed.
Checked: guard text is placed before each section's write recipe (Work Status guard before core-status.json write; Chat-path guard before the task-chat-*/results/ recipe) — correct ordering for a top-down reader. Anchor link resolves correctly. No hardcoded host-specific paths in the added lines (/private/tmp is the repo's own documented isolation pattern, not a literal user path). All CI green including the specific refuse AGENTS.md stale vs CLAUDE.md lint and CLA.
Ready to merge — no blockers. (One non-blocking note already surfaced in-thread: this is an advisory guard, not code-enforced; real enforcement is running automations outside the repo cwd, which the PR text itself calls out as the actual fix.)
|
@cla-assistant check |
1 similar comment
|
@cla-assistant check |
Box: multi-agent hygiene — instruction-file scoping (reliability)
Problem (live-caught 2026-07-11): an agent auto-loads
AGENTS.md/CLAUDE.mdby walking up from its cwd. When a scheduled/one-shot agent runs with cwd inside a Sutando checkout — e.g. a Codex PR-review automation — it inherits the core's operational runbook and follows the "Chat-path task tracking" rule, self-writingtask-chatfiles intoworkspace/tasks/that the live core then processes as real owner requests. It ran every 10 min for days and was invisible from the Claude side (Codex automations are daemon-persistent; nothing inCronList/crontab/launchd showed it).Fix: add a core-only guard atop the runtime-operational sections. Any automation/one-shot agent that merely opened the repo is a guest in the checkout, not the core — it must NOT write task/result files, start the watcher, run the proactive loop, or write
state/liveness. Isolate such automations in/private/tmpwith no repo cwd (the safe pattern).Both guards are placed before the first status/task write in their section, so an agent reading top-down hits the exemption before the instruction it exempts.
Scope of the diff (measured at head
504ec2ad)Two files,
+4/+4, insertions only — two guard paragraphs plus spacing, mirrored between the two files.AGENTS.mdis generated, so its half isscripts/agents-md-sync.shoutput, not a hand edit.This supersedes an earlier description in this body that claimed
CLAUDE.md +2and a much largerAGENTS.mddiff carrying 78 lines of pre-existing drift. That was accurate when written and is now obsolete: the drift catch-up landed onmainindependently, so the regen here is a clean+4that mirrors the+4inCLAUDE.md. There is no longer any drift-catch-up scope in this PR — reviewers should not go looking for it.Verification at head
504ec2adbash scripts/agents-md-sync.sh --checkAGENTS.md is up to date, exit 0git diff --check origin/main...HEADgit diff --stat origin/main...HEADFreshness: refreshed from current
mainviaupdate-branch(da38a861→504ec2ad) and taken out of draft, so the current required check set runs on this head. The prior head predated today's gates and exposed only smoke + CLA — that was the basis of the freshness block, not a failure.Earlier review point, already resolved: the per-host label recipe. Both per-host paths (
PERSONAL_CLAUDE.md,pending-questions.md) resolve<hostname>viabash scripts/sutando-config.sh host-labelon this head — matchingCLAUDE.mdand the code readerpersonal_path()→_host_label(). Verified by grep at this head: zerohostname | sedoccurrences in either file, twosutando-config.sh host-labeloccurrences in each. (The grep was checked against a known-positive on the same files so a zero couldn't come from a broken pattern.)No behavior change for the real core (it still does all of this — it is the core). Only automations that inherit the file are told to stand down.