Repository navigation
Host: count and journal a traced unmount's abort-path outcome - #4
Open
mengmengmengqiang wants to merge 1 commit into
Open
mengmengmengqiang wants to merge 1 commit into
mengmengmengqiang wants to merge 1 commit into
Conversation
A traced normal unmount that ends a stalled, already-detached connection carries LayerFS's `unmount` telemetry section (layerfs #501, tokenaissance/layerfs#2): `stage`, `abort`, `bytes` or `errno`, `detached`, `loops_exited`, `loops_joined`, `active`, `retained` and `waited_ms`. The Host now reads it from the trace line's `daemon` telemetry before that telemetry is stripped, counts it by stage and abort disposition, and journals each outcome to `<stateDir>/unmounts.jsonl`. Layers: Entities `packages/model/src/unmount.js` (the value; no imports, no I/O); Use Case `packages/model/src/unmount-count.js` (records through the journal port, counts read from it); Interface Adapter `packages/host/src/unmount-journal.js` (the JSONL file); the Host's trace collection in `collector.js` feeds them, `index.js` composes them, and the count surfaces as the `layerfsTraces` service's `unmountsOf(project)`. No metrics server, OTel or event bus: the journal is the one sink and `unmountsOf` its one reader. A plain unmount carries no section and records nothing. Verification, LAYERFS_SOURCE at ../layerfs-main (9a03429adc, not the pinned fabefdf87a: that is the LayerFS checkout available here): - pnpm test: host, agent wrappers, bindings-shared, mock (10 scenarios), cli, playback, initlimit, historycache, model trace, model unmount, checks: PASS (120 s alarm), twice. - node packages/model/tests/unmount.mjs and packages/host/tests/host.mjs: PASS, three runs each. - The real SDK trace writer of the #501/Ephemeral-AI-Lab#2 branch (api/agent/src/trace.js) was driven for a success, a retained stop, a short write, a failed write and a plain unmount; every line parses exactly, including the section's key order and bytes/errno placement. - pnpm run build: the configured build needs a deepseek-harness checkout with tsdown (DSH_SOURCE); none is here. The client bundle is unchanged. - pnpm run check:layerfs: refused, the available LayerFS checkout is at 9a03429adc, not the pinned fabefdf87a. Tests deleted or skipped: none.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
A normal unmount that has to end a stalled, already-detached connection takes LayerFS's abort path and now reports it beside the ordinary reply, as the
unmounttelemetry section of the traced call (Ephemeral-AI-Lab/layerfs#501, tokenaissance/layerfs#2). The SDK trace line carries that telemetry underdaemon; success still answersUnmounted, a stopped unmount keeps itsTeardownCustody, and a plain unmount carries no section at all.The Host is the durable half: the daemon is ephemeral and the trace files are temporary, so this change reads the section from the trace line, records each outcome in the Host's state folder, and counts it by stage and abort disposition. It adds no second consumer — no metrics server, OTel or event bus: the journal is the one sink and
unmountsOfits one reader. A plain unmount records nothing.Four-layer map
packages/model/src/unmount.jssectionOf/outcomeOf, the stable key set,STAGESandABORTS. Plain data, no imports, no I/O.packages/model/src/unmount-count.jsUnmountOutcomes.record(journals through the port) andcountOf/summaryOf(counts by stage and abort disposition, withwaitedMstotal and max).packages/host/src/unmount-journal.jsUnmountJournal: the append-only<stateDir>/unmounts.jsonl, read back per project, a torn last line skipped.packages/host/src/collector.js,index.jsrecordUnmounton the raw line, beforestripped), the composition root, and the count surfacelayerfsTraces.unmountsOf(project).Dependency rule:
unmount.jsimports nothing;unmount-count.jsimports only the Entities;unmount-journal.jsimports onlynode:fsandnode:path; the Host imports the model. Nothing underpackages/modelimports the Host, and there is no in-memory counter to drift from the journal — the aggregate is counted from what the adapter stored.Telemetry keys
The section's stable keys, as the daemon writes them (
daemon: [{ timing, unmount: { … } }]):stageJoin(the stalled drain was ended by the abort),Abort(the write was short or failed),RevokeorClose(a later step stopped with the abort recorded)abortWritten,ShortorFailedbytesShortwrite's accepted count (only then)errnoFailedwrite's original errno (only then)detachedloops_exited,loops_joinedactive,retainedwaited_msThe journal record adds
at,project,agent,idandreply(Unmountedon success, or the refusal's code —Failedfor a retained stop). The aggregate is{ total, byStage, byAbort, waitedMs: { total, max }, records }; a stage or disposition outside the stable sets is counted under its own key instead of being dropped.Visual evidence
The Host side is the use case plus the journal; the daemon only renders the telemetry section. The count surface is
layerfsTraces.unmountsOf(project), backed by<stateDir>/unmounts.jsonl; no metrics server, no alerting in this PR.Tests
Run in a clean worktree of this branch,
LAYERFS_SOURCE=../layerfs-main(the checkout available here is at9a03429adc, not the pinnedfabefdf87a), each under a 120 s alarm:pnpm testpassed twice;node packages/model/tests/unmount.mjsandnode packages/host/tests/host.mjspassed three runs each.packages/model/tests/unmount.mjs— the section in the daemon map, success (Written) and retained (Failed),Short+bytes,Failed+errno, a plain unmount and malformed sections, the key set, and the use case's counting and journal port.packages/host/tests/host.mjs— driven through the real trace-file collector: a success, a retained stop, a short write and a failed write are counted and journaled underwebsite; a plain unmount and a line with no section add nothing; the journal is read back by a restarted Host; another project sees zeros;daemontelemetry still never reaches the Session records.Beyond the suite, the real SDK trace writer of the #501/#2 branch (
api/agent/src/trace.js) was driven for a success, a retained stop, aShortand aFailedwrite and a plain unmount: every line parses exactly, including the section's key order and thebytes/errnoplacement.Not run here
pnpm run build: the configured client build needs a deepseek-harness checkout withtsdown(DSH_SOURCE); none is present in this environment. The client bundle is unchanged by this PR.pnpm run check:layerfs: refused by the environment,layerfs-source: .layerfs -> …/layerfs-main, at 9a03429adc, not the pinned fabefdf87a.Limitations
LAYERFS_DAEMON_UNMOUNT_ABORTstderr line is not.References
entities(value) ←use cases(counting) ←adapters(journal) ←frameworks(trace collection, service).