Skip to content

prd.json is reachable from the worker (invariant 2 leak) #10

@samkeen

Description

@samkeen

Related: real enforcement of invariant 2 (worker physically can’t reach harness state, even with bash) is opt-in process isolation, planned in #13. This issue is the default-mode drift surface; complementary, both worth doing.

What

CLAUDE.md invariant 2 says:

The agent doesn't see harness mechanics. No prd.json structure, no events.jsonl, no summary.json, no token counts, no judge, no checkpoints.

prd.json lives in the workspace (it has to — that's where loop._load_prd reads it from), so the worker can read_file it freely. In the 5/1 demo session the worker did exactly that on T-002 iter 2:

$ jq -c 'select(.payload.task_id=="T-002" and .payload.iter==2 and .type=="tool_call")' events.jsonl
{...,"tool":"read_file","args":{"path":"tests/test_t002_package.py"}}
{...,"tool":"read_file","args":{"path":"prd.json"}}

No scope-creep happened in this run, but reading prd.json exposes every future task to the worker — and the model could legitimately decide to pre-implement T-003 while "doing" T-002. The harness has no defense.

Why memory.py doesn't already fix this

memory.build_user_prompt() correctly never injects prd into the user prompt — only the current task is rendered. The leak is purely via tool access to the file on disk.

Proposed directions (pick one)

  1. Block reads to prd.json in pre_tool. Simplest. Cost: an explicit error message in the agent's view, which is itself a small leak of "there's a prd.json you can't see."
  2. Move prd.json out of the worktree. Store it under sessions/<id>/prd.json (or <source>/.tilth/prd.json), and have _load_prd read from there. Worker has no way to see it. Cost: breaks the demo's "prd is a seed file in your repo" affordance; the demo workspace's PRD becomes a setup step rather than just git clone.
  3. Make prd a .tilth/ directory inside the workspace and have pre_tool block .tilth/. Middle ground.

Option 2 feels closest to the invariant's intent — it's the same separation that already keeps events.jsonl / summary.json / checkpoint.json outside the worktree.

Related

  • tilth/loop.py:_load_prd, tilth/loop.py:_save_prd
  • tilth/tools/files.py:read
  • tilth/hooks/pre_tool.py
  • CLAUDE.md (invariant 2)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions