Skip to content

Commit cc36eda

Browse files
committed
fix(shell): backfill codex plans before sync
1 parent ed459e5 commit cc36eda

5 files changed

Lines changed: 38 additions & 18 deletions

File tree

packages/app/src/lib/core/templates-entrypoint/git-hooks.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,15 @@ cd "$REPO_ROOT"
148148
149149
${renderPostPushPrEnsure()}
150150
151-
# CHANGE: sync captured Codex plans to the current branch PR after push.
152-
# WHY: issue #369 requires the agent plan to be uploaded to PR discussion.
153-
# REF: issue-369
151+
# CHANGE: backfill Codex session plans before syncing the current branch PR.
152+
# WHY: live Codex hooks can be unavailable in already-running sessions; session logs are the durable fallback.
153+
# REF: issue-375
154154
if [ "${"${"}DOCKER_GIT_SKIP_PLAN_TO_GIT:-}" != "1" ]; then
155155
if ! command -v plan-to-git >/dev/null 2>&1; then
156156
echo "[plan-to-git] Error: plan-to-git not found" >&2
157157
exit 1
158158
fi
159+
plan-to-git import-codex --no-sync
159160
plan-to-git sync
160161
fi
161162

packages/app/tests/docker-git/core-templates.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ describe("app planFiles", () => {
116116
"gh pr create --repo \"$base_repo\" --base \"$base_branch\" --head \"$head_arg\" --fill"
117117
)
118118
expect(entrypoint.contents).toContain("plan-to-git hook --source codex")
119+
expect(entrypoint.contents).toContain("plan-to-git import-codex --no-sync")
119120
expect(entrypoint.contents).toContain("CODEX_REQUIREMENTS_FILE=\"/etc/codex/requirements.toml\"")
120121
expect(entrypoint.contents).toContain("managed_dir = \"/opt/docker-git/hooks\"")
121122
expect(entrypoint.contents).toContain("[[hooks.UserPromptSubmit]]")
@@ -124,16 +125,18 @@ describe("app planFiles", () => {
124125

125126
const cdIndex = entrypoint.contents.indexOf("cd \"$REPO_ROOT\"")
126127
const ensurePrIndex = entrypoint.contents.indexOf(
127-
"docker_git_ensure_open_pr\n\n# CHANGE: sync captured Codex plans"
128+
"docker_git_ensure_open_pr\n\n# CHANGE: backfill Codex session plans"
128129
)
130+
const planImportIndex = entrypoint.contents.indexOf("plan-to-git import-codex --no-sync")
129131
const planSyncIndex = entrypoint.contents.indexOf("plan-to-git sync")
130132
const sessionBackupIndex = entrypoint.contents.indexOf(
131133
"docker-git-session-sync backup --verbose --background --require-comment"
132134
)
133135

134136
expect(cdIndex).toBeGreaterThanOrEqual(0)
135137
expect(ensurePrIndex).toBeGreaterThan(cdIndex)
136-
expect(planSyncIndex).toBeGreaterThan(ensurePrIndex)
138+
expect(planImportIndex).toBeGreaterThan(ensurePrIndex)
139+
expect(planSyncIndex).toBeGreaterThan(planImportIndex)
137140
expect(sessionBackupIndex).toBeGreaterThan(planSyncIndex)
138141
})
139142
)

packages/lib/src/core/templates-entrypoint/git-hooks.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,15 @@ cd "$REPO_ROOT"
148148
149149
${renderPostPushPrEnsure()}
150150
151-
# CHANGE: sync captured Codex plans to the current branch PR after push.
152-
# WHY: issue #369 requires the agent plan to be uploaded to PR discussion.
153-
# REF: issue-369
151+
# CHANGE: backfill Codex session plans before syncing the current branch PR.
152+
# WHY: live Codex hooks can be unavailable in already-running sessions; session logs are the durable fallback.
153+
# REF: issue-375
154154
if [ "${"${"}DOCKER_GIT_SKIP_PLAN_TO_GIT:-}" != "1" ]; then
155155
if ! command -v plan-to-git >/dev/null 2>&1; then
156156
echo "[plan-to-git] Error: plan-to-git not found" >&2
157157
exit 1
158158
fi
159+
plan-to-git import-codex --no-sync
159160
plan-to-git sync
160161
fi
161162

packages/lib/tests/core/git-post-push-wrapper.test.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,11 @@ if [[ -n "\${FAKE_PLAN_TO_GIT_LOG_PATH:-}" ]]; then
171171
printf '%s\\t%s\\n' "$PWD" "$*" >> "$FAKE_PLAN_TO_GIT_LOG_PATH"
172172
fi
173173
174-
if [[ "\${1:-}" != "sync" ]]; then
174+
if [[ "\${1:-}" != "import-codex" && "\${1:-}" != "sync" ]]; then
175175
if [[ -n "\${FAKE_PLAN_TO_GIT_LOG_PATH:-}" ]]; then
176176
printf '%s\\tunexpected-command:%s\\n' "$PWD" "\${1:-<empty>}" >> "$FAKE_PLAN_TO_GIT_LOG_PATH"
177177
fi
178-
echo "fakePlanToGit: expected sync command, got: \${1:-<empty>}" >&2
178+
echo "fakePlanToGit: expected import-codex or sync command, got: \${1:-<empty>}" >&2
179179
exit 127
180180
fi
181181
@@ -383,7 +383,10 @@ describe("git post-push wrapper", () => {
383383
expect(nodeCwd).toEqual([harness.repoDir])
384384
expect(nodeRepoRoot).toEqual([harness.repoDir])
385385
expect(nodeScript).toEqual(["backup --verbose --background --require-comment"])
386-
expect(planToGit).toEqual([`${harness.repoDir}\tsync`])
386+
expect(planToGit).toEqual([
387+
`${harness.repoDir}\timport-codex --no-sync`,
388+
`${harness.repoDir}\tsync`
389+
])
387390
expect(gh).toContain(`${harness.repoDir}\tpr create --repo org/repo --base main --head issue-375 --fill`)
388391
})
389392
).pipe(Effect.provide(NodeContext.layer)))
@@ -403,7 +406,10 @@ describe("git post-push wrapper", () => {
403406
expect(nodeCwd).toEqual([harness.repoDir])
404407
expect(nodeRepoRoot).toEqual([harness.repoDir])
405408
expect(nodeScript).toEqual(["backup --verbose --background --require-comment"])
406-
expect(planToGit).toEqual([`${harness.repoDir}\tsync`])
409+
expect(planToGit).toEqual([
410+
`${harness.repoDir}\timport-codex --no-sync`,
411+
`${harness.repoDir}\tsync`
412+
])
407413
expect(gh).toContain(`${harness.repoDir}\tpr create --repo org/repo --base main --head issue-375 --fill`)
408414
expect(gitLog.some((line) => line.startsWith(`${harness.externalDir}\t-C ${harness.repoDir} push`))).toBe(true)
409415
})
@@ -477,7 +483,7 @@ describe("git post-push wrapper", () => {
477483
})
478484
).pipe(Effect.provide(NodeContext.layer)))
479485

480-
it.effect("propagates plan sync failures after ensuring a PR and before session backup", () =>
486+
it.effect("propagates plan import failures after ensuring a PR and before session backup", () =>
481487
withHarness((harness) =>
482488
Effect.gen(function*(_) {
483489
yield* _(
@@ -492,7 +498,7 @@ describe("git post-push wrapper", () => {
492498
const gh = yield* _(readLogLines(harness.ghLogPath))
493499

494500
expect(nodeScript).toEqual([])
495-
expect(planToGit).toEqual([`${harness.repoDir}\tsync`])
501+
expect(planToGit).toEqual([`${harness.repoDir}\timport-codex --no-sync`])
496502
expect(gh).toContain(`${harness.repoDir}\tpr create --repo org/repo --base main --head issue-375 --fill`)
497503
})
498504
).pipe(Effect.provide(NodeContext.layer)))
@@ -511,7 +517,10 @@ describe("git post-push wrapper", () => {
511517
const planToGit = yield* _(readLogLines(harness.planToGitLogPath))
512518

513519
expect(nodeScript).toEqual(["backup --verbose --background --require-comment"])
514-
expect(planToGit).toEqual([`${harness.repoDir}\tsync`])
520+
expect(planToGit).toEqual([
521+
`${harness.repoDir}\timport-codex --no-sync`,
522+
`${harness.repoDir}\tsync`
523+
])
515524
})
516525
).pipe(Effect.provide(NodeContext.layer)))
517526

@@ -529,7 +538,10 @@ describe("git post-push wrapper", () => {
529538
const gh = yield* _(readLogLines(harness.ghLogPath))
530539

531540
expect(nodeScript).toEqual(["backup --verbose --background --require-comment"])
532-
expect(planToGit).toEqual([`${harness.repoDir}\tsync`])
541+
expect(planToGit).toEqual([
542+
`${harness.repoDir}\timport-codex --no-sync`,
543+
`${harness.repoDir}\tsync`
544+
])
533545
expect(gh).toContain(`${harness.repoDir}\tpr list --repo org/repo --state open --head issue-375 --json url --jq .[0].url // ""`)
534546
expect(gh.some((line) => line.includes("pr create"))).toBe(false)
535547
})

packages/lib/tests/core/templates.test.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,7 @@ describe("renderEntrypointGitHooks", () => {
493493
expect(hooks).toContain("[post-push-pr] Error: cannot create PR from detached HEAD")
494494
expect(hooks).toContain("[post-push-pr] Error: failed to list open PRs")
495495
expect(hooks).toContain("DOCKER_GIT_SKIP_PLAN_TO_GIT")
496+
expect(hooks).toContain("plan-to-git import-codex --no-sync")
496497
expect(hooks).toContain("plan-to-git sync")
497498
expect(hooks).toContain("plan-to-git hook --source codex")
498499
expect(hooks).toContain("[features]")
@@ -521,13 +522,15 @@ describe("renderEntrypointGitHooks", () => {
521522
expect(hooks).toContain("[session-backup] Error: gh CLI not found")
522523

523524
const cdIndex = hooks.indexOf('cd "$REPO_ROOT"')
524-
const ensurePrIndex = hooks.indexOf("docker_git_ensure_open_pr\n\n# CHANGE: sync captured Codex plans")
525+
const ensurePrIndex = hooks.indexOf("docker_git_ensure_open_pr\n\n# CHANGE: backfill Codex session plans")
526+
const planImportIndex = hooks.indexOf("plan-to-git import-codex --no-sync")
525527
const planSyncIndex = hooks.indexOf("plan-to-git sync")
526528
const sessionBackupIndex = hooks.indexOf("docker-git-session-sync backup --verbose --background --require-comment")
527529

528530
expect(cdIndex).toBeGreaterThanOrEqual(0)
529531
expect(ensurePrIndex).toBeGreaterThan(cdIndex)
530-
expect(planSyncIndex).toBeGreaterThan(ensurePrIndex)
532+
expect(planImportIndex).toBeGreaterThan(ensurePrIndex)
533+
expect(planSyncIndex).toBeGreaterThan(planImportIndex)
531534
expect(sessionBackupIndex).toBeGreaterThan(planSyncIndex)
532535
})
533536
})

0 commit comments

Comments
 (0)