Repository navigation
Description
Filed by the domain:spec execution seat (session session_01H2oQebDDxYKfWZusyd8GXk, seat post #6017) at the ACCEPT of PR #15227 (#14414, contract review 5536760564, 2026-09-04T06:43Z). Unassigned; domain:*, type and priority are triage's — this seat does not produce them (the lane table puts packages/services/* under domain:services).
Named reader: the seat that owns packages/services/service-automation.
Blocked-by: #14414
The ruling this executes (maintainer, 2026-09-03, recorded on #14414 as comment 5519397028)
"iteration is single-valued: the zero-based iteration of the enclosing loop, carried through any nesting … The branch index lives on a new optional branch key, present only on steps inside a parallel branch. The engine's runRegion tagger stops discarding the outer region's index for steps the inner region already tagged." Execution order: "the spec half lands first … then the engine tagger in service-automation as a follow-on card … ⛔ no engine-only patch in between." The spec half is PR #15227 (ExecutionStepLogSchema.branch, the iteration describe rewritten) — this card unblocks when it lands.
What to build (packages/services/service-automation/src/engine.ts)
- The
runRegiontagger (thetag()closure that fillsparentNodeId/iteration/regionKindonly on steps that do not already carry aparentNodeId) stops letting the innermost region win outright: a step inside aparallelbranch getsbranch= the branch index andregionKind: 'parallel-branch'; when that parallel node sits inside aloopbody, the step also carriesiteration= the enclosing loop's iteration.try/catchinside a loop keeps today's behaviour (loop iteration oniteration, nobranch). - The engine's local
StepLogEntryinterface still documentsiterationas "Zero-based loop iteration or parallel branch index" — it gainsbranch?: numberand the single-meaning comment (it is not derived from the spec type; keep the two in step by a pin, not by prose). docs/qa/platform-checklist/areas/automation.jsonitemautomation.flow-run-step-nestinggains itsloop { parallel }clause (the dev of PR feat(spec)!: ExecutionStepLog.iteration is single-valued (the enclosing loop iteration); the parallel branch index moves to a new optionalbranchkey (#14414) #15227 measured it has none; it is writable only once the engine writesbranch).
Pins (the ruling's own): a loop { parallel } fixture where a branch step carries both the loop iteration and its branch index; try_catch inside loop unchanged; a parallel node NOT inside a loop writes branch and no iteration. The step records must still parse under ExecutionStepLogSchema from the spec at the head that carries PR #15227 (branch refuses negative / fractional values at the branch path).
Re-check (symbols, not lines):
git grep -n "branch" origin/main -- packages/services/service-automation/src/engine.ts | grep -c "regionKind\|StepLogEntry" # expect 0 today
git grep -n "parallel-branch" origin/main -- packages/services/service-automation/src/engine.ts # the tagger (positive control)
Not this card
⛔ Not the objectui FlowRunsPanel grouping key (${regionKind}#${iteration} at its :156) — a card in objectui, gated on a spec release that carries branch. ⛔ Not a second index key: option B (keep the overload, add loopIteration) was not taken.
Refs: #14414 / PR #15227 (the contract half) · #13681 (the loop { try_catch } containment sibling; its try/catch rule is unchanged) · ADR-0031 (structured regions)
Activity
os-justin commentedon Sep 4, 2026
Unlock note —
domain:specseat,session_01H2oQebDDxYKfWZusyd8GXk, 2026-09-04T08:34Z. The upstream this card names (Blocked-by: #14414) is closed: PR #15227 merged as8ab926beat 08:14Z, andorigin/main4dd5041bcarries the spec half —ExecutionStepLogSchema.iterationis single-valued (the enclosing loop's iteration) and the new optionalbranchkey is declared atpackages/spec/src/automation/execution.zod.ts:151. The engine tagger inpackages/services/service-automation/src/engine.ts(runRegion) still writes the innermost region only and never writesbranch; itsStepLogEntry.iterationcomment still carries the old two-meaning wording; the QA checklist itemautomation.flow-run-step-nestinghas noloop { parallel }clause yet. All of that is this card's. The unlock scan returns the card topm:queueafter re-verifying its file surface on the merged ref; no label is changed by this note.Generated by Claude Code
os-zhuang commentedon Sep 4, 2026
分诊路由 + 解锁(本评论来自分诊座位)· R+150
domain:services·bug·priority:p2·pm:blocked→pm:queue。解锁现验(
origin/main,fetch 后实测 2026-09-04T19:5xZ,⛔ 未只信卡上的解锁评论):⇒
Blocked-by: #14414(PR #15227)的契约半边已在 main 上,连裁决里那句「iteration 单义、branch 另立」都逐字落进了 describe ⇒ 前置放电。引擎侧同一次读取:
git grep -c "parallel-branch" -- packages/services/service-automation/src/engine.ts→ 1(tagger 在),而卡面要求的branch写入尚未出现 ⇒ 缺陷仍在,活没被人顺手干掉。落点:
packages/services/service-automation/src/engine.ts⇒ 车道表packages/services/*归 services(与卡面自陈一致)。bug判据:tagger 今天让最内层区域整个赢掉,把外层 loop 的iteration丢了 —— 写进运行日志的是错的结构化数据,不是缺一个特性 ⇒ Bug 侧。p2:错的是可观测性数据(运行步骤日志的归组),不动执行语义、不丢用户数据 ⇒ 不是 p1。StepLogEntry的iteration注释仍写着「Zero-based loop iteration or parallel branch index」,那句 overload 要随之改单义,且与 spec 类型靠 pin 保持同步,不靠散文;②docs/qa/platform-checklist/areas/automation.json的automation.flow-run-step-nesting要补loop { parallel }子句 —— 它今天写不出来,正是因为引擎还没写branch。⛔ objectui 的FlowRunsPanel归组键不在本卡。Generated by Claude Code
os-warren commentedon Sep 6, 2026
Claim: PM loop round R4⚠️ 若你发现
Session:
session_01XpTx2tbq3pZRYAdoGt6E6YBranch:
claude/issue-15230-region-tagger-branch-keyWorktree:
objectstack-issue-15230Domain:
domain:servicesFile surface:
packages/services/service-automation/src/engine.ts·docs/qa/platform-checklist/areas/automation.json· a new pin file inpackages/services/service-automation/src/Container & model: M,
mode:subagentClause-②: no — 契约增量已由 PR #15227 落在
packages/spec/src/**并在那一轮过了契约复审(5536760564);本卡只让引擎去符合它,不碰任何契约文件。StepLogEntry是导出的、且这次改动不止是加一个可选输出键,停下来报告,由 PM 改判——这条声明是可审计的,不是免检牌。Serial constraints cleared:
engine.ts上一个在飞 PR #16302(#14955)已于本轮落地(main 的提交信息里(#16302)命中 1,仪器带阳性对照);⛔ 其余三张同落engine.ts的卡(#15429 · #15660 · #15812)本轮不并发派,单写者路径一次只许一个。前置已放电 —— 我在真实 ref 上重测过,⛔ 不是只读卡上的解锁评论
:151,我读到的是:157—— main 移动过。认符号不认行号,你也一样。缺陷仍在,同一次读取:
⇒ 活没被人顺手干掉。⛔ 动手前请你自己再跑一遍这三条,读数对不上就停下报告。
要做什么 —— 裁决是维护者的,⛔ 不要重开设计
维护者 2026-09-03 的裁定(#14414 评论 5519397028)逐字如下:
三件事,卡面已写全:
runRegion的tag()闭包不再让最内层区域整个赢掉:parallel分支里的步骤拿branch= 分支序号、regionKind: 'parallel-branch';该 parallel 节点若又坐在loopbody 里,同一步骤还要带上外层 loop 的iteration。loop里的try/catch保持今天的行为(iteration 记 loop 的,无branch)。StepLogEntry加branch?: number,并把:745那句「loop iteration or parallel branch index」改成单义。⭐ 它与 spec 类型靠 pin 保持同步,不靠散文 —— 卡面明写,请落成断言。docs/qa/platform-checklist/areas/automation.json的automation.flow-run-step-nesting补loop { parallel }子句。它今天写不出来,正是因为引擎还没写branch。Pins(裁决自带的三条,一条都不能少)
loop { parallel }fixture:分支步骤同时带 loop iteration 与 branch 序号;try_catch在loop里 —— 不变(这是防回归的对照臂,不是新行为);parallel节点不在 loop 里 —— 写branch,不写iteration。⭐ 步骤记录必须能在带 PR #15227 的 head 上通过
ExecutionStepLogSchema解析(branch路径拒负数与小数——请把这条也测出来,⛔ 不要只测正例)。边界
⛔ 不碰 objectui 的
⚠️ 改完若有测试转红,那是真实读数,报告,⛔ 不要靠调断言让它变绿。
FlowRunsPanel归组键(${regionKind}#${iteration})—— 那是 objectui 的卡,且要等带branch的 spec 发版。⛔ 不取方案 B(保留 overload、另加
loopIteration)—— 裁决没取它。⛔ 不动
packages/spec/**:契约半边已经落了。交付纪律
⭐ 红先于绿:三条 pin 要先在未改的树上跑出预期的红(并把预测写在跑之前),再实现。变异证明按本车道惯例走 blob hash,恢复腿用
git checkout HEAD -- 绝对路径并以git diff HEAD为空 + hash 相等双证。⭐ 门族在最终 head 上用
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands机械推导并全跑;退出码紧接单条重定向之后捕获,⛔ 永不走管道。任何 exit 3PREREQUISITE NOT MET都要满足前置后重跑成真读数,⛔ 不得记作 pass、也不得记作红。⭐ changeset:判断并说理由(引擎写进运行日志的结构化数据变了,flow 作者可观测 ⇒ 大概率不是
skip-changeset形状),⛔ 不要默认。⛔ 不在 PR 正文预测 CI 状态。
⛔ worktree-first,一任务一 worktree;⛔ 永不
git stash。domain:servicesPM 席位 · 认领由 PM 在派发时写Generated by Claude Code
test(service-automation): pin the region index keys BEFORE the tagger…
fix(service-automation): runRegion carries the outer region's index t…
docs(qa): name the filed fixture gap in the loop { parallel } clause (#…
docs(qa): drop an unsupported release-count claim from the loop { par…
os-warren commentedon Sep 6, 2026
os-dev-report
{ "issue": 15230, "status": "done", "branch": "claude/issue-15230-region-tagger-branch-key", "pr": "https://github.com/objectstack-ai/objectstack/pull/16367", "premise_still_valid": true, "summary": "Re-ran all four dispatch probes on origin/main 3e270d4e2 before touching anything and every one matched (execution.zod.ts:157 carries `branch`; `parallel-branch` count in engine.ts = 1, the positive control; engine writes `branch` nowhere = 0; engine.ts:745 still carries the overload comment verbatim). runRegion's tag() closure now splits what \"innermost wins\" governs: IDENTITY (parentNodeId / regionKind / retryAttempt) still belongs to the innermost region outright, while INDEX (iteration / branch) is carried through nesting, an enclosing region filling only what the inner one left undefined — so `loop { parallel }` branch steps carry the row on `iteration` AND the branch on `branch`, `loop { loop }` still keeps the inner loop's iteration, and `loop { try_catch }` is byte-for-byte unchanged. parallel-node.ts now passes `branch: i` instead of `iteration: i`; StepLogEntry gains `branch?: number` with the overload comment rewritten single-meaning. Three files are beyond the surface the card named and each is declared in the PR body: builtin/parallel-node.ts (unavoidable — the tagger writes what `grouping` hands it and the branch index is produced at that call site; the alternative is having the shared tagger sniff `regionKind === 'parallel-branch'` and re-home the value), builtin/try-catch-node.ts (COMMENT ONLY — it asserted \"a loop's own tagger can never reach past this one to a try/catch step\", which this change falsifies; the forwarding it explains is untouched), and one sentence of the pending #14456 changeset which said \"`parallel` branch tagging is unchanged\" and would otherwise ship as a flat contradiction in the same release note as mine. Clause-② check performed: StepLogEntry IS exported, but the change is exactly one optional output key, so the stop condition (\"more than one optional output key\") is not met and I did not stop. The QA clause landed at item revision 3, but its fixture does not exist — showcase declares loop / parallel / try_catch as three separate flows and nests none in another — so the clause scores blocked(fixture); filed as #16356 and named in the item's fixtures.requires rather than left silent.", "tests": "RED FIRST, predictions written before the run and held exactly. Unmodified tree (BASE 3e270d4e2, pin file only, committed at 60f48e6d1 to preserve the reading). vitest `Test Files 1 failed (1) | Tests 2 failed | 2 passed (4)`: loop{parallel} failed with received `leafA@iteration=0/branch=undefined` x3 and `leafB@iteration=1/branch=undefined` x3 against expected iteration=0,1,2 per branch — i.e. `branch` never written and `iteration` carrying the BRANCH index, the defect verbatim; bare parallel failed `AssertionError: expected undefined to be +0` at `expect(leafA?.branch).toBe(0)`. The two control arms were GREEN on that same unmodified tree exactly as predicted: `loop { try_catch }` (try/catch already forwards loopFrame.iteration at its own call site) and the spec's refusal of branch -1 / 1.5 at the `branch` path. The TYPE pin is invisible to vitest (esbuild strips types) and was measured under tsc, where it read `src/builtin/region-index-keys.test.ts(70,27): error TS2344: Type 'RegionKeys' does not satisfy the constraint 'keyof StepLogEntry'. Type '\"branch\"' is not assignable to type 'keyof StepLogEntry'.` NOT-MEASURED trap checked, not assumed: `tsc --listFiles` confirms the pin file is in BOTH the build program (tsconfig.json) and the test program (tsconfig.test.json, 575 files), so the green is about that file. GREEN AFTER, at e64991ffa: `pnpm --filter @objectstack/service-automation test` under the shared lock, `os-verify-lock: VERDICT command-exit 0`, `Test Files 121 passed (121) / Tests 1426 passed (1426)`; `typecheck` (tsc --noEmit + check:test-typecheck) EXIT=0, verdict line `check:test-typecheck: OK — @objectstack/service-automation's test layer compiles ... 0 file(s) / 0 error(s)`. GATES: derived mechanically with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` (58 commands) plus the 4 rosters it flags as living in a directory one of my paths is in = 62; every exit code captured as `cmd > f 2>&1; EXIT=$?`, never through a pipe. First pass at fde99b1ea: 60 exit 0 and 2 exit 3 PREREQUISITE NOT MET (check:dual-build-cjs-loads and check:type-check-debt, both naming an unbuilt dist/) — recorded as neither pass nor red, the prerequisite satisfied with `turbo run build --filter='./packages/*' --filter='./packages/*/*'` (VERDICT command-exit 0) and both re-run into real readings, the latter printing `check-type-check-coverage --re-measure: OK — 5 ledger entr(ies) re-measured, 55 raw tsc error(s) total, none above its recorded number`. FULL UNION RE-RUN ON THE FINAL COMMIT e64991ffa: 62/62 exit 0, verdict lines quoted from the gates themselves, e.g. `check-platform-checklist: OK — 15 areas, 264 items ... symbol anchors: 624/624 resolved`. LINT: NOT narrowed — `pnpm lint` = `eslint . --no-inline-config` over the whole repo, LINT_EXIT=0. One further commit followed the union (93fa7157b, dropping an unsupported 'through two releases' claim from one JSON string), so per the discipline the affected + ratchet families were re-run on that new head: check:platform-checklist, check-platform-checklist-watchdog, check:nul-bytes, check:doc-authoring, check:pm-governed-prose, check-affected-docs, check-drift-comment, check:keyed-text-bounds, check:pm-label-desc-cap, check:skills-token-ratchet, check:pm-skill-ratchet, check:dual-build-cjs-loads, check:type-check-debt, check:type-check-coverage — all exit 0. NO ABLATION WAS RUN, and I am not reporting one: this change adds no guard whose ability to fail needed proving. The discriminating evidence is the red-first pin pair measured on the unmodified tree and re-measured after, which is why the pin file was committed at its RED state before the implementation commit.", "mcp_calls": "6 — issue_read get, issue_read get_comments, search_issues (one targeted dedup query, non-empty so no control word needed), issue_write create (#16356), create_pull_request, add_issue_comment (this report). The public-repo zero-quota page payload was used first and returned only 2 of the 3 comments (the PM claim was absent from the render), which is why get_comments was spent.", "open_questions": [], "out_of_scope_findings": [ "filed as #16356: app-showcase nests no control-flow construct inside another (loop / parallel / try_catch are three separate flows), so the new automation.flow-run-step-nesting `loop { parallel }` clause can only score blocked(fixture) until such a flow lands" ] }Generated by Claude Code
os-warren commentedon Sep 6, 2026
ACCEPT — PR #16367. Verified by PM measurement on the PR's own head
93fa7157b, not from the report.The three files beyond the card's named surface — each checked, each legitimate
builtin/parallel-node.tsiteration: i→branch: i. The alternative — having the shared tagger sniffregionKind === 'parallel-branch'and re-home the value — would put a second writer of the same meaning into the tagger, which is the shape this card exists to remove.builtin/try-catch-node.ts.changeset/contained-failure-visibility.mdgit show origin/main:resolves it; it landed withd30ccb9bd(PR #15609) and is still unreleased. Its sentence "parallelbranch tagging is unchanged" would otherwise ship as a flat contradiction in the same release note as this entry.Clause-② — the declaration held, and the seat checked rather than assumed
My claim declared
Clause-②: nowith an auditable stop condition: stop ifStepLogEntryis exported AND the change is more than one optional output key. Measured:engine.ts:720—export interface StepLogEntry— so it is exported, and the change is exactly one optional output key (branch?: number). The stop condition is not met; the seat correctly did not stop, and said so instead of staying quiet. That is the declaration limb working as designed.The type-level pin is not vacuous — my own ablation, prediction written first
The card asked for the engine interface and the spec type to be kept in step "by a pin, not by prose". A pin nobody has seen fail is prose. Predicted: deleting
branch?: numberfromStepLogEntrymakesRegionKeys(which lists'branch') stop beingkeyof StepLogEntry⇒ TS2344 at the pin. Observed, ontsc --noEmit -p tsconfig.test.json, exit 1:Both halves fire — the⚠️ And the seat is right that this is invisible to
Pickhalf and the invariantEqhalf. The seat's report quoted only(70,27); the(70,5)line means a widening on one side would be caught too, not just a missing key.vitest(esbuild strips types): a greenpnpm testsays nothing about it.Behavioural ablation — mine, prediction written first
Predicted: moving the index writes back inside the
parentNodeId === undefinedblock turns pin 1 red and leaves pins 2 and 3 green, because the try/catch arm gets itsiterationfrom its own call-site forwarding and the bare parallel getsbranchfrom the innermost grouping. Observed exactly:Tests 1 failed | 3 passed (4), the failure being⇒ The control arm is really a control (it does not move with the change), and the tagger split is load-bearing for the case the card is about. Unablated, the pin file reads
Test Files 1 passed (1) / Tests 4 passed (4).Two things for the maintainer's eye — flagged, not blocking
FlowRunsPanelgrouping key${regionKind}#${iteration}as "a card in objectui, gated on a spec release that carriesbranch" — but that spec half already landed (execution.zod.tscarriesbranch), so the gate the card named is no longer closed. Between this PR releasing and that objectui card landing, everyparallel-branchstep groups underparallel-branch#undefined, becauseiterationis now deliberately absent there. ⛔ I did not verify the objectui side — that repo is not checked out in this session, so this is the card's own citation plus this diff, not a measurement.minor, reasoned as matching the contract half. Moving a value from one published key to another is arguably breaking for a reader ofiterationon a parallel-branch step; the changeset says so plainly in its "Reading a run recorded before this change" paragraph rather than hiding it. Naming it because the ruling chose the shape, not the seat.On the QA clause that cannot run
The added
loop { parallel }clause scoresblocked(fixture)— showcase carriesloopandparallelas separate flows and nests neither. The seat wrote the clause anyway, named the gap insidefixtures.requires, and filed #16356. Correct call: the thing the clause pins is settled by the ruling, and a clause missing from this item is exactly what let theiterationoverload sit unmeasured. ⛔ It is recorded as blocked, not as passing.domain:servicesPM seat · verification by local measurement and two independent ablations, not from the seat's reportGenerated by Claude Code
github-actions commentedon Sep 6, 2026
os-closed-card-sweep — machine-findable marker for this generated comment.
Removed the pm-loop state label(s) this closed card no longer claims:
pm:dispatched.runRegioncarries the outer region's index through nesting; the parallel branch index moves tobranch(#15230) #16367, merged.65ec530f8c, merged intomain.bug,priority:p2,domain:services— ownership, priority and outcome are not state claims.A state label claims work is in flight. This card is closed on a merged delivery, so the claim
is stale; every other label is left exactly as it was found. Nothing here is a judgement about
the card, and no verdict-bearing label is ever touched by this sweep.
posted by half-state-patrol run 34055667490 · trigger
scheduleGenerated by Claude Code
fix(service-automation): `runRegion` carries the outer region's index…