Repository navigation
Goal-bound agent sessions for Claude Code and Codex (behind ALIGN_GOAL_SESSIONS=1) - #8
Conversation
Review: PR #8 @
|
|
@allsmog: re-check at the new head
I didn't re-check the four Medium findings (5, 6, 8, 9); their files weren't touched. Base: this PR still targets |
78ad340 to
4266780
Compare
People sign in as themselves (Google or GitHub; a test sign-in for local use), belong to several workspaces, and work in pods with lead/member roles. Goals are versioned and private, pod or workspace visible. The local agent (packages/align-agent) meters Claude Code and Codex from their own transcripts and rollouts. A user shares a session, or a whole folder (align share --dir), and each shared session is bound to a goal by binding epochs that change only at a prompt boundary. Hooks give the agent the goal once, a short diff when it changes (at the next prompt or tool call), and nothing on unchanged prompts. A stop from the web blocks the next tool call, fail closed. There is no daemon and no telemetry setup: hooks start a short one-pass sync that exits, and the Stop hook carries each turn's usage up. Aligning a session to someone else's goal needs that owner's approval; an approved "work toward" moves future usage and the session's directives to the target goal, and revoking suspends the binding. Spend is reported per goal, labelled as list-price estimates, with no per-person view. v1 and Northstar are unchanged: every v2 route is 404 unless ALIGN_V2=1, and the snapshot gate runs with v2 off and on. Wire contract: docs/v2/WIRE.md.
deviceSecret wrote a fresh secret over the old one on any read error or a file of the wrong length, which would silently change every session, repo and request key and orphan sessions the device already shared. It now creates the secret only when the file does not exist (exclusive create, so two first-run processes cannot write different secrets) and otherwise refuses with a clear message. Found by the pre-push review (f_2c5d0c1c5473).
Adds list-price rows for claude-sonnet-5-5 and claude-haiku-5-5. Haiku 5.5 has two rate cards: prompts above 100K tokens (input plus cache read and write) use the higher card. Price table version align-prices-2026-10-22.2.
…ilure sync - align install now writes a skills-directory plugin for Claude Code (CLAUDE_CONFIG_DIR/skills/align) instead of merging hooks into settings.json. Claude Code loads it in every new session; `claude plugin disable align@skills-dir` turns it off; uninstall deletes the folder. Handlers an earlier Align merged into settings.json are removed (with a backup) so they do not run twice. Install refuses to replace a skills/align folder that is not Align's. Codex keeps the hooks.json merge. - PreToolUse fires inside subagents too, and its context reaches only the subagent (checked with Claude Code 2.1.309). The mid-turn delta now skips calls with agent_id so the main agent still receives the goal change; the stop latch still applies in subagents. - Parallel tool calls run their PreToolUse hooks at once. A lock directory lets exactly one of them deliver the delta. - Claude Code runs StopFailure instead of Stop when a turn ends on an API error. It is now installed and starts the same end-of-turn sync, so that turn's usage uploads without waiting for the next prompt. Agent suite 40/40; test-all 17/17 (Northstar identical with v2 off and on). Two live tmux runs with hooks loaded only from the plugin: goal change delivered 2/2, stop held 2/2, +0.6% prompt tokens vs no Align.
Server - Approved alignments with a source goal make it a child (contributes_to). Children record the parent version they last reviewed and show as stale when the parent moves on; POST /goals/:goal/review confirms the fit. - depends_on is a versioned goal field (at most 5). - Activity feed: POST/GET /workspaces/:ws/activity. Edits, commits, task labels and claims, kept 24 h (claims 1 h), visible to readers of the session's goal. Paths and text only with work-signal consent; otherwise hashed repo and path ids. - Goal page: tree card with stale pill and "Still fits", depends-on picker. Agent - Hooks log edits (including shell writes), reads, commit subjects and ::align task / ::align claim. The sync pass also detects git working-tree changes and new commits, uploads activity and fetches the pod feed. - A card of at most 300 chars at prompt or session start, at most once per 10 minutes unless work overlaps. A one-time hold for same-laptop edits to a file another session just changed; a note for cross-person overlap or claims. align pod and ::align pod show the detail. - Parent and dependency changes arrive as one note naming the changed measure. Tests: agent 49/49, test-v2 144/144, test-all 17/17.
The pod card and cross-person notes put text from other machines into agent context: names, task labels, commit subjects, claim reasons and paths. Anyone in the pod could use those fields to steer another person's agent. - From other people the card and notes now carry only a first name (letters only) and paths made of path characters; overlap and note files use this session's own path for the file. - Their task labels, commit subjects and claim reasons appear only in `align pod`, which marks quoted text as information, not instructions. - This machine's own sessions are unchanged. - The envelope invariant comment and README say what reaches the model. Tests: agent 50/50 (new injection test), test-all 17/17.
A summary from another machine with a blank account name was treated as one of this machine's sessions, so its task label and commit subject reached model context. Remote summaries now carry remote: true and the card, the pod report and the quoted-text marker check that flag. Tests: agent 50/50 (blank-name case added), test-all 17/17.
…>; rename the ALIGN_V2 flags requireSameSite now matches lib/http.ts (LIM-2437): when hosted, Origin is required and must be exactly https://<Host>, so an http:// page, another port or a missing Origin is refused. The local rule is unchanged. The site check runs before the session lookup, so a cross-site write is refused without reading a session. test-v2 covers none, http, another port, null and foreign (403 cross_site) and the exact Origin (401 without a session). ALIGN_V2 is now ALIGN_GOAL_SESSIONS and ALIGN_V2_TEST_IDP is ALIGN_GOAL_SESSIONS_TEST_IDP. Comments and docs no longer say Align v2.
4266780 to
6791fdd
Compare
|
Merged without an approving review; per the founder (Fri ~10 AM PT) it stays merged. The review findings and needed fixes are in LIM-2441 (https://linear.app/liminal-space-io/issue/LIM-2441), for after the Sprint. Six Highs are confirmed in the code on main |
What this is
The redesign after the CPSec test on 10-22. It replaces passcodes and fixed team slots with people who sign in as themselves, and connects their own Claude Code and Codex sessions to team goals. Everything is behind
ALIGN_GOAL_SESSIONS=1; with it unset every/app*and/api/v2/*path is 404 and v1 and Northstar are unchanged.lib/v2,app/api/v2,app/app): Better Auth 1.7.7 sign-in (Google, GitHub; email and password only withALIGN_GOAL_SESSIONS_TEST_IDP=1on a local server), several workspaces per person, pods with lead/member roles, versioned goals (private, pod or workspace visible), device pairing with key-bound rotating tokens, shared sessions with binding epochs, directives by cursor with three receipt states, alignment requests with computed approvers, spend per goal. Our own authorization module; a workspace owner cannot read someone's private goal.packages/align-agent): thealignCLI and hooks for Claude Code and Codex (SessionStart, UserPromptSubmit, PreToolUse, Stop). No daemon, no telemetry settings. Usage comes from the harnesses' own transcripts and rollouts, on this machine. Hooks answer from local files and start a short one-passalign syncthat exits; the Stop hook carries each turn's usage up.align share --dir <folder> --goal <goal>shares every new session started in that folder (the folder path stays local; this also covers/clear). Sessions and folders connect to goals by goal id. Per-session share, bind and unshare stay available, in the CLI and in-session as::align ...lines that never reach the model.docs/v2/WIRE.md. Harness facts with sources:packages/align-agent/HARNESS_FACTS.md.Verified
On
4266780, rebased onto main4dbf876(all of #10, #11, #13, #14, #15 and #16):scripts/test-all.sh 3512: all 18 steps pass, including test-v2 145/145, test-tenancy 175/175, test-ui-loop 41/41, snapshot and snapshot-v2on identical to 850971e, ui-northstar identical to the v6 baseline (125 files). Run on the tree before the CI: rebased-on-main check for main's branch protection #16 rebase, which only added.github/workflows/rebased-on-main.yml.packages/align-agent:npm test50/50 (load average about 2).What the last commit changes:
lib/v2/http.tsrequireSameSite) now follow main's LIM-2437 rule: Origin is required and must be exactlyhttps://<Host>. An http:// page, another port,nullor no Origin gets 403cross_site. The local rule is unchanged. The site check now runs before the session lookup. test-v2 has a new hosted check for all five refusals and the exact Origin.ALIGN_V2is nowALIGN_GOAL_SESSIONS, andALIGN_V2_TEST_IDPis nowALIGN_GOAL_SESSIONS_TEST_IDP.Earlier, on 10-22 (before the rebase):
scripts/test-all.sh 3512on 8405c8f (rebased on LIM-2424: custom record, launch decision, Switch team, Halden fixture (stacked on #6) #7 at 260a252): all 17 steps pass, including test-v2 133/133, test-tenancy 166/166, snapshot and ui-northstar identical to 850971e with v2 off and on.packages/align-agent:npm test35/35 twice on 8405c8f. On 07e5cf6 (device secret fix, plus a new test) 35/36 while the machine was at load average 14; the one failure is the hook-latency test, which passed alone at normal load and does not touch the changed file. To rerun at normal load.--settingsandCODEX_HOME(the real~/.claudeand~/.codexchecksums were unchanged):lstool call;touchdenied, file not created), receiptsreceivedandenforcedwithin 1 s; resume worked;Known gaps
LIM-id.claude-sonnet-5-5andclaude-haiku-5-5are not in the price table, so they show as unpriced.::alignline is blocked, Claude Code puts the text back in the input box.