Skip to content

feat(session): persist /goal directives across compaction and resume#695

Merged
mksglu merged 7 commits into
mksglu:nextfrom
ken-jo:feat/persist-session-goal
May 31, 2026
Merged

feat(session): persist /goal directives across compaction and resume#695
mksglu merged 7 commits into
mksglu:nextfrom
ken-jo:feat/persist-session-goal

Conversation

@ken-jo

@ken-jo ken-jo commented May 24, 2026

Copy link
Copy Markdown
Contributor

What

A /goal <text> directive, or explicit goal: / objective: marker, was not preserved across compaction or resume. extractIntent kept only the coarse mode (investigate / implement) and discarded the actual objective, so restored context could say only <intent mode="implement"/> without saying what to implement.

This PR makes the stated session goal first-class session memory so a resumed model can continue toward the user's objective without asking the user to restate it.

Change

  • Goal extraction (src/session/extract.ts): add extractGoal() and wire it into extractUserEvents(). It captures /goal <text>, goal: <text>, and objective: <text> as a goal event.
  • Durability under the session cap: store goal at priority: 4, matching the existing SessionDB eviction contract where higher numeric priority survives before lower-priority events.
  • Snapshot restore (src/session/snapshot.ts): render the latest goal in a <session_goal> section immediately after <how_to_search>, before files, tasks, and <intent>.
  • Analytics copy (src/session/analytics.ts): label goal as "Session goal" and relabel intent as "Session intent".
  • Codex compact coverage (tests/hooks/codex-goal-compact.test.ts): run the actual Codex hook sequence UserPromptSubmit -> PreCompact -> SessionStart(source=compact) and assert the slash /goal objective is restored in hookSpecificOutput.additionalContext.
  • Bundles: regenerate affected runtime bundles (server.bundle.mjs, cli.bundle.mjs, hooks/session-extract.bundle.mjs, hooks/session-snapshot.bundle.mjs).

No behavior change to the coarse intent mode.

Codex Review Follow-Up

Codex review found that the original priority: 1 goal event would be evicted first at the per-session 1000-event cap because SessionDB removes the lowest numeric priority first. That would break the core continuity claim for long Codex sessions before compaction.

Follow-up changes:

  • changed goal priority to 4;
  • added an event-cap regression proving a goal survives after 1000 lower-priority events;
  • added Codex hook lifecycle coverage proving slash /goal survives through compact restore when the prompt text reaches UserPromptSubmit.

Verification

Local

  • npx vitest run tests/hooks/codex-goal-compact.test.ts tests/session/session-goal.test.ts tests/session/session-db.test.ts tests/session/session-snapshot.test.ts --reporter=dot — 4 files / 132 tests passed.
  • npx tsc --noEmit — passed.
  • npm run build — passed (tsc, bundle, assert-bundle, assert-asymmetric-drift).
  • git diff --check — passed.

Independent Codex Subagent Verification

A separate Codex subagent verified the goal behavior without editing files:

  • session goal/unit + DB/snapshot suite: 4 files / 295 tests passed;
  • bundle probes confirmed /goal, goal:, and Objective: create goal events with priority: 4;
  • bundle DB probe confirmed a goal survives the 1000-event cap;
  • bundle snapshot probe confirmed latest goal wins and <session_goal> is ordered before files/intent.

A second Codex subagent independently exercised the Codex hook compact path:

  • ran tests/hooks/codex-goal-compact.test.ts successfully;
  • manually simulated hooks/codex/userpromptsubmit.mjs -> hooks/codex/precompact.mjs -> hooks/codex/sessionstart.mjs(source=compact) with a fake CODEX_HOME;
  • confirmed a stored goal row with { category: "goal", priority: 4 };
  • confirmed restored additionalContext contains <session_goal>, contains the objective, and orders goal before <intent>.

Remote CI

  • test (ubuntu-latest) — success.
  • test (macos-latest) — success.
  • test (windows-latest) — success.
  • openclaw-e2e (ubuntu-latest) — success.
  • openclaw-e2e (macos-latest) — success.

Constraints / Assumptions

  • This preserves /goal ... only when the host passes the submitted slash-command text to UserPromptSubmit. If a host consumes the slash command before hooks see it, context-mode cannot infer that exact text from this path.
  • goal: and objective: remain explicit marker forms for hosts or workflows where slash-command text is not available to hooks.
  • The PR stores the latest stated goal. It does not implement goal completion semantics, multiple named goals, or goal history UX.
  • priority: 4 intentionally follows today's SessionDB eviction contract. A future cleanup can introduce named priority constants to remove numeric ambiguity.
  • The PR targets next and remains a draft while maintainers review the new behavior and test shape.

Future Work

  • Consider extending context-mode with a more explicit goal feature surface, instead of treating goals only as parsed prompt text. For example, a future context-mode command/API could set, inspect, clear, or update the current session goal directly while reusing the same <session_goal> restore path.
  • Consider replacing numeric priorities with named constants (CRITICAL, HIGH, etc.) so future continuity-critical events cannot accidentally use the wrong eviction priority.

Closes #694

ken-jo and others added 3 commits May 25, 2026 02:35
…d resume

A /goal <text> directive (or a goal: / objective: marker) was never preserved:
extractIntent stored only a coarse mode (investigate/implement) and discarded
the goal text, so the objective was lost across compaction and --resume. The
resume snapshot restored only <intent mode=...>.

Capture the goal text as a first-class 'goal' event (priority 1) and restore
the most recent one verbatim in a <session_goal> section placed first in the
resume snapshot, so the resuming model reads its active objective before
anything else and keeps working toward it.

- extract: extractGoal() detects /goal <text> / goal: / objective:
- analytics: 'goal' label 'Session goal'; 'intent' relabeled 'Session intent'
  (it stores a mode, not the goal text)
- snapshot: buildGoalSection() -> <session_goal>, assembled right after how_to_search

Verified: new tests/session/session-goal.test.ts (7 cases) + full suite
(3700 pass) + build (tsc/bundle/asserts). End-to-end: a fresh agent given only
the restored snapshot correctly identified the goal and derived a next step
toward it with no prompting.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
src↔bundle drift from the goal-persistence change. Normally CI regenerates
bundles on merge; included here so the full diff is reviewable. This PR
carries the drift label / is opened as draft and must NOT be merged as-is.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ken-jo pushed a commit to ken-jo/context-mode that referenced this pull request May 24, 2026
Goal persistence now uses the DB's actual priority ordering, where higher numeric values survive eviction. The new regression stores a goal, fills the session event cap with lower-priority events, and asserts the objective remains available for resume snapshots.

Constraint: PR mksglu#695 fixes goal continuity across compaction and resume, so the goal event must survive long sessions before snapshot restore.

Rejected: Keep priority 1 as critical | existing SessionDB eviction deletes lower numeric priorities first.

Confidence: high

Scope-risk: narrow

Directive: Keep goal priority aligned with SessionDB eviction semantics unless the whole priority scale is migrated.

Tested: npx vitest run tests/session/session-goal.test.ts --reporter=dot; npx vitest run tests/session/session-db.test.ts --reporter=dot; npx vitest run tests/session/session-snapshot.test.ts --reporter=dot; npx tsc --noEmit; npm run build; git diff --check
ken-jo pushed a commit to ken-jo/context-mode that referenced this pull request May 24, 2026
Codex goal persistence now has an integration test that runs the real Codex UserPromptSubmit, PreCompact, and SessionStart compact hooks. The test verifies that a slash /goal directive is restored in additionalContext as <session_goal> after compaction.

Constraint: PR mksglu#695 specifically targets Codex slash goal continuity across compact/resume, not only pure extractor/snapshot units.

Rejected: Rely on extractor-only coverage | it does not prove the Codex hook lifecycle preserves the slash command.

Confidence: high

Scope-risk: narrow

Directive: Keep this test tied to Codex hooks so future hook-path drift cannot silently drop /goal.

Tested: npx vitest run tests/hooks/codex-goal-compact.test.ts tests/session/session-goal.test.ts tests/session/session-db.test.ts tests/session/session-snapshot.test.ts --reporter=dot; npx tsc --noEmit; git diff --check
@ken-jo ken-jo changed the title [drift] feat(session): persist /goal directives so they survive compaction and resume feat(session): persist /goal directives across compaction and resume May 24, 2026
ken-jo added 2 commits May 25, 2026 03:23
Goal persistence now uses the DB's actual priority ordering, where higher numeric values survive eviction. The new regression stores a goal, fills the session event cap with lower-priority events, and asserts the objective remains available for resume snapshots.

Constraint: PR mksglu#695 fixes goal continuity across compaction and resume, so the goal event must survive long sessions before snapshot restore.

Rejected: Keep priority 1 as critical | existing SessionDB eviction deletes lower numeric priorities first.

Confidence: high

Scope-risk: narrow

Directive: Keep goal priority aligned with SessionDB eviction semantics unless the whole priority scale is migrated.

Tested: npx vitest run tests/session/session-goal.test.ts --reporter=dot; npx vitest run tests/session/session-db.test.ts --reporter=dot; npx vitest run tests/session/session-snapshot.test.ts --reporter=dot; npx tsc --noEmit; npm run build; git diff --check
Codex goal persistence now has an integration test that runs the real Codex UserPromptSubmit, PreCompact, and SessionStart compact hooks. The test verifies that a slash /goal directive is restored in additionalContext as <session_goal> after compaction.

Constraint: PR mksglu#695 specifically targets Codex slash goal continuity across compact/resume, not only pure extractor/snapshot units.

Rejected: Rely on extractor-only coverage | it does not prove the Codex hook lifecycle preserves the slash command.

Confidence: high

Scope-risk: narrow

Directive: Keep this test tied to Codex hooks so future hook-path drift cannot silently drop /goal.

Tested: npx vitest run tests/hooks/codex-goal-compact.test.ts tests/session/session-goal.test.ts tests/session/session-db.test.ts tests/session/session-snapshot.test.ts --reporter=dot; npx tsc --noEmit; git diff --check
@ken-jo ken-jo force-pushed the feat/persist-session-goal branch from a579219 to 8d5081f Compare May 24, 2026 18:24
@mksglu

mksglu commented May 24, 2026

Copy link
Copy Markdown
Owner

Reach out to me via DM I would like to invite you our private engineering group on WhatsApp. Send me e-mail. @ken-jo

@ken-jo

ken-jo commented May 24, 2026

Copy link
Copy Markdown
Contributor Author

Hi @mksglu, Claude Code and Codex verification are both complete on this PR.

@mksglu mksglu marked this pull request as ready for review May 24, 2026 21:48
Comment thread src/session/extract.ts
* Without this, a `/goal` directive is lost across compaction/resume.
*/
const GOAL_DIRECTIVE_PATTERN =
/^(?:\/goal\s+|(?:goal|objective)\s*:\s*)(.+)$/is;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

What about Chinese/Japan lang?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good question — let me clarify the design intent and what I measured.

Marker keywords (goal / objective): these mirror only the goal keyword convention that Codex and Claude Code already support. I deliberately did not add per-language translations (目标 / 目標 / 목표 / …) as marker keywords — that felt like overfitting (which languages? simplified vs. traditional? where do we stop?).

Goal body (the text after the marker): this already supports any language — the capture group (.+) with the s flag is Unicode-agnostic. I verified it:

Input Result
/goal 中文目标内容 (slash + Chinese text) ✅ match, captured 中文目标内容
goal: 日本語の目標 (English keyword + Japanese text) ✅ match
objective: 한국어 목표 (English keyword + Korean text) ✅ match
目标: 提高性能 (Chinese marker keyword) ❌ no match
目標:… (Japanese keyword + fullwidth colon) ❌ no match

So CJK content is fully supported today; only the marker keyword is English-only. (Note: CJK input also tends to use the fullwidth colon U+FF1A, which the current ASCII-: regex wouldn't match either — so marker i18n would mean both keywords and colon variants.)

Given that the body is already language-agnostic, do you still want the marker keywords themselves to be multilingual?

My suggestion: support only the language-neutral /goal slash command for now, and split per-language marker matching into a separate follow-up — implementing it later once the feature has stabilized and seen enough real-world usage, rather than guessing the language set up front. Happy to go either way — just let me know your preference.

@ken-jo ken-jo requested a review from mksglu May 25, 2026 14:36
ken-jo and others added 2 commits May 27, 2026 14:54
Resolve bundle build conflicts by regenerating cli.bundle.mjs +
server.bundle.mjs from merged source (esbuild) rather than hand-merging
minified output. src/session/analytics.ts auto-merged cleanly: goal
category labels + next's perf refactors coexist. Build green
(assert-bundle OK x6, assert-asymmetric-drift OK); full suite 3716 passed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@mksglu mksglu merged commit 9e2e623 into mksglu:next May 31, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants