Skip to content

fix(telegram): fence staging-temp reaping to dead publishers and exact identity - #3620

Closed
twoimo wants to merge 3 commits into
Yeachan-Heo:devfrom
twoimo:fix/telegram-staging-temp-leak
Closed

fix(telegram): fence staging-temp reaping to dead publishers and exact identity#3620
twoimo wants to merge 3 commits into
Yeachan-Heo:devfrom
twoimo:fix/telegram-staging-temp-leak

Conversation

@twoimo

@twoimo twoimo commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Thank you for the precise review on #3614. Both P1 findings are repaired, and I found that neither actually required coordination with #3596exactUnlink and the no-follow readEndpointFile capture already exist on dev and are already imported by this very file, and the publisher identity you asked for is already present in the staged name the writer produces. So the repair stays entirely inside the unprotected reaper, and no DAEMON_GENERATION bump or guard regeneration is needed.

P1-1 — reaper was not publisher-liveness bound

You were right that shape + mtime cannot distinguish a blocked-but-live publisher from crash residue. Reaping now requires a proven-dead publisher:

  • parseNotificationStagingTemp extracts {destination, pid, stagedAtMs} from the writer's own name shape, gated by the existing validDaemonPid, so pid 0, overflow, and non-integer values yield no claim.
  • classifyNotificationStagingPublisher returns alive | dead | unknown, and maps a throwing probe to unknown.
  • Only dead proceeds. alive, unknown, and no-claim all retain and count as skipped.

This is a different mechanism than the generation encoding you suggested, and I want to be explicit about why: encoding a generation into the staged name would change writeJsonAtomic, which is a protected declaration, requiring the DAEMON_GENERATION bump and manifest regeneration that #3596 owns. The pid is already in the name, so the safety property you asked for — reap only proven-dead claims — is reachable without touching the writer at all. If you would still prefer the generation-encoded form, I am glad to do it once #3596 lands.

P1-2 — pathname-only stat then unlink

Removal is now identity-bound end to end:

  • Identity is captured no-follow through the existing readEndpointFile seam, which rejects symlinks and directories (Endpoint is not a regular file) and concurrent mutation (Endpoint changed while it was read). Both propagate into the existing best-effort catch, so the file is retained.
  • Multi-link files are rejected via a new optional nlink on the stat seam, failing closed when the seam or the field is absent.
  • Deletion goes through exactUnlinkAcceptedWithRetainedEvidence with a distinct .gjc-delete-notification-staging-temp-<uuid>.json quarantine name, so the native verifies dev+ino+size+mtimeNs+sha256 before unlinking.
  • The grace decision now uses the captured identity.mtimeNs rather than a second path-following stat, so the decision and the delete bind the same inode. This also removes a fractional-mtime negative-age hazard.
  • The new quarantine prefix is added to NOTIFICATION_LEAK_ARTIFACT_PREFIXES, so a retained quarantine self-heals on a later pass instead of becoming a new permanent leak.

If either seam is missing, the temp is retained — never an unfenced unlink.

Verification

Nine tests, public surface only, no as any and no private access. On unmodified dev 8 of 9 fail; with the repair 9 of 9 pass. Ran the file 8 consecutive times with zero flakes.

New coverage for exactly the races you named: live publisher never reaped however old, indeterminate liveness retained, unparseable claim retained, dead-past-grace reaped through the identity fence, ABA replacement between capture and delete refused, and a symlink shaped like a staging temp never followed or deleted.

I also isolated each fix and reverted only it, with the other in place, to prove the coverage is specific rather than incidental:

  • Reverting only the liveness gate: the live-publisher and indeterminate-liveness tests fail (your P1-1, reproduced).
  • Reverting only the identity fence: the ABA-replacement and symlink tests fail (your P1-2, reproduced).

The file was verified byte-identical to baseline after each revert/restore cycle.

Gates: 585 pass / 0 fail across five telegram suites, tsc --noEmit clean, biome check clean, and telegram-daemon-generation-guard --validate-current-tree exits 0 with no generation bump demanded. Rebased onto current dev `7f5167c86`.

One honest gap

The startup self-heal call site lives inside the guard-protected run declaration, so threading an injectable pidAlive there tripped the manifest digest check. I reverted that one line; startup self-heal uses the production probe, which is behaviorally correct, but that specific path is not test-injectable. Both exported entry points are fully injectable and every fence branch is covered through them.

@twoimo
twoimo force-pushed the fix/telegram-staging-temp-leak branch from cdb9106 to 614be54 Compare July 31, 2026 03:11
@twoimo

twoimo commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Note on the three Affected path validation failures

These failures are not caused by this pull request. They come from
packages/coding-agent/test/sdk-machine-lifecycle-topology.test.ts, which this
PR does not touch — the diff here is limited to telegram-daemon.ts, its own
test file, and a CHANGELOG line.

I verified this rather than assuming it. In the same worktree, at this PR's exact
head, I reverted the entire diff back to dev and re-ran the failing file:

# with this PR's diff applied
sdk-machine-lifecycle-topology.test.ts -> 3 pass / 4 fail

# with this PR's diff fully reverted to dev (byte-identical to dev for all 3 files)
sdk-machine-lifecycle-topology.test.ts -> 3 pass / 4 fail   <-- identical

The four failing cases are the same in both runs:

  • shipped mcp-serve sdk stdio drives authenticated G03-G07 lifecycle topology with durable effects
  • shipped daemon session CLI drives authenticated G03-G07 lifecycle topology with durable effects
  • shared-agent distinct saved-source IDs remain isolated across inverted MCP and daemon concurrency
  • shared-agent equal saved IDs select one owner without cross-workspace effects in either adapter direction

All four fail on the same assertion shape:

expect(received).toMatchObject(expected)
-   "ok": true,
+   "error": { "code": "cleanup_pending",
+              "message": "Saved session cleanup is pending in transcript:
+                          Exact transcript deletion rejected: cleanup_pending" },

The current dev tip is b1876e593fix(acp): keep retained artifact deletion pending (#3569). That change makes session.delete report cleanup_pending
when retained artifacts are still present, and it updated
acp-session-delete-wire.test.ts, sdk-broker.test.ts,
sdk-broker-lifecycle-e2e.test.ts and others — but
sdk-machine-lifecycle-topology.test.ts still asserts the pre-change
ok: true contract via its expectRetainedAuthorityDeleteSucceeded helper. The
dev tip's own commit status is also red for this workflow, so this is
reproducible independently of any pull request.

This PR's own suites are green at this head, on this base:

  • its own regression file: 9 pass / 0 fail
  • the telegram/notification sweep it belongs to: 528 pass / 0 fail
  • bun scripts/telegram-daemon-generation-guard.ts --validate-current-tree: exit 0
  • tsc -p packages/coding-agent/tsconfig.json --noEmit: exit 0
  • biome: exit 0

I have deliberately not touched sdk-machine-lifecycle-topology.test.ts here, so
that this PR stays a single-concern change. Happy to send the dev repair as its
own separate pull request if that would be useful — please let me know which you
would prefer.

@twoimo
twoimo force-pushed the fix/telegram-staging-temp-leak branch from 614be54 to cd949d4 Compare July 31, 2026 03:58
@Yeachan-Heo

Copy link
Copy Markdown
Owner

CI merge hold — inherited current-dev deletion regression.

The four failing tests exactly match the base and PR #3618: shipped MCP G03-G07, shipped daemon G03-G07, shared-agent distinct saved-source IDs, and shared-agent equal saved-ID owner selection. All return cleanup_pending / Exact transcript deletion rejected: cleanup_pending where proven exact retained-authority cleanup should complete. The staging-temp reaping diff is not the failing surface.

This is inherited #3538 red owned by the sole #3596 product repair lane. No REQUEST_CHANGES/closure is made solely from inherited red, and the contributor branch remains untouched. Merge is held until repaired current dev is green, this exact work is refreshed/retested against that dev, exact CI is terminal green, and a fresh hostile exact-head review returns P0/P1=0. The failed workflow was not rerun or cancelled.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@twoimo

twoimo commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up on my note above: I have opened #3628 with a fix for these three failures.

To restate the attribution briefly — the failing shard is sdk-machine-lifecycle-topology.test.ts, which asserts session.delete returns ok: true. #3569 deliberately changed that to fail closed with cleanup_pending and updated its own acp-session-delete-wire.test.ts, but this test file was not migrated. I confirmed the attribution by reverting my entire diff to dev in the same worktree and re-running: the identical 4 failures reproduce with none of my changes present.

This PR touches only telegram files and cannot influence that test. #3628 is test-only and based on the current dev tip.

@twoimo
twoimo force-pushed the fix/telegram-staging-temp-leak branch from cd949d4 to 01e96c5 Compare July 31, 2026 04:32
@twoimo

twoimo commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Correction / follow-up on the three Affected path validation failures noted above.

I had opened #3628 as a fixture-side adjustment to those failures. The maintainer reviewed it and correctly rejected that approach: encoding permanent cleanup_pending in the topology fixture asserts a false platform invariant and would have masked a genuine product regression instead of surfacing it. The proper repair is product-side and belongs to #3596's lane (keep fail-pending when deletion cannot be proven; return success when exact descriptor-bound single-link cleanup is durably proven). #3628 is closed and I will not re-land a fixture-side variant.

So the status of those three failures on this PR is unchanged and, I believe, still independent of this diff:

  • They reproduce at plain dev with this PR's entire diff reverted.
  • This diff touches no session, ACP, broker, or machine-lifecycle file.
  • The cleanup_pending value originates in packages/coding-agent/src/sdk/broker/lifecycle.ts, which this PR does not modify.

This PR's own gates remain green at the current head: its regression file passes, the telegram/topic sweep passes, the daemon generation guard exits 0, tsc --noEmit exits 0, and biome is clean. I have rebased onto the current dev tip so the review is against an exact head.

Happy to adjust anything on my side — just flagging that I no longer believe there is a contributor-side action available for those three checks, and I did not want to leave my earlier offer of a fix standing after it was correctly rejected.

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Exact-head CI hold update — prior cd949d4e... receipt is superseded.

Five failures have two independent inherited owners:

  1. Four sdk-machine-lifecycle-topology.test.ts failures — shipped MCP, shipped daemon, distinct saved-source IDs, equal saved-ID owner selection — are the existing descriptor-bound cleanup_pending regression owned exclusively by fix(session): complete descriptor-bound cleanup and live migration leases #3596/Dev CI: two shards red — managed-session dropSession fails on retained exactUnlink placeholder #3538.
  2. internal-urls docs index loading is inherited current-dev red owned by fix(docs): refresh model profile index #3630.

No staging-temp-reaping-specific failure is present. This exact head is not merge-ready, but these inherited failures are not contributor mutation authority. No REQUEST_CHANGES/closure is made solely from them; the contributor branch and workflows remain untouched. Reconsideration requires repaired current dev from both owners, refreshed exact-head/current-base CI, terminal green checks, and a fresh hostile exact-head P0/P1=0 review. No workflow was rerun or cancelled.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@twoimo

twoimo commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Small CI status note, so the red checks on this PR are not mistaken for defects in the diff. Both failures reproduce on an unmodified dev tree and neither is reachable from the files this PR touches.

1. Local public surfaces — stale generated docs index (new since 7874b42ef)

- packages/coding-agent/src/internal-urls/docs-index.generated.ts:
  Generated docs index is stale. Run bun run generate-docs-index.

7874b42ef (feat(models): add LunaMaxxing Codex profile) edited docs/models.md without regenerating the embedded index, so the committed index no longer matches the committed docs. The stale entry is the models.md key. Checked out at the dev tip with a completely clean tree, bun run check:public-sync exits 1; running bun --cwd=packages/coding-agent run generate-docs-index produces a 1-line diff and it exits 0 again. The same staleness also fails test/docs-index-lazy.test.ts (4 pass / 1 fail) on plain dev.

I have deliberately not sent a regeneration PR: #3622, #3483 and #3608 already carry that exact regeneration, so a fourth one would be redundant.

2. test:@gajae-code/coding-agent:shard-1-of-8 — the cleanup_pending topology regression

Still the same dev-side item I described earlier. Thank you for the clear verdict on #3628 — you were right that the repair belongs on the product side and that pinning the fixture to permanent cleanup_pending would have masked the regression #3596 is fixing. I have closed that attempt and will not re-send a test-side variant.

The dev tip's own check-runs are red independently of this PR (7 failures at 7874b42ef, including Local public surfaces, docs-index-lazy, and shards 1/3/7/8).

This PR's own verification, re-run at the current base after rebasing onto 7874b42ef:

  • own regression file: green, and still proven fail-without-fix (source reverted to dev with the test kept → the regression fails; restored → green)
  • full notifications-telegram-daemon* + notifications-topic-* sweep: 565 pass / 0 fail
  • bun scripts/telegram-daemon-generation-guard.ts --validate-current-tree: exit 0
  • tsc -p packages/coding-agent/tsconfig.json --noEmit: exit 0
  • bun x @biomejs/biome check on the changed files: clean
  • no file owned by fix(session): complete descriptor-bound cleanup and live migration leases #3596 is touched, and DAEMON_GENERATION is unchanged

Happy to rebase again whenever dev moves. Thanks for taking the time to review.

@twoimo
twoimo force-pushed the fix/telegram-staging-temp-leak branch from 01e96c5 to caa1213 Compare July 31, 2026 04:52
@Yeachan-Heo

Copy link
Copy Markdown
Owner

Exact-head CI hold update — prior 01e96c5a... hold is superseded.

The docs index is now green. The exact remaining failure set is only the four SDK machine lifecycle cleanup_pending cases (MCP, daemon, distinct saved-source IDs, equal saved-ID owner selection), all inherited from the #3596/#3538 descriptor-bound proven-cleanup product regression. Focused staging-temp coverage, coding-agent check, CLI smoke, TS build, and docs index pass.

The hold is therefore narrowed to that sole inherited owner. No contributor mutation, REQUEST_CHANGES, closure, rerun, or cancellation is performed. Reconsideration requires the #3596 product repair merged into current dev, a refreshed exact-head/current-base terminal-green run, and a fresh hostile P0/P1=0 review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@twoimo
twoimo force-pushed the fix/telegram-staging-temp-leak branch 2 times, most recently from 10fa755 to beaed4c Compare July 31, 2026 05:58
@twoimo
twoimo force-pushed the fix/telegram-staging-temp-leak branch from beaed4c to a227267 Compare July 31, 2026 06:36
@twoimo

twoimo commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Head refreshed onto current dev 4512ddfaa

Thank you for the clear hold conditions. This is the head-refresh step only — I am not asking for reconsideration yet, since the dev-side prerequisite is still outstanding.

What changed: rebased from e510bff00 onto current dev tip 4512ddfaa (clean, no conflicts). No source changes were made during the rebase; the two commits are unchanged in content.

Re-verified on the new base:

Gate Result
Own regression suite pass, 0 fail
Telegram sweep (daemon + topic-registry + own regression) 0 fail
telegram-daemon-generation-guard.ts --validate-current-tree 0
tsc -p packages/coding-agent/tsconfig.json --noEmit 0
Forbidden/owned surfaces touched none
DAEMON_GENERATION untouched

Dev-side prerequisite still open. At dev tip 4512ddfaa, packages/coding-agent/test/sdk-machine-lifecycle-topology.test.ts still reports 3 pass / 4 fail, every failure being the same shape:

- ok: true
+ ok: false
+ error: { code: "cleanup_pending",
+          message: "Saved session cleanup is pending in transcript:
+                    Exact transcript deletion rejected: cleanup_pending" }

Because both of these fixes must live in packages/coding-agent/src/sdk/bus/telegram-daemon.ts, the affected-path selector in scripts/ci-dev-affected.ts force-selects test:@gajae-code/coding-agent:shard-1-of-8 (that directory is listed in CODING_AGENT_SHARD_ONE_COVERAGE_PATHS), and that shard carries the topology test. So the inherited red will persist on these heads until the dev-side repair lands — it is not reachable from either changeset.

I will refresh again once repaired dev is green, and only then request a fresh exact-head review. Happy to hold as long as needed, and grateful for the review time.

@twoimo

twoimo commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Exact-head CI status: one remaining dependency, now identified

Following up on my earlier head-refresh note with a narrower result. I have now attributed every failing test in this PR's CI run, and the remaining red reduces to a single dependency.

This PR's exact-head CI: 14 SUCCESS / 5 SKIPPED / 3 FAILURE.

The three failures are test:@gajae-code/coding-agent:shard-1-of-8 plus its two aggregators (evidence producer and Affected path validation, both of which report only "required affected shards did not succeed"). So there is one root cause, not three.

Shard-1's sole failing test in the latest run is:

InteractiveMode goal mode integration > completes goal state even when a goal_updated extension hook throws

That test is unrelated to this PR. It fails on unmodified dev because #emitExtensionEvent now calls extensionRunner.emit(event, undefined, deliveryScope) (the per-attempt scope facility from #3592/#3608), while the test asserts exact single-argument arity. I opened #3637 for it; that PR is terminal green (13 SUCCESS / 0 FAILURE).

I also found and fixed the other two inherited-red root causes that were affecting this PR:

Why shard-1 is unavoidable for this PR: scripts/ci-dev-affected.ts lists packages/coding-agent/src/sdk/bus/ in CODING_AGENT_SHARD_ONE_COVERAGE_PATHS, so any change to telegram-daemon.ts force-selects shard-1. That is structural and not something this PR can route around.

Consequence: once #3637 merges, I expect this PR to reach terminal green on a refreshed head. I will rebase and re-run as soon as that lands, per your hold conditions.

On this PR's own correctness, unchanged and re-verified on the current base:

  • own regression suite: green, and fails on unmodified dev (fail-on-dev proof)
  • telegram sweep: green
  • bun scripts/telegram-daemon-generation-guard.ts --validate-current-tree: exit 0
  • bun run check:types: exit 0
  • biome check: exit 0
  • DAEMON_GENERATION: untouched
  • forbidden/owned surfaces: none touched

Two corrections to earlier statements of mine, for the record:

  1. I previously reported that fix(session): complete descriptor-bound cleanup and live migration leases #3596's head repaired the sdk-machine-lifecycle-topology.test.ts failures. That comparison was invalid — my local natives binary was stale. With freshly built natives, topology passes at the current dev tip as well, so my claim did not isolate fix(session): complete descriptor-bound cleanup and live migration leases #3596. I have posted a retraction on fix(session): complete descriptor-bound cleanup and live migration leases #3596.
  2. I earlier believed biome was not CI-enforced. It is, via the check:@gajae-code/coding-agent task. fix(coding-agent): restore import order in managed cleanup fixture #3635 came out of that correction.

Thank you for your patience with the inherited-red churn — I recognize it is noise on your review queue, and I have tried to remove its causes rather than ask you to look past them.

@twoimo
twoimo force-pushed the fix/telegram-staging-temp-leak branch from 1a6ebe5 to c7c7395 Compare July 31, 2026 08:20
@twoimo

twoimo commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Exact-head CI is now terminal green — hold conditions 1–3 satisfied

Thank you for your patience while the inherited dev-side red was cleared. This head is now terminal green with zero failing checks, so I wanted to summarise the state against your stated hold criteria.

Your hold text was:

No REQUEST_CHANGES disposition is made solely from inherited red. Merge is held until repaired current dev is green, this head is refreshed/retested on that dev, exact CI is terminal green, and a fresh hostile exact-head review returns P0/P1=0.

Current status of each clause:

  1. Repaired current dev is green — for everything this PR's CI selects. Dev tip dd53a6895 now contains the two repairs that were producing this PR's inherited red:

    • #3635 (merged, dev 14908e8eb) cleared check:@gajae-code/coding-agent, which was failing on a biome organizeImports violation introduced by 4512ddfaa.
    • #3637 (merged, dev dd53a6895) cleared shard-1's only failing test, InteractiveMode goal mode integration > completes goal state even when a goal_updated extension hook throws.
    • The four sdk-machine-lifecycle-topology cleanup_pending failures no longer appear in shard-1's selected file set, and pass locally at dev tip (7 pass / 0 fail).
    • Three dev-red families remain (agent-session-message-pipeline, agent-session-auto-compaction-continue, agent-session-abort-timeout), but none of them are in shard-1's 152-file selection, so none affect this PR. #3641 covers the first; the other two are product-side in agent-session.ts under your #3638.
  2. Head refreshed/retested on that dev — rebased onto dd53a6895, no conflicts, force-pushed with --force-with-lease.

  3. Exact CI is terminal green17 SUCCESS / 5 SKIPPED / 0 FAILURE on this exact head, including test:@gajae-code/coding-agent:shard-1-of-8, check:@gajae-code/coding-agent, Telegram daemon generation guard, Local public surfaces, and all four gjc-state-gates.

  4. Fresh hostile exact-head review returning P0/P1=0 — this is the only remaining clause, and it is yours to make. I have re-requested review.

Local verification re-run on the refreshed head:

  • Own regression suite: green, and still fails on unmodified dev (fail-on-dev proof intact).
  • bun scripts/telegram-daemon-generation-guard.ts --validate-current-tree → exit 0.
  • bun run --cwd packages/coding-agent check:types → exit 0.
  • bun x @biomejs/biome check → exit 0.
  • DAEMON_GENERATION untouched; zero files touched under any surface owned by #3596.

I have deliberately not included the #3637 fix in this branch, to keep this PR's diff scoped to its own defect and avoid overlapping an already-open PR.

Please let me know if you would like anything restructured, or if you would prefer a different decomposition of the change. I am happy to revise.

@twoimo
twoimo force-pushed the fix/telegram-staging-temp-leak branch 2 times, most recently from 69967ba to bf9abfa Compare July 31, 2026 09:03
@twoimo

twoimo commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up: the previous run on this branch had its Telegram daemon generation guard job cancelled by infrastructure, which cascaded into evidence producer and the Affected path validation aggregator (both of which only report "required affected shards did not succeed"). shard-1-of-8 itself passed on that run.

Since re-running jobs requires admin rights on this repository, I re-pushed the identical tree under a new commit sha to trigger a fresh run:

  • previous head 69967ba12, tree 061bb0e01
  • current head bf9abfa9a, tree 061bb0e01 (byte-identical tree)

The fresh run is now terminal green: 17 SUCCESS / 5 SKIPPED / 0 FAILURE.

Both this PR and #3618 are green at the same time on current dev, with no inherited red remaining. Thank you for your patience with the CI churn.

@twoimo
twoimo force-pushed the fix/telegram-staging-temp-leak branch from bf9abfa to b8189ed Compare July 31, 2026 09:15
twoimo added 2 commits July 31, 2026 18:47
The notification self-heal reaper never claimed `.tmp` staging files, so every
failed publication left one unreachable file in the agent notifications
directory permanently.

`writeJsonAtomic` stages a sibling `<name>.<pid>.<epoch-ms>.<suffix>.tmp` and
renames it over the destination. If the staging write or the rename fails, or
the process dies between the two, that temp is never published and never read
again. No prefix in NOTIFICATION_LEAK_ARTIFACT_PREFIXES matched it, so the
reaper walked past it. This accumulates across the roots registry, daemon
state, callback aliases, seen-update ids, and the topic registry snapshot, and
is most visible where a rename-blocking condition persists (Windows EPERM from
an antivirus or indexer handle, EACCES, EIO, ENOSPC).

Reaping is shape-matched and still bounded by the existing five-minute mtime
grace window, so a temp an in-flight publication is still staging is never
removed. Fixing it in the reaper rather than the writer also reclaims temps
orphaned by a crash, which no writer-side unwind can reach, and leaves the
protected `writeJsonAtomic` lifecycle declaration byte-identical so no
DAEMON_GENERATION bump is required.
…t identity

The staging-temp reaper decided purely on filename shape plus a
path-following mtime, so a live-but-blocked publisher whose temp aged past
the grace window could have its in-flight file deleted, and the
readdir/stat -> unlink gap allowed an ABA replacement or a symlink to be
destroyed by pathname.

Reaping now requires a proven-dead publisher and an identity-bound delete:

- The publisher pid is parsed from the writer name shape the writer already
  produces, and only a probe result of `dead` proceeds. `alive` and any
  indeterminate or throwing probe retain the file and count as skipped.
- Identity is captured no-follow via the existing `readEndpointFile` seam,
  multi-link files are rejected, and removal goes through `exactUnlink`,
  which verifies dev+ino+size+mtimeNs+sha256 before unlinking. The grace
  decision uses the captured `mtimeNs`, so the same inode is bound from
  decision through deletion.
- Missing seams retain rather than fall back to an unfenced unlink, and the
  staging quarantine name is added to the leak-artifact prefixes so a
  retained quarantine self-heals instead of becoming a new leak.

No protected declaration changes, so no DAEMON_GENERATION bump is required.
@twoimo
twoimo force-pushed the fix/telegram-staging-temp-leak branch from b8189ed to 4e68136 Compare July 31, 2026 09:47

@Yeachan-Heo Yeachan-Heo left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

REQUEST_CHANGES — exact-head hostile review of b8189edb4d7fb01dec9ae8c56ab7baa8753f026c against base e255909797c5022434e76bfbb4eea03b9bd7149c.

P0=0, P1=1. reapAbandonedNotificationStagingTemp accepts a native cleanup_pending retained quarantine whose new prefix is included in the generic leak-artifact scan. That generic reaper uses path-following stat followed by bare unlink; a same-name replacement in the interval can be deleted without no-follow identity proof, undoing the exact-unlink ABA fence. Retained staging quarantines must be reaped through exact no-follow identity authority, with a replacement-race regression test.

Exact checks are terminal 17 success / 5 skipped / 0 failed, but source authority blocks merge. There is no source-file overlap with #3596’s session/native owner; only a runtime dependency on the existing native exact-unlink contract. #3618 shares telegram-daemon.ts but modifies disjoint topic-settlement hunks. Contributor branch/workflows were not mutated, rerun, or cancelled.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@twoimo

twoimo commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

Thank you for identifying the retained-quarantine gap. Successor head ef73be63a removes the generic path-following stat plus bare unlink path.

Every recognized retained notification leak artifact now requires the same no-follow endpoint capture, single-link regular-file check, captured-identity age check, and native exact unlink used by abandoned staging temps. If any identity seam is unavailable or changes, the artifact is retained. A further retained cleanup uses an existing recognized exact-unlink placeholder prefix, so it remains supervised by the same safe path.

I added the requested replacement-race regression for a retained staging quarantine: the pathname is replaced after no-follow identity capture and before exact unlink, and the successor survives while the reaper reports the artifact skipped.

Verification on the successor head:

  • staging-temp/leak suite: 10 pass, 0 fail (45 assertions)
  • Biome check clean on affected files
  • coding-agent typecheck clean

The previous review head is superseded; no merge is requested.

@Yeachan-Heo Yeachan-Heo left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Hostile disposition: REQUEST_CHANGES

Fresh dev refresh: dev@66475d50377073cd2e028da0a0377964babbaf45; exact PR head: ef73be63a1f72d4efc1a19744dfa77aa4ce61029. The patch addresses a predecessor fence/settlement finding with useful tests, but the PR is stale against current dev and the review does not establish a direct, independent mapping of every earlier P1 to the successor code and regression behavior. In particular, the claimed two-phase durable settlement and compensation path needs exact-current-base verification, including refused settlement, stale rollback, epoch saturation, durable commit failure, and route/quarantine invariants. Green CI alone is insufficient.\n\nNo merge or build performed. Rebase onto current dev and provide a fresh signed verdict with the direct predecessor-to-successor mapping and focused regression results.\n\n—\n*[repo owner\x27s gaebal-gajae (clawdbot) 🦞]*

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Hostile disposition: REQUEST_CHANGES

Fresh dev refresh: dev@66475d50377073cd2e028da0a0377964babbaf45; exact PR head: ef73be63a1f72d4efc1a19744dfa77aa4ce61029. The patch addresses a predecessor fence/settlement finding with useful tests, but the PR is stale against current dev and the review does not establish a direct, independent mapping of every earlier P1 to the successor code and regression behavior. In particular, the claimed two-phase durable settlement and compensation path needs exact-current-base verification, including refused settlement, stale rollback, epoch saturation, durable commit failure, and route/quarantine invariants. Green CI alone is insufficient.\n\nNo merge or build performed. Rebase onto current dev and provide a fresh signed verdict with the direct predecessor-to-successor mapping and focused regression results.\n\n—\n*[repo owner\x27s gaebal-gajae (clawdbot) 🦞]*

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.

2 participants