Skip to content

Track session CWD in WorkingContext and emit as [working-context] + [project-instructions] #595

@Aaronontheweb

Description

@Aaronontheweb

Summary

When a Netclaw session is working on a specific project directory, it needs to:

  1. Carry a CurrentWorkingDirectory field in SessionState.WorkingContext.
  2. Re-read project-scoped identity files (.netclaw/AGENTS.md, CLAUDE.md, AGENTS.md) on every turn, walking up from the CWD so project instructions survive compaction and stay current as CWD changes mid-session.

Not in scope here: making tool calls respect the session CWD. That's tracked separately — see the paired issue "Resolve shell + file tool paths against session CWD".

Why this is separate from the working-context-grounding OpenSpec change

Scope protection. The durable task-state change (WorkingContext with RecentFiles / OpenGoals / ProgressMarkers) should not gate on the CWD tracking, and CWD tracking has its own design questions:

  • Where does CWD come from when the session is created?
  • Who sets it — user command? session-join metadata? tool-call side effect?
  • Precedence when multiple project identity files exist at different levels of the directory tree
  • Backward compat with sessions that have no CWD set

Reference implementations

  • OpenCode packages/opencode/src/session/instruction.ts:127-135, 164-178findUp walker for AGENTS.md / CLAUDE.md / CONTEXT.md. First match wins at each level. Framing: Instructions from: {path}\n{content}.
  • Claude Code memory docs — project-root CLAUDE.md is re-injected from disk after compaction. Nested CLAUDE.md files only re-inject when a file in that subdirectory is read. OpenCode's walker is the cleaner simplification.

Acceptance criteria

  • WorkingContext.CurrentWorkingDirectory field added, settable via a session-level API and persisted in SessionSnapshot + SessionCompacted event.
  • [working-context] block emitted by InjectDynamicContextLayers includes CWD.
  • New [project-instructions] block emitted by InjectDynamicContextLayers, populated by walking up from CWD to find project identity files, framed as Instructions from: {path}\n{content}.
  • Project identity files re-read from disk on every turn (EveryTurn semantics).
  • Compaction does not affect the block — it is always current on the next FireLlmCall. Integration test forces a compaction and asserts the block is still present with correct content.
  • Regression test: session at CWD=X gets CLAUDE.md from X injected; switching CWD to Y mid-session picks up Y's CLAUDE.md on the next turn.
  • Backward compat: sessions without a CWD set continue to work; the [project-instructions] block is simply empty.

Background

Filed as a follow-up to the compaction architecture rework. The failing Slack session on 2026-04-11 lost grounding about the project it was working on after compaction. The structured summary in the compaction-rework change gets us 80% of the way there; project identity file re-reading closes the remaining gap for sessions that shift context across projects.

Metadata

Metadata

Assignees

No one assigned

    Labels

    context-pipelineLLM context assembly: prompt layers, dynamic injection, memory recall, temporal groundingenhancementNew feature or requestsessionsLLM session actor, turn lifecycle, pipelines

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions