Skip to content

Antigravity sidecar state dir from config#292

Merged
joycel-github merged 3 commits into
mainfrom
antigravity-sidecar-state-dir-from-config
Jul 13, 2026
Merged

Antigravity sidecar state dir from config#292
joycel-github merged 3 commits into
mainfrom
antigravity-sidecar-state-dir-from-config

Conversation

@joycel-github

Copy link
Copy Markdown
Collaborator

Follow-up to #289.

Problem

Sidecar's trajectory storage directory was hardcoded in Python. No way
for users to relocate it.

Fix

Plumb state_dir through: ax.yamlAntigravityHarnessConfig → Go
client → sidecar --state-dir arg. Mirrors the existing
antigravity-interactions.state_dir pattern.
Python default (~/.ax/antigravity/conversations) kept as transitional
fallback for substrate mode (ActorTemplate doesn't inject the flag
yet); removable once it does.

Tasks

  • Test local
  • Test substrate

Sidecar's trajectory storage directory was hardcoded in Python. This
plumbs it through as a yaml config that users can override, matching
the pattern already used for the Interactions harness's state_dir.

Flow:
- ax.yaml: harnesses.antigravity.state_dir (optional; empty = default)
- config.go: AntigravityHarnessConfig.StateDir field (yaml surface only,
  no default logic on the Go side)
- cliutil.go: passes yaml value as-is (empty string when unset)
- antigravity Go client: appends --state-dir arg only when non-empty
- Python sidecar: argparse --state-dir with default
  ~/.ax/antigravity/conversations. Default is a transitional fallback
  for substrate mode (where ActorTemplate doesn't inject the flag yet).
  Removable once substrate can set the field via ActorTemplate.

Servicer's state_dir is required (no fallback) -- production path always
receives it from argparse; tests pass tmp_path.

Verified end-to-end with ax exec:
- No yaml state_dir  -> ~/.ax/antigravity/conversations/{conv_id}/
- yaml state_dir: X  -> X/{conv_id}/
- Different conversations remain isolated across both paths.

Closes part of the local-mode gap in issue #269 (harness_config
contract design); a follow-up will do the same for substrate via
ActorTemplate.
- P1: two Python tests referenced tmp_path without declaring the fixture;
  add tmp_path to their signatures.
- P2: state_dir CLI arg was not tilde-expanded, so ~/.ax/... became a
  literal ~ dir. Add .expanduser().
- P3: no Go-side test asserted --state-dir forwarding. Extract
  buildSidecarArgs() as a small pure helper and add table test for the
  empty/non-empty cases, plus a yaml parse test for
  AntigravityHarnessConfig.StateDir.
Follow-up to review feedback: buildSidecarArgs was overkill for a 3-line
conditional. Move it back into New() inline. Also drops the associated
TestBuildSidecarArgs (the arg-forwarding rule is now covered only by
end-to-end verification).
@joycel-github joycel-github marked this pull request as ready for review July 10, 2026 21:59
antigravityHarness, err = antigravity.New(ctx, address, true)
// Local mode: the harness owns the Python sidecar. Empty StateDir
// means the sidecar applies its own default
antigravityHarness, err = antigravity.New(ctx, address, cfg.Harnesses.Antigravity.StateDir, true)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Users shouldn't know about state_dir. It's an AGY SDK implementation detail that shouldn't be transparent to AX users.

Instead use the config.AXAssetsDir() to generate a state_dir.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

sure - will create a follow up to remove it from ax.yaml for both this and interaction harness

@rakyll

rakyll commented Jul 11, 2026

Copy link
Copy Markdown
Member

I'm approving this to unblock the other changes, but let's address my comment in a follow up.

@joycel-github joycel-github merged commit 4e49083 into main Jul 13, 2026
8 checks passed
joycel-github added a commit that referenced this pull request Jul 13, 2026
Parse HarnessStart.harness_config (JSON-in-bytes) and overlay it onto
the server's default LocalAgentConfig before each turn.

- Blocks a request from overriding AX-owned persistence fields
  (save_dir, conversation_id) via _AX_MANAGED_CONFIG_FIELDS; these are
  injected last so a request can't redirect trajectory storage.
- Reconstructs the config (not model_copy) so the SDK re-validates the
  overlaid values and surfaces its own error; invalid config fails the
  turn with INVALID_ARGUMENT instead of crashing.
- save_dir derives from the injected state_dir (#292) as
  state_dir / conversation_id.
joycel-github added a commit that referenced this pull request Jul 13, 2026
- Inline _parse_harness_config into _build_config_for so the parsed
  dict stays a local intermediate; the method's only boundary type is
  the validated LocalAgentConfig (no dict[str, object] across a helper
  boundary). Addresses the "introduce a type for the config" comment.
- Make per-conv save_dir test assertions portable: compare against
  str(tmp_path / conversation_id) instead of hardcoding "/conv-1", and
  drop the now-vestigial Path.home monkeypatch (save_dir derives from
  the injected state_dir since #292). Addresses the Windows path
  separator comment.

_AX_MANAGED_CONFIG_FIELDS keeps guarding both conversation_id and
save_dir on purpose: harness_config is a separate client-controlled
surface from the request's conversation_id, so blocking it prevents a
request from redirecting another conversation's trajectory storage.
joycel-github added a commit that referenced this pull request Jul 13, 2026
* antigravity: parse and validate request harness_config

Parse HarnessStart.harness_config (JSON-in-bytes) and overlay it onto
the server's default LocalAgentConfig before each turn.

- Blocks a request from overriding AX-owned persistence fields
  (save_dir, conversation_id) via _AX_MANAGED_CONFIG_FIELDS; these are
  injected last so a request can't redirect trajectory storage.
- Reconstructs the config (not model_copy) so the SDK re-validates the
  overlaid values and surfaces its own error; invalid config fails the
  turn with INVALID_ARGUMENT instead of crashing.
- save_dir derives from the injected state_dir (#292) as
  state_dir / conversation_id.

* antigravity: address harness_config review comments

- Inline _parse_harness_config into _build_config_for so the parsed
  dict stays a local intermediate; the method's only boundary type is
  the validated LocalAgentConfig (no dict[str, object] across a helper
  boundary). Addresses the "introduce a type for the config" comment.
- Make per-conv save_dir test assertions portable: compare against
  str(tmp_path / conversation_id) instead of hardcoding "/conv-1", and
  drop the now-vestigial Path.home monkeypatch (save_dir derives from
  the injected state_dir since #292). Addresses the Windows path
  separator comment.

_AX_MANAGED_CONFIG_FIELDS keeps guarding both conversation_id and
save_dir on purpose: harness_config is a separate client-controlled
surface from the request's conversation_id, so blocking it prevents a
request from redirecting another conversation's trajectory storage.
joycel-github added a commit that referenced this pull request Jul 14, 2026
state_dir is an AGY SDK implementation detail (where trajectory /
resume-cursor storage lives), not something AX users should configure.
Remove the yaml surface for both built-in harnesses and derive the path
internally from config.AXAssetsDir().

- config: drop StateDir from AntigravityHarnessConfig and
  AntigravityInteractionsHarnessConfig.
- antigravity: add DefaultStateDir() -> ~/.ax/antigravity/conversations,
  mirroring antigravityinteractions.DefaultStateDir(). cliutil now passes
  this into antigravity.New instead of the yaml value.
- cliutil: interactions harness always uses DefaultStateDir(); drop the
  yaml read + fallback.

The internal APIs (antigravity.New's stateDir arg, the required
AntigravityInteractionsConfig.StateDir field, and the Python sidecar's
--state-dir default) are unchanged -- only the user-facing ax.yaml knob
is removed.

Addresses rakyll's follow-up on #292.
joycel-github added a commit that referenced this pull request Jul 14, 2026
state_dir is an AGY SDK implementation detail (where trajectory /
resume-cursor storage lives), not something AX users should configure.
Remove the yaml surface for both built-in harnesses and derive the path
internally from config.AXAssetsDir().

- config: drop StateDir from AntigravityHarnessConfig and
  AntigravityInteractionsHarnessConfig.
- antigravity: add DefaultStateDir() -> ~/.ax/antigravity/conversations,
  mirroring antigravityinteractions.DefaultStateDir(). cliutil now passes
  this into antigravity.New instead of the yaml value.
- cliutil: interactions harness always uses DefaultStateDir(); drop the
  yaml read + fallback.

The internal APIs (antigravity.New's stateDir arg, the required
AntigravityInteractionsConfig.StateDir field, and the Python sidecar's
--state-dir default) are unchanged -- only the user-facing ax.yaml knob
is removed.

Addresses rakyll's follow-up on #292.
joycel-github added a commit that referenced this pull request Jul 14, 2026
state_dir is an AGY SDK implementation detail (where trajectory /
resume-cursor storage lives), not something AX users should configure.
Remove the yaml surface for both built-in harnesses and derive the path
internally from config.AXAssetsDir().

- config: drop StateDir from AntigravityHarnessConfig and
  AntigravityInteractionsHarnessConfig.
- antigravity: add DefaultStateDir() -> ~/.ax/antigravity/conversations,
  mirroring antigravityinteractions.DefaultStateDir(). cliutil now passes
  this into antigravity.New instead of the yaml value.
- cliutil: interactions harness always uses DefaultStateDir(); drop the
  yaml read + fallback.

The internal APIs (antigravity.New's stateDir arg, the required
AntigravityInteractionsConfig.StateDir field, and the Python sidecar's
--state-dir default) are unchanged -- only the user-facing ax.yaml knob
is removed.

Addresses rakyll's follow-up on #292.
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