Files
everyinc__compound-engineer…/tests/ce-babysit-pr-contract.test.ts
2026-08-12 11:30:04 -07:00

648 lines
38 KiB
TypeScript

import { readFile } from "fs/promises"
import path from "path"
import { describe, expect, test } from "bun:test"
// Cross-skill contract parity for ce-babysit-pr's delegation seams. These tokens are protocol
// shared between a producer skill and a consumer skill; a rename or drop on one side alone breaks
// the loop silently (babysit mis-parses a ce-debug status, or references a trajectory field
// pr-snapshot no longer emits). Each assertion below fails under exactly that one-sided drift.
//
// Sensitivity note: presence/exact-set based. It catches renames and drops (the drift that
// actually happens); a field added to BOTH sides in the same change is in sync and intentionally
// does not fail. The emitter-set check additionally catches an emitter-only addition.
async function readRepoFile(relativePath: string): Promise<string> {
return readFile(path.join(process.cwd(), relativePath), "utf8")
}
const BABYSIT = "skills/ce-babysit-pr/SKILL.md"
const WATCH_LOOP = "skills/ce-babysit-pr/references/watch-loop.md"
const CEDEBUG_PIPELINE = "skills/ce-debug/references/pipeline-mode.md"
const CERESOLVE = "skills/ce-resolve-pr-feedback/SKILL.md"
const CERESOLVE_FULL_MODE = "skills/ce-resolve-pr-feedback/references/full-mode.md"
const PR_SNAPSHOT = "skills/ce-babysit-pr/scripts/pr-snapshot"
// ce-debug's pipeline-mode structured return. babysit branches on this exact set (Step 2 step 5)
// and warns "do not invent infra-retry/stale" — so both the vocabulary and the ban are protocol.
const CEDEBUG_STATUS = ["fixed-and-pushed", "diagnosed-no-fix", "flaky-infra", "needs-human"]
// pr-snapshot's trajectory block (emitted by _update_trajectory). The subset each consumer cites
// by name is protocol for that consumer: rename a field in the emitter and the citation dangles.
const TRAJECTORY_FIELDS = [
"check_recur_max",
"recurring_checks",
"unresolved_threads",
"unresolved_series",
"unresolved_trend",
"new_threads_this_tick",
"stream_alternations",
"heads_since_progress",
]
const BABYSIT_TRAJECTORY_REFS = [
"check_recur_max",
"recurring_checks",
"unresolved_trend",
"new_threads_this_tick",
"stream_alternations",
"heads_since_progress",
]
const CERESOLVE_TRAJECTORY_REFS = ["unresolved_trend", "new_threads_this_tick"]
function emittedTrajectoryKeys(script: string): string[] {
const fn = script.slice(script.indexOf("def _update_trajectory"))
const retStart = fn.indexOf("return {")
const block = fn.slice(retStart, fn.indexOf("\n }", retStart))
return [...block.matchAll(/"([a-z_]+)":/g)].map((m) => m[1])
}
describe("ce-babysit-pr cross-skill contract parity", () => {
test("ce-debug pipeline return-status enum agrees between producer and babysit consumer", async () => {
const [producer, consumer] = await Promise.all([readRepoFile(CEDEBUG_PIPELINE), readRepoFile(BABYSIT)])
for (const status of CEDEBUG_STATUS) {
expect(producer, `ce-debug must still emit '${status}'`).toContain(status)
expect(consumer, `babysit must still branch on '${status}'`).toContain(status)
}
// The ban babysit states must remain true of the producer, or the warning is stale.
expect(producer).not.toContain("infra-retry")
expect(consumer).toContain("do not invent `infra-retry`")
})
test("pr-snapshot emits exactly the canonical trajectory field set", async () => {
const keys = emittedTrajectoryKeys(await readRepoFile(PR_SNAPSHOT))
expect(keys.sort()).toEqual([...TRAJECTORY_FIELDS].sort())
})
test("the delegated-mutation exclusion boundary is stated at all three ends of the chain", async () => {
// Babysit passes a bounded target-fixer scope whose exclusions
// (never rebase/force-push/merge/approve) the delegates must honor. Babysit may separately own
// a confirmed manager's post-push transaction; that exception must never leak into a child.
// 'rebase' and 'force-push' are specific enough to canary the exclusion block; 'merge' is not
// (merge-ready / merge conflict are ordinary prose here).
const [babysit, cedebug, ceresolve] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile(CEDEBUG_PIPELINE),
readRepoFile(CERESOLVE),
])
for (const [name, text] of [["babysit", babysit], ["ce-debug", cedebug], ["ce-resolve", ceresolve]] as const) {
expect(text, `${name} must state the 'rebase' exclusion`).toContain("rebase")
expect(text, `${name} must state the 'force-push' exclusion`).toContain("force-push")
}
})
test("every trajectory field cited in consumer prose is one pr-snapshot actually emits", async () => {
const script = await readRepoFile(PR_SNAPSHOT)
const emitted = new Set(emittedTrajectoryKeys(script))
const [babysit, ceresolve] = await Promise.all([readRepoFile(BABYSIT), readRepoFile(CERESOLVE)])
for (const field of BABYSIT_TRAJECTORY_REFS) {
expect(emitted.has(field), `babysit cites '${field}' but pr-snapshot no longer emits it`).toBe(true)
expect(babysit).toContain(field)
}
for (const field of CERESOLVE_TRAJECTORY_REFS) {
expect(emitted.has(field), `ce-resolve cites '${field}' but pr-snapshot no longer emits it`).toBe(true)
expect(ceresolve).toContain(field)
}
})
test("babysit's default mode is a self-sustaining in-session watch backed by pr-snapshot watch", async () => {
// The self-initiation contract: babysit does NOT do one tick and hand back a resume command by
// default; it backgrounds the token-free change-detector and stays in-session, woken by a sentinel.
const [babysit, script] = await Promise.all([readRepoFile(BABYSIT), readRepoFile(PR_SNAPSHOT)])
expect(babysit, "must describe the self-sustaining in-session watch").toMatch(/self-sustaining[, ]+in-session watch/i)
expect(babysit, "must invoke the pr-snapshot watch detector").toContain("pr-snapshot watch")
expect(babysit, "must wait on the BABYSIT_WAKE sentinel").toContain("BABYSIT_WAKE")
// producer side: the watch subcommand emits the sentinel and can wake on each precedence reason
expect(script).toContain("def cmd_watch")
expect(script).toContain("BABYSIT_WAKE")
for (const reason of ["terminal", "blocked-external", "blocked-external-drained", "actionable", "feedback-candidate", "stack-blocked", "needs-human", "branch-currency", "merge-ready", "invocation-superseded"]) {
expect(script, `watch must be able to wake on '${reason}'`).toContain(reason)
}
})
test("branch-currency wakes only after review and failing-CI attention, before merge-ready", async () => {
const script = await readRepoFile(PR_SNAPSHOT)
const wake = script.slice(script.indexOf("def _wake_reason"), script.indexOf("def _emit_wake"))
const orderedReturns = [
'return "actionable"',
'return "feedback-candidate"',
'return "blocked-failing"',
'return "branch-currency"',
'return "merge-ready"',
]
let prior = -1
for (const statement of orderedReturns) {
const current = wake.indexOf(statement)
expect(current, `${statement} must be present in _wake_reason`).toBeGreaterThan(-1)
expect(current, `${statement} must preserve branch-currency wake precedence`).toBeGreaterThan(prior)
prior = current
}
})
test("watch ownership is latest-valid-wins and stale wake generations are coalesced", async () => {
const [babysit, watchLoop, script] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile("skills/ce-babysit-pr/references/watch-loop.md"),
readRepoFile(PR_SNAPSHOT),
])
for (const text of [babysit, watchLoop]) {
expect(text).toContain("watch_generation")
expect(text).toMatch(/successful[^.]{0,100}(fetch|snapshot)[^.]{0,160}supersed/i)
expect(text).toMatch(/newer invocation[^.]{0,160}(cancel|stop)[^.]{0,160}(preflight|first fetch)/i)
expect(text).toMatch(/stale[^.]{0,120}wake[^.]{0,160}(coalesc|ignore|discard)/i)
expect(text).toMatch(/invocation-superseded[^.]{0,180}(end|stop)[^.]{0,120}(old )?(loop|watch)/i)
}
expect(script).toContain('"watch_generation"')
expect(script).toContain("_reserve_watch_candidate")
expect(script).toContain("_terminate_replaced_watch")
})
test("the paginated snapshot is canonical for review-thread state", async () => {
const babysit = await readRepoFile(BABYSIT)
expect(babysit).toMatch(/fresh `snapshot`[^.]{0,160}(canonical|source of truth)/i)
expect(babysit).toContain("`hasNextPage == false`")
})
test("babysit reconciles every passed comment so the loop can settle (never-settle fix)", async () => {
// Regression guard: marking only the comments ce-resolve explicitly 'handled' left its
// silently-dropped bot wrappers actionable forever, so counts.comments never reached 0.
const babysit = await readRepoFile(BABYSIT)
expect(babysit).toMatch(/silently drop/i)
expect(babysit, "must mark every passed comment, not only the handled ones").toMatch(/mark \*?every\*? comment you passed/i)
expect(babysit).toContain("never settle")
})
test("ce-resolve routes a whole-PR URL to full mode, a comment-fragment URL to targeted", async () => {
// babysit hands ce-resolve the fork->upstream PR URL; a bare /pull/N must run full mode against
// the parsed host/repo, while a comment-fragment URL stays targeted.
const ceresolve = await readRepoFile(CERESOLVE)
expect(ceresolve).toContain("PR URL")
expect(ceresolve).toContain("no comment fragment")
expect(ceresolve).toMatch(/#discussion_r|#issuecomment/)
})
test("ce-resolve passes the GHE host inline on every bundled-script call, not via one export", async () => {
// A single `export GH_HOST` does not survive between separate Bash tool calls, so each script
// call carries the host inline; on GHE, dropping it silently queries github.com.
const fullMode = await readRepoFile(CERESOLVE_FULL_MODE)
const prefixCount = (fullMode.match(/GH_HOST=<derived-host>/g) || []).length
expect(prefixCount, "each bundled-script call needs its own inline GH_HOST prefix").toBeGreaterThanOrEqual(4)
expect(fullMode, "must state that a single export does not carry between Bash calls").toContain("does **not** carry")
})
test("babysit's final merge-ready checkpoint self-refreshes a stale PR description via ce-commit-push-pr", async () => {
// Incremental commits during a watch leave the PR description stale; babysit must reflect on
// that before declaring merge-ready and route a stale one to ce-commit-push-pr's description
// update — autonomously, as an owned/pre-authorized mutation, not a user prompt.
const babysit = await readRepoFile(BABYSIT)
expect(babysit).toContain("PR-description freshness")
expect(babysit).toContain("description-update mode")
expect(babysit).toContain("ce-commit-push-pr")
expect(babysit, "description refresh must be in the owned mutation envelope").toMatch(/refresh(es|ing) (the |a )PR description/)
})
test("settle policy: the normal watch arm omits --settle-seconds; the script owns the 300s default", async () => {
// Regression guard (PR #1126 watch): stating "use ~600s whenever review bots are present" in the
// looks-ready gate made agents pre-widen the initial arm, so a finished review sat unrecognized
// until the longer window elapsed. The initial arm must always ride the script default.
const [babysit, script] = await Promise.all([readRepoFile(BABYSIT), readRepoFile(PR_SNAPSHOT)])
const watchCommands = [...babysit.matchAll(/^.*pr-snapshot" watch.*$/gm)].map((m) => m[0])
expect(watchCommands.length, "the watch invocation must still be shown").toBeGreaterThanOrEqual(1)
for (const cmd of watchCommands) {
expect(cmd, "the normal watch invocation must not set --settle-seconds").not.toContain("--settle-seconds")
}
expect(script, "the script must own the 300s settle default").toMatch(/--settle-seconds"[^)]*default=300/s)
// No prose may reintroduce the bots-present pre-widening rule the wake protocol replaced.
expect(babysit).not.toMatch(/whenever the repo uses review bots/i)
})
test("settle policy: an incomplete review lifecycle gets a 15-minute floor and 30-minute ceiling", async () => {
const [babysit, script] = await Promise.all([readRepoFile(BABYSIT), readRepoFile(PR_SNAPSHOT)])
expect(babysit).toContain("incomplete review lifecycle")
expect(babysit).toContain("15 minutes")
expect(babysit).toContain("30 minutes")
expect(babysit).toMatch(/trajectory.*extend/i)
expect(babysit).toMatch(/never.*shorten/i)
expect(babysit).toMatch(/must not re-arm.*same unchanged signal/i)
expect(babysit).toMatch(/unattributed lifecycle incomplete/i)
expect(babysit).toMatch(/reviewer when identifiable.*observed signal/i)
expect(babysit).toMatch(/only uncleared condition.*incomplete lifecycle.*15 quiet minutes.*30-minute terminal ceiling/i)
expect(script).toContain("review_signal_seen_on_head")
// A done signal on the current head must end the wait, not start another settle period.
expect(babysit).toContain("never extends the wait")
expect(babysit).toContain("no further settle period")
})
test("watcher silence is defined as no-information, with a fresh snapshot for mid-watch status asks", async () => {
// Regression guard: an agent narrated detector silence as "review still active"; silence only
// means no wake condition has fired.
const babysit = await readRepoFile(BABYSIT)
expect(babysit).toContain("Watcher silence carries no PR-state information")
expect(babysit, "a mid-watch status ask must be answered from a fresh snapshot").toMatch(
/asks for status before a wake.*fresh `snapshot`/s,
)
})
test("live updates report PR state without leaking routine watcher mechanics", async () => {
// Regression guard: progress narration led with a wake race and re-arm details instead of the
// user-relevant outcome (feedback already addressed; CI still running).
const babysit = await readRepoFile(BABYSIT)
expect(babysit).toContain("PR state first in live updates")
expect(babysit).toMatch(/wake, snapshot, re-arm, or head as internal implementation detail/)
expect(babysit).toMatch(/only when they explain a failure or required user action/)
})
test("authority boundary: babysit never merges; readiness is reported as the user's call", async () => {
const babysit = await readRepoFile(BABYSIT)
expect(babysit).toMatch(/\*\*never\*\* merges the PR/i)
expect(babysit).toContain("looks ready — your call")
expect(babysit).toMatch(/never .safe to merge./)
expect(babysit).toMatch(/merge-readiness[^.]{0,120}never[^.]{0,80}merge authorization/i)
})
test("stack-aware routing is automatic, CLI-first, and never uses checkout as a probe", async () => {
const [babysit, watchLoop, script] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile("skills/ce-babysit-pr/references/watch-loop.md"),
readRepoFile(PR_SNAPSHOT),
])
expect(script).toContain('"gh", "stack", "view", "--json"')
expect(script).toContain("fetch_pr_chain")
expect(script).toContain("manager_status")
for (const status of ["confirmed", "absent", "probe-error"]) {
expect(script, `pr-snapshot must preserve manager status '${status}'`).toContain(status)
}
expect(babysit).toMatch(/automatically classify.*PR chain/i)
expect(babysit).toContain("gh stack view --json")
expect(babysit).toMatch(/GraphQL fallback/i)
expect(babysit).toMatch(/stack-field schema-unavailable[\s\S]{0,100}.absent./i)
expect(babysit).toMatch(/separate read-only lookup[\s\S]{0,100}default branch/i)
expect(babysit).toMatch(/auth, transport, rate-limit, malformed[\s\S]{0,100}.probe-error./i)
expect(babysit).toContain("Discovery never runs `gh stack checkout`")
expect(babysit).not.toMatch(/gh stack checkout\s+<[^>]+>/)
expect(watchLoop).not.toMatch(/gh stack checkout\s+<[^>]+>/)
})
test("managed and manual dependency chains have distinct currency and readiness contracts", async () => {
const babysit = await readRepoFile(BABYSIT)
expect(babysit).toMatch(/managed stack[\s\S]{0,1600}pre-existing target staleness[^.]{0,180}never this route/i)
expect(babysit).toMatch(/ready as the next PR in the stack/i)
expect(babysit).toMatch(/manual dependency chain/i)
expect(babysit).toMatch(/relative to (its|the) parent/i)
expect(babysit).toMatch(/do not redirect/i)
expect(babysit).toMatch(/upstack.*residual/i)
})
test("a target push in a confirmed managed stack is followed by recoverable upstack maintenance", async () => {
const [babysit, watchLoop] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile("skills/ce-babysit-pr/references/watch-loop.md"),
])
for (const text of [babysit, watchLoop]) {
expect(text).toMatch(/gh stack rebase "?<first-dependent-branch>"? --upstack --no-trunk/)
expect(text).toContain("gh stack push")
expect(text).not.toMatch(/capability proven before delegation supplies\s+`--force-with-lease --atomic`/)
expect(text).not.toContain("so every changed dependent branch updates or none do")
expect(text).not.toContain("so all changed remote branches update or none do")
expect(text).toMatch(/re-probe|compare every baseline-affected/)
expect(text).toMatch(/observed progress/)
expect(text).toMatch(/gh stack rebase --abort[\s\S]{0,300}(residual|needs-human)/i)
expect(text).toMatch(/manual dependency[\s\S]{0,500}(never|do not)[\s\S]{0,120}(rebase|rewrite|restack)/i)
expect(text).toMatch(/target[^.]{0,160}(local|head)[^.]{0,160}(pushed SHA|unchanged)/i)
expect(text).toMatch(/github\/gh-stack#216/)
}
expect(babysit).toMatch(/after (an|any) authorized target-head push[\s\S]{0,1800}gh stack rebase "?<first-dependent-branch>"? --upstack --no-trunk/i)
expect(babysit).toMatch(/manager-owned[\s\S]{0,200}(implicit|babysit)[\s\S]{0,200}author/i)
})
test("managed-stack mutation records a pre-push baseline instead of stopping for unproven atomicity", async () => {
const babysit = await readRepoFile(BABYSIT)
const terminal = babysit.indexOf("1. **Terminal check first.**")
const baseline = babysit.indexOf("**Managed-stack pre-push baseline.**")
const feedback = babysit.indexOf("3. **Feedback before CI.**")
const baselineBlock = babysit.slice(baseline, feedback)
expect(terminal).toBeGreaterThan(-1)
expect(baseline).toBeGreaterThan(-1)
expect(terminal).toBeLessThan(baseline)
expect(baseline).toBeLessThan(feedback)
expect(babysit).not.toContain("**Managed-stack atomicity gate.**")
expect(babysit).not.toContain("atomicity-unproven")
expect(baselineBlock).toMatch(/remote-tracking OID/i)
expect(baselineBlock).toMatch(/Do not stop for missing atomic multi-ref push proof/i)
expect(baselineBlock).toContain("github/gh-stack#216")
expect(baselineBlock).toMatch(/prefer all-or-none[\s\S]{0,120}proves atomic push/i)
expect(baselineBlock).toMatch(/always re-probe after push/i)
expect(baselineBlock).toMatch(/If either precondition fails[\s\S]{0,200}do not invoke a delegate/i)
})
test("post-push re-probe compares dependents against baseline, not the pushed target OID", async () => {
const [babysit, watchLoop] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile(WATCH_LOOP),
])
for (const text of [babysit, watchLoop]) {
expect(text).toContain("open dependent")
expect(text).toContain("intentional post-push OID change as divergence")
}
})
test("stack-land waits for actual MERGED after merge-queue enqueue", async () => {
const [babysit, stackCommands] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile("skills/ce-babysit-pr/references/stack-commands.md"),
])
expect(babysit).toMatch(/merge-queue[\s\S]{0,200}enqueue[\s\S]{0,200}OPEN/i)
expect(babysit).toMatch(/until it is actually `MERGED`/i)
expect(stackCommands).toMatch(/merge-queue[\s\S]{0,160}OPEN[\s\S]{0,160}MERGED/i)
})
test("user-facing resume commands render for the active host", async () => {
const [babysit, watchLoop] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile(WATCH_LOOP),
])
for (const text of [babysit, watchLoop]) {
const renderingRule = text.match(/\*\*User-runnable resume syntax\.\*\*[^\n]+/)?.[0]
expect(renderingRule).toBeDefined()
expect(renderingRule).toContain("/ce-babysit-pr <url>")
expect(renderingRule).toContain("$ce-babysit-pr <url> [posture:…]")
expect(renderingRule).not.toContain("/skill:ce-babysit-pr")
expect(renderingRule).toMatch(/Codex[\s\S]+output one form only/i)
expect(text).toContain("exec '<host-rendered resume invocation>'")
}
})
test("sequential babysitting is a confirmed-managed-stack-only, one-watcher scope", async () => {
const [babysit, watchLoop] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile("skills/ce-babysit-pr/references/watch-loop.md"),
])
expect(babysit).toMatch(/only when[^.]{0,180}`manager_status == "confirmed"`[^.]{0,180}stack-wide continuation/i)
expect(babysit).toMatch(/repository-level stack availability[^.]{0,180}not a managed stack/i)
expect(babysit).toMatch(/looks ready or later settles under `target`[\s\S]{0,220}offer once[\s\S]{0,220}upstack/i)
expect(babysit).toMatch(/without asking again at each layer/i)
expect(babysit).toMatch(/Never skip past a draft/i)
expect(babysit).toMatch(/manual dependency chain[^.]{0,240}(never|must not)[^.]{0,120}stack-wide continuation/i)
expect(babysit).toMatch(/unsettled downstack[^.]{0,260}offer once[^.]{0,260}lowest unsettled/i)
expect(babysit).toMatch(/already `stack-ready` or `stack-land`[^.]{0,200}unsettled downstack[^.]{0,200}lowest unsettled[^.]{0,80}without asking/i)
expect(babysit).toMatch(/draft[^.]{0,180}(only|unless)[^.]{0,180}explicit/i)
expect(babysit).toMatch(/one active (PR )?(target|watcher)/i)
expect(watchLoop).toMatch(/one active (PR )?(target|watcher)/i)
})
test("posture enum and stack-land merge carve-out are load-bearing", async () => {
const [babysit, commands, watchLoop] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile("skills/ce-babysit-pr/references/stack-commands.md"),
readRepoFile(WATCH_LOOP),
])
expect(babysit).toContain("posture:target|stack-ready|stack-land")
expect(babysit).toContain("Settled ≠ merged")
expect(babysit).toMatch(/Under `target` and `stack-ready` it \*\*never\*\* merges the PR/)
expect(babysit).toContain("gh stack merge <bottom-most-open-settled-PR>")
expect(babysit).toMatch(/layer transition[\s\S]{0,200}not a run-level Terminal/i)
expect(babysit).toMatch(/Under `stack-land`, run the land step[\s\S]{0,80}before[\s\S]{0,40}plain advance/i)
expect(babysit).toMatch(/Under `posture:stack-land`[\s\S]{0,120}stack-land land step/i)
expect(babysit).toMatch(/when the run posture is not `target`[\s\S]{0,120}posture:stack-ready/i)
expect(babysit).toContain("references/stack-commands.md")
expect(commands).toContain('gh stack rebase "<first-open-dependent-branch>"')
expect(commands).toContain("gh stack merge <BOTTOM_MOST_OPEN_SETTLED_PR> --yes --squash")
expect(commands).toContain("gh stack sync --remote <tracking-remote>")
expect(commands).toContain("gh stack push --remote <tracking-remote>")
expect(commands).not.toMatch(/--remote origin\b/)
expect(commands).toMatch(/never hard-code `origin`/i)
expect(commands).toMatch(/gh pr merge/)
expect(commands).toMatch(/Forbidden on managed stack members/i)
expect(watchLoop).toMatch(/re-state the same `posture:`/)
expect(watchLoop).toMatch(/just-landed MERGED[\s\S]{0,80}layer transition/i)
})
test("managed-stack continuation preserves one fixed invocation budget", async () => {
const [babysit, watchLoop] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile("skills/ce-babysit-pr/references/watch-loop.md"),
])
for (const text of [babysit, watchLoop]) {
expect(text).toContain("--invocation-id \"$RUN_INVOCATION_ID\"")
expect(text).toContain("--session-started-at \"$RUN_STARTED_AT\"")
expect(text).toContain("--invocation-budget-seconds \"$RUN_BUDGET_SECONDS\"")
expect(text).toMatch(/(one|same|fixed)[^.]{0,220}(invocation )?budget/i)
expect(text).toMatch(/(layer|state dir)[^.]{0,260}(same|fixed|continue-invocation)[^.]{0,180}(invocation|budget)/i)
}
})
test("mark writes are fenced by the active invocation tuple", async () => {
const [babysit, watchLoop, script] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile("skills/ce-babysit-pr/references/watch-loop.md"),
readRepoFile(PR_SNAPSHOT),
])
for (const text of [babysit, watchLoop]) {
expect(text).toMatch(/mark[\s\S]{0,500}(invocation ID|RUN_INVOCATION_ID)[\s\S]{0,500}(start anchor|RUN_STARTED_AT)[\s\S]{0,500}(budget|RUN_BUDGET_SECONDS)/i)
}
expect(script).toMatch(/m\.add_argument\("--invocation-id", required=True/)
expect(script).toMatch(/def cmd_mark\(args\):[\s\S]{0,180}_apply_invocation\(box, args, now\)/)
})
test("blocked approval drains review automatically before a bounded handback", async () => {
const babysit = await readRepoFile(BABYSIT)
expect(babysit).toContain("blocked-external-drained")
expect(babysit).toContain("--blocked-external-drain-seconds")
for (const bound of ["300", "900", "1800"]) expect(babysit).toContain(bound)
expect(babysit).toMatch(/without asking|do not ask/i)
expect(babysit).toMatch(/pipeline[^.]{0,300}(terminate|return)/i)
})
test("deadline precedence preserves stop results without starting another work round", async () => {
const [babysit, watchLoop] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile("skills/ce-babysit-pr/references/watch-loop.md"),
])
for (const text of [babysit, watchLoop]) {
expect(text).toMatch(/deadline[\s\S]{0,300}terminal[\s\S]{0,200}merge-ready[\s\S]{0,300}max-runtime/i)
}
})
test("pipeline success requires clean chain currency in both loaded contracts", async () => {
const [babysit, watchLoop] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile(WATCH_LOOP),
])
const pipelineStart = babysit.indexOf("2. **Bounded stop, not merge-ready.**")
const pipelineEnd = babysit.indexOf("3. **Native residual surfacing", pipelineStart)
const pipelineDelta = babysit.slice(pipelineStart, pipelineEnd)
for (const text of [babysit, watchLoop]) {
expect(text).toMatch(/success only when[^.]{0,320}`all_checks_ok`[^.]{0,320}`stack_blocker`[^.]{0,80}(null|clear)/i)
expect(text).toMatch(/success only when[^.]{0,640}`mergeability_certain`[^.]{0,160}`merge_state_status == "CLEAN"`[^.]{0,240}`branch_currency_blocker`[^.]{0,100}(null|clear)/i)
}
expect(pipelineStart).toBeGreaterThan(-1)
expect(pipelineEnd).toBeGreaterThan(pipelineStart)
expect(pipelineDelta).toMatch(/`all_checks_ok`[\s\S]{0,260}`mergeability_certain`[\s\S]{0,160}`merge_state_status == "CLEAN"`[\s\S]{0,260}`stack_blocker`[\s\S]{0,160}`branch_currency_blocker`[^.]{0,80}(null|clear)/i)
expect(pipelineDelta).toMatch(/open\/claimed\/parked current currency item[^.]{0,240}residual/i)
})
test("merge identity distinguishes historical base metadata from current-base readiness and branch currency", async () => {
const [babysit, watchLoop, script] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile(WATCH_LOOP),
readRepoFile("skills/ce-babysit-pr/scripts/pr-snapshot"),
])
for (const text of [babysit, watchLoop]) {
expect(text).toContain("historical_oid")
expect(text).toContain("baseRefOid")
expect(text).toContain("baseRef.target.oid")
expect(text).toContain("potentialMergeCommit")
expect(text).toContain("mergeability-pending")
expect(text).toMatch(/historical[^.]{0,180}(diagnostic|never blocks|does not block)/i)
expect(text).toMatch(/mergeable against[^.]{0,180}(head contains|latest base)/i)
expect(text).toMatch(/reporting `CLEAN` does not/i)
expect(text).not.toMatch(/live base ref matches the PR object's cached base/i)
}
expect(script).toMatch(/baseRef\s*\{\s*target\s*\{\s*oid\s*\}\s*\}/)
expect(script).toMatch(/potentialMergeCommit\s*\{\s*oid\s+parents/)
expect(script).toContain('"historical_oid": historical_oid')
expect(script).not.toContain('"pr_oid": pr_oid')
})
test("branch-currency mutation is claimed, invocation-fenced, and reconciled before retry", async () => {
const [babysit, watchLoop] = await Promise.all([readRepoFile(BABYSIT), readRepoFile(WATCH_LOOP)])
for (const text of [babysit, watchLoop]) {
expect(text).toContain("--currency-key")
expect(text).toContain("--currency-disposition claimed")
expect(text).toContain("--currency-outcome mutation-observed")
expect(text).toContain("--currency-outcome proven-no-mutation")
expect(text).toContain("--currency-outcome ambiguous")
expect(text).toMatch(/claimed[^.]{0,180}reconciliation-only/i)
expect(text).toMatch(/exactly one retry[^.]{0,260}(proven|conclusive)[^.]{0,180}no[- ]mutation[^.]{0,180}backoff/i)
expect(text).toMatch(/ambiguous[^.]{0,220}never[^.]{0,160}(retry|resubmit)/i)
}
expect(babysit).toMatch(/stale invocation[^.]{0,220}(reject|invalidate)/i)
expect(babysit).toMatch(/SKILL_DIR=[^\n]+;[^\n]+STATE_DIR=[^\n]+;[^\n]+RUN_INVOCATION_ID=[^\n]+;[^\n]+RUN_STARTED_AT=[^\n]+;[^\n]+RUN_BUDGET_SECONDS=[^\n]+;\n\s*PY=[\s\S]*?"\$PY"[^\n]+--currency-disposition claimed/i)
expect(watchLoop).toMatch(/max-runtime[^.]{0,160}(claim|mutation)/i)
})
test("behind maintenance uses positive host capability and exact observed OIDs", async () => {
const [babysit, watchLoop] = await Promise.all([readRepoFile(BABYSIT), readRepoFile(WATCH_LOOP)])
const behindStart = babysit.indexOf('- **`BEHIND`: host-owned update only.**')
const behindEnd = babysit.indexOf('- **`DIRTY`: exact-base local repair only.**', behindStart)
const behindBlock = babysit.slice(behindStart, behindEnd)
for (const text of [babysit, watchLoop]) {
expect(text).toContain("host_branch_update_capability == true")
expect(text).toMatch(/(denied|false)[\s\S]{0,160}(unknown)[\s\S]{0,260}needs-human/i)
expect(text).toMatch(/host[^.]{0,120}(update|operation)[^.]{0,180}(once|one time)/i)
expect(text).toMatch(/confirm[^.]{0,220}(contains|ancestor)[^.]{0,160}(observed )?base OID/i)
expect(text).toMatch(/host[^.]{0,160}(accept|mutation-observed)[^.]{0,180}(not|isn't)[^.]{0,120}(complet|confirm|success)/i)
expect(text).toMatch(/host_branch_update_capability[^.]{0,260}(not|never)[^.]{0,140}(Git push|direct push|push authority)/i)
expect(text).toContain("expected_head_sha")
expect(text).toMatch(/expected_head_sha[^.]{0,180}(observed|claimed)[^.]{0,100}head/i)
expect(text).toMatch(/422[^.]{0,180}(mismatch|stale|reconcile)/i)
}
expect(babysit).toMatch(/BEHIND[\s\S]{0,1000}revalidate[^.]{0,240}head[^.]{0,160}base OIDs/i)
expect(behindStart).toBeGreaterThan(-1)
expect(behindEnd).toBeGreaterThan(behindStart)
expect(behindBlock).toMatch(/confirm the exact claimed observation[^.]{0,180}fresh snapshot[^.]{0,180}ancestry evidence[^.]{0,180}observed base OID/i)
expect(behindBlock).toMatch(/claimed item's own `branch_currency_blocker`[^.]{0,120}need not be null beforehand/i)
})
test("dirty maintenance proves push authority, previews exact-base conflicts, and parks semantic choices", async () => {
const [babysit, watchLoop] = await Promise.all([readRepoFile(BABYSIT), readRepoFile(WATCH_LOOP)])
for (const text of [babysit, watchLoop]) {
expect(text).toMatch(/DIRTY[\s\S]{0,500}host_branch_update_capability[^.]{0,120}(irrelevant|does not apply)/i)
expect(text).toMatch(/ordinary direct[- ]push authority[^.]{0,180}(exact )?head ref/i)
expect(text).toMatch(/(without mutating[^.]{0,220}ordinary direct-push authority|ordinary direct push authority[^.]{0,220}without mutating)/i)
expect(text).toMatch(/verified clean PR-head checkout/i)
expect(text).toMatch(/conflicted paths[^.]{0,140}stage blob identities[^.]{0,160}(exclude|excluding|not include)[^.]{0,80}base OID/i)
expect(text).toMatch(/mechanical[^.]{0,240}positive intent evidence[^.]{0,240}no reasonable (alternate|alternative) behavior/i)
expect(text).toMatch(/two plausible (resolutions|behaviors)[\s\S]{0,320}(abort|needs-human|park)/i)
expect(text).toMatch(/(stale|unauthorized|incomplete|unbounded)[\s\S]{0,280}(abort|needs-human|park)/i)
expect(text).toContain("--currency-inspected-fingerprint")
expect(text).toContain("--semantic-conflict-fingerprint")
expect(text).toMatch(/mark[^.]{0,120}mutation-observed[^.]{0,160}(local )?merge (starts|begins)/i)
expect(text).toMatch(/normal push/i)
expect(text).toMatch(/remote head movement alone[^.]{0,180}(not|isn't)[^.]{0,100}(proof|confirmation|success)/i)
expect(text).toMatch(/confirm[^.]{0,220}(equals|contains)[^.]{0,180}(validated )?merge commit/i)
expect(text).toMatch(/(never|do not)[^.]{0,100}rebase[^.]{0,120}(force-push|force push)/i)
}
expect(babysit).toMatch(/exact observed base OID[^.]{0,180}non-mutating merge preview/i)
})
test("semantic currency parks are fingerprinted before a new mutation and scope stays target-local", async () => {
const [babysit, watchLoop] = await Promise.all([readRepoFile(BABYSIT), readRepoFile(WATCH_LOOP)])
for (const text of [babysit, watchLoop]) {
expect(text).toMatch(/parked_semantic_fingerprints[\s\S]{0,500}--currency-inspected-fingerprint/i)
expect(text).toMatch(/unchanged[^.]{0,160}(park|needs-human)[^.]{0,160}(changed|different)[^.]{0,180}(reopen|retire)/i)
expect(text).toMatch(/manual dependenc/i)
expect(text).toContain("target-local")
expect(text).toMatch(/never[^.]{0,140}(rewrite|mutate)[^.]{0,100}dependent/i)
expect(text).toMatch(/(managed|probe-error)[^.]{0,220}(exclude|no branch-currency mutation|perform no branch-currency mutation)/i)
expect(text).toMatch(/interrupted (local )?merge[^.]{0,220}(reconcile|abort)/i)
}
expect(watchLoop).toMatch(/root[^.]{0,180}(dependent|child)[^.]{0,180}(allow|eligible)/i)
})
test("bounded-class sweep contract: babysit routes it, ce-resolve classifies/enumerates/bounds it", async () => {
// A correct finding recurring across sibling sites must be swept as one class, not dripped
// one-per-head. The split is protocol: babysit only recognizes + routes ("request a
// bounded-class assessment"); ce-resolve owns whether the sites are equivalent, the enumerated
// locations, and the fixer's mutation boundary. Dropping either side silently reverts to
// one-site-per-round (babysit routes a request nothing fulfills, or the resolver never sweeps).
const [babysit, watchLoop, fullMode, rubric, fixer] = await Promise.all([
readRepoFile(BABYSIT),
readRepoFile("skills/ce-babysit-pr/references/watch-loop.md"),
readRepoFile(CERESOLVE_FULL_MODE),
readRepoFile("skills/ce-resolve-pr-feedback/references/evaluation-rubric.md"),
readRepoFile("skills/ce-resolve-pr-feedback/references/agents/pr-comment-resolver.md"),
])
// Babysit side: recognizes + routes, does not decide/execute the sweep.
expect(babysit, "babysit Step 2 must request the assessment inline").toMatch(/bounded-class assessment/i)
expect(watchLoop).toMatch(/bounded-class assessment/i)
// Resolver side: owns classification, enumeration, and the fixer's enumerated mutation boundary.
expect(rubric).toContain("A validated finding can span sites this PR itself introduced")
expect(fullMode).toMatch(/Class fix:/)
expect(fixer, "class-fix mutation boundary must reach the fixer prompt").toMatch(/enumerated set is the mutation boundary/i)
})
test("drafts stay opt-in against automatic skill handoffs", async () => {
// A calling skill's auto-handoff (ce-commit-push-pr post-PR) must not count as the user
// explicitly naming a draft; only a human's invocation or an explicit watch-mode token may
// arm a watch on one.
const babysit = await readRepoFile(BABYSIT)
const boundary = babysit.match(/\*\*Draft PRs are opt-in\.\*\*[\s\S]+?(?=\n-)/)?.[0]
expect(boundary).toBeDefined()
expect(boundary).toContain("A calling skill's automatic handoff is neither")
expect(boundary).toContain("report the draft status and stop")
// Callee-side enforcement: Step 1 resolves draft state pre-bootstrap, and the snapshot
// helper emits it — from the current-view builder through the output payload — so every
// caller (LFG's auto-handoff included) hits the stop before a watcher is armed.
expect(babysit, "Step 1 must resolve draft state via pr_is_draft").toContain("pr_is_draft")
const snapshot = await readRepoFile("skills/ce-babysit-pr/scripts/pr-snapshot")
const emissions = snapshot.match(/"pr_is_draft"/g) ?? []
expect(emissions.length, "pr-snapshot must carry pr_is_draft in view builder and output payload").toBeGreaterThanOrEqual(2)
// The live fetch must actually request the field, or every emission is null (Bugbot round 3).
expect(snapshot, "fetch()'s gh pr view field list must request isDraft").toContain("url,number,isDraft,")
})
test("stop summaries open with a pinned status line and carry a counted run recap", async () => {
// Observed drift: merge-ready stops reported only current PR state (CI green, no threads),
// burying the ready call in prose and dropping the hour of resolved feedback/CI work entirely.
const babysit = await readRepoFile(BABYSIT)
const step4 = babysit.match(/## Step 4: Report \/ summary([\s\S]+?)## Step 5:/)?.[1]
expect(step4).toBeDefined()
// Ready declarations are pinned to the two ready emoji; merged celebrates distinctly.
expect(step4).toContain("✅ Looks merge-ready")
expect(step4).toContain("🟡 Cautiously looks ready")
expect(step4).toContain("🎉 Merged")
expect(step4).toContain("Your call to merge")
expect(step4).toMatch(/never opens with anything but ✅ or 🟡/)
// The recap must be rebuilt from durable sources (the PR's remote record), never memory alone.
expect(step4).toContain("A run recap at every true stop")
expect(step4).toMatch(/never from conversation memory alone/)
expect(step4).toContain("remote record")
})
})