Repository navigation
refactor(service-automation): move the run-context and resume bodies into run-resume.ts (#25222 slice 6) - #25567
Conversation
…into run-resume.ts Move-only. The bodies of fifteen AutomationEngine methods move unedited into a new run-resume.ts: resolveRunContext, seedRunGrants, resolveDecisionSegmentContext and expandDeclaredLookups, which resolve a run's context; resume, refuseGatedResume, refuseUndeclaredSuspension, loadSuspendedRun, loadSuspendedRunStrict and creditChildRun, which load and gate a suspended run; and failSuspendedRun, settleUnresumableSuspension, cancelRun, cloneVariablesAtPause and failAncestors, which fail, settle or cancel one. Each method keeps its name, docblock, signature and position in the class and calls its body with a RunResumeHost literal holding exactly the members the body reads; creditChildRun reads none and is 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, userGrantsResolver, recordExpander and nodeTypeVocabularySealed go through getters, because a method reassigns each. refuseInvalidScreenInput and buildSubflowResumeSignal stay in the class: they read the module-private ENGINE_BUILT_SIGNAL. The imports only moved bodies read move with them. 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:
⛔ 5 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 646fce712c2f6c84102340726207f7f89e2cc9d8 && git checkout 646fce712c2f6c84102340726207f7f89e2cc9d8
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 9c43e19f659c40bb065601bda50c7e05250169f2 ad9caf7866d7a4f403b38a890b6a1f21d4334651 && git checkout -B drift-repro 9c43e19f659c40bb065601bda50c7e05250169f2 && git merge --no-ff ad9caf7866d7a4f403b38a890b6a1f21d4334651
node scripts/docs-audit/affected-docs.mjs --json 9c43e19f659c40bb065601bda50c7e05250169f2
|
席位核对 · ACCEPT · 2026-10-24T01:45Z
Generated by Claude Code |
Requested by Sales · project thread
Part of #25222
Clause-②: no
Metadata-protocol: none
Ruling-ref: 6348891160
This is slice 6 of the
engine.tssplit, and its claim is comment 6377626691. The bodies of fifteenAutomationEnginemethods move, unedited, into a new module,packages/services/service-automation/src/run-resume.ts:resolveRunContext,seedRunGrants,resolveDecisionSegmentContextandexpandDeclaredLookups, which resolve a run's context, grants and declared lookups;resume,refuseGatedResume,refuseUndeclaredSuspension,loadSuspendedRun,loadSuspendedRunStrictandcreditChildRun, which load, gate and resume a suspended run;failSuspendedRun,settleUnresumableSuspension,cancelRun,cloneVariablesAtPauseandfailAncestors, which fail, settle or cancel a suspended run, along with its ancestors.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 frommainat03243fc6after slice 5 (#25551) 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 fifteen bodies holds one.check-tenant-audit-censusandcheck-system-context-censusgive byte-identical output at base and head (see Readers).resumeandcancelRun. So no public body with an inferred return type moves.retryExecutionandgetConnectorDescriptors;resolveCanonicalDescriptor,resolveResumeAuthorityandresolveEffectiveSuspension.refuseGatedResumeandrefuseUndeclaredSuspensioncall them through host closures, and they stay in the class;knownNodeTypes,getUnknownNodeTypeAudit,sealNodeTypeVocabularyandwarnUnknownNodeTypes.refuseInvalidScreenInputandbuildSubflowResumeSignal.ENGINE_BUILT_SIGNALandengineBuilt, anddocs/adr/0019-approval-as-flow-node.mdcalls that symbol "module-private".engine.ts. The package's export map is.only andindex.tswould re-export neither, so the symbol would stay out of reach outside the package. Even so, the ADR's line would no longer be true.engine.ts+132 / -546, the new module +689, and the test 1 / 1.What stays in
engine.ts.d.ts.The shape (slices 1 to 5)
The host interface.
RunResumeHostholds the 25 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 thePickofRunResumeHostover exactly the members its own body reads, read off each body's AST in first-read order.creditChildRunreads no member, so it is called directly, without a host.Methods are passed as late-bound closures,
(...a) => this.m(...a), so a spy or test double set on the instance is still the one called. There are 17 of them:seedRunGrants,expandDeclaredLookups,refuseGatedResume,resumeInternal,resolveEffectiveSuspensionandresolveSuspendedNodeType;resolveResumeAuthority,resolveDeclaredResumeAuthority,resolveCanonicalDescriptor,loadSuspendedRunStrictandevictConsumedSuspension;journalConsumedSuspension,forgetSuspendedRun,recordLog,claimAdvance,loadSuspendedRunandfailSuspendedRun.Four fields go through a getter, in slice 5's
storeform ([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), because a method reassigns each. The write sites areengine.tsat this head:store:setSuspendedRunStore(:2769), and the constructor (:2745);userGrantsResolver:setUserGrantsResolver(:3988);recordExpander:setRecordExpander(:3998);nodeTypeVocabularySealed:sealNodeTypeVocabulary(:9424).For each one, the stub builds
const readX = () => this.X;and the host memberget X() { return readX(); }. Each read in the body reads the engine's current value, as it did inside the class.this.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.Four fields go by value:
logger,suspendedRuns,cacheOnlySuspensionsandconsumedSuspensions. The seat asked for proof that none is reassigned after construction:engine.tsat this head):private logger: Logger;private suspendedRuns = new Map…private cacheOnlySuspensions = new Set…private consumedSuspensions = new Map…git grep -nE "this\.(logger|suspendedRuns|cacheOnlySuspensions|consumedSuspensions)\s*=[^=]" -- packages/services/service-automation/srcfinds 1 line, in the constructor:this.logger = logger;(:2744).git grep -nE "\.(suspendedRuns|cacheOnlySuspensions|consumedSuspensions)\s*(=[^=>]|\?\?=)"finds nothing. The bracket form,['logger']and the other three names followed by=or??=, finds nothing either. Andgit grep -nE "\.logger\s*(=[^=>]|\?\?=)" -- packages/services/service-automationfinds only that constructor line.MapandSet, and never rebind the fields.Async. The stubs of the twelve async methods drop
asyncand return the moved function's promise, as in slices 3 to 5.asyncis the only edit to those signature lines, and the.d.tsdoes not printasync.The moved bodies keep saying
this., so the moved lines are unedited.Module-level declarations and imports
engine.ts:buildRunTrigger,NodeExecutionResult,StepLogEntryandSuspendedRun.buildRunTriggerby value, and the rest,AutomationEngineincluded, as types.buildRunTriggeris read only inside function bodies, at call time. A comment above the import says so, as in slices 3 and 5.engine.ts's own imports:FlowParsed,FlowRunSummary,AutomationContext,AutomationResultandResumeSignal;RESUME_AUTHORITY_SERVICEanddescribeThrownForLog;./runtime-identity.jsand./guard-refusal.jsimport lines, verbatim. These are moved lines.engine.tsimport lines change../runtime-identity.jskeepsrunTriggerUserIdandtype SeededRunContext.runIsUnscopedUserMode,flowTouchesDataandrunOrgUserIdswere read only by moved bodies.import { refuseNode } from './guard-refusal.js';goes, because only a moved body readrefuseNode.engine.tsgains the./run-resume.jsimport, after the./run-history.jsone, with a 4-line comment naming its bodies.{@link}s.RESUME_AUTHORITY_SERVICEandSeededRunContextare no longer read inengine.tscode, butengine.tsdocblocks still{@link}them: 2 links each.tsccounts a{@link}as a use undernoUnusedLocals, so both stay imported and the links still resolve.validateScreenInputsandflowTouchesData, whichengine.tsneither reads nor links, to its imports.tscfailed with TS6133 on both. So the setting is live for this file.Proof of move-only
git diff --color-moved=plain --color-moved-ws=allow-indentation-change 03243fc6 HEADoverengine.tsandrun-resume.ts. It detects 534 of the 546 deleted lines and 582 of the 821 added lines as moved.async../run-resume.jsimport and its comment, and the narrowed./runtime-identity.jsimport;async;engine.tsagainst the new module.Module lines. 579 of the module's 689 lines are in 15 verbatim runs of base
engine.ts: the fifteen method texts, de-indented one level. The script names each of the other 110:export functionheads, and fourthisparameter lines, each aPickof the host.Deleted lines. Every line deleted from
engine.ts(546 in all, 8 of them blank) is among the module's lines, apart from the 12 async heads above. The two deleted import lines are among them, verbatim.Added lines. Of the 132 lines added to
engine.ts, 101 are found nowhere in base. They are:./runtime-identity.jsimport;./run-resume.jsimport with its comment;async;The other 31, such as
logger: this.logger,and};, are lines base already contains elsewhere.Literals. No string or template literal in the fifteen method bodies spans a line, checked on the TypeScript AST of base
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
03243fc6, the head ofmain, so the merge-base reading and the base reading are one reading.dist/, built at03243fc6in this container, in a worktree of that commit.dist/, built at this PR's commit.turbo 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 loadSuspendedRunStrict;made public (loadSuspendedRunStrict;) gives6218e57ef5778f6cd7830065199a40ae791e8f5948ce7ba5b329a19b77ef6aa3.private loadSuspendedRunStrictRenamed;) givesbc864cef7530af8f0921d14956e47c22a3d20aad6be293d6b494009cb1020c4e.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-resume.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
03243fc6, 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 byte-identical at base and head.packages/spec/livenessrow cites any of the fifteen 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 identical apart from the scanned-file count, 6691 at base and 6692 at head, the extra file beingrun-resume.ts.engine.tsnorrun-resume.tsis on the anchored surface.docs/adr/0019-approval-as-flow-node.mdis not edited, and its "module-private" line aboutENGINE_BUILT_SIGNALstays true (see Scope).check:startup-registry-verdict(not derived; run by hand at both ends, on the hand-gate list): 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 and the scanned-file count.run-resume.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.tsfrom base:68to:143move up by 1. Lines after base:143move down by 20 until the first moved body.Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon this commit derives 60 families from the 3 changed paths: 1,369 changed lines, under the 3,000-line human-merge threshold.dispatch-gates.mjs --ranreconciles: 60 derived, 60 run, 0 NOT-MEASURED, 0 UNRUN.node scripts/ci/prune-turbo-cache.mjs --self-test, which ci: prune each Test Core Turbo seed to its own run's tasks before saving it #25523 added tomain.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.typecheck(tsc --noEmitandcheck:test-typecheck) passes.origin/main. None of them touchesservice-automation/src, the readers above, their ledgers, ordocs/adr/0019-approval-as-flow-node.md.scripts/check-system-context-census.mjs, but only itsplugin-securityrows.CHANGELOG.mdandpackage.json.mainhas moved to9c43e19f(feat(pm): the queue guard refuses a project-thread Tier S record reviewed from the PR's own account #25436, ascripts/pmqueue guard, and docs(releases): fold #25541, #25550 and #25429 into the 18.1.0 notes #25562, release notes), which touches none of these files.Acceptance notes
refuseInvalidScreenInputandbuildSubflowResumeSignal. Those two stay so that ADR-0019's "module-private" line aboutENGINE_BUILT_SIGNALstays true. The claim says so.storeform. No get/set pair is needed.exportis added toengine.ts.registerFlow, wait on the 33-site ruling.🤖 Generated with Claude Code
https://claude.ai/code/session_01Mnj4nkWuhJ18xEdZvb2cjE