Skip to content

[2] Keep work tied to its account and task - #1347

Open
tautvydasLiekis wants to merge 33 commits into
masterfrom
feat/scope-work-continuation
Open

tautvydasLiekis wants to merge 33 commits into
masterfrom
feat/scope-work-continuation

Conversation

@tautvydasLiekis

@tautvydasLiekis tautvydasLiekis commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Each model request is now tied to the account, repository and user input it belongs to, so a PR is charged only for its own task. Before this, a fresh session on the same branch could inherit unrelated work, and saved work had no account boundary.

Linked issue

Depends on #1320. No public issue is linked.

What does this PR do?

  • No more branch-only matching. A new session starts its own work unless it names recorded work or a plan.
  • Plans connect planning and implementation. /work <plan path>, or pasting the complete retained plan, continues the work that wrote it. An accepted continuation keeps the session's later inputs in that work, like /work <plan>, until an input names other work. /work refuses a plan saved for another account or repository and keeps the current work.
  • Accounts stay separate. Changing the verified account or repository starts new work. Missing identity stays unknown; old history never gets today's login.
  • Optional model matching. /work matching on lets the selected model compare saved task text in separate paid calls. It is off by default.
  • Queued messages keep their own work. Typing during a reply leaves the running requests with their current input; the queued message picks its work when Pi delivers it. An extension message and a user message with identical text are told apart by Pi's delivery order.
  • Corrections. /work link <source-work-id> <segment-id> records a correction and /work unlink <link-id> revokes it. Original request records stay unchanged. Continuing a verified plan also confirms the input that wrote it.
  • Cost per PR in /resources. Work attribution and the PR/MR status are one built-in extension, "Cost per PR", on by default. Disabling it there, or with kimchi resources disable extensions.cost-per-pr, takes effect at the next start; other extensions stop recording work at once.
  • Nothing printed into the TUI. A failed save of a plan's retained copy goes to the opt-in work-attribution log; the UI notice stays.
  • Symlinked directories. An edit through a directory that links into another repository is recorded against that repository. Before, Git rejected the path and the edit lost its evidence.

How a message picks its work

flowchart TD
    M["Delivered user message"] --> X{"Explicit work choice?"}
    X -->|Yes| K["Keep that choice"]
    X -->|No| P{"Names recorded work or a saved plan?"}
    P -->|Yes| V{"One verified owner, same account and repository,<br/>no named file owned by other work?"}
    V -->|Yes| A["Continue the plan's work"]
    V -->|No| U["Keep current work; mark input unknown"]
    P -->|No| E{"Model matching on?"}
    E -->|No| K
    E -->|Yes| C["Ask the selected model"]
    C -->|Same task or one earlier task| I["Keep or continue it; saved as an inference"]
    C -->|Clearly new task| N["Start separate work"]
    C -->|Uncertain| U
Loading

Each request's work, input segment and scope are saved before it is sent. New files sit next to work.json under ~/.config/kimchi/harness/work/<workId>/: scope.json (original account and repository), plans/ (plan snapshots) and intent.json (task text, only when model matching is on). This PR prices nothing; #1348 does.

Evidence

On 92ab8abcd:

  • Real models, 11 October: a binary of this PR and of every PR above it ran the same TMUX session flow with real GLM-5.3 and GLM-5.3 Flash. Inference and bill lookups used the real Kimchi API; GitHub, GitLab and the reporting backend were local. GLM-5.3 saved a plan with submit_plan; a fresh Flash session that sent Implement .kimchi/plans/notes.md, … continued the plan's work before its first request, and the planning input was confirmed through a work link; a restart started separate work. The binary built without this PR kept the two sessions apart.
  • That run found one bug, fixed here: in a session continuing a plan, a question naming a file the same work had written, after a Bash command changed it, ended the continuation and marked the input unknown, so its spend stayed out of the PR's total. Plans and files that only the current work recorded now name no other task. Two new tests fail without the fix; rerun with real models, the question stays in the plan's work. The fix is merged into every PR above this one (#1351 carries it into its rewritten continuation check).
  • pnpm check passes and the work attribution tests pass (392). CI on this head: build, pr-checks, master-checks, TUI e2e in 4 shards, ACP e2e and MCP e2e pass.

Earlier, on 3983c2bf3 after the final review fixes: CI passed (build, pr-checks, master-checks, TUI e2e in 4 shards, ACP e2e and MCP e2e); on the stack top 95614e7c6, pnpm check and the full unit suite passed except 5 tests that need tools this machine lacks; a new test covered a saved plan named together with a file outside every Git worktree; and the lab passed 73/73 default scenarios with a scripted model.

Limits

  • Old unscoped history, conflicting owners and uncertain model matches stay unresolved rather than guessed.
  • A session whose work has no saved scope, such as work from before this change, starts new work at its next verified message. Earlier requests keep their work.
  • Model matching stops above 256 saved tasks, 32 plan versions per work, a latest plan over 16,000 bytes or 12,000 characters of compared text. The input then stays unresolved, and /work names the limit that stopped it.
  • A message that names a Markdown file reads all local work journals before its request. An index is separate work.
  • Remote agents are outside this flow.

Checklist

  • I have read CONTRIBUTING.md and agree to the CLA
  • This PR links to an open issue above
  • Tests pass locally (pnpm run test) — all but 5 that need tools this machine lacks; see Evidence
  • Lint passes (pnpm run check)
  • Documentation updated if behavior changed

🤖 Generated with Claude Code

@tautvydasLiekis tautvydasLiekis added the new feature Introduces a new feature label Oct 5, 2026
@kimchi-review

kimchi-review Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Kimchi Code Review

Property Value
Commit 51c67fe
Author @tautvydasLiekis
Files changed 31
Review status Completed
Comments 0
Duration 43s

Summary

🚫 Merge request too large to review

📏 Size: 31 file(s) changed, +3856 / −342 lines

🧠 Cognitive load: 5/5 (1 = trivial, 5 = extremely demanding)

Account/repo scoping, opt-in semantic matching, a new segment model, and work-link corrections all converge in the core input handler with subtle async invalidation rules. Removing branch fallback rewrites continuation semantics, and large new integration test suites add volume on top.

✂️ Suggested split

  1. Return user identity from API-key verification — Adds userId plus endpoint/abort/retry options; standalone foundation for account scoping.
    • src/api/organizations.ts
    • src/api/organizations.test.ts
  2. Capture account and repository scope for each work — Introduces scope.json capture and verification with its test mock.
    • src/extensions/work-attribution/scope.ts
    • src/extensions/__mocks__/work-scope.ts
  3. Replace branch fallback with pasted-plan and artifact matching — Continuation resolves saved, pasted, or attributed files under matching scope; drops branch inference.
    • src/extensions/work-attribution/continuation.ts
    • src/extensions/work-attribution/continuation.test.ts
  4. Add opt-in semantic task matching — Stores intent text and compares it via the selected model; extends context mock with modelRegistry.
    • src/extensions/work-attribution/semantic.ts
    • src/extensions/__mocks__/context.ts
  5. Attribute requests by segment and scope with correction links — Core orchestration: segments, scope enforcement, matching decisions, and workLink revisions.
    • src/extensions/work-attribution.ts
    • src/extensions/work-attribution.test.ts
    • src/extensions/work-attribution/links.ts
    • src/extensions/work-attribution/summary.ts
    • src/extensions/work-attribution/continuation.integration.test.ts
  6. Propagate attribution to plans, ferments, and child agents — Tools pin originating requests, children inherit segments; documents the new attribution model.
    • src/extensions/permissions/index.ts
    • src/extensions/permissions/index.test.ts
    • src/extensions/ferment/tools/lifecycle.ts
    • src/extensions/ferment/tools/lifecycle.test.ts
    • src/extensions/agents/manager/agent-runner.ts
    • src/extensions/agents/manager/agent-runner.test.ts
    • docs/work-attribution.md

👉 Please split this merge request into the smaller merge requests suggested above — the AI review will run automatically on each of them.

⚠️ Full review skipped — this merge request exceeds the reviewable size limit. See the split proposal above.

What to expect

Kimchi will analyze the changes in this pull request and post:

  • A summary of the overall changes
  • Inline comments on specific lines with findings categorized by issue type

The review typically completes within a few minutes. This comment will be updated once the review is ready.

Interact with Kimchi
  • @getkimchi review — re-trigger a full review on the latest commit
  • @getkimchi summary — regenerate the PR summary
  • @getkimchi ignore — skip this PR (no review will be posted)
  • Reply to any inline comment to ask follow-up questions or request clarification
Configuration

Reviews are configured by your organization admin.
Review instructions, excluded directories, and severity thresholds can be adjusted per repository in the Kimchi dashboard.


Powered by Kimchi — AI-powered code review by CAST AI

@kimchi-review kimchi-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚫 Merge request too large to review

📏 Size: 31 file(s) changed, +3856 / −342 lines

🧠 Cognitive load: 5/5 (1 = trivial, 5 = extremely demanding)

Account/repo scoping, opt-in semantic matching, a new segment model, and work-link corrections all converge in the core input handler with subtle async invalidation rules. Removing branch fallback rewrites continuation semantics, and large new integration test suites add volume on top.

✂️ Suggested split

  1. Return user identity from API-key verification — Adds userId plus endpoint/abort/retry options; standalone foundation for account scoping.
    • src/api/organizations.ts
    • src/api/organizations.test.ts
  2. Capture account and repository scope for each work — Introduces scope.json capture and verification with its test mock.
    • src/extensions/work-attribution/scope.ts
    • src/extensions/__mocks__/work-scope.ts
  3. Replace branch fallback with pasted-plan and artifact matching — Continuation resolves saved, pasted, or attributed files under matching scope; drops branch inference.
    • src/extensions/work-attribution/continuation.ts
    • src/extensions/work-attribution/continuation.test.ts
  4. Add opt-in semantic task matching — Stores intent text and compares it via the selected model; extends context mock with modelRegistry.
    • src/extensions/work-attribution/semantic.ts
    • src/extensions/__mocks__/context.ts
  5. Attribute requests by segment and scope with correction links — Core orchestration: segments, scope enforcement, matching decisions, and workLink revisions.
    • src/extensions/work-attribution.ts
    • src/extensions/work-attribution.test.ts
    • src/extensions/work-attribution/links.ts
    • src/extensions/work-attribution/summary.ts
    • src/extensions/work-attribution/continuation.integration.test.ts
  6. Propagate attribution to plans, ferments, and child agents — Tools pin originating requests, children inherit segments; documents the new attribution model.
    • src/extensions/permissions/index.ts
    • src/extensions/permissions/index.test.ts
    • src/extensions/ferment/tools/lifecycle.ts
    • src/extensions/ferment/tools/lifecycle.test.ts
    • src/extensions/agents/manager/agent-runner.ts
    • src/extensions/agents/manager/agent-runner.test.ts
    • docs/work-attribution.md

👉 Please split this merge request into the smaller merge requests suggested above — the AI review will run automatically on each of them.

⚠️ Full review skipped — this merge request exceeds the reviewable size limit. See the split proposal above.

@tautvydasLiekis
tautvydasLiekis force-pushed the feat/scope-work-continuation branch 10 times, most recently from 1da526c to 3a9082d Compare October 7, 2026 20:53
@tautvydasLiekis
tautvydasLiekis force-pushed the feat/discover-work-pull-requests branch from 2ea1635 to d2f7946 Compare October 7, 2026 21:20
@tautvydasLiekis
tautvydasLiekis force-pushed the feat/scope-work-continuation branch 11 times, most recently from ca16415 to 51e7e57 Compare October 9, 2026 08:17
@tautvydasLiekis
tautvydasLiekis force-pushed the feat/scope-work-continuation branch from 51e7e57 to 6c8264e Compare October 9, 2026 10:46
Base automatically changed from feat/discover-work-pull-requests to master October 9, 2026 11:00
@tautvydasLiekis
tautvydasLiekis marked this pull request as ready for review October 9, 2026 11:03
@tautvydasLiekis
tautvydasLiekis force-pushed the feat/scope-work-continuation branch from 6c8264e to 489bb3a Compare October 9, 2026 11:35
}
}

/** Separate inference using the model selected at input time; never changes the chat model. */

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.

question

Any reason work matching runs on the session model instead of the classifier candidates? permissions solved the same shape of problem already, right? 🤔 with per-candidate deadlines, graceful degradation. same deal here: small bounded json verdict, cost-sensitive, runs per delivered message when matching is on.

we could move classifier-models.ts to a shared location and reuse it here and keeping the session model as fallback when kimchi-dev/* refs don't resolve?

workMatchingEnabled() ? "unknown" : "session",
workMatchingEnabled() ? "matching-unresolved" : "matching-disabled",
)
const captured = await captureWorkScope(cwd)

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.

nit

captureWorkScope is on the message-send hot path, so every prompt and queued message pays for an uncached git subprocess, repeated config reads, and occasionally a 1s API verification.

suggestions:

  • Cache workRepository per cwd and load config once per capture.
  • When identity verification expires, reuse the last-good identity if the key and API URL are unchanged, refreshing in the background. Otherwise, return unknown and refresh asynchronously.

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

Labels

new feature Introduces a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants