Skip to content

spike(40): Proposed Change contract (RFC #8) piloted on Slides — VALIDATED - #46

Merged
criptogus merged 2 commits into
mainfrom
spike/002-proposed-change-slides
Aug 13, 2026
Merged

spike(40): Proposed Change contract (RFC #8) piloted on Slides — VALIDATED#46
criptogus merged 2 commits into
mainfrom
spike/002-proposed-change-slides

Conversation

@criptogus

Copy link
Copy Markdown
Owner

Spike 40 — RFC 0008 Proposed Change no Slides

Valida o contrato unificado Proposed Change no caso mais difícil de preview (shape-level). VALIDADO em < 1 dia (time-box: 2 semanas).

  • spikes/002-proposed-change-slides/ — contract (ciclo RFC RFC: unified "proposed change" contract in agent-core #8), slides-adapter (ops tipadas + apply atômico), harness E2E
  • Aceite: (1) change flui como um único ProposedChange tipado ✅ (2) preview semântico shape-level ✅ (3) reject = save byte-idêntico ✅ (4) time-box ✅
  • Byte-preservation: 41/42 partes byte-for-byte (só slide1.xml difere)
  • Achados: ids sp_* são contadores de processo (não estáveis entre sessões — audit elementId vale só na sessão que aplicou); cor normaliza para #RRGGBB no re-parse
  • Estimativa cross-app: Slides ~3-4d, Docs ~4-6d, PDF ~6-8d (preview é o gargalo)

Closes #40

Gustavo Caetano added 2 commits August 6, 2026 17:15
…DATED

Shape-level ops (set_shape_text, set_shape_style, add_shape, remove_shape)
over the pptx-engine dirty-flag model, atomic applyProposal (validate all
ops before mutating any), RFC #8 lifecycle + audit persistence, and a
harness proving: per-part byte-preservation (only slide1.xml differs),
reject = byte-identical save, audit reloads in the RFC shape.

Verdict: the contract fits Slides with no structural adaptation; the
cross-app promise can be made public. Costs: Slides ~3-4d, Docs ~4-6d,
PDF ~6-8d (preview is the bottleneck).

Findings: element ids are process-local counters (not stable across
parse sessions — audit elementId valid only in the applying session);
fill color normalizes to #RRGGBB on re-parse.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8de1eb39b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const slide = slideOf(deck, op.slideIndex)
addElement(slide, {
kind: op.kind,
offset: { x: op.x, y: op.y, w: op.w, h: op.h },

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Pass shape dimensions using the engine's cx/cy fields

Every add_shape operation supplies w and h, but addElement expects an EmuRect containing cx and cy. Running the added harness therefore writes <a:ext cx="undefined" cy="undefined"/> into slide1.xml, so the supposedly validated output contains invalid geometry and the inserted shape may not render correctly in PowerPoint. Map the operation dimensions to cx/cy before calling the engine.

Useful? React with 👍 / 👎.

}
if (op.elementId) elementOf(deck, op.slideIndex, op.elementId)
}
for (const op of proposal.operations) OPS[op.type](deck, op)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Validate the entire operation sequence before mutating

When operations interact, this second loop can throw after earlier operations have already mutated and dirtied the deck. For example, two removals of the same element both pass the preflight existence check, then the first removal succeeds and the second throws; a remove followed by a style operation has the same problem. This violates the adapter's atomicity guarantee and can leave a failed proposal partially applied, so validation must account for sequential effects or application must occur on a rollback-capable clone.

Useful? React with 👍 / 👎.

Comment on lines +96 to +97
export function applyProposal(deck, proposal) {
for (const op of proposal.operations) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject proposals that have not been accepted

When a caller passes a draft, proposed, or rejected proposal, applyProposal ignores its status and mutates the deck anyway. In particular, applying a rejected proposal directly contradicts the lifecycle guarantee that rejection must not change document bytes and makes the public mutation gate unsafe unless every caller independently duplicates the check. Require proposal.status === 'accepted' before validating or executing operations.

Useful? React with 👍 / 👎.

Comment on lines +47 to +49
transform: el.transform
? { x: el.transform.x, y: el.transform.y, w: el.transform.w, h: el.transform.h }
: null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Read preview geometry from transform.offset

For every parsed slide element, the engine stores coordinates under el.transform.offset using x, y, cx, and cy; the properties read here do not exist. Consequently persisted previews contain transform: {} for every element, depriving the approval surface of the position and dimensions needed for a shape-level semantic preview. Read the offset object and map cx/cy to the desired preview width and height.

Useful? React with 👍 / 👎.

@criptogus

Copy link
Copy Markdown
Owner Author

Reopening to trigger a fresh pull_request run — CI recovered after the Actions outage; this PR was created while events were being dropped.

@criptogus criptogus closed this Aug 7, 2026
@criptogus criptogus reopened this Aug 7, 2026
@criptogus
criptogus merged commit ca01d92 into main Aug 13, 2026
4 checks passed
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.

Spike: Proposed Change contract (RFC #8) piloted on Slides

1 participant