Repository navigation
refactor(service-automation): move the run-history bodies into run-history.ts (#25222 slice 5) - #25551
Conversation
…story.ts Move-only. The bodies of nine AutomationEngine methods move unedited into a new run-history.ts: listRuns, listRunsPage, runRecordToLogEntry, suspendedRunToLogEntry, getRun, listSuspendedRuns and readSuspendedRuns, which read runs back, and recordLog and finishRefusedRun, which record them. Each method keeps its name, docblock, signature and position in the class and calls its body with a RunHistoryHost literal holding exactly the members the body reads; runRecordToLogEntry and suspendedRunToLogEntry read none and are called directly. Each async stub drops async and returns the promise the moved function makes. Fields assigned only at their declaration or in the constructor go by value. store goes through a getter, because setSuspendedRunStore replaces it. formatRunSummaryLine's import moves with recordLog, its one reader. The ENGINE_SOURCES grep pins read the new module too. Claude-Session: https://claude.ai/code/session_01Mnj4nkWuhJ18xEdZvb2cjE Co-Authored-By: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 4 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 6 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 6 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin dc30c59f11d1facde5a9b156885ada0b346398c6 && git checkout dc30c59f11d1facde5a9b156885ada0b346398c6
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ab596f82c961f9975e40012fcdbca0d291c69326 9047bcb70475ef42a87dc04015a1cdccf0ee7326 && git checkout -B drift-repro ab596f82c961f9975e40012fcdbca0d291c69326 && git merge --no-ff 9047bcb70475ef42a87dc04015a1cdccf0ee7326
node scripts/docs-audit/affected-docs.mjs --json ab596f82c961f9975e40012fcdbca0d291c69326
|
席位核对 · ACCEPT · 2026-10-24T00:10Z
Generated by Claude Code |
Requested by Sales · project thread
Part of #25222
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6348891160
This is slice 5 of the
engine.tssplit. The claim is comment 6376387561, and the seat agreed the slice on the remaining-bodies table (comment 6376268510). The bodies of nineAutomationEnginemethods move, unedited, into a new module,packages/services/service-automation/src/run-history.ts:listRuns,listRunsPage,runRecordToLogEntry,suspendedRunToLogEntry,getRun,listSuspendedRunsandreadSuspendedRuns, which read runs and suspended runs back;recordLogandfinishRefusedRun, which record a run's log entry and finish a refused run.The change is move-only, and the card stays open for the later slices. The branch is
claude/project-thread-nirtwt, which the harness pins for this session. It was restarted frommainat673ebfd4after slice 4 (#25542) merged. The branch name has no issue number, so the claim comment is the identity record.Scope
this.FIELD.delete/insert/update(sites in 21 methods is still pending with Sales. This slice moves 0 of those sites, because none of the nine bodies holds one.check-tenant-audit-censusandcheck-system-context-censusgive byte-identical output at base and head (see Readers).retryExecutionandgetConnectorDescriptors;resolveCanonicalDescriptor,resolveResumeAuthorityandresolveEffectiveSuspension;knownNodeTypes,getUnknownNodeTypeAudit,sealNodeTypeVocabularyandwarnUnknownNodeTypes.listSuspendedRunsDurable,getSuspendedScreenandcompactStepsForHistoryare one line each, so a stub would be no shorter than the body, and they stay in the class.engine.ts+80 / -659, the new module +781, and the test 1 / 1.What stays in
engine.ts.d.ts.recordLog's docblock and its signature ([#10101],[#13909]) stay above the signature too.The shape (slices 1 to 4)
RunHistoryHostholds the 14 members these bodies read, each typed as the class member itself (AutomationEngine['m']), so a renamed member is a compile error. There is no cast, and no member becomes public or protected.thisas thePickofRunHistoryHostover exactly the members its own body reads, read off each body's AST in first-read order.runRecordToLogEntryandsuspendedRunToLogEntryread no member, so they are called directly, without a host.(...a) => this.m(...a), so a spy or test double set on the instance is still the one called. There are six:listRunsPage,suspendedRunToLogEntry,runRecordToLogEntry,evictConsumedSuspension,compactStepsForHistoryandrecordLog.storegoes through a getter. This is the first time the series uses one, and it follows the seat's shape call.setSuspendedRunStorereassignsstore, so passing it by value could hand a body a store the engine has since replaced.const readStore = () => this.store;and the host memberget store() { return readStore(); }, the form [maintainer] refactor(plugin-security): splitsrc/security-plugin.ts(10,746 lines, +2,803 in 30 days) along itsSecurityPluginseams, one move-only slice per PR #25223 cut 1 (PR refactor(plugin-security): move SecurityPlugin's leaf readers into security-plugin-object-inputs.ts (#25223 cut 1) #25290) uses for non-readonly fields. Each read ofthis.storein the body reads the engine's current store, as it did inside the class.store, or any other field: the generator refuses athis.X =,op=,++or--in a moved body, and found none. So no get/set pair (refactor(plugin-auth): move AuthManager's request routing into auth-manager-request-routing.ts (#25224 cut 10) #25508) is needed.executionLogs,logger,suspendedRuns,cacheOnlySuspensions,maxLogSize,runSummaryLogandconsumedSuspensions. The seat asked for proof that none is reassigned after construction:engine.tsat this head):private executionLogs: ExecutionLogEntry[] = [];private readonly maxLogSize: number;private readonly runSummaryLog: RunSummaryLogLevel;private logger: Logger;private suspendedRuns = new Map…private cacheOnlySuspensions = new Set…private consumedSuspensions = new Map…git grep -nE "this\.(executionLogs|logger|suspendedRuns|cacheOnlySuspensions|maxLogSize|runSummaryLog|consumedSuspensions)\s*=[^=]" -- packages/services/service-automation/srcfinds 3 lines, all in the constructor:this.logger = logger;,this.maxLogSize = …andthis.runSummaryLog = ….git grep -nE "\.(executionLogs|suspendedRuns|cacheOnlySuspensions|maxLogSize|runSummaryLog|consumedSuspensions)\s*(=[^=>]|\?\?=)"finds those 2 constructor lines and one test,src/run-summary.test.ts:295, which overwritesrunSummaryLogwith'off'before the run starts.recordLog, the one body that readsrunSummaryLog, is synchronous, so the body reads the value the test set. That test passes at head.recordLog. It changes them in place, withthis.executionLogs.push(…)and.splice(…)on the engine's own array, and never rebinds the field.listRuns,listRunsPage,getRunandreadSuspendedRuns) dropasyncand return the moved function's promise, as in slices 3 and 4.asyncis the only edit to those signature lines, and the.d.tsdoes not printasync.this., so the moved lines are unedited.Module-level declarations and imports
engine.ts:buildRunTrigger,isTerminalRunStatus,ExecutionLogEntry,RunRecord,StepLogEntryandSuspendedRun.buildRunTriggerandisTerminalRunStatusby value, and the rest,AutomationEngineincluded, as types.engine.ts's own imports:ExecutionStatus,FlowParsed,AutomationContext,AutomationResultandRunListResult;summarizeRun,formatRunSummaryLineanddescribeThrownForLog.engine.tsimport line changes.formatRunSummaryLinewas read only byrecordLog, soimport { summarizeRun, formatRunSummaryLine } from './run-summary.js';keepssummarizeRunalone. That is a moved line: the module carries it verbatim.engine.tstoo.engine.tsgains the./run-history.jsimport, after the./dag-traversal.jsone, with a 3-line comment naming its bodies.storeis a getter.Proof of move-only
git diff --color-moved=plain --color-moved-ws=allow-indentation-change 673ebfd4 HEADoverengine.tsandrun-history.ts. It detects 655 of the 659 deleted lines and 684 of the 861 added lines as moved.listRuns,listRunsPage,getRunandreadSuspendedRuns, each of which dropsasync../run-history.jsimport and its comment;async;engine.tsagainst the new module.engine.ts: the nine method texts, de-indented one level. The script names each of the other 84:export functionheads, and fourthisparameter lines, each aPickof the host.engine.ts(659 in all, 6 of them blank) is among the module's lines, apart from the 4 async heads above.engine.ts, 66 are found nowhere in base: the narrowed./run-summary.jsimport, the./run-history.jsimport with its comment, the four heads withoutasync, and the stub lines. The other 14, such aslogger: this.logger,and};, are lines base already contains elsewhere.engine.ts. So de-indenting changed no string value..d.ts: see the next section.@objectstack/service-automation's suite passes the same at base and head, 199 of 199 files and 2628 of 2628 tests..d.tsreading (ruling 6348891160, item 2)The merge base is
673ebfd4, the head ofmain, so the merge-base reading and the base reading are one reading.dist/, built at673ebfd4in this container, in a worktree of that commit.dist/, built at this PR's commit. The package's dependencies were built byturbo run build --filter=@objectstack/service-automation(31 of 31 tasks), and both sides resolve the same built dependencies.The readings:
index.d.tsandindex.d.cts, which are identical to each other on both sides: base1a5162a4dd929399460a3936e22747a74cf0a8db24d398ffbab5d7ead7bf212e, head1a5162a4dd929399460a3936e22747a74cf0a8db24d398ffbab5d7ead7bf212e.0df32585069f743135440763aabff8c23473231181297c8d164f5f7ee4f10232, head0df32585069f743135440763aabff8c23473231181297c8d164f5f7ee4f10232.index.d.tsbase vs head: 0 lines, with 19,626 lines on each side. This slice reorders no union, so the normalised diff is empty too.node -e 'const s=require("fs").readFileSync(process.argv[1],"utf8");process.stdout.write(s.replace(/"[^"\n]*"(?:\s*\|\s*"[^"\n]*")+/g,r=>r.split(/\s*\|\s*/).sort().join(" | ")))' dist/index.d.ts | sha256sumindex.d.ts, a private member whose body moved. Each control changes that one line, then runs the same normaliser:private recordLog;made public (recordLog;) gives0ec48a1a498558b9a4c85d40790ef91f63014441ec38485bc40b564ad81f8c3b.private recordLogRenamed;) givesad22516615ca006846b45d71c4647406ba6465f5fd420778a8d709e5517473ca.0df32585…, so the normaliser hides neither kind of change.Readers
One reader re-pointed.
src/flow-activation-ledger.test.ts'sENGINE_SOURCESholds the #10243 grep pins, which are absence checks. It gains./run-history.ts, so a moved body is not a place the retired mechanism could come back unseen. That 1-line edit is the PR's third file.Readers examined. The base for each is
673ebfd4, in a worktree of that commit.check-tenant-audit-census(derived): output byte-identical to base.UNTYPED_RECEIVERSrow.--self-testoutput is identical too.check-system-context-census(derived): output byte-identical. It finds 149 elevation read sites in 22 packages across 77 files, in 129 symbols.check:liveness(spec lane, not derived; run by hand aspackages/spec'stsx scripts/liveness/check-liveness.mts): output identical at base and head.packages/spec/livenessrow cites any of the nine methods.check:platform-checklist(not derived; run by hand): output identical at base and head. 987 of 997 symbol anchors resolve; the other 10 are the same ones, the named [finding] the platform-checklist corpus resolves symbol anchors with its OWN rule, not the shared resolver — a permissive token match where the ruling says there is to be exactly one implementation #16898 residual.check-adr-symbol-anchors(not derived; run by hand): output identical at base and head. 2384 anchors across 142 records resolve.check:adr-anchors(decision letters; run by hand at the merge base and the head): output byte-identical. It reads 84 anchored files, 52279 citations across 6684 files, and 1956 decision-letter citations. Neitherengine.tsnorrun-history.tsis on the anchored surface.check:startup-registry-verdict(not derived; run by hand at both ends, and on the hand-gate list from this slice on, as the seat asked): 46 seams, 40 of them read-only, none recording a verdict, at base and at head.--listis the same at both ends apart from line numbers.knownNodeTypes,getUnknownNodeTypeAudit,sealNodeTypeVocabularyandwarnUnknownNodeTypes.run-history.ts.path:linecitations. The only one into this package'sengine.tsisservice-automation/src/engine.ts:44, inpackages/spec/scripts/liveness/evidence.test.ts. It is a realm-attribution fixture thatscanEvidenceparses without reading the file.engine.tsafter base:129move down by 14 until the first moved body.Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon this commit derives 59 families from the 3 changed paths: 1,522 changed lines, under the 3,000-line human-merge threshold.dispatch-gates.mjs --ranreconciles: 59 derived, 59 run, 0 NOT-MEASURED, 0 UNRUN.check:liveness,check:platform-checklist,check-adr-symbol-anchors,check:adr-anchorsandcheck:startup-registry-verdict, with the readings in the Readers section above.@objectstack/service-automation'svitest rungives 199 of 199 files and 2628 of 2628 tests, the same at base and head.tsc --noEmit, and ESLint on the three files, pass.service-automation/src, the readers above or their ledgers. refactor(plugin-auth): move the better-auth global before-hook into auth-manager-before-hook.ts (#25224 cut 13) #25548 and refactor(plugin-security): move the authorization middleware out of security-plugin.ts (#25223 cut 12) #25545 editscripts/check-dispatcher-error-vocabulary.mjsandscripts/check-system-context-census.mjs, but only rows forplugin-authandplugin-security. The Version Packages PR chore: version packages #24876 touches only the package'sCHANGELOG.mdandpackage.json.mainhas moved toab596f82(test(cli): the shared-name install-local jobs test lets each install run to the setup deadline, not a fixed 120 s #25547, aclitest), which touches none of these files.Acceptance notes
store, in the [maintainer] refactor(plugin-security): splitsrc/security-plugin.ts(10,746 lines, +2,803 in 30 days) along itsSecurityPluginseams, one move-only slice per PR #25223 cut 1 form. No get/set pair is needed.registerFlow, wait on the 33-site ruling.🤖 Generated with Claude Code
https://claude.ai/code/session_01Mnj4nkWuhJ18xEdZvb2cjE