Repository navigation
feat: workspace rules in Claude Code and Codex sessions through the plugins - #1626
Merged
Merged
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Builds on #1605 (merged). Rebased onto
main: #1605 added on-disk "remember" requests, and they are combined with the stored proposals here (enqueuetakes{ id, forget },requests_fullapplies only to shown cards, stored proposals are exempt from the one-day card limit). This brings workspace rules to Claude Code and Codex sessions through the altimate-code plugins. Plugin PRs: AltimateAI/altimate-claude-plugin#13 and AltimateAI/altimate-codex-plugin#7.The plugins can't show proposal cards, and altimate-code doesn't run the host's conversation. Its hooks see the prompts, and after a turn the Stop check reads the host's transcript file. So the host's hooks call new
learnsubcommands:learn hook <event>SessionStart,UserPromptSubmitandStophooks. It reads the host's hook JSON on stdin, prints the hook output at session start only, and never fails the host.learn rules [--session]--session, they are recorded as shown to that host session (Delivery.recordStart), so "corrected again", usage and the shown-rule edit path count it.learn signal add --detectcorrectionReasonaccepts it. "Remember this as a team rule: …" becomes a stored explicit proposal. No model call.learn check --sessionRuleTrigger.afterTurnwithpersist: true, with the digest read from the Claude Code or Codex transcript (RulesHost.sourceFromTranscript). A Stop that arrives while the session's check is running marks it as having more work, and the check runs again before it releases its claim. Corrections left over from a session that ended are checked by a later Stop.learn proposals [reply <id>]Stored proposals. When no client is attached to show cards,
RuleProposal.askkeeps the proposal inrules.json(proposals) instead of making a card. Before, it was saved locally with nothing sent for review. A reply claims the proposal on disk first, then answers it through the samereplypath, so the scope check,approve_anywayand the refused/failed handling don't change. Proposals are queued under their card id. A send already recorded as successful for the same wording is not queued again, and a closing answer is recorded on its claim, so only the same answer finishes it after a crash. If the server accepted a proposal but the answer was never recorded, the proposal can be sent again (the known no-submission-id limit). Limits: at most 20 proposals raised from corrections; past that, their corrections stay unchecked until some are answered. A "remember" the person asked for is kept past that limit, up to 100. A session proposal is dropped after 14 days; a "remember" waits until it's answered. Only the verified account and workspace a proposal was raised for can see and answer it.Behavior change: a "remember … as a team rule" in a headless
altimate-code run(no TUI or extension attached) is now stored forlearn proposalsinstead of being saved to project training. Subagent sessions still save as before.Gates. Team rules are given when workspaces are on, the project is linked and the person is signed in as the account they were pulled for, whatever the learning setting (as in an altimate-code session). Capturing corrections and drafting also need learning on. With no model configured for altimate-code, the Stop check fails and says so at the next start: it never signs up for one. The hook commands run without the instance bootstrap (
Instance.provide), so a hook call starts no plugins, LSP, scheduler or background sync. Host session ids areclaude:<id>/codex:<id>, and reflection treats them as external sessions.Review
An independent review of the full diff found 3 MAJOR issues, all fixed here with tests:
Minor findings fixed: a claim TTL and same-process double replies, the
recordStartlock fallback, the notice age limit, host-injected transcript rows, sanitized JSON output, no telemetry per hook call, and host-only--session.Accepted: Stop can start before the prompt hook has recorded its correction. That correction is then checked on the next turn.
Consensus review (Claude, Codex, Gemini)
The review converged on NEEDS REVISION with 5 MAJOR findings. All are fixed in
f2213dbb40, each with a test:The fixable minor findings were fixed too: the stale
learn checkoutcome, pinned rules first, a failed check reported at the next session start, dedupe on the full proposal identity, and thetranscript_pathfile check. The rest are documented: the drafting model requirement, auto-reflection of host corrections, the Codex sandbox, and "remember" requests in CI.Second consensus review (full feature)
The second review again converged on NEEDS REVISION, with 6 MAJOR findings. All are fixed in
dfecbdafa5, each with a test:learn proposalsand replies now apply it too, and a draft whose workspace changed mid-draft isn't stored.The plugins now start no CLI in repositories with neither rules state nor a workspace link (claude-plugin
6e87e8f, codex-pluginadad91a).Rejected: running the hook from the git root. A link can be for a folder inside the repository, and the CLI resolves the root itself.
PR review round (hkaltai, ralphstodomingo, bots)
Every open thread is answered inline. The changes, grouped by what they fix:
pull_failed_atis set only for a pull that ran and failed (or an unconfirmed link or account), and only holds while the same credential is signed in. Signed out is not a failure.closeLearnLocksaborts learn-lock waits that haven't started and waits up to 1.5s for held ones. Telemetry flush is capped at 1s. The start deadline is 10s.turnStartedAt, so "corrected again" counts;recordStartgives only rules it recorded, and re-dates only those it gave;bytesRead.explicit;answeringmarker that never acted no longer blocks;rules --jsonkeeps line breaks, andcheck --jsonis printable;--detectneeds a host session;Not done: running
mayAnswerin parallel (at most 100 local checks). The security finding is fixed in #1669.Second PR review round (hkaltai, bots)
Fixed in
37a607959d, each with a test that fails without it:closeLearnLockswaits until the lease is released.The sync-state trust finding is fixed in #1669 (state outside the repository, delivered only for the account that pulled it). This PR rebases onto it once it merges.
Test plan
bun run typecheckbun test test/altimate/rules test/server/altimate-rules-routes.test.ts test/altimate/learn: 2217 pass at the second consensus round. After the PR review round: rules, learn, telemetry and cli suites 3831 pass (2--time-budgettiming tests also fail onmainlocally).host.test.ts(76) andhost-cli.test.ts(8, real CLI) cover a crash between writes, a busy claim, a stale claim, two checks of one session, sign-out, a scope change, a failed or flagged reply, and the transcript formats.analyze.ts --markers --strict)altimate-backend/altimate-default:learn hook stoprecorded a correction that existed only in the transcript and stored "Name order timestamp columnsordered_at, notorder_date.";learn hook user-prompt-submitwith "Remember this as a team rule: …" stored an explicit proposal.🤖 Generated with Claude Code
Summary by CodeRabbit