6cf95433bf2b70ab07efb3be5ae5a20668d1c61a
6 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
74cba4b46d |
batch-core: one shared landed-lane helper for the source-issue surfaces (75 → 72) (#2783)
## batch-core continued — the source-issue cluster Follow-on to #2780 (merged). Scope is still `packages/core` + `packages/dashboard/src`. ### The defect Five places asked the same question — *has this task landed?* — and all five compared against the literal `done`: | surface | consequence on a renamed board | |---|---| | GitHub source-issue commenter | never comments on or closes the source issue | | GitLab source-issue commenter | same | | GitLab `closedAt` backfill reconciler | finds nothing, reports a clean scan | | session-diff boundary | finished tasks diff against an already-merged branch | | tracking-comment transition | (already converted; left alone) | The commenters are the sharpest case: they returned **before reading a single setting**, so on a renamed board the feature looked *disabled* rather than broken — an operator checking `githubCommentOnDone` would see it enabled and still get nothing. The backfill is the quietest: `scanned: N, filled: 0` reads as "nothing to do", so the failure was indistinguishable from success. ### The fix One home: `packages/dashboard/src/task-lifecycle-lanes.ts`. Callers now only ask. Five copies of one question is exactly how the halves drift apart — the motivating incident is FN-6115 → FN-6118 → FN-6123, where the same affordance was fixed three times because it lived in two components. This also folds in the duplicate landed-lane helper I had left in `register-session-diff-routes.ts` in the previous PR, which was the sixth copy waiting to happen. Two helpers, and the difference is deliberate: - **`landedColumnsForTask`** — `complete ∪ archived`. Membership, since a board may declare more than one column carrying either role, and `columnsWithFlag(...)[0]` would silently ignore the second. - **`completeColumnsForTask`** — complete only. The GitLab backfill's own FNXC note records that archived tasks live in `archiveDb` and are *intentionally* excluded, so it must not widen to the archived role just because the shared helper offers it. Today it lists with `includeArchived: false` and would see no archived rows either way — but that is an incidental property of the query, not the contract. The test pins the difference so the two are not later "simplified" into one, which would change that caller's behaviour without touching it. Both treat an **empty** resolved set as *unexpressed*, not absent — the v1 hazard: `synthesizeDefaultColumns` upgrades a v1 graph with `traits: []` on every column, so reading empty as "no complete lane" would stop these surfaces firing on every pre-v2 project. The reconciler is two-stage on purpose: the cheap provider and `closedAt` tests run first and reject almost everything, so a workflow read only happens for real candidates, and it shares one IR cache across the scan — one read per distinct workflow rather than per task. ### Census `batch-core` scope **75 → 72**; repo total **338**. ### Verification - `pnpm --filter @fusion/dashboard exec tsc --noEmit -p tsconfig.json` → 0 errors - `pnpm lint` → 0 errors - commenter + reconciler suites → **63 passed**; helper suite → **5 passed** - **Mutation-verified:** making the helper ignore its resolved set fails 1 of 5. --- ## Round 2 — server.ts, chat.ts, and a correction **Census: 75 → 67** across this PR. ### The correction (see the review thread above) My first pass gated the source-issue commenters on `landedColumnsForTask` (`complete ∪ archived`), which **widened** the trigger — `to === "done"` never fired on archival, and the landed set does. Both commenters now use `completeColumnsForTask`, and the unused `hasTaskLanded` wrapper is gone. The ratchet for it is pinned on the **default** board, deliberately: a widening is visible exactly where the legacy names still apply, so no renamed-board fixture would catch it. ### `chat.ts` — three sites, and a pair that had to move together - **Chat verification** required `column === "in-progress"`, so on a renamed board every chat-driven verification was refused with a message naming a column the board does not have. - **The planner refinement pair.** Two separate guards decide this feature: `createSession` *registers* the tool only for a finished task, and the tool's own `execute()` *refuses* a non-finished source. Both compared `done`. Converting only one half would have offered the tool and then had it refuse itself — the half-converted-pair shape. The new test asserts **both** halves in one case (tool present *and* refinement created), and each half reverted independently fails it. Existing `chat-manager` coverage caught neither revert, which is why the case exists rather than relying on the suite that was already there. Complete-only again, not the landed set: an archived task is off the board and is not a refinement source. ### `server.ts` - **Planner-chat retention** — the archival cutoff was a literal, so on a renamed board task-planner chat sessions were retained forever; the rule this listener exists to enforce never fired. Resolved, and awaited inside the existing fire-and-forget chain rather than by making the listener `async` — `task:moved` has synchronous subscribers whose ordering is load-bearing elsewhere, and a chat-row delete is not the right place to introduce a microtask boundary into that emit. - **`isBadgeEligibleTask` — deliberately NOT converted, and marked as backlog.** On a renamed board it is genuinely wrong: an archived card stays badge-eligible, its snapshot is never evicted, and the cache grows for the daemon's lifetime — the exact memory leak the predicate was added to fix, back under a different column name. What blocks it is measured, not assumed: both callers are synchronous `task:updated` / `task:created` listeners whose next statement is documented as *"Update local cache immediately"*, so awaiting lets a second event for the same task interleave between the eligibility check and the cache write. I did **not** add an optional `archivedColumns` parameter, because nothing could fill it — the callers are the sync listeners. That is the inert-injection shape this PR's own review caught twice on #2780: the predicate would read as converted, its test would pass by injecting the value, and production would keep the literal. The unblocking change (a resolved-archived-lane cache on the badge-snapshot scope, keeping the predicate synchronous) is recorded at the site. ### Verification - `tsc --noEmit` → 0 errors; `pnpm lint` → 0 errors - `chat-manager` → 101 passed; commenter/reconciler/helper/badge suites → 55 passed - Mutation-verified per fix, including each half of the refinement pair separately <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved task lifecycle handling for renamed workflow lanes, including completed, archived, landed, and in-progress states. * Task lists now exclude completed tasks regardless of the completion lane’s name. * Chat verification and refinement actions now recognize configured workflow lanes. * GitHub and GitLab completion comments trigger only for genuinely completed tasks, not archived tasks. * Knowledge index refreshes and GitLab metadata updates now support custom completion lanes. * **Tests** * Added regression coverage for renamed completion lanes and archived-task behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
1b9c7a7ca2 |
fix(FN-7575): stop double-commenting when a task is both imported and tracked
A task can carry BOTH linkages at once, pointing two services at ONE issue: - GitHub: maybeCreateTrackingIssue() ADOPTS a github sourceIssue as githubTracking.issue (github-tracking.ts, `source_issue_linked`). - GitLab: buildGitLabTaskProvenance() always returns sourceIssue AND gitlabTracking.item for the same item, so on GitLab EVERY imported task with gitlabCommentOnDone on was double-commented. With comment-on-done enabled the issue-comment service and the tracking-comment service both posted. Reproduced against the real wiring: two comments on acme/widgets#42 ("✅ Task FN-1 ... resolved." then "✅ Done — ..."). The issue-comment services now suppress themselves when the tracking service provably posts to the SAME target, and the tracking comment wins — it carries commit/branch/PR/files/merged plus the release lines. Identity, never "both linked": the two may legitimately target DIFFERENT issues (a tracking issue linked separately from the source issue), which is two comments on two issues and must keep working. GitHub matches on case-insensitive owner/repo + number; GitLab is identical by construction because resolveGitLabTarget() prefers the tracked item. Both guards mirror the tracking services' `from === to` no-op guard: on a same-column re-emit the tracking service stays silent, so suppressing there would drop the only comment rather than dedupe it. The net split is now disjoint: issue-comment owns "imported but not tracked", tracking owns "tracked". Suppression is logged (once per completion, not the high-frequency skip-noise FN-8024 removed) because a custom comment template silently not rendering on a tracked issue is otherwise unexplainable. Behavior change, documented in settings-reference.md: githubCommentTemplate / gitlabCommentTemplate no longer render on a tracked issue. Tests updated where they encoded the double-post path (they exercised the services in isolation, so the duplicate was invisible). GitLab fixtures now distinguish tracked vs imported-not-tracked shapes. Also asserts a PRE-EXISTING gap left unchanged: resolveGitLabTarget() early-returns on an unresolvable item and never falls back to sourceMetadata, so neither service comments there. Verified non-vacuous: the 4 suppression tests fail against the pre-fix source; the "still posts" tests pass either way by design. Gate green (294/122/63). Fusion-Task-Id: FN-7575 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
b9ad5b0cec |
docs(FN-7575): correct the FNXC rationale on the issue-comment services
My previous commit's comments claimed GitHubIssueCommentService "effectively
never fires" and was not the surface that posts. That is wrong, and acting on it
would have deleted working, documented behavior.
The issue-comment and tracking-comment services gate on different, disjoint
linkages:
- {GitHub,GitLab}IssueCommentService -> task.sourceIssue, the IMPORT linkage set
unconditionally by buildGitHubIssueSource, plus the documented
githubCommentOnDone / gitlabCommentOnDone settings (settings-reference.md:637,
651 — no Settings UI, but reachable via the settings API/file).
- {GitHub,GitLab}TrackingCommentService -> githubTracking.enabled /
gitlabTracking.item, the explicit TRACKING linkage.
resolveImportedIssueGithubTracking() returns undefined unless
githubLinkImportedIssuesToTracking or the tracking defaults resolve on, so an
imported issue with tracking off has sourceIssue and NO tracking linkage — the
issue-comment service is then the ONLY surface that comments. Neither service is
redundant; record that so neither is deleted as a "duplicate" later.
Also records the pre-existing overlap: a task carrying BOTH linkages with
comment-on-done enabled receives two comments, one per service. Orthogonal to the
release lines and left undeduped.
Comments only — no behavior change; the 76 comment-surface tests still pass.
Fusion-Task-Id: FN-7575
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
||
|
|
71275abffe |
fix(FN-7575): post release version lines on the surface that actually comments
FN-7575 (issue #1916) added "Current version:" / "Target release:" lines to GitHubIssueCommentService, but that service is gated on `githubCommentOnDone` — default false, with no Settings UI — so it effectively never fires. The "✅ Done —" comments on linked issues are posted by GitHubTrackingCommentService, which had no version logic. The lines were invisible in production for ~10 days; issue #1916's own close comment is the proof. Extract the self-repo check and next-minor computation into a shared fusion-release-version.ts and apply it across all four done-comment surfaces (GitHub/GitLab x tracking/issue) so they cannot drift again. - Release lines join `optionalLines` rather than being appended to the finished string, so they count against DONE_COMMENT_MAX_LENGTH and shrink the title budget; appending would silently blow the cap on long titles. - Version resolution is a lazy resolver, so getCliPackageVersion()'s filesystem walk only runs for self-repo comments. - GitLab self-repo matching uses item.projectPath: resolveGitLabTargetFromItem() prefers the numeric projectId, which never matches the slug. - Non-self repos stay byte-for-byte unchanged (asserted). Per the Surface Enumeration rule, regression tests assert the invariant across every done-comment surface — both the pure formatters and the services that post — plus case-insensitive slug matching (issue #1916 is "Runfusion/Fusion"), in-progress transitions, the 0.0.0 sentinel, unparseable versions, the lazy-resolution guarantee, and the truncation ladder under the length cap. Verified non-vacuous: 10 of the new tests fail against the pre-fix source. Fusion-Task-Id: FN-7575 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
78d4db94d8 |
FN-7575: add release version lines to Fusion self-repo done comments
Extends GitHubIssueCommentService so task-close comments on issues in the runfusion/fusion repo itself append both a current-version and target-next-minor-release line, while comments on all other linked repos stay byte-for-byte unchanged.
- Add isFusionSelfRepo() and computeNextMinorVersion() helpers to github-issue-comment.ts
- Append "Current version: v{current}" and "Target release: v{next-minor}" lines only when the linked source issue's repo is runfusion/fusion (case-insensitive)
- Fall back silently (no version lines) when the resolved version is unparseable or the unresolved 0.0.0 sentinel
- Add changeset (minor) documenting the new behavior
- Update docs/settings-reference.md and docs/gitlab-parity-inventory.md
- Expand github-issue-comment.test.ts coverage for self-repo vs other-repo behavior and version edge cases
Files changed:
.changeset/fn-7575-release-version-comment.md | 7 +
docs/gitlab-parity-inventory.md | 2 +-
docs/settings-reference.md | 2 +-
packages/dashboard/src/__tests__/github-issue-comment.test.ts | 142 ++++++++++++++++++++-
packages/dashboard/src/github-issue-comment.ts | 69 +++++++++-
5 files changed, 212 insertions(+), 10 deletions(-)
Fusion-Task-Id: FN-7575
Fusion-Task-Lineage: b7cf7e6f-8d96-4442-8595-5d54ea911481
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
|
||
|
|
e2bc644fd5 | feat(FN-2623): merge fusion/fn-2623 |