Skip to content

refactor(workflow): idiot-proof the workflow end to end - #4874

Open
iscekic wants to merge 32 commits into
mainfrom
kilo-workflow-cleanup
Open

refactor(workflow): idiot-proof the workflow end to end#4874
iscekic wants to merge 32 commits into
mainfrom
kilo-workflow-cleanup

Conversation

@iscekic

@iscekic iscekic commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Idiot-proofing (ponytail) pass over .kilo_workflow/ and its dev/local + mobile-e2e support code. Zero product changes — this is the agent workflow only.

Three kimi-k3 review rounds on the full diff; round 3 ended No findings. 251/251 dev-local tests, changed-file typecheck/format/lint all green.

What changed

  • E2E slot state machine split. The 338-line e2e-slot.sh became five dumb wrappers (e2e-take-slot/e2e-free-slot/e2e-slot-status/e2e-start-resource/e2e-stop-resource) plus one shared state library. Slots never kill foreign resources; status reports UNACCOUNTED and always exits 0.
  • Learnings pruned. 37 deleted learnings each graduated into a script guard, a doc line, or a merged learning. No dangling references.
  • Fail-loud everywhere. Wrong usage dies immediately with the fix in the message: dispatch-role validates role/section/worktree up front, wrappers forward args to the state engine's guards, pick-reviewers refuses when run in the wrong repo, remote-cli unknown subcommands no longer fall through to start.
  • remote-cli.sh requires the email. The old default minted tokens for an account login.sh never creates (now per-platform); exec with no prepared env says to run start <email> first. Recovery hint points at the slot + e2e-start-resource.sh protocol instead of a bare slotless pnpm dev:start.
  • dev:capture --follow re-targets the tmux pipe instead of silently keeping the old one, and log pipes use repo-absolute node_modules/.bin/tsx (tmux servers started outside a direnv shell have no tsx on PATH).
  • kilo-design skill moved from .kilo/skills/ to .agents/skills/kilo-design-cloud (synced from Kilo-Org/kilo-design); the grilling skill the workflow references is vendored into .kilo/skills/.

Review rounds

  • r1: 3 findings (remote-cli default account, slot-status exit contract, pick-reviewers silent degrade) — all fixed with tests.
  • r2: 3 findings (wrappers swallowed args making guards unreachable, slotless recovery hint, unknown-subcommand fallthrough) — all fixed with tests.
  • r3: No findings.

iscekic added 7 commits July 29, 2026 13:32
Delete learnings whose fixes landed in scripts or WORKFLOW.md (slot teardown,
steer.sh, dispatch-role.sh, monitor rules, TTY rule, env-facts rule, PR image
upload), merge overlapping entries (kilo startup crashes, Android emulator
claims, Android cold-start deep link, device-auth pending limit, iOS26 Maestro
quirks), and trim stale or contradicting paragraphs.
New scripts encode what WORKFLOW.md previously taught in prose: await-role.sh
(round completion, sentinel, void/stall detection), launch-interactive.sh
(planner/orchestrator launches: env strip, TTY, tmux targeting),
slice-diff.sh (slice diff + tamper fingerprint), pr-threads.sh (review-thread
GraphQL plumbing), pick-reviewers.sh (reviewer ranking). e2e-slot.sh now
resolves the caller's own tmux session. WORKFLOW.md codifies the principle:
anything that can be automated away, should be — agents' tokens go to the
work. Merge the two monitor-mode sections, teach every role to grep learnings
when stuck, and graduate the review-threads learning into its script.
e2e-slot.sh: tear down before freeing the slot (releasing marker), drop the
worktree's Android claims on release, abort on partial slot writes, reject
explicit acquire owners. dispatch-role.sh: validate inputs, write a .meta
sidecar. await-role.sh: role-specific sentinels, dead-target fast VOID,
FINDINGS: 0 is void. slice-diff.sh: --binary content-sound fingerprint,
path-scoped commit base, --check mode. pr-threads.sh: cursor pagination,
comment counts, asserting resolve, one-step close. pick-reviewers.sh: empty
result exits 0, per-PR vote dedupe, no error-body parsing. steer.sh:
invocation-tied confirmation, dash-safe greps. New baseline.sh (verifier
byte-identical restore proof), await-interactive.sh (monitor states),
pr-gate.sh (head-pinned mechanical gate). Runbook claims Android serials at
adb visibility; verifier defers teardown to slot release; seven more
graduated learnings deleted.
Native build cache key now covers root patches/, pnpm-workspace.yaml, and
pnpm-lock.yaml. dev:start auto-selects a free port offset on collision and
persists it in the manifest (env > manifest > hash); the docker bridge probe
retries transient failures. Guard test fails when a mobile test file matches
no vitest project. GitHub stub serves paginated /pulls/{n}/files. New
e2e/maestro.sh serializes Maestro per device; login.sh holds a per-email OTP
mutex. Slot release kills the worktree's android emulator session (the
session name is the boot provenance). Runbook: fresh githubUserId for stub
seeding, maestro via the wrapper, batch many criteria per device session.
Five more graduated learnings deleted; .playwright-mcp gitignored.
Slot lifecycle: exclusive mkdir-based release transition, teardown failures
keep the slot, emulator session killed before claims drop with console-port
verification, acquire deadline (45 min) + state-dir validation + owner-last
writes, explicit release refused for live sessions. Dispatch/await: wrapper-
owned .exit file replaces the spoofable in-log marker, repro vs verify
sentinel contracts (--mode repro), role-definition existence and run-id slug
checks, machine-global 3s launch-spacing gate (also in launch-interactive,
where --log is now mandatory). slice-diff --check judges rounds itself and
the spec shows the exact commands. baseline.sh refuses re-snapshots and
in-repo dirs, publishes atomically, reports all divergence. pr-gate: thread
enumeration failures fail the gate, exact bot login + waiver detection,
--assignee/--label/--wait. pr-threads close is retry-safe and repo-checked.
pick-reviewers propagates API failures. maestro.sh keeps its lock trap. New
init-section.sh replaces the hand-assembled section setup. Planner runs two
parallel plan-reviewers per round and overlaps the repro gate with drafting;
E2E follow-ups carry cumulative coverage; slots adopt existing stacks;
runbook launches emulators on explicit probed ports. Five more graduated
learnings deleted.
Ponytail pass over .kilo_workflow plus its dev/local and e2e support code:
split the e2e slot state machine into take/free/status/start/stop scripts,
prune and dedupe learnings, fail loudly on wrong usage everywhere, and act
on three kimi-k3 review rounds (remote-cli required email, slot-status
always-exit-0, pick-reviewers wrong-repo refusal, arg-forwarding wrappers,
slot-aware recovery hint, unknown-subcommand refusal, repo-absolute tsx in
log pipes). Move kilo-design to .agents/skills/kilo-design-cloud and vendor
the grilling skill the workflow references.
# Conflicts:
#	.kilo_workflow/dispatch-role.sh
@iscekic iscekic added the human-ready The PR is ready for human review. label Jul 29, 2026
@iscekic iscekic self-assigned this Jul 29, 2026
Comment thread .kilo_workflow/.e2e-slot-state.sh Outdated
Comment thread .kilo_workflow/e2e-slot-status.sh
@kilo-code-bot

kilo-code-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: 4 Issues Found | Recommendation: Address before merge

Executive Summary

The biggest risk is a broken caller contract in e2e-start-resource.sh's android action: its own usage string now advertises --wait as a caller-suppliable flag, but the script unconditionally appends --wait itself, which double-supplies the flag and trips parseEmulatorStartArgs's strict argument-count check.

Overview

Severity Count
CRITICAL 0
WARNING 2
SUGGESTION 2
Issue Details (click to expand)

WARNING

File Line Issue
.kilo_workflow/e2e-start-resource.sh 71 android action always appends --wait, conflicting with the documented [--wait] caller flag and breaking parseEmulatorStartArgs's arg-count check if a caller supplies it
.kilo_workflow/launch-gate.sh 18 Removed upper-bound clamp on remaining can let sleep block far longer than 3s if $GATE/last is ever ahead of now

SUGGESTION

File Line Issue
.kilo_workflow/pr-gate.sh 13 Orphaned comment line left over after removing the --bot option, now misattributed to --label
.kilo_workflow/pr-gate.sh 57 Poll interval changed to 30s but header doc still says "poll every 120s"
Files Reviewed (incremental, 9eb007d..7024c49, 91 files changed)
  • .kilo_workflow/*.sh workflow scripts (await-interactive, await-role, dispatch-role, e2e-free-slot, e2e-start-resource, e2e-stop-resource, launch-gate, pr-gate, pr-threads, upload-pr-attachment) and WORKFLOW.md/README.md — 4 issues
  • .kilo_workflow/learnings/ deletions/edits — checked for dangling references; one pre-existing dangling reference found in an unchanged file, not introduced by this diff, so not reported as a new finding
  • apps/mobile/e2e/ Maestro → Appium/WebdriverIO migration (appium.sh, flows/*.js, wdio/*.js, login.sh, logout.sh, preflight.sh, AGENTS.md) — no issues
  • dev/local/ wrapper and test files (appium-wrapper.test.ts, cli.ts, mobile-android.ts/test, mobile-simulator.ts/test, mobile-workflow.test.ts, workflow-scripts.test.ts) — no issues
  • services/cloud-agent-next/wrapper/src/session-bootstrap.ts/test, apps/mobile/src/components/agents/chat-composer.tsx, apps/mobile/src/components/kilo-chat/message-bubble-a11y.test.ts, apps/extension/AGENTS.md, apps/mobile/AGENTS.md — no issues, no credential/token logging introduced
  • package.json, pnpm-workspace.yaml, .kilo/agent/e2e-verifier.md — no issues (pnpm-lock.yaml skipped as a generated file)

Fix these issues in Kilo Cloud

Previous Review Summaries (7 snapshots, latest commit 9eb007d)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 9eb007d)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Since the last reviewed commit, pr-gate.sh was reworked to fetch bot comments via the REST issues-comments endpoint filtered by since (matching on updated_at so edited Kilobot summaries are seen) instead of the GraphQL comments field filtered by createdAt, with matching test coverage added for the edited-summary case; no correctness issues found in the changed lines.

Files Reviewed (2 files, incremental)
  • .kilo_workflow/pr-gate.sh - reworked bot-comment lookup to use REST issues/comments?since= filtered on updated_at, matching bot logins with and without the [bot] suffix; dropped the now-unused comments field from the gh pr view --json call
  • dev/local/workflow-scripts.test.ts - updated existing gh stub to the new REST-based API shape and added a new test covering a bot summary comment edited (not just created) after the head commit

Previous review (commit 5346644)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The only change since the last reviewed commit is a formatting-only reflow of a writeFileSync call in a test file (oxfmt); no behavioral difference and no new issues.

Files Reviewed (1 file, incremental)
  • dev/local/workflow-scripts.test.ts - formatting only (multi-line reflow of an existing pnpm stub writeFileSync call), no logic change

Previous review (commit 7083eb4)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental changes (portability, CWD-independence for repo wrappers, defensive arg parsing, and matching test updates) are correct with no new issues.

Files Reviewed (10 files, incremental)
  • .kilo_workflow/await-interactive.sh - stat probe order fixed (GNU -c before BSD -f)
  • .kilo_workflow/await-role.sh - same stat probe order fix
  • .kilo_workflow/baseline.sh - same stat probe order fix (3 call sites)
  • .kilo_workflow/e2e-start-resource.sh - now cds to the script's own repo root before running wrappers, independent of caller CWD
  • .kilo_workflow/e2e-stop-resource.sh - same CWD-independence fix
  • .kilo_workflow/launch-gate.sh - guards non-numeric last timestamp and clamps remaining to avoid bad sleep durations
  • AGENTS.md - doc-only addition of DESIGN.md reference
  • apps/mobile/e2e/login.sh - adds sha256sum fallback before shasum for portability
  • apps/mobile/e2e/remote-cli.sh - fixes set -e short-circuit exit on missing env file, adds arg validation to status
  • dev/local/workflow-scripts.test.ts - updated tests cover the new CWD-independence and arg-validation behavior

Previous review (commit 55b8827)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Both changed files fix real portability bugs correctly: the stat probe order in the slot reaper is swapped so GNU stat -c is tried before BSD stat -f (previously GNU's stat -f succeeded with unrelated filesystem info instead of falling through), and the lock-liveness test now uses a PID that is guaranteed dead on both Linux and macOS instead of a low PID that could be a live process on Linux.

Files Reviewed (2 files, incremental)
  • .kilo_workflow/.e2e-slot-state.sh - 0 issues (stat probe order fixed for Linux/GNU portability)
  • dev/local/mobile-ios-build.test.ts - 0 issues (test PID changed to one dead on both platforms)

Previous review (commit f766903)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The latest commit fixes both previously-flagged issues (worktree resolution now uses the script's own on-disk location; the status header comment now documents the exit-1 usage-error case for forwarded args), and the remaining changed files are markdown table-formatting fixes with no functional impact.

Files Reviewed (8 files, incremental)
  • .kilo_workflow/.e2e-slot-state.sh - fixed: worktree now resolved via git -C "$(dirname "$0")" on the script's own location instead of the caller's CWD
  • .kilo_workflow/e2e-slot-status.sh - fixed: header comment now documents that forwarding extra args causes exit 1
  • .agents/skills/kilo-design-cloud/SKILL.md - 0 issues (markdown table separator formatting)
  • .agents/skills/kilo-design-cloud/overlay.md - 0 issues (markdown table separator formatting)
  • .agents/skills/kilo-design-cloud/reference/product-judgment.md - 0 issues (markdown table separator formatting)
  • .agents/skills/kilo-design-cloud/reference/token-architecture.md - 0 issues (markdown table separator formatting)
  • .agents/skills/kilo-design-cloud/reference/voice.md - 0 issues (markdown table separator formatting)
  • .specs/mcp-gateway-auth.md - 0 issues (spacing fix in a table cell: GET|PUT|DELETE to GET | PUT | DELETE)

Previous review (commit 13e8eb1)

Status: 2 Issues Found | Recommendation: Address before merge

Executive Summary

The new slot-acquisition script resolves the owning worktree from the caller's CWD instead of the script's own location, a behavior change from the deleted e2e-slot.sh that can record the wrong worktree for a slot.

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
.kilo_workflow/.e2e-slot-state.sh 84 Worktree resolution now uses caller CWD (git rev-parse --show-toplevel) instead of the script's own location (old code used git -C "$(dirname "$0")"), which can record the wrong worktree if invoked from outside the intended directory.

SUGGESTION

File Line Issue
.kilo_workflow/e2e-slot-status.sh 7 Header comment claims status "always exits 0", but forwarding extra args to the state engine's status subcommand under set -euo pipefail can cause a non-zero exit.
Files Reviewed (2 files, incremental)
  • .kilo_workflow/WORKFLOW.md - 0 issues (doc-only clarification of worktree/learnings error guidance)
  • .kilo_workflow/init-section.sh - 0 issues (error messages made more actionable; $SECTION remains in scope at both sites)

Fix these issues in Kilo Cloud

Previous review (commit 85e3ac9)

Status: 2 Issues Found | Recommendation: Address before merge

Executive Summary

The new slot-acquisition script resolves the owning worktree from the caller's CWD instead of the script's own location, a behavior change from the deleted e2e-slot.sh that can record the wrong worktree for a slot.

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
.kilo_workflow/.e2e-slot-state.sh 84 Worktree resolution now uses caller CWD (git rev-parse --show-toplevel) instead of the script's own location (old code used git -C "$(dirname "$0")"), which can record the wrong worktree if invoked from outside the intended directory.

SUGGESTION

File Line Issue
.kilo_workflow/e2e-slot-status.sh 7 Header comment claims status "always exits 0", but forwarding extra args to the state engine's status subcommand under set -euo pipefail can cause a non-zero exit.
Files Reviewed (55+ files)
  • .kilo_workflow/.e2e-slot-state.sh, .kilo_workflow/e2e-slot.sh, .kilo_workflow/e2e-free-slot.sh, .kilo_workflow/e2e-slot-status.sh, .kilo_workflow/e2e-start-resource.sh, .kilo_workflow/e2e-stop-resource.sh, .kilo_workflow/e2e-take-slot.sh - 2 issues
  • .kilo_workflow/dispatch-role.sh, await-interactive.sh, await-role.sh, baseline.sh, init-section.sh, launch-gate.sh, launch-interactive.sh, pick-reviewers.sh, pr-gate.sh, pr-threads.sh, slice-diff.sh, steer.sh - 0 issues
  • apps/mobile/e2e/AGENTS.md, login.sh, logout.sh, maestro.sh, remote-cli.sh, github-api-stub/server.mjs, flow YAMLs, apps/mobile/src/lib/vitest-project-coverage.test.ts - 0 issues
  • dev/local/cli.ts, docker-api-probe.ts(.test.ts), mobile-android-build.ts(.test.ts), mobile-android.ts(.test.ts), mobile-ios-build.ts(.test.ts), mobile-native-build.ts(.test.ts), mobile-simulator.ts, mobile-workflow.test.ts, port-offset-lease.test.ts, process-lock.ts(.test.ts), runner.ts, services.ts(.test.ts), tmux.ts(.test.ts), workflow-scripts.test.ts, maestro-wrapper.test.ts - 0 issues
  • AGENTS.md, CONTRIBUTING.md, DEVELOPMENT.md, apps/web/AGENTS.md, .gitignore, package.json, skills-lock.json, .kilo_workflow/README.md, WORKFLOW.md, .kilo/agent/*.md, .kilo/command/kilo-design.md, .kilo/skills/grilling/SKILL.md, .kilo/skills/repository-verification/SKILL.md, .agents/skills/kilo-design-cloud/**, deleted .kilo/skills/kilo-design/**, ~37 deleted .kilo_workflow/learnings/*.md - 0 issues

Fix these issues in Kilo Cloud


Reviewed by claude-sonnet-5 · Input: 40 · Output: 17.2K · Cached: 1.1M

Review guidance: REVIEW.md from base branch main

iscekic added 4 commits July 29, 2026 23:58
Five kilo-auto/efficient rounds over 18 workflow hypotheticals found two
repeat misses: agents hand-repairing a non-init-section worktree (5/5
divergent) and over-filing learnings to learnings/system/ (2/5). Fail loud
with the remedy in init-section's preflight, document that a hand-created
worktree cannot be repaired, and state that system/ is the rare exception.
Kilobot: invoked by absolute path from a sibling repo, acquire recorded the
sibling's root in the slot and broke status UNACCOUNTED matching. Also
correct the slot-status header: arguments are a usage error, only the
no-arg report is exit 0.
GNU stat -f means --file-system and exits 0, defeating the BSD-first mtime
fallback in reap; probe GNU syntax first. The lock-active test used pid 2 as
its dead pid, which is kthreadd on Linux — use a pid above both platforms'
max.
@iscekic

iscekic commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

(bot) @kilocode-bot please review

@kilo-code-bot

kilo-code-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Review: full diff (143 files, origin/main...kilo-workflow-cleanup)

Reviewed the whole diff, not the last commit. Focus per request: the E2E slot scripts, dispatch-role/pick-reviewers/remote-cli/dev:capture, shell correctness (quoting, exit codes, GNU-vs-BSD), deleted learnings, the kilo-design move, and test coverage. I exercised the slot scripts by hand on Linux against stubbed tmux/pnpm/git rather than only reading them.

Overall this is a solid hardening pass. Two findings I'd fix before merge, then a set of minor ones.

1. stat -f is still BSD-first in three scripts — same bug 55b8827 just fixed for the slot reap

55b8827 correctly moved the slot reap to GNU-first, but the identical pattern survives in:

  • .kilo_workflow/await-role.sh:70
  • .kilo_workflow/await-interactive.sh:36
  • .kilo_workflow/baseline.sh:57,60,78 (stat -f %p ... || stat -c %a)

On GNU coreutils, stat -f %m FILE treats both args as filesystems: it errors on %m (so exit status is non-zero and the || fallback also runs) and prints the filesystem block report for FILE to stdout. The captured value is therefore multi-line garbage with the real mtime appended. Reproduced on Linux with the exact function body from await-role.sh:

$ mtime() { stat -f %m "$1" 2>/dev/null || stat -c %Y "$1" 2>/dev/null || echo 0; }
$ AGE=$(( NOW - $(mtime probe.log) ))
bash: line 1: File: unbound variable      # exit 1

Impact:

  • await-role.sh / await-interactive.sh die with a cryptic bash error the moment they reach the quiet-log check, i.e. on every round that has not finished yet — exactly the RUNNING/STALLED path the script exists for.
  • baseline.sh writes that garbage into the untracked.tsv / included.tsv fingerprints. Because the injected text includes free-block counts, it changes between snapshot and check while the run consumes disk — a byte-identical worktree can report divergence.

Fix is the one already applied in .e2e-slot-state.sh: probe stat -c first, stat -f second, and keep the || echo 0 guard.

2. e2e-start-resource.sh / e2e-stop-resource.sh still resolve their target from the caller's CWD

a2e292d fixed acquire to resolve the worktree from the script's own location, because WORKFLOW.md (Slot state, ~L324) tells agents working in a sibling repo to "invoke it by absolute path from a cloud worktree". The two resource wrappers were not given the same treatment — they exec pnpm dev:start / pnpm dev:stop with the inherited CWD:

  • Run /…/.worktrees/<section>/.kilo_workflow/e2e-start-resource.sh stack mobile from ~/Projects/.worktrees/<section>-kilocode and pnpm runs in the kilocode worktree (bun repo, no dev:start), while the slot record says the stack belongs to the cloud worktree. The state and the resource can now disagree silently — the exact class the acquire fix closed.
  • e2e-stop-resource.sh additionally has no _held check (deliberate, per the "unaccounted → stop it" doc path), so a wrong CWD there stops the other worktree's stack instead of failing. That contradicts the "slots never kill foreign resources" property.

Suggested: cd "$(git -C "$(dirname "$0")" rev-parse --show-toplevel)" before the exec, or fail loudly when the caller's toplevel differs from the script's.

Minor

  1. remote-cli.sh status exits 1 on its happy path. cmd_status's last statement is [ -f "$ENV_FILE" ] && echo …; with no prepared env the function returns 1 and set -e propagates it, so a perfectly successful report exits non-zero. Pre-existing, but this PR is precisely about exit-code honesty. Relatedly, status/stop still shift || true and silently discard extra args, so remote-cli.sh status bogus is accepted while an unknown subcommand now refuses.
  2. pick-reviewers.sh bot filter. (^|[-_/])bot([-_[]|$)|\[bot\]$ correctly drops kilo-code-bot (verified: this PR's issue comments are authored by exactly that login, which is also why the pr-gate.sh --bot default is right), but misses infix/no-separator bots such as kilobot or coderabbitai. If gh pr view --json reviews can surface author.__typename == "Bot", that's a sounder filter than the regex.
  3. login.sh uses shasum -a 256 unconditionally for the OTP lock key, while baseline.sh and slice-diff.sh both probe sha256sum || shasum. On a box without perl's shasum the login dies before taking the mutex.
  4. launch-gate.sh --locked trusts $GATE/last: a corrupt value makes the arithmetic fail under set -e, and a future-dated value sleeps for the skew. Worth clamping remaining to 0..3.
  5. .specs/mcp-gateway-auth.md: GET | PUT | DELETE still splits into extra table cells — unescaped | breaks the row regardless of surrounding spaces. Use `GET`/`PUT`/`DELETE` or \|. Also unrelated to the PR's scope.
  6. "251/251 dev-local tests": the CI test job reports tests 251 / pass 250 / fail 0 with one SKIP (restartServiceInTmux injects escaped env values into a non-POSIX shell). Nit on the claim, not on the work.
  7. Coverage gaps for the described fixes. workflow-scripts.test.ts covers take/start/stop/status/free, the forwarded-session refusal, dead-owner reclaim and stale ownerless reap — good. Not covered: e2e-slot-status.sh <arg> → exit 1 (the contract the last commit corrected in the header), e2e-free-slot.sh <arg> → exit 1, e2e-start-resource.sh without a slot, and finding 2's sibling-CWD case. I verified the first three by hand (all behave as documented); the last one is the actual gap.
  8. NOTICE.md dropped with the kilo-design move. The old .kilo/skills/kilo-design/ carried an Apache-2.0 NOTICE for reference files adapted from pbakaus/impeccable. The vendored kilo-design-cloud files look like fresh Kilo content (no impeccable-derived text), so this is probably correct — worth confirming the attribution now lives in Kilo-Org/kilo-design for anything still derived.
  9. Doc coherence after the move. Root AGENTS.md's guidance map no longer references DESIGN.md at all, and the new skill is apps/web-only. The old skill's scope explicitly covered mobile, marketing pages and Storybook, so those surfaces now have no design pointer beyond their nearest AGENTS.md. Intentional?

Verified clean

  • Deleted learnings: all 37 basenames grepped repo-wide at head — zero dangling references. e2e-slot.sh appears only in an explanatory test comment. README.md and WORKFLOW.md list every script in .kilo_workflow/, and every relative link in both resolves.
  • Shell syntax: all 19 .kilo_workflow scripts plus the apps/mobile/e2e scripts pass bash -n. Quoting spot-checks (printf %q command assembly in dispatch-role, ---guarded greps in steer.sh, grep -qxF -- in slot-status) look right.
  • Slot lifecycle, exercised on Linux with stubs: take → start → stop → status → free all as documented; dev:start --no-attach --reuse-running mobile web forwarded verbatim; take/free/status <arg> all exit 1 with the fix in the message; start without a slot prints the take-a-slot remedy; status exits 0 while printing UNACCOUNTED stack: for a foreign kilo-dev-* session and UNACCOUNTED resource: for a foreign android claim record; dead-owner slots reclaimed to stderr.
  • UNACCOUNTED stack matching is exact: tr -c 'A-Za-z0-9_\n-' '_' mirrors getSessionName()'s replace(/[^A-Za-z0-9_-]/g, '_').
  • pr-threads.sh + pr-gate.sh: pipefail makes a mid-pagination gh failure propagate out of threads(), and pr-gate wraps the call in an if, so a half-paginated list cannot read as "0 unresolved". --bot default matches this repo's real bot login.
  • dispatch-role.sh: role/section/label/worktree/scratch/--mode/--file validation, unique dispatch id, wrapper-owned .exit + .meta sidecar, in-pane KILO_*/OPENCODE* strip computed inside the pane, -t "$CALLER_SESSION:" window targeting — all consistent with await-role.sh's contract.
  • login.sh/maestro.sh lock nesting: device lock is always taken before the OTP lock and KILO_MAESTRO_LOCKED/KILO_OTP_LOCKED make the re-entry idempotent, so no ABBA deadlock; per-platform email removes the cross-shard OTP invalidation.
  • dev:capture --follow: tmux pipe-pane without -o does replace the existing pipe, and --stop-follow restores the plain filtered pipe; repo-absolute node_modules/.bin/tsx in buildLogPipeCommand is the right fix for tmux servers with no direnv PATH.

Not deeply reviewed: dev/local/mobile-android.ts (+360) and the port-offset lease internals beyond their new tests.

Built for iscekic by Kilo

@kilo-code-bot

kilo-code-bot Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

The review has been posted: #4874 (comment)

Summary of findings for @iscekic:

Fix before merge:

  1. stat -f still BSD-first in await-role.sh:70, await-interactive.sh:36, baseline.sh:57/60/78 — same bug 55b8827 fixed only for the slot reap. On GNU, this breaks await-role on every unfinished round and pollutes baseline.sh fingerprints with volatile data.
  2. e2e-start-resource.sh/e2e-stop-resource.sh still resolve pnpm from caller's CWD instead of script location (unlike acquire, fixed in a2e292d). Combined with stop having no _held check, invoking from a sibling worktree per WORKFLOW.md's own prescribed usage can stop a foreign stack.

Minor: remote-cli.sh status exits 1 on happy path; status/stop drop extra args silently; pick-reviewers bot-regex edge case; login.sh missing sha256sum probe; launch-gate spacing arithmetic unclamped; broken .specs markdown table row; "251/251" is actually 250 pass + 1 skip; NOTICE.md (Apache-2.0 attribution) dropped with the skill move; root AGENTS.md has no pointer for mobile/marketing/Storybook design docs anymore.

No code changes were made — this was a review-only pass. Let me know if you want me to spawn an agent to fix the two blocking issues.

- probe GNU stat first in await-role, await-interactive, and baseline (the
  same GNU -f/--file-system trap the slot reap fix closed; baseline was
  writing mount garbage into fingerprints)
- run e2e start/stop-resource from the script's own repo, not the caller's
  CWD — a sibling-worktree invocation could start/stop the wrong stack
- remote-cli status exits 0 on its happy path and refuses extra arguments
- login.sh probes sha256sum before shasum, like baseline and slice-diff
- launch-gate clamps a corrupt or future-dated launch timestamp
- restore the DESIGN.md pointer in the root AGENTS.md design row
- cover the new contracts: start without a slot, sibling-CWD wrappers,
  status/free argument refusal
@iscekic

iscekic commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

(bot) Dispositions on the full-diff review (fixed in 7083eb4 unless noted):

  1. stat BSD-first in await-role/await-interactive/baseline — valid, fixed: all sites now probe GNU first. Baseline was indeed writing mount garbage into fingerprints on Linux.
  2. start/stop-resource resolving from the caller's CWD — valid, fixed: both wrappers cd to the script's own repo before exec; the lifecycle test now proves a sibling-CWD invocation still runs pnpm in the cloud worktree.
  3. remote-cli status exit 1 on the happy path + swallowed args — valid, fixed: report always exits 0, extra args refuse with exit 2.
  4. pick-reviewers bot regex — rejected: gh pr view --json reviews exposes only author.login, so non-separator bots like coderabbitai are undetectable there; separator-bots (incl. kilo-code-bot) are caught, the ranking is advisory, and a miss fails loudly at --add-reviewer time.
  5. login.sh shasum — valid, fixed: probes sha256sum first, matching baseline/slice-diff.
  6. launch-gate corrupt/future timestamp — valid, fixed: non-numeric clamps to 0, future-dated clamps to the 3s cap.
  7. .specs/mcp-gateway-auth.md table pipes — rejected as out of scope: pre-existing in an untouched file; deserves its own PR.
  8. 251/251 vs CI skip — noted: the non-POSIX-shell test is macOS-only by design and skips on Linux runners; it passes locally.
  9. Coverage gaps — valid, fixed: start-without-slot refusal, sibling-CWD wrappers, status/free arg refusal all covered now.
  10. NOTICE.md — confirmed clean: the vendored kilo-design-cloud files contain no impeccable/pbakaus-derived text (grepped); attribution where applicable lives upstream in Kilo-Org/kilo-design.
  11. DESIGN.md pointer dropped from root AGENTS.md — unintentional, fixed: restored to the design row.

iscekic added 10 commits July 30, 2026 01:11
Kilobot edits its standing summary comment instead of posting anew, so the
gate's createdAt filter never saw approvals after the first review. Fetch
issue comments via REST with since=<head-date>, which filters by updated_at,
and accept the [bot]-suffixed REST login.
Appium drivers (xcuitest, uiautomator2) install into a machine-global
APPIUM_HOME (~/.cache/kilo-appium) on first use instead of living in the
dependency tree, so only appium and the webdriverio client are deps.
- wdio/ client (per-device deterministic ports, XCUITest/UiAutomator2 caps),
  helpers (full-string-regex selectors, topmost-first index, polling waits),
  run-flow runner (exit code is the verdict), hierarchy dumper
- appium.sh: per-device lock via process-lock, per-device appium server
  lifecycle, driver bootstrap into APPIUM_HOME
- the six launch/login flows ported from YAML to JS, same helper names and
  wait budgets
- login.sh/logout.sh rewired to appium.sh; interfaces unchanged

Old wrapper and YAML flows remain until device parity is proven.
…apper test

appium's postinstall no-ops when installed as a project dependency;
appium-ios-tuntap and edgedriver are unused by the e2e setup. The JUnit
exit-code hack test is obsolete: a node flow's exit code is the verdict.
Removed all maestro-rooted learnings outright (likely stale under the
new driver). Tool-independent product/platform knowledge (a11y bounds,
picker races, drag-cancel timing, mVCP blanks, deep links, simulator
limits) was kept and reworded to the appium wrapper.
Replaced by appium.sh + flows/*.js earlier in this branch. BREAKING:
maestro.sh and the YAML flow files are gone.
… tests

- open-app.js answers Android 'isn\u0027t responding' ANR dialogs with Wait
  and gives Android a 420s launch budget under load (returns early when
  healthy); iOS keeps 30s
- appium.sh bumps whole port blocks when the hashed block is occupied
  and records the winning port for reconnects
- mobile-workflow tests assert the JS flows; appium-wrapper tests cover
  port determinism, slugs, and usage errors
iscekic added 10 commits July 30, 2026 02:47
Triage of every learning, one by one:
- folded 9 into the runbook / WORKFLOW.md / extension AGENTS.md
  (git-token-service binding, deep-link route rule, CLI model id prefix,
  claim-killed-mid-boot recovery, oxfmt yaml caution, fixture patch
  discipline, dispatch --auto rationale, extension analytics key,
  firefox newSession flake)
- deleted 12 obsolete or product-bug learnings (fixes landed: mvcp
  suppression, share-extension pods root, seed collision doc'd; tablet
  startup is a product bug)
- updated 4 stale ones for the appium JS flows and current code
41 -> 28 committed learnings; system copies updated in the main checkout.
…te guards

Ponytail deletions: pr-threads reply/resolve (close covers both), pr-gate
--bot, await --timeout/--stall/--quiet knobs, upload --version mode,
launch-gate impossible clamp, duplicated prose in WORKFLOW.md/README.

Automation (learning graduation):
- dispatch-role refuses e2e-verifier onto a dirty worktree (baseline
  restore would read concurrent edits as false failures)
- e2e-free-slot refuses while the worktree owns a live stack, emulator
  session, remote CLI, stub, or device claim
- e2e-stop-resource ios/android stops the Appium servers of claimed devices
- e2e-start-resource stack refreshes session-ingest bindings, migrates,
  and fails fast on services or the docker bridge never coming up
- same-worktree simulator reclaim boots a device left Shutdown by a
  killed-mid-boot claim (was: alreadyOwned + build 405)
- dev:restart mobile clears this worktree's stale Metro file-map
  (white-screen-after-relayout learning graduated and deleted)
From two independent read-only reviews, applied:
- free-slot refusal names per-kind remedies and matches the runner's
  per-char slug rule; cleanup docs stop the remote CLI and stub first
- appium.sh adopts only its own recorded server (a hash-colliding
  foreign server is skipped by the bump loop) and rejects two flows per
  test invocation
- preflight session-ingest probe prints its fix; stop-resource surfaces
  an Appium server that would not stop
- planner learnings go to $SCRATCH (dirty tree now blocks repro dispatch)
- slice-diff snapshot is per-round; E2E slot teardown waits for
  serialized cases; VOID/VIOLATION escalation unified; pr-threads
  close-only wording; starter-turned-planner needs tmux; mock exception
  keeps justification; yml/yaml format rule covers every file
driver.findElements returns protocol references, not WDIO Element
objects, so element commands need the extracted id. First XCUITest
session on a device builds WebDriverAgent and blows a 15s connection
timeout; give session creation 5 minutes with retries.
…sign-out

XCUITest rejects W3C key-action batches, so text entry goes through
Element Send Keys on the active element and field clearing through
Element Clear. The logout flow's terminal assert raced the sign-out
API call; it now waits for the login page.
- UiSelector consumes backslash escapes silently; regex escapes become
  character classes on the Android selector path
- the sign-out alert title carries a literal '?', matched escaped now;
  a leftover alert from an interrupted run is confirmed and handled
- settle-app's first wait gets the long Android budget (bundle refetch
  after a preflight reconnect), early-returning when healthy
- ANDROID_HOME/SDK_ROOT exported by the wrapper for the uiautomator2
  driver, mirroring the repo's resolution

Verified live: login/logout/re-login on iOS 26.5 sim and Android API 35
emulator, hierarchy on iOS, open-app cold launch on Android.
…d sessions

Wall-time optimizer findings applied (no quality/safety trade):
- e2e-start-resource bundle: stack ∥ iOS claim+build ∥ Android
  start+claim+build, one log per chain — slowest chain sets wall time
- emulator-start --wait polls liveness/visibility/boot_completed in the
  wrapper (8-min envelope); manual boot polling prose graduated away
- appium.sh test runs N flows on one driver session (5-20s saved each)
- selector probes skip the topmost-first sort unless tapping
- pr-gate Kilobot wait polls 30s (was 120); await-role 5s (was 15);
  await-interactive 10s (was 30); pr-threads close fetches thread_info
  once; preflight takes the wrappers' platform hint
Verified: multi-flow runs pass on iOS and Android.
Found by rehearsing the full slot lifecycle end to end:
- stack arm retries a failed reuse once, then stops and starts fresh
  (the runner's own remedy for down never-startable services)
- android arm reuses a live own emulator (record + boot check) and
  starts with --wait otherwise; arg order fixed
- stop-resource's claim grep matched nothing (mangled quoting), so
  Appium servers outlived their bundles
- preflight's platform hint skipped the probe but not the claim branch
echo "recorded emulator not booted; starting fresh" >&2
pnpm dev:mobile:android emulator-stop || true
fi
exec pnpm dev:mobile:android emulator-start "$@" --wait

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

WARNING: --wait is unconditionally appended, but the usage string one line above documents it as a caller-suppliable flag.

The usage string on line 10 (android <avd> [--gpu ...] [--wait]) advertises --wait as an option the caller may pass. But exec pnpm dev:mobile:android emulator-start "$@" --wait always appends --wait itself. If a caller follows the documented usage and passes --wait explicitly, "$@" already contains it, so the final arg list has --wait twice. parseEmulatorStartArgs in dev/local/mobile-android.ts computes expectedArgs from a single args.includes('--wait') check and then requires args.length === expectedArgs, so the duplicate flag makes the length check fail and the dispatch throws its Usage: error instead of starting the emulator.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

last=$(cat "$GATE/last" 2>/dev/null || echo 0)
case "$last" in ''|*[!0-9]*) last=0 ;; esac
remaining=$(( 3 - (now - last) ))
[ "$remaining" -le 0 ] || sleep "$remaining"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

WARNING: Removed upper-bound clamp on remaining can allow an unbounded sleep.

[ "$remaining" -le 3 ] || remaining=3 was deleted. remaining=$(( 3 - (now - last) )) is normally <= 3 when now >= last, but if $GATE/last ever holds a timestamp in the future relative to now (clock skew/NTP resync, or a corrupted file that still parses as digits), now - last goes negative and remaining can become arbitrarily large, so sleep "$remaining" on this line can block far longer than the intended 3-second gate — hanging every subsequent kilo launch that goes through --locked until that sleep completes.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Comment thread .kilo_workflow/pr-gate.sh
#
# --assignee gate-fail unless this handle is among the assignees
# --label gate-fail unless this label is present (monitors: human-ready)
# substring 'bot' matching would let any bot vouch for the head

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

SUGGESTION: Orphaned comment line left over after removing --bot.

The --bot option and its description were removed on the lines above, but this line (substring 'bot' matching would let any bot vouch for the head) is the second half of that removed description and is now left dangling directly under the --label bullet, making it read as a caveat about --label rather than about bot-login matching. Consider deleting this orphaned line.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Comment thread .kilo_workflow/pr-gate.sh
| "\(.user.login) @ \(.updated_at): \(.body | gsub("\\s+"; " ") | .[0:200])"] | .[]' <<<"$COMMENTS")
[ -n "$BOTCOMMENTS" ] && break
[ "$(date +%s)" -ge "$DEADLINE" ] && break
sleep 30

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

SUGGESTION: Poll interval no longer matches the header doc.

The header comment (--wait poll every 120s up to this budget ...) still says every 120s, but this line now sleeps 30s between polls — 4x more frequent than documented. Not harmful, but the doc comment should be updated to avoid misleading callers about how the --wait budget is consumed.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human-ready The PR is ready for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant