| author | |
| committer | |
| log | d536b0580d044967dc644498d8c5a6e96464f767 |
| tree | d601f869c0caa9f1785656747971f501a2f29244 |
| parent | c66f15f39e61fda0a9a100578833ec12e7526859 |
| signature | Signed by PGP key B5690EEEBB952194 |
4 files changed, 23 insertions(+), 10 deletions(-)
apps/server/src/orchestration/ThreadSettlementPolicy.test.ts+2-2| ... | ... | @@ -95,8 +95,8 @@ describe("resolveAutoSettlementAt", () => { |
| 95 | 95 | expect(decide(makeThread({ latestUserMessageAt: "2026-08-25T12:00:00.000Z" }))).toBe(false); |
| 96 | 96 | }); |
| 97 | 97 | |
| 98 | it("keeps open pull requests active", () => { | |
| 99 | expect(decide(makeThread(), { state: "open", updatedAt: NOW })).toBe(false); | |
| 98 | it("settles inactive threads with open pull requests", () => { | |
| 99 | expect(decide(makeThread(), { state: "open", updatedAt: NOW })).toBe(true); | |
| 100 | 100 | }); |
| 101 | 101 | |
| 102 | 102 | it("settles closed requests and honors the merge setting", () => { |
apps/server/src/orchestration/ThreadSettlementPolicy.ts-1| ... | ... | @@ -81,7 +81,6 @@ export function resolveAutoSettlementAt(input: { |
| 81 | 81 | if (pullRequestSettles(thread, pullRequest, input.autoSettleOnMerge)) { |
| 82 | 82 | return activityAt ?? thread.createdAt; |
| 83 | 83 | } |
| 84 | if (pullRequest.state === "open") return null; | |
| 85 | 84 | } |
| 86 | 85 | if (input.autoSettleAfterDays === null || activityAt === null) return null; |
| 87 | 86 | return Date.parse(activityAt) < Date.parse(input.now) - input.autoSettleAfterDays * DAY_MS |
apps/server/src/orchestration/ThreadSettlementReactor.test.ts+17-3| ... | ... | @@ -408,6 +408,7 @@ describe("ThreadSettlementReactor", () => { |
| 408 | 408 | const fixture = yield* makeHarness({ |
| 409 | 409 | snapshot: makeSnapshot([ |
| 410 | 410 | makeThread("merged-in-app", { |
| 411 | latestUserMessageAt: "2026-08-27T00:00:00.000Z", | |
| 411 | 412 | linkedPullRequest: { |
| 412 | 413 | projectId: PROJECT_ID, |
| 413 | 414 | repository: "owner/repository", |
| ... | ... | @@ -415,7 +416,10 @@ describe("ThreadSettlementReactor", () => { |
| 415 | 416 | url: "https://example.test/owner/repository/pull/42", |
| 416 | 417 | }, |
| 417 | 418 | }), |
| 418 | makeThread("slow-periodic-lookup", { branch: "another-feature" }), | |
| 419 | makeThread("slow-periodic-lookup", { | |
| 420 | branch: "another-feature", | |
| 421 | latestUserMessageAt: "2026-08-27T00:00:00.000Z", | |
| 422 | }), | |
| 419 | 423 | ]), |
| 420 | 424 | branchPullRequest: () => |
| 421 | 425 | Ref.updateAndGet(branchLookupCount, (count) => count + 1).pipe( |
| ... | ... | @@ -460,7 +464,12 @@ describe("ThreadSettlementReactor", () => { |
| 460 | 464 | const state = yield* Ref.make<"open" | "merged">("open"); |
| 461 | 465 | const mergedThreadSettled = yield* Deferred.make<void>(); |
| 462 | 466 | const fixture = yield* makeHarness({ |
| 463 | snapshot: makeSnapshot([makeThread("branch-thread", { branch: "saved-feature" })]), | |
| 467 | snapshot: makeSnapshot([ | |
| 468 | makeThread("branch-thread", { | |
| 469 | branch: "saved-feature", | |
| 470 | latestUserMessageAt: "2026-08-27T00:00:00.000Z", | |
| 471 | }), | |
| 472 | ]), | |
| 464 | 473 | branchPullRequest: () => |
| 465 | 474 | Ref.get(state).pipe( |
| 466 | 475 | Effect.map((pullRequestState) => ({ state: pullRequestState, updatedAt: NOW })), |
| ... | ... | @@ -500,6 +509,7 @@ describe("ThreadSettlementReactor", () => { |
| 500 | 509 | const fixture = yield* makeHarness({ |
| 501 | 510 | snapshot: makeSnapshot([ |
| 502 | 511 | makeThread("merged-in-app", { |
| 512 | latestUserMessageAt: "2026-08-27T00:00:00.000Z", | |
| 503 | 513 | linkedPullRequest: { |
| 504 | 514 | projectId: PROJECT_ID, |
| 505 | 515 | repository: "owner/repository", |
| ... | ... | @@ -508,6 +518,7 @@ describe("ThreadSettlementReactor", () => { |
| 508 | 518 | }, |
| 509 | 519 | }), |
| 510 | 520 | makeThread("unrelated-linked", { |
| 521 | latestUserMessageAt: "2026-08-27T00:00:00.000Z", | |
| 511 | 522 | linkedPullRequest: { |
| 512 | 523 | projectId: PROJECT_ID, |
| 513 | 524 | repository: "owner/repository", |
| ... | ... | @@ -683,7 +694,10 @@ describe("ThreadSettlementReactor", () => { |
| 683 | 694 | const fixture = yield* makeHarness({ |
| 684 | 695 | snapshot: makeSnapshot( |
| 685 | 696 | [ |
| 686 | makeThread("missing-own-project", { linkedPullRequest }), | |
| 697 | makeThread("missing-own-project", { | |
| 698 | latestUserMessageAt: "2026-08-27T00:00:00.000Z", | |
| 699 | linkedPullRequest, | |
| 700 | }), | |
| 687 | 701 | makeThread("missing-branch-project", { branch: "saved-feature" }), |
| 688 | 702 | ], |
| 689 | 703 | [makeProject(LINKED_PROJECT_ID, "/workspace/linked")], |
docs/user/thread-sidebar.md+4-4| ... | ... | @@ -13,10 +13,10 @@ request merges if **Auto-settle merged threads** is enabled. |
| 13 | 13 | Each server stores its own copy of the automatic settlement settings and checks them even when no |
| 14 | 14 | web, desktop, or mobile client is connected. By default, it settles threads after three days without |
| 15 | 15 | activity and when their pull request merges. An eligible idle thread also settles when its pull |
| 16 | request closes. An open pull request blocks inactivity settlement. Active work, pending input, and | |
| 17 | live background work keep the thread active. T3 Code settles from a closed or merged pull request | |
| 18 | only when its timestamp is not older than the user's latest activity. If that timestamp is not | |
| 19 | available, the inactivity rule still applies. A manual un-settle also keeps the thread active. | |
| 16 | request closes. An open pull request does not block inactivity settlement. Active work, pending | |
| 17 | input, and live background work keep the thread active. T3 Code settles from a closed or merged | |
| 18 | pull request only when its timestamp is not older than the user's latest activity. If that timestamp | |
| 19 | is not available, the inactivity rule still applies. A manual un-settle also keeps the thread active. | |
| 20 | 20 | |
| 21 | 21 | **Settled** lists threads by when their work finished, newest first. A thread you settle yourself |
| 22 | 22 | sorts by the moment you settled it. A thread that settled on its own sorts by its last message or |