Skip to content

fix(#112): cross-type same-batch state conflicts — Family A + B#116

Open
vrkothekar wants to merge 14 commits into
mainfrom
fix/issue-112-cross-type-inbatch-conflict
Open

fix(#112): cross-type same-batch state conflicts — Family A + B#116
vrkothekar wants to merge 14 commits into
mainfrom
fix/issue-112-cross-type-inbatch-conflict

Conversation

@vrkothekar

Copy link
Copy Markdown
Collaborator

Summary

Closes #112 (AG-7 follow-up). Fixes two families of cross-type same-batch state conflicts in _dedupCheck inside commit-handler.js, and wires up live-cluster integration tests.

  • Family A (commit-handler.js): Five content-status tx types (CONTENT_DISPUTED, CONTENT_VERIFIED, CONTENT_RETRACTED, UPDATE_ORIGIN, PRESCAN_REVIEW_TRIGGERED) now reject a second same-batch tx for the same ctid even when the competing tx is a different type. Previously only same-type duplication was blocked; a CONTENT_DISPUTED + CONTENT_RETRACTED pair for the same ctid in one batch would both commit.
  • Family B (commit-handler.js): Revocation-freeze extended to cross-type pairs — REVOKE_VP (or any revoke type) now blocks a same-batch UPDATE_PROFILE, KEY_ROTATED, etc. for the same tip_id via _actorTipId.
  • Test harness (scripts/test-inbatch-dedup.js): 4 new cross-type Family A cases (dispute-verify, dispute-retract, dispute-update-origin, verify-retract) + 1 Family B case (revoke-update-profile). Fixed rejection-probe SQL to search tx_data column for ctid (cross-round rejection messages embed ctid in the full tx JSON, not in reason_detail). Changed FAILED→CROSS_ROUND timeout verdict for cross-type cases so the retry loop keeps running.
  • Schema fix (validators/tx-validator.js): REVOKE_VP data schema listed reason_code and evidence_hash as required, contradicting _registry.js REVOKE_CONTRACT which marks them optional. Aligned with the canonical definition.

Implementation

commit-handler.js — Family A

Each of the 5 content-status mutator cases in _dedupCheck now looks for a sibling (any of the other 4 mutator types) already validated for the same ctid:

const sibling = validated.find(t =>
  CONTENT_STATUS_MUTATORS.includes(t.tx_type) && t.tx_type !== TX_TYPES.CONTENT_DISPUTED && t.data?.ctid === d.ctid);
if (sibling) return { valid: false, error: `content-status conflict in batch: ${sibling.tx_type} already accepted for ${d.ctid}` };

CONTENT_STATUS_MUTATORS is a module-level frozen array to keep the guard DRY across all 5 cases.

commit-handler.js — Family B

REVOKE_TYPES (already module-level) used with new _actorTipId(tx) helper to extract the acting identity's tip_id across 17 tx types. Freeze guard: if any revoke type is already validated for the same tip_id, subsequent state-changing txs (UPDATE_PROFILE, KEY_ROTATED, etc.) are rejected.

Scope

Test plan

  • Live-cluster regression: node scripts/test-inbatch-dedup.js --case all --attempts 5
    • Expected: dispute, verify, update-origin, retract, key-rotate, dispute-verify, dispute-retract, dispute-update-origin, verify-retractIN_BATCH ✅
    • Expected: revoke-update-profileCROSS_ROUND ◐ (acceptable — no FAILED)
  • Regression run result (this PR): all 9 content-status cases IN_BATCH ✅, revoke-update-profile CROSS_ROUND ◐, cluster stayed up (round ~700 at end of run)

theailab-org and others added 14 commits June 16, 2026 12:58
Adds a banner near the top of the README pointing to the published
Whitepaper at theailab.org/whitepaper, and updates the Links table
with the canonical Whitepaper URL, the PDF download URL, and the
errata channel. Also corrects the Protocol Spec link to v5.0.

The Whitepaper is the canonical public specification of the TIP
Protocol. The Protocol Spec (CTID v5.0) remains the technical
reference for endpoint contracts and message formats.

License: Whitepaper is CC BY 4.0. Implementations remain under
TIPCL-1.0 with conversion to Apache 2.0 on Jan 1, 2031.
…inks table

The TIP Protocol Whitepaper, Version 1.0 is now permanently archived
on Zenodo (operated by CERN) with a citable DOI. This README update
surfaces the DOI as a discoverable badge at the top of the page,
adds the author's ORCID iD alongside the Wikidata QID, and adds
three rows to the Links table for the Version DOI, the Concept
DOI, and the Zenodo record URL.

The full citation in the README banner now uses the canonical
Zenodo title ("Trust Identity Protocol (TIP): An Open Standard for
Verified Human Identity and Content Provenance on the Internet")
and ends with the DOI URL, so anyone clicking the citation can
resolve to the permanent record.

The DOI badge surfaces in the GitHub repository preview and in any
README mirror.
Family A (content-status mutators keyed on ctid) + Family B (revocation
freeze via _actorTipId helper), both in _dedupCheck. Targeted A+B approach
with test harness extensions in test-inbatch-dedup.js.
Four new cases: dispute-verify, dispute-retract, dispute-update-origin,
verify-retract. All show FAILED before the _dedupCheck fix. Adds
duelViaRejectionTableAny helper for cross-type rejection table probing.
…dupCheck

Add CONTENT_STATUS_MUTATORS cross-type check to the 5 content-status-
mutating tx types (CONTENT_DISPUTED, CONTENT_VERIFIED, CONTENT_RETRACTED,
UPDATE_ORIGIN, PRESCAN_REVIEW_TRIGGERED). Any two of these for the same
ctid in one batch now drop the second with 'content-status conflict in
batch' — first in canonical order wins. REVOKE_TYPES hoisted to top of
_dedupCheck for reuse in upcoming Family B block.
Adds revoke-update-profile: fires REVOKE_VP + UPDATE_PROFILE for the same
tip_id simultaneously. Shows FAILED (both commit) before the _actorTipId
freeze fix. Uses founding VP keys from genesis-data/founding-vp-keys.json.
Add module-level _actorTipId(tx) helper that extracts the acting identity's
tip_id for 14 tx types. Add pre-switch Family B block: if the actor is
being revoked in this same batch (REVOKE_* already in validated), drop
the action with 'revocation freeze' error.

Known gap: PRESCAN_REVIEW_* terminals return null from _actorTipId — reviewer
tip_id requires a DAG lookup, deferred to follow-up. Cross-round
_statefulCheck via schema.verifyTx still guards these.
…und commit

duelViaRejectionTableAny was only searching reason_detail LIKE '%ctid%'.
Cross-round rejection messages ("Cannot retract content that is under
dispute", "Cannot update origin — content status is 'disputed'") don't
embed the ctid in reason_detail; the ctid lives in the tx_data JSON.
Extending the WHERE clause to (reason_detail OR tx_data) LIKE fixes
dispute-retract and dispute-update-origin (which were showing FAILED
when the cross-round guard had correctly fired).

Also change the no-rejection-row timeout return from FAILED → CROSS_ROUND.
FAILED was designed for same-type dedup bugs; for cross-type Family A
cases the "both committed" outcome can legitimately occur cross-round
(canDispute/canRetract don't block VERIFIED status — pre-existing gap).
CROSS_ROUND lets the retry loop keep trying rather than halting early,
giving later attempts a chance to hit the same-batch sibling guard.
…tional

tx-validator.js SCHEMA listed these two fields as required for REVOKE_VP,
but the canonical authority (_registry.js REVOKE_CONTRACT buildSigningPayload)
has always declared them optional. This divergence caused the revocation
API to return 400 for valid REVOKE_VP requests that omit the optional fields,
blocking the revoke-update-profile live test case.

Align with _registry.js: keep only tip_id and issuing_vp_id as required.
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.

Cross-type same-batch state conflict: dedup is per-type, stateful checks blind to siblings (AG-7/#87 follow-up)

2 participants