Skip to content

fix: unify run_python persistence semantics — writes always persist - #247

Merged
ShotaroKataoka merged 5 commits into
mainfrom
feature/run-python-unified-semantics
Aug 1, 2026
Merged

fix: unify run_python persistence semantics — writes always persist#247
ShotaroKataoka merged 5 commits into
mainfrom
feature/run-python-unified-semantics

Conversation

@ShotaroKataoka

Copy link
Copy Markdown
Contributor

Summary

run_python had divergent save semantics between Local (build trigger; writes always hit disk) and Remote (persistence gate; writes silently discarded without it). After the v0.5.1 persona unification, local-oriented wording became the cloud instruction and composer/vibe agents intermittently "forgot" save=True, losing work (deck ba8d79da).

This PR removes the failure class instead of prompting around it:

  • Writes always persist — Local: direct disk writes (as before); Remote: diff-based write-back of the sandbox workspace (only changed files upload)
  • save is deprecated — accepted and ignored with a deprecation note (no caller breakage)
  • output.pptx / pptxS3Key auto-refresh on build-relevant changes (deck.json, presentation.json, specs/outline.md, slides/, includes/) — cheap build, no render
  • measure_slides is the only trigger for expensive verification (SVG render + measure + preview + compose)
  • generate_pptx = explicit finalize/handoff (WebP previews + KB sync + full-deck warnings report)
  • Superseded PPTX artifacts are deleted after each refresh (update_deck returns UPDATED_OLD) — auto-refresh doesn't accumulate orphans
  • Guards: staged files=[...] names colliding with the deck workspace are rejected (deck sessions only, exact/prefix split); artifact refresh failures surface as result.pptx_error with compensation delete; run_style_python warns when style.html is written without style_name; lint pass no longer rewrites unchanged slide files (false rebuild fix)

Contract

deck changed measure_slides cheap build artifact refresh expensive verify
no
yes
no
yes

Implemented as a pure decision table (_post_processing_plan) + real-wiring branch tests.

Tested

  • make all: 646 passed / lint clean; 30 new tests in tests/test_run_python_semantics.py (contract matrix via monkeypatched rig around the actual run_python, files collision, artifact failure surfacing, superseded-artifact cleanup, lint rewrite guard, Local/Remote save-flag compat)
  • Local compose one-pass (real build + preview + measure; caught and fixed the false-rebuild regression)
  • Cloud E2E (deck 5fa63146): 10 slides persisted continuously without save, output.pptx auto-followed changes, pptxS3Key points at the newest artifact

Docs

Personas (vibe/spec/single/composer), import-pptx guide, style_remote wiring, architecture.md updated — no save= mentions remain. CHANGELOG under [Unreleased].

…e save flag

Local and Remote run_python had divergent 'save' semantics: Local persisted
writes unconditionally (save only gated the build), Remote silently discarded
writes without save=True. The v0.5.1 persona unification exposed this as
agent instability in the cloud E2E (vibe agent 'forgetting' save=True).

New unified contract:
- File writes always persist — no 'unsaved' state, no flag to forget
- PPTX artifact rebuilds automatically when the deck changed (cheap build)
- measure_slides is the only trigger for the expensive verification pass
- save is accepted but ignored (deprecation note in result)
- Remote write-back is diff-based (baseline snapshot), fixing the stale-copy
  clobber risk; read-only users run without persistence (readOnly note)
- run_style_python (Remote) unified the same way: style.html persists
  automatically when changed

personas/guides/wiring/docs updated (repo-wide save= mentions: 0).
tests/test_run_python_semantics.py pins the semantics on both adapters.

SPEC: 20260801-1122_run-python-unified-semantics
Progress: Phase 0-2 complete; make all 628 passed / lint clean
Next: redeploy cloud stack, user re-runs compose E2E (v0.5.1 gate)
…eparation, files collision guard

Review findings (all 6 addressed):
- High: expensive SVG render/compose ran on deck_changed without
  measure_slides (contract violation), and compose/preview skipped when
  measuring an unchanged deck. Introduced _post_processing_plan (pure
  decision table: build/artifact/verify) and rewired the gates; no more
  measure-error noise without a measure request
- High: files=[...] staged by basename could shadow deck.json and persist
  via the unconditional write-back — collision names now rejected
- Medium: artifact refresh failures now surface as result.pptx_error with
  best-effort compensation delete of the orphaned upload
- Medium: run_style_python warns when style.html was written without
  style_name (no more silent discard)
- Low: import-pptx.md stale generate_pptx freshness claim updated
- Low: _upload_deck_workspace return annotation fixed

Added real-wiring branch tests for the 4-pattern contract matrix
(monkeypatched rig around the actual run_python).

SPEC: 20260801-1122_run-python-unified-semantics
Progress: review fixes complete; make all 640 passed / lint clean
Next: local compose smoke → cloud redeploy → user re-E2E
…nged slide files

Unconditional rewrites on diagnostics bumped mtimes every call, falsely
marking the deck as changed and rebuilding output.pptx on read-only runs
(caught by the local compose one-pass check). Rewrite only when
sanitization actually changed the content.

SPEC: 20260801-1122_run-python-unified-semantics
Progress: local compose one-pass verified (build+preview OK, no read-only rebuild)
Next: cloud redeploy → user re-E2E
…on tests, scoped files guard

- Add regression tests for the lint rewrite guard (diagnostics-only →
  no rewrite/no rebuild; _spAutoFit sanitize → rewrite + single rebuild)
- Apply the same cleaned != slide_data guard to Remote lint (no
  reserialization for diagnostics-only results; byte-identical test)
- Scope the staged-files collision guard to deck sessions; exact match
  for file entries (deck.jsonl no longer rejected), prefix match for
  directories; extracted _validate_staged_files for unit testing
- Fix import-pptx.md 'download link' claim and remote generate_pptx
  docstring to match the actual return and finalize/handoff role

SPEC: 20260801-1122_run-python-unified-semantics
Progress: re-review conditions met; make all 644 passed / lint clean
Next: cloud redeploy with this commit → user re-E2E
… on refresh

The automatic artifact refresh uploads a new UUID key per build-relevant
change; one E2E session accumulated 19 orphaned PPTX objects (only the
newest is referenced by the deck record). update_deck now returns the
previous attribute values (ReturnValues=UPDATED_OLD) and both refresh
paths (run_python auto-refresh, generate_pptx) delete the superseded
artifact after a successful record update — best effort, with the
existing lifecycle rules (pptx/ 90d, noncurrent 30d) as backstop.

SPEC: 20260801-1122_run-python-unified-semantics
Progress: E2E passed (deck 5fa63146); artifact cleanup fixed; 646 passed
Next: PR → merge → v0.5.1 tag
@ShotaroKataoka ShotaroKataoka added blog:skip ブログ対象外 bug Something isn't working labels Aug 1, 2026
@ShotaroKataoka
ShotaroKataoka merged commit 4898b1f into main Aug 1, 2026
16 of 17 checks passed
@ShotaroKataoka
ShotaroKataoka deleted the feature/run-python-unified-semantics branch August 1, 2026 05:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

blog:skip ブログ対象外 bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant