Skip to content

With --manifest-path into another project, rollback, remove, repair, vex, scan and get read the vendored ledger from --cwd #745

Description

[agent] Filed by the scheduled architecture audit routine (CLI and core). Register: discussion #560 register.

Kind: bug. Source: new finding (register C43).

Problem

GlobalArgs::project_root states the rule: "Every command that reads more than one store must derive them from THIS root, so --manifest-path can never interleave two projects' state". CLI_CONTRACT's manifest_not_found row also says "All sources always come from the SAME project".

Only some commands follow it. Every command reads the manifest from resolved_manifest_path(), but the vendored ledger (.socket/vendor/state.json) is loaded from two different roots:

Ledger root Sites
project_root() (follows --manifest-path) list and apply through ProjectContext::new; vendor --check
--cwd rollback; remove; repair; apply --check; vex (LoadedLedgers::load(&common.cwd, manifest_path)); scan and get through ProjectContext::rooted(.., cwd) / get, and the scan/get vendored paths (load_state(&common.cwd) in scan/vendor_flow.rs, scan/hosted.rs, get.rs)

rollback is the sharpest case, as rollback.rs L1218-L1238 shows:

  • It takes apply.lock in the manifest's .socket/ through socket_dir_of(&manifest_path, &cwd).
  • Under that lock it loads, reverts and saves the cwd project's vendored ledger.
  • A concurrent default-manifest run in the cwd project locks a different file, so two processes can write the same ledger without mutual exclusion.

Proof by execution (debug build of 045d7ec, run twice; same results both times)

Setup: a/ is --cwd, and b/.socket/manifest.json is {"patches":{}}. Every command runs with --cwd a --manifest-path ../b/.socket/manifest.json (rollback and repair also get --offline).

Command corrupt a/.socket/vendor/state.json corrupt b/.socket/vendor/state.json
list --json success warning "unreadable vendor ledger … b/.socket/vendor/state.json"
vex --json --output f error vendor_ledger_corrupt error no_patches (ledger never read)
rollback --json partial_failure, warning vendor_state_unreadable naming a/… success
repair --json partialFailure, event vendor_state_unreadable naming a/… success

So on the same input, list reads project b's ledger, while vex, rollback and repair read project a's.

Symptoms / impact

  • No existing issue covers this.
  • Impact: with a foreign --manifest-path, rollback and remove revert one project's agent-mode patches and the other project's vendored artifacts and ledger. vex attests a mix of both projects. rollback's lock doesn't cover the ledger it writes.
  • --manifest-path and SOCKET_MANIFEST_PATH are documented global options, so CI wrappers that pin the manifest location hit this.

Proposed change

  • One rule: every vendored-ledger read and write derives its root from GlobalArgs::project_root().
    • Route rollback, remove, repair, apply --check and vex through ProjectContext::new (or load_state(&common.project_root())).
    • Delete the ProjectContext::rooted(common, cwd) calls in scan and get, and the rooted constructor if it has no remaining caller.
  • scan/get vendored writes (scan/vendor_flow.rs, scan/hosted.rs, get.rs) save the ledger to the same root. Lockfile discovery stays where each command's contract puts it today. If a maintainer prefers to refuse a --manifest-path outside --cwd's project for multi-store commands, that is a contract change and should become a decision. The default fix just makes the code follow the rule it already documents.

Size and scope

  • Files: commands/{rollback,remove,repair,apply,vex,get,context}.rs, commands/scan/{mod,vendor_flow,hosted}.rs.
  • Estimate: about 40–80 production lines changed, plus tests.
  • Out of scope: consolidating ProjectContext with a run-level context (C10).

Acceptance criteria

  • Every load_state(/LoadedLedgers::load( call in crates/socket-patch-cli/src takes project_root() (or a value derived from it); a grep-style architecture test pins this.
  • Regression test: with a corrupt ledger in --cwd and a clean --manifest-path ../b/.socket/manifest.json, list, vex, rollback, remove and repair all succeed; with the corruption moved to b, all of them report it.
  • Regression test: rollback --manifest-path ../b/.socket/manifest.json never modifies a/.socket/vendor/state.json.
  • The existing args.rs project_root_* tests and the list same-project tests stay green.
  • CLI_CONTRACT: the "SAME project" sentence moves from the list row to the --manifest-path flag row, so it covers every command.

Dependencies

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)bugSomething isn't workingpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions