Skip to content

feat(governance): reconcile delivery and plan adjustments - #6305

Merged
bokelley merged 7 commits into
mainfrom
bokelley/governance-ledger-adjustments
Aug 17, 2026
Merged

feat(governance): reconcile delivery and plan adjustments#6305
bokelley merged 7 commits into
mainfrom
bokelley/governance-ledger-adjustments

Conversation

@bokelley

@bokelley bokelley commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

  • define fact-specific authority for the buyer, seller, and buyer-appointed governance agent instead of treating any one party as authoritative for every fact
  • add canonical, seller-authenticated delivery statements and separately attributed buyer delivery observations, with explicit mismatch and append-only reconciliation behavior
  • allow the plan owner to close an operational governance period without turning governance into the source of truth for final billing
  • make report_plan_adjustment bilateral: the seller reports immutable evidence and the authenticated plan owner accepts or disputes it before any economic or ledger effect
  • expose gross commitment, verified adjustments, economic net cost, restored headroom, conservative delivery exposure, reporting-period state, and reconciliation status in audit logs
  • add gross_commitment (default) and opt-in verified_net_cost accounting modes while retaining original commitments for anti-fragmentation thresholds

Why

The original adjustment design left an important trust question implicit: a seller could report one delivery amount to governance and another to the buyer, then attempt to use an adjustment to manufacture headroom. The protocol needs to preserve who asserted each fact, detect disagreement, and prevent either side's later report from silently overwriting the other.

Governance is an operational control plane, not a billing ledger. The governance ledger is authoritative for authorization and headroom; it is not independent proof of physical delivery, a refund, or the final payable amount. Seller delivery is a seller-attributed statement, buyer measurement is a buyer-attributed observation, and a commercial adjustment becomes verified only through seller report plus plan-owner review.

Behavior and impact

  • seller delivery statements carry an immutable seller-scoped ID, monotonic action sequence, RFC 8785/JCS SHA-256 digest, period, cumulative spend, and currency; the exact digest-bearing payload is retained for audit
  • buyer delivery observations identify the exact seller delivery check and remain separately attributed; open mismatches are marked disputed, retain both values, and use the higher credible amount as conservative_exposure
  • an open dispute blocks adjustment acceptance until it is reconciled or the authenticated plan owner explicitly closes that governance period
  • closing an unresolved period records closed_unresolved, freezes both parties' evidence for that period, and stops the historical mismatch from blocking later governance; it does not settle an invoice or assert billing truth
  • a different subsequent seller reporting period starts open and unmatched, without carrying the old buyer observation into the current-period summary
  • adjustment reporting alone cannot reduce net cost or restore headroom; only the authenticated plan owner can verify or dispute the exact record
  • seller adjustment and evidence IDs are immutable, cumulative adjustments cannot exceed the original commitment, and cumulative verified decommitments cannot exceed the latest undelivered obligation
  • verified refunds and credits reduce economic net cost; they restore headroom only in verified_net_cost mode. Makegoods never restore cash headroom
  • delivery observations do not add the already-authorized spend to the commitment ledger a second time

Proposed WG resolutions

Issue #6304 carried needs-wg-review on three structural questions. This PR proposes specific resolutions so ratification is explicit rather than incidental to merge:

  1. Separate report_plan_adjustment task — yes; the bilateral report/review lifecycle is distinct enough from report_plan_outcome to warrant its own task surface.
  2. Authority split — seller uses action: "report" to submit evidence; the authenticated plan owner uses action: "review" to accept or dispute. Neither party is unilaterally authoritative.
  3. Accounting mode — per-plan budget.accounting_mode field; default is gross_commitment (headroom restored only for decommitments); opt-in verified_net_cost restores headroom for refunds and credits as well.

Validation

  • 78 focused governance tests
  • 570 training-agent tests
  • npm run test:schemas (28 schema checks plus deprecation metadata), including explicit period-close request/response cases
  • npm run test:storyboard-sample-request-schema
  • npm run test:examples (75 examples)
  • npm run test:docs-nav
  • npm run test:oneof-discriminators
  • changeset protocol-scope and Changesets status checks
  • TypeScript compiler output contained no errors in changed governance/training-agent files; the repository-wide typecheck still has unrelated baseline errors outside this diff

Closes #6304

Review follow-ups applied on-branch

A second review pass (schemas/docs read + adversarial implementation review + test-coverage audit) surfaced issues that are now fixed in three commits:

Reference implementation (fix(governance): close adjustment lifecycle gaps...):

  • Disputed adjustments are terminal and no longer consume the cumulative-adjustment cap, so a seller can re-report a corrected amount after a buyer dispute; accept-time re-validates cumulative verified ≤ commitment.
  • Delivery observations must bind to the canonical (latest-sequence) seller statement for the binding + period; naming a superseded statement's check is a CONFLICT.
  • Dispute-blocks-acceptance considers every open reporting period, not just the globally latest delivery outcome.
  • Period closure is plan-owner-only; delivery observations may come from the plan owner or the original intent caller.
  • observation_id replay/conflict detection is scoped to the authenticated reporter.
  • Re-sync without accounting_mode preserves the existing mode; cap checks are epsilon-tolerant at float boundaries; the review accept path builds the full response before mutating the ledger and idempotency cache.

Docs/schemas (docs(governance): fix migration note...): the changeset migration note now names the correct schemas and required fields (including the previously omitted check_id/governance_context break) with before/after payloads; get-plan-audit-logs-response.json entries[].delivery is modeled instead of a deprecated stub; the audit-logs example no longer shows a verified adjustment coexisting with an open dispute; new normative sentences cover dispute terminality, canonical-statement binding, any-open-period blocking, and closure authority.

Coverage: governance-delivery-monitor storyboards now probe the adjustment lifecycle (report→accept happy path, over-adjustment rejection, unauthorized reporter, dispute-blocks-acceptance with closed_unresolved unblocking); schema-validation vectors exercise the report_plan_adjustment request/response conditionals and the delivery_metrics digest requirement; unit tests add a golden digest vector transcribed from the check_governance doc example, digest-mismatch rejection, pre-flight zero-spend cancellation, missing-statement decommitment review, sequence regression, and an accounting-mode-by-type matrix (755 tests green).

Dispute semantics (resolved per WG option b, implemented at 306ad185): disagreement handling splits by evidence source. A forwarded seller_statement_copy whose statement ID or digest mismatches the canonical statement is seller equivocation — disputed, blocks adjustment acceptance while the period is open. A forwarded copy whose identity matches but whose values differ is internally inconsistent (the digest covers period, spend, currency) and rejects as VALIDATION_ERROR. A buyer_measurement with matching period/currency but different cumulative_spend is recorded as the non-blocking measurement_variance; a structural mismatch (period or currency) stays disputed. The verified-decommitment ceiling uses the conservative delivered figure — commitment − max(latest seller-stated, latest buyer-observed cumulative spend) — so a seller understating its statement gains no decommitment room and a buyer inflating an observation only shrinks its own restorable headroom: manufactured variance can freeze value, never free it. Covered by unit tests (763 green), a storyboard variance phase, and schema vectors for the new enum value.

@bokelley bokelley changed the title [codex] feat(governance): add append-only plan adjustments feat(governance): add append-only plan adjustments Aug 9, 2026
@bokelley bokelley closed this Aug 9, 2026
@bokelley bokelley reopened this Aug 9, 2026
@bokelley bokelley changed the title feat(governance): add append-only plan adjustments feat(governance): reconcile delivery and plan adjustments Aug 9, 2026
@bokelley
bokelley marked this pull request as ready for review August 9, 2026 16:47

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ladon verdict: Escalate to human review

Escalate — gated schema paths modified without required approval.

This PR touches multiple files under static/schemas/source/** (gated paths), and review_decision is REVIEW_REQUIRED — not APPROVED. Per decision-table row 2, a gated-paths PR cannot auto-approve until a human/CODEOWNERS approval lands.

The reviewer found no Critical, High, or Medium findings: the AdCP 3.2 campaign-governance addition (new report_plan_adjustment task, attributed delivery reconciliation, gross_commitment/verified_net_cost accounting, expanded audit state) is coherent across schema and docs, the double-charge guard and adjustment attribution/authorization logic are correct, and the minor changeset is right because every governance schema is x-status: experimental. Extensive new test coverage is present. Only one Low note (delivery-metrics block ordering before the plan-existence guard, unreachable in practice) — left out of the gate.

Absent blocking findings this would approve, but the hard gated-paths gate requires explicit human/CODEOWNERS review before merge.

Escalation reasons:

  • Gated files under static/schemas/source/** modified/added (governance schemas + core x-entity-types + index) with review_decision: REVIEW_REQUIRED — human/CODEOWNERS approval required before merge.

Why human review

  • Gated paths touched (static/schemas/source/** — governance schemas, x-entity-types.json, index.json) and review_decision is REVIEW_REQUIRED, not APPROVED — hard approval gate requires human/CODEOWNERS review before merge.
  • This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/x-entity-types.json (modified) matches static/schemas/source/**; static/schemas/source/governance/check-governance-request.json (modified) matches static/schemas/source/**; static/schemas/source/governance/check-governance-response.json (modified) matches static/schemas/source/**; static/schemas/source/governance/get-plan-audit-logs-response.json (modified) matches static/schemas/source/**; static/schemas/source/governance/report-plan-adjustment-request.json (added) matches static/schemas/source/**; static/schemas/source/governance/report-plan-adjustment-response.json (added) matches static/schemas/source/**; static/schemas/source/governance/report-plan-outcome-request.json (modified) matches static/schemas/source/**; static/schemas/source/governance/report-plan-outcome-response.json (modified) matches static/schemas/source/**; static/schemas/source/governance/sync-plans-request.json (modified) matches static/schemas/source/**; static/schemas/source/index.json (modified) matches static/schemas/source/**) and the current GitHub review decision is 'REVIEW_REQUIRED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.

@aao-secretariat aao-secretariat Bot added the ladon/needs-human-review Ladon has escalated this PR for human review. label Aug 9, 2026
@lukasz-pubx

Copy link
Copy Markdown

Proposal review

Reviewed at 1b17e74bf3414ceeb77adc1514286c11e5e08195 (tree: governance schemas + report_plan_adjustment/report_plan_outcome/check_governance/get_plan_audit_logs/specification docs, compliance specialisms, training-agent reference impl & tests; baseline: repo base 502b22dbc + live docs build 3.1.2 — report_plan_outcome, reference/experimental-status).

Verdict

The tree delivers every claim in the description, and the design quality is high: the new two-action report_plan_adjustment task is fully wired (index.json, docs.json nav, x-entity types and field map), seller-attributed delivery statements, buyer observations, reconciliation states, period closure, accounting_mode, and the expanded audit surface all match the description.

Named merits:

  • The worked digest example is real. I recomputed the RFC 8785/JCS SHA-256 in the check_governance example and got 4b55f115…4633 exactly. Spec examples that actually verify are rare and valuable.
  • The decommitment cap is anchored to seller-attested spend ("Its amount cannot exceed authoritative commitment - latest cumulative seller spend"), so a buyer cannot use its own lower observation to manufacture headroom — the design resists both directions of abuse.
  • Anti-fragmentation stickiness is preserved and explicitly reconciled with the existing "Commitments are sticky within the window" convention, closing the commit-adjust-commit loophole.
  • The committednet_committedledger_committed alias strategy keeps legacy readers coherent instead of silently changing one field.

The minor changeset is defensible since every touched governance schema is x-status: experimental — but see finding 2 on the migration-note obligation that status carries.

Findings

1. warning — exact-match comparison makes disputed the steady state for real buyer measurement

The spec defines disagreement implicitly as inequality: governance "compares period, cumulative spend, currency" and "If the records disagree, governance MUST: mark the governed action disputed … reject adjustment acceptance while the disputed governance period remains open" (specification.mdx, Evidence authority section). The reference implementation confirms the strict reading — observation.cumulative_spend !== sellerStatement.cumulativeSpend || … reporting_period.start !== … (governance-handlers.ts), including exact string equality on period timestamps.

Failure narrative: a buyer's ad server measures $12,489.37 against the seller's $12,500 statement — a routine sub-1% discrepancy that is the norm in delivery measurement, not evidence of equivocation. Every buyer_measurement observation disputes; every open dispute blocks all adjustment acceptance for the action; buyers learn to routinely close periods closed_unresolved. The audit state built to flag seller equivocation becomes indistinguishable from measurement noise, and the escape valve becomes the main road.

Ask — any of: (a) define a materiality rule (e.g., plan-configurable discrepancy_tolerance with a stated default) below which the state is consistent or a new non-blocking status; (b) scope acceptance-blocking to seller_statement_copy ID/digest mismatches — true equivocation — while buyer_measurement variance produces a recorded-but-non-blocking status; or (c) state normatively that exact match is intentional, buyer_measurement is expected to yield disputed in normal operation, and period closure is the routine resolution path.

2. warning — breaking reshape of experimental surfaces without the migration note the experimental contract requires

Two surfaces change incompatibly: the base delivery object was "deprecated": true with additionalProperties: true and no required fields; the PR makes six fields required with additionalProperties: false — the schema test flips accordingly ("legacy unbound delivery snapshots must be rejected"). Likewise check_governance delivery_metrics goes from one required field (reporting_period) to eight. This is permitted for experimental surfaces, but the contract says breaking changes require "At least 6 weeks published in release notes and the changelog" and "A migration note describing the change, with before/after examples where possible" (reference/experimental-status). The changeset contains neither.

Failure narrative: an existing delivery-phase seller upgrades and every delivery report is schema-rejected with no in-repo migration guidance.

Ask — add a migration note (before/after payloads for both surfaces) in the changeset or a docs migration section, or state in the PR that the note and 6-week notice will ship with the 3.2 release notes.

3. warning — new normative surface has zero agent-observable compliance coverage

Issue #6304's acceptance criteria: "Cover cancellation, partial decommitment, refund, credit, makegood, duplicate replay, conflicting replay, over-adjustment, and unauthorized reporter cases in conformance tests." The PR covers these well in training-agent unit tests (661 lines), but grep -rn report_plan_adjustment static/compliance/source/ returns nothing — the only compliance change updates governance-delivery-monitor to satisfy the new required fields. No storyboard probes the adjustment report→review lifecycle, the dispute-blocks-acceptance rule, or closed_unresolved closure. (Conceded: report_plan_outcome delivery observations had no specialism coverage before this PR either — that part is inherited — but report_plan_adjustment is brand-new surface.) Unit tests prove the reference implementation behaves; storyboards are what prove a seller behaves.

Ask — add storyboard scenarios (adjustment happy path + at least the over-adjustment and unauthorized-reporter rejections, and one dispute/closure probe in the delivery-monitor specialism), or declare the deferral and its tracking issue in the PR.

4. warning — the headline use case (cancellation before delivery) has an undocumented prerequisite

"A decommitment also requires a canonical seller delivery statement" (report_plan_adjustment.mdx); the reference impl returns CONFLICT: "A canonical seller delivery statement is required before decommitment can be verified" (governance-handlers.ts). But #6304's motivating case is releasing commitment "after cancellation" — and a buy cancelled before flight start has no delivery statement. The implied flow (seller first submits a zero-spend phase: "delivery" statement, then reports the decommitment) is sound — it forces the seller to attest zero delivery — but it is stated nowhere.

Ask — document the zero-statement pre-flight-cancellation flow with an example, or explicitly define the disposition when no statement exists.

5. note — "credible" in conservative_exposure is undefined

Schema: "Maximum credible seller or buyer amount while evidence is unresolved"; prose: "use the highest credible reported amount." If the rule is max(seller_reported_spend, buyer_observed_spend), say max — "credible" invites governance-agent discretion and divergent exposure numbers across implementations. Ask — replace with the defined rule, or define the credibility test.

6. note — seller correction path while a period is open is implicit

Reconciliation is specified buyer-side ("The buyer submits a newer observation against the applicable canonical seller statement"), and closure prohibits new statements for a closed period — but whether a seller may issue a corrected statement (new statement_id, higher sequence) for the same open period, and that the latest statement then becomes "the applicable canonical" one (as seller_reported_spend = "Latest canonical seller cumulative spend" implies), is never stated. Ask — one sentence confirming the seller correction path and which statement an observation should bind to after a correction.

7. note — this PR decides all three questions #6304 explicitly reserved for the WG

Separate task (yes), roles (seller reports / plan owner reviews), and gross-vs-net accounting (per-plan accounting_mode, default gross_commitment) are all answered here while the issue still carries needs-wg-review. All three answers look right — but the PR should say these are the proposed WG resolutions so ratification is explicit rather than incidental.


No blockers: the internal docs↔schema↔example↔test coherence is unusually good (digest math, schema conditionals, and x-entity wiring all check out).

@bokelley

bokelley commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @lukasz-pubx — the digest-math verification and authority-table analysis are exactly the kind of independent review this surface needed. Surfacing these for @bokelley below, organized by what needs a design decision vs. what's a ready-to-apply text fix.


Finding 1 — Dispute semantics for buyer_measurement (design decision needed)

The exact-match check is the most consequential open question. Sub-1% delivery variance between a buyer's ad-server measurement and the seller's statement is the industry norm, not evidence of equivocation. As written, every buyer_measurement observation that doesn't happen to match the seller's figure to the cent will produce disputed, and period closure becomes the routine path rather than the escape valve.

Three paths, @bokelley's call:

Option (a) — Materiality tolerance

// plan synced with a discrepancy band
{
  "governance_config": {
    "discrepancy_tolerance": { "relative": 0.02 }
  }
}
// observations within ±2% → status: "consistent", not "disputed"

Adds per-plan configurability; introduces a new field and logic branch.

Option (b) — Source-type split

// seller_statement_copy: ID/digest mismatch → "disputed" (blocks acceptance)
// buyer_measurement: spend variance → "measurement_variance" (recorded, non-blocking)

Maps most cleanly to the PR's own authority table — seller-copy mismatch is true equivocation; buyer-measurement variance is expected noise. Requires a new enum value and spec language.

Option (c) — Document intent explicitly (lowest friction)
Add one normative sentence to specification.mdx ▸ Evidence authority:

Exact match is required. buyer_measurement observations that differ from the seller statement are expected to produce disputed in normal operation; period closure is the routine operational resolution path, not a signal of dispute in the commercial sense.

No schema change. Closes the ambiguity without new surface.

If the design intent was always "period closure is the normal path," (c) is the right answer. If measurement variance blocking adjustment acceptance is a real operational concern for the target adopters, (b) is the cleanest architectural resolution.


Finding 2 — Migration note missing from changeset (required by experimental contract)

experimental-status.mdx requires "a migration note describing the change, with before/after examples" for breaking changes to experimental surfaces. add-governance-adjustments.md has none. Two surfaces break incompatibly:

delivery object (check-governance-response.json / get-plan-audit-logs-response.json):

- "deprecated": true, "additionalProperties": true  // no required fields
+ "additionalProperties": false                      // six fields now required

check_governance delivery_metrics: from reporting_period-only required → 8 required fields (statement_id, sequence, statement_digest, reporting_period, cumulative_spend, currency, seller_reference, plus canonical_payload).

Suggested addition to the changeset body (or a ## Migration section in specification.mdx):

delivery schema — The deprecated unbound delivery snapshot is superseded. Before: additionalProperties: true, no required fields. After: six required fields (statement_id, sequence, statement_digest, reporting_period, cumulative_spend, currency), additionalProperties: false. delivery_metrics — Required fields expand from 1 (reporting_period) to 8; before/after examples in check_governance.mdx show both shapes. Since this lands in the first 3.2 beta, the beta publication is the 6-week-notice vehicle — note that explicitly.


Finding 3 — Compliance storyboard gap

No static/compliance/source/ storyboard covers the adjustment flow. Options:

  • Add storyboards (happy path + over-adjustment + unauthorized-reporter rejections + one dispute/closure probe in the delivery-monitor specialism) before merge.
  • Or note the deferral explicitly in the PR body with a tracking issue reference so it resurfaces before 3.2 ships.

The deferral path is lower friction if storyboards aren't blocking merge; note that report_plan_outcome delivery observations also had no specialism coverage before this PR, so this isn't introducing a new gap so much as not closing an existing one for the new surface.


Findings 4–6 — Ready-to-apply text fixes

Finding 4 — In report_plan_adjustment.mdx, after "A decommitment also requires a canonical seller delivery statement. Its amount cannot exceed…", add:

For actions cancelled before any delivery occurs, the seller first submits a check_governance delivery phase with cumulative_spend: 0 and the applicable reporting_period. This attests zero delivery and satisfies the statement prerequisite before a decommitment can be reviewed.

Finding 5 — In specification.mdx, replace "use the highest credible reported amount as conservative_exposure" with:

use max(seller_reported_spend, buyer_observed_spend) as conservative_exposure

And update the conservative_exposure schema description in get-plan-audit-logs-response.json from "Maximum credible seller or buyer amount while evidence is unresolved" to "Greater of seller-reported and buyer-observed spend while evidence is unresolved."

Finding 6 — In specification.mdx, in the "Reconciliation is append-only" paragraph, add:

A seller may also issue a corrected statement for the same open period by submitting a new check_governance delivery call with a higher sequence. The latest in-sequence statement becomes the applicable canonical statement; the buyer's next observation SHOULD bind to it. Earlier statements remain in the audit trail.


Finding 7 — WG ratification

Issue #6304 still carries needs-wg-review on the three structural questions (separate report_plan_adjustment task, seller-reports/plan-owner-reviews authority split, and accounting_mode default to gross_commitment). The PR body should call out that these are the proposed WG resolutions so ratification is explicit rather than incidental to merge.


Generated by Claude Code

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ladon verdict: Escalate to human review

Escalate — hard approval gate (gated paths) unsatisfied, and a Breaking-class protocol change is present.

This PR modifies published protocol source under static/schemas/source/** (governance schemas, x-entity-types.json, index.json), which is under a hard, non-overridable approval gate. gated_paths: true and review_decision: REVIEW_REQUIRED (not APPROVED) → decision-table row 2 forces escalate. Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.

Additionally, the reviewer notes the changeset's Migration section justifies "Breaking downgrades to Normative" for two x-status: experimental surfaces via a minor changeset. Per the repo's "Breaking-class changes are never auto-approved" hard gate, ratification is a human act — escalate unless review is APPROVED.

Escalation reasons

  • Gated paths touched (static/schemas/source/** — governance schemas, x-entity-types.json, index.json) and review_decision is REVIEW_REQUIRED, not APPROVED. Human/CODEOWNERS review required before merge.
  • Breaking-class protocol change to experimental surfaces (per changeset Migration section) is never auto-approved; requires human ratification.

Findings
No critical/high/medium findings this pass. Delta is confined to documentation clarifications (conservative_exposure wording, corrected-statement paragraph, cancel-before-delivery prerequisite) and a Migration section — all internally consistent. The escalation is driven by the deterministic gate and the breaking-change governance gate, not by any actionable code defect.

Why human review

  • Gated paths touched (static/schemas/source/** — governance schemas, x-entity-types.json, index.json) and review_decision is REVIEW_REQUIRED, not APPROVED — hard approval gate requires human/CODEOWNERS review before merge.
  • Changeset Migration section documents Breaking-class changes to two x-status: experimental surfaces (Breaking downgrades to Normative); Breaking-class changes are never auto-approved and require human ratification unless review_decision is APPROVED.
  • This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/x-entity-types.json (modified) matches static/schemas/source/**; static/schemas/source/governance/check-governance-request.json (modified) matches static/schemas/source/**; static/schemas/source/governance/check-governance-response.json (modified) matches static/schemas/source/**; static/schemas/source/governance/get-plan-audit-logs-response.json (modified) matches static/schemas/source/**; static/schemas/source/governance/report-plan-adjustment-request.json (added) matches static/schemas/source/**; static/schemas/source/governance/report-plan-adjustment-response.json (added) matches static/schemas/source/**; static/schemas/source/governance/report-plan-outcome-request.json (modified) matches static/schemas/source/**; static/schemas/source/governance/report-plan-outcome-response.json (modified) matches static/schemas/source/**; static/schemas/source/governance/sync-plans-request.json (modified) matches static/schemas/source/**; static/schemas/source/index.json (modified) matches static/schemas/source/**) and the current GitHub review decision is 'REVIEW_REQUIRED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ladon verdict: Escalate to human review

Escalate — gated paths touched without an approving review.

This PR modifies/adds files under static/schemas/source/** (governance schemas, core/x-entity-types.json, index.json), which sit under a hard, non-overridable approval gate (gated_paths: true). The current GitHub review_decision is REVIEW_REQUIRED, not APPROVED, so decision-table row 2 fires and the outcome must be escalate.

The reviewer's delta pass surfaced no critical/high/medium findings — the only change since the prior escalate touches scripts/lint-storyboard-scoping.cjs, adding report_plan_adjustment to the exempt set, and its exemption rationale (plan_id unconditionally required) matches the new schema. The diff is mechanically clean, but a clean diff does not lift the gate.

Additionally, per the repo's "Breaking-class changes are never auto-approved" hard gate, the prior pass noted Breaking-class changes to x-status: experimental surfaces documented in the changeset Migration section. Ratification is a human act and requires review_decision: APPROVED.

Escalation reasons

  • Gated paths (static/schemas/source/**) touched and review_decision is REVIEW_REQUIRED, not APPROVED — human/CODEOWNERS approval required before merge.
  • Breaking-class protocol changes require human ratification unless review_decision is APPROVED.

Human/CODEOWNERS approval will lift the gate and allow this to fall through to a normal approve on the next pass.

Why human review

  • Gated paths touched (static/schemas/source/** — governance schemas, core/x-entity-types.json, index.json) and review_decision is REVIEW_REQUIRED, not APPROVED — hard approval gate requires human/CODEOWNERS review before merge.
  • Breaking-class changes to x-status: experimental surfaces (per changeset Migration section) are never auto-approved; ratification requires human approval unless review_decision is APPROVED.
  • This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/x-entity-types.json (modified) matches static/schemas/source/**; static/schemas/source/governance/check-governance-request.json (modified) matches static/schemas/source/**; static/schemas/source/governance/check-governance-response.json (modified) matches static/schemas/source/**; static/schemas/source/governance/get-plan-audit-logs-response.json (modified) matches static/schemas/source/**; static/schemas/source/governance/report-plan-adjustment-request.json (added) matches static/schemas/source/**; static/schemas/source/governance/report-plan-adjustment-response.json (added) matches static/schemas/source/**; static/schemas/source/governance/report-plan-outcome-request.json (modified) matches static/schemas/source/**; static/schemas/source/governance/report-plan-outcome-response.json (modified) matches static/schemas/source/**; static/schemas/source/governance/sync-plans-request.json (modified) matches static/schemas/source/**; static/schemas/source/index.json (modified) matches static/schemas/source/**) and the current GitHub review decision is 'REVIEW_REQUIRED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.

@bokelley bokelley added this to the 3.2.0 milestone Aug 14, 2026
@bokelley
bokelley force-pushed the bokelley/governance-ledger-adjustments branch from 3bc374b to d950ef0 Compare August 15, 2026 21:23
@bokelley
bokelley enabled auto-merge (squash) August 15, 2026 21:23
@bokelley
bokelley requested a review from pkras August 16, 2026 20:17
@bokelley
bokelley disabled auto-merge August 16, 2026 22:01
bokelley and others added 3 commits August 17, 2026 01:00
…authority gaps

Rewrite the add-governance-adjustments changeset migration section to
name the correct schema (report-plan-outcome-request.json, not
check-governance-response.json), correct the delivery_metrics required
field count to 7, document the third breaking change (check_id and
governance_context now required for outcome: "delivery"), and replace
the unverifiable before/after doc-example claim with inline before/after
snippets.

Bring get-plan-audit-logs-response.json entries[].delivery out of its
deprecated stub state to match the MDX description already in place,
giving it the buyer observation's optional field shape without adding a
required array (audit surfaces retain historical entries).

Make the get_plan_audit_logs example internally consistent: a
disputed/open governed action next to an accepted $25,000 decommitment
read as a contradiction of the spec's own blocking rule.

Sharpen the two same-named adjustments_verified fields (budget: dollars,
makegoods contribute zero; summary: count, makegoods included) so the
wire-compatible name carries an unambiguous unit and makegood treatment
in both the schema and the docs table.

Add normative statements the implementation already enforces but the
spec didn't say: superseded-statement rejection, adjustment disputes as
terminal records with a new-ID correction path, the cumulative-verified-
adjustment cap, that "disputed governance period remains open" covers
any reporting period, and that only the plan owner may set
period_closed. Mirrored briefly into report_plan_adjustment.mdx and
report_plan_outcome.mdx.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…overage

Addresses verified review findings on the campaign-governance adjustment
lifecycle (PR #6305):

- Disputed adjustments no longer consume the cumulative-adjustment cap,
  so a seller can re-report a corrected amount after a buyer dispute.
  Accept-time also re-validates verified-sum <= commitment.
- Delivery observations must bind to the canonical (latest-sequence)
  seller statement for the binding + reporting period, not whichever
  check the buyer happened to name.
- Dispute-blocks-acceptance considers every open reporting period for a
  binding, not just the globally latest delivery outcome.
- Period closure is a plan-owner-only authority; delivery observations
  may be reported by the plan owner or the original intent caller,
  including through the plan-resolution fallback.
- Delivery observation_id dedup is scoped to the authenticated reporter
  (reporterCaller), not the plan owner.
- Re-syncing a plan without accounting_mode preserves the existing
  plan's mode instead of silently resetting to gross_commitment.
- Cap checks at commitment boundaries use an epsilon-tolerant
  comparison so exact-boundary floats aren't spuriously rejected.
- The adjustment-review accept path builds the full response (including
  the throwable plan-summary computation) before mutating the ledger
  and the review idempotency cache.

Test and compliance coverage:

- Regression tests for all fixes plus a golden digest vector transcribed
  from the check_governance documentation example, digest-mismatch
  rejection, pre-flight cancellation, missing-statement decommitment
  review, statement sequence regression, and an
  accounting-mode-by-adjustment-type matrix.
- governance-delivery-monitor storyboard phases for the adjustment
  lifecycle: report/accept happy path, over-adjustment rejection,
  unauthorized-reporter rejection, and dispute-blocks-acceptance with
  closed_unresolved unblocking.
- Schema-validation vectors for report-plan-adjustment request/response
  conditionals and the delivery_metrics statement-digest requirement.
- The changeset migration snippet uses a placeholder statement_digest
  instead of reusing the doc example's real digest against a different
  payload.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- docs/building/by-layer/L1/security.mdx and
  docs/building/operating/transport-errors.mdx used HTML comments, which
  the MDX parser rejects; use MDX comments.
- scripts/x-entity-field-map.json declared evidence_id twice after the
  governance-adjustment entries landed; JSON last-wins already resolved
  it to __scope_specific__, so drop the shadowed audience_evidence entry
  (evidence_id is scope-specific per the map's own comment).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bokelley

Copy link
Copy Markdown
Contributor Author

Follow-up status on the findings in this thread, as of da42e322:

Applied earlier (in d950ef0d/current tree): finding 2 (migration note in the changeset), finding 4 (zero-spend pre-flight cancellation flow documented), finding 5 (max(seller_reported_spend, buyer_observed_spend) rule), finding 6 (seller correction path), finding 7 (proposed WG resolutions in the PR body).

Applied at da42e322: finding 3 — governance-delivery-monitor now carries adjustment-lifecycle storyboards (happy path, over-adjustment, unauthorized reporter, dispute-blocks/closure-unblocks), plus schema vectors for the report_plan_adjustment conditionals. A second implementation review also surfaced and fixed five reference-implementation defects (dispute-cap deadlock, stale-statement binding, latest-period-only dispute check, closure authority bound to intent caller, observation dedup scoped to the wrong identity) — details in the PR body's "Review follow-ups" section. The migration note itself was corrected: it previously named the wrong schemas and required-field sets; it now matches the shipped schemas, covers the check_id/governance_context break, and carries verifiable before/after payloads.

Still open — finding 1 (dispute semantics), proposed resolution for WG review: option (b) source-type split, hardened against manufactured variance by moving the protection into the math rather than the blocking state. seller_statement_copy ID/digest mismatch (true equivocation) stays hard-blocking disputed. buyer_measurement spend variance becomes recorded, non-blocking measurement_variance, and the decommitment ceiling becomes commitment − max(seller-stated spend, buyer-observed spend) while evidence disagrees. Under that rule a seller understating spend gains nothing (the buyer's higher figure caps the release) and a buyer inflating its observation only shrinks its own restorable headroom, so neither side has an incentive to fake a dispute; the residual abuse is attributed, recoverable DoS via period closure. One supporting invariant: a forwarded copy whose digest matches the canonical statement but whose spend differs is internally inconsistent (the digest covers the spend) and rejects as VALIDATION_ERROR rather than recording a dispute.

@bokelley

Copy link
Copy Markdown
Contributor Author

Clean closure on findings 2–7. The option (b) hardened proposal for finding 1 is technically sound — a few notes that may help the WG evaluation:

The ceiling math is incentive-compatible. Using commitment − max(seller-stated, buyer-observed) while evidence disagrees means a seller who understates spend to manufacture headroom is blocked by the buyer's higher figure, and a buyer who inflates its observation only shrinks its own restorable headroom. Neither side gains from variance; the residual risk (attributed, recoverable DoS via period closure) is weaker than what the original blocking state prevented.

The digest invariant closes the subtle equivocation path correctly. RFC 8785/JCS SHA-256 covers the full canonical payload including cumulative_spend, so a forwarded copy that passes the digest check but carries a different spend value is internally inconsistent — not a disagreement between parties. VALIDATION_ERROR rather than a dispute record is the right disposition.

What implementing option (b) requires:

  • A new measurement_variance enum value in reconciliation status
  • Modified decommitment ceiling logic (from commitment − seller-stated to commitment − max(seller-stated, buyer-observed) while evidence disagrees)
  • A pre-dispatch consistency check on seller_statement_copy submissions (digest + spend coherence before any state is recorded)

The ladon/needs-human-review flag and @pkras review request are the right gate for WG ratification — nothing else needed from the triage side.


Generated by Claude Code

Resolves the remaining open review question on dispute semantics with
the WG-proposed option (b) plus a conservative decommitment ceiling:

- A forwarded seller_statement_copy whose statement ID or digest does
  not match the canonical statement is seller equivocation: disputed,
  and blocks adjustment acceptance while the period is open.
- A forwarded copy whose identity matches but whose values differ is
  internally inconsistent (the digest covers period, spend, currency)
  and is rejected as VALIDATION_ERROR rather than recorded.
- A buyer_measurement with matching period and currency but different
  cumulative_spend is expected measurement noise: recorded as the new
  non-blocking measurement_variance status with a warning finding.
- A buyer_measurement whose period or currency differs is incomparable
  and remains disputed.
- The verified-decommitment ceiling uses the conservative delivered
  figure, commitment - max(latest seller-stated, latest buyer-observed
  cumulative spend), so a seller understating its statement gains no
  decommitment room and a buyer inflating an observation only shrinks
  its own restorable headroom. Manufactured variance can freeze value,
  never free it.
- Closing a period over either kind of disagreement records
  closed_unresolved; only disputed blocks acceptance.

Outcome-response findings now retain their details payloads. Storyboard
coverage converts the dispute probe to a forwarded-copy digest mismatch
and adds a measurement-variance non-blocking phase; schema vectors cover
the new enum value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bokelley

Copy link
Copy Markdown
Contributor Author

Finding 1 is now resolved and implemented at 306ad185 per the proposal above (option b + conservative ceiling): seller_statement_copy ID/digest mismatch → disputed (blocking); identity-matching copy with different values → VALIDATION_ERROR (internally inconsistent — the digest covers period, spend, and currency); buyer_measurement spend variance → non-blocking measurement_variance; period/currency mismatch → disputed; decommitment ceiling = commitment − max(latest seller-stated, latest buyer-observed cumulative spend). Spec, task docs, schema enums, reference implementation, unit tests, storyboards, and schema vectors all updated together. That closes out every finding from this review.

@bokelley
bokelley merged commit 77d17df into main Aug 17, 2026
48 checks passed
@bokelley
bokelley deleted the bokelley/governance-ledger-adjustments branch August 17, 2026 07:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ladon/needs-human-review Ladon has escalated this PR for human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(governance): define append-only commitment adjustments

2 participants