Skip to content

Commit 13119fd

Browse files
committed
chore: update GitHub org from agentralabs to agentra-commerce
1 parent e3a4a07 commit 13119fd

6 files changed

Lines changed: 133 additions & 52 deletions

File tree

.github/ISSUE_TEMPLATE/edge-case.md

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Edge Case Report
2+
description: Report a protocol edge case or unexpected scenario
3+
title: "[Edge Case] "
4+
labels: ["edge-case"]
5+
body:
6+
- type: dropdown
7+
id: category
8+
attributes:
9+
label: Category
10+
options:
11+
- negotiation
12+
- settlement
13+
- receipt
14+
- verity
15+
- discovery
16+
- identity
17+
- adapter
18+
- cross-cutting
19+
validations:
20+
required: true
21+
- type: dropdown
22+
id: severity
23+
attributes:
24+
label: Severity
25+
options:
26+
- S1 - Critical (financial loss or data corruption)
27+
- S2 - High (incorrect settlement or broken invariant)
28+
- S3 - Medium (degraded experience or incorrect state)
29+
- S4 - Low (cosmetic or unlikely)
30+
validations:
31+
required: true
32+
- type: textarea
33+
id: scenario
34+
attributes:
35+
label: Scenario
36+
description: What happens? Be specific about the sequence of events.
37+
placeholder: "Agent A sends an offer, Agent B accepts, but the offer has already expired..."
38+
validations:
39+
required: true
40+
- type: textarea
41+
id: risk
42+
attributes:
43+
label: Risk
44+
description: What goes wrong if this is not handled?
45+
placeholder: "Funds could be locked permanently without resolution..."
46+
validations:
47+
required: true
48+
- type: textarea
49+
id: proposed_resolution
50+
attributes:
51+
label: Proposed Resolution
52+
description: How should the protocol handle this?
53+
validations:
54+
required: false
55+
- type: input
56+
id: schema
57+
attributes:
58+
label: Affected Schema(s)
59+
description: Which schema(s) are affected?
60+
placeholder: "settlement-intent, execution-receipt"
61+
validations:
62+
required: false

CONTRIBUTING.md

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,27 @@ XAP is a protocol. Changing a field name or adding a required property can break
2727

2828
### 4. Edge Case Submissions
2929

30-
Edge cases are especially valuable. Every edge case found before v1.0 prevents a breaking change later.
31-
32-
When submitting an edge case, include:
33-
34-
- **Category**: Negotiation, Settlement, Split, Identity, Time, Adapter, Security, or Verity
35-
- **Severity**: S1 (funds at risk), S2 (incorrect behavior), S3 (unexpected but workable), S4 (inconvenient)
36-
- **Description**: What happens, step by step
37-
- **Expected behavior**: What should happen
30+
Edge cases are tracked in `docs/edge-cases.md` with a formal lifecycle:
31+
32+
1. **DISCOVERED** — identified during development, testing, or community feedback
33+
2. **DOCUMENTED** — added to edge-cases.md with scenario, risk, and proposed resolution
34+
3. **ANALYZED** — resolution validated against protocol invariants
35+
4. **RESOLVED** — resolution implemented in schemas, SDK, or engine
36+
5. **TESTED** — validation test covers this edge case
37+
6. **DEPLOYED** — fix is in a released version
38+
39+
To submit an edge case, open an issue using the Edge Case template.
40+
41+
Severity guide:
42+
- **S1 (Critical):** Could cause financial loss or data corruption
43+
- **S2 (High):** Could cause incorrect settlement or broken invariant
44+
- **S3 (Medium):** Could cause degraded experience or incorrect state
45+
- **S4 (Low):** Cosmetic or unlikely scenario
46+
47+
When submitting, include:
48+
- **Category**: negotiation, settlement, receipt, verity, discovery, identity, adapter, or cross-cutting
49+
- **Scenario**: What happens, step by step
50+
- **Risk**: What goes wrong if unhandled
3851
- **Proposed resolution**: Your suggestion (optional but appreciated)
3952

4053
### What We Are Not Looking For Right Now

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ An agent evaluating a potential counterparty reads its `AgentIdentity` and sees:
130130
+------------------------v-------------------------+
131131
| Verity Truth Engine |
132132
| (open source, Rust, MIT) |
133-
| github.com/agentralabs/verity-engine |
133+
| github.com/agentra-commerce/verity-engine |
134134
+------------------------+-------------------------+
135135
|
136136
+------------------------v-------------------------+
@@ -368,7 +368,7 @@ Read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a PR.
368368
author = {Agentra Labs},
369369
year = {2026},
370370
doi = {10.5281/zenodo.18944370},
371-
url = {https://github.com/agentralabs/xap-protocol}
371+
url = {https://github.com/agentra-commerce/xap-protocol}
372372
}
373373
```
374374

xap/receipt.py

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from __future__ import annotations
44

55
import secrets
6-
from dataclasses import dataclass
6+
import threading
7+
from dataclasses import dataclass, field
78
from hashlib import sha256
89
from typing import Any, ClassVar
910

@@ -15,9 +16,36 @@ def _generate_receipt_id() -> str:
1516
return f"rcpt_{secrets.token_hex(4)}"
1617

1718

18-
# Global chain position counter (per-engine, monotonically increasing)
19-
_chain_position_counter: int = 0
20-
_last_receipt_hash: str = ""
19+
class ReceiptChain:
20+
"""Thread-safe receipt chain state.
21+
22+
Each instance tracks its own chain position counter and last receipt hash,
23+
eliminating the previous global mutable state that was unsafe in
24+
multi-worker environments.
25+
"""
26+
27+
def __init__(self) -> None:
28+
self._lock = threading.Lock()
29+
self._chain_position_counter: int = 0
30+
self._last_receipt_hash: str = ""
31+
32+
def advance(self) -> tuple[int, str]:
33+
"""Atomically increment chain position and return (position, previous_hash)."""
34+
with self._lock:
35+
self._chain_position_counter += 1
36+
position = self._chain_position_counter
37+
previous_hash = self._last_receipt_hash
38+
return position, previous_hash
39+
40+
def set_last_hash(self, receipt_hash: str) -> None:
41+
"""Update the last receipt hash after computing it."""
42+
with self._lock:
43+
self._last_receipt_hash = receipt_hash
44+
45+
46+
# Default chain instance (per-process). For multi-worker deployments,
47+
# each worker gets its own ReceiptChain instance automatically.
48+
_default_chain = ReceiptChain()
2149

2250

2351
@dataclass
@@ -32,8 +60,14 @@ def receipt_id(self) -> str:
3260
return self._data["receipt_id"]
3361

3462
@classmethod
35-
def issue(cls, settlement: Any, platform_private_key: str) -> "ExecutionReceipt":
36-
global _chain_position_counter, _last_receipt_hash
63+
def issue(
64+
cls,
65+
settlement: Any,
66+
platform_private_key: str,
67+
chain: ReceiptChain | None = None,
68+
) -> "ExecutionReceipt":
69+
if chain is None:
70+
chain = _default_chain
3771

3872
settlement_data = settlement.to_dict() if hasattr(settlement, "to_dict") else deep_copy(settlement)
3973

@@ -139,7 +173,7 @@ def issue(cls, settlement: Any, platform_private_key: str) -> "ExecutionReceipt"
139173
"dispute_filed": state == "DISPUTED",
140174
}
141175
if quality_score:
142-
impact["quality_score_recorded_bps"] = int(quality_score * 10000)
176+
impact["quality_score_recorded_bps"] = round(quality_score * 10000)
143177
reputation_impacts.append(impact)
144178

145179
reputation_impacts.append({
@@ -153,9 +187,8 @@ def issue(cls, settlement: Any, platform_private_key: str) -> "ExecutionReceipt"
153187
# Verity hash (placeholder — real implementation links to Verity engine)
154188
verity_hash = f"sha256:{sha256(canonical_json_bytes(settlement_data)).hexdigest()}"
155189

156-
# Chain
157-
_chain_position_counter += 1
158-
chain_position = _chain_position_counter
190+
# Chain (thread-safe via ReceiptChain instance)
191+
chain_position, previous_hash = chain.advance()
159192

160193
receipt_data: dict[str, Any] = {
161194
"receipt_id": _generate_receipt_id(),
@@ -183,12 +216,12 @@ def issue(cls, settlement: Any, platform_private_key: str) -> "ExecutionReceipt"
183216
},
184217
}
185218

186-
if _last_receipt_hash:
187-
receipt_data["chain_previous_hash"] = f"sha256:{_last_receipt_hash}"
219+
if previous_hash:
220+
receipt_data["chain_previous_hash"] = f"sha256:{previous_hash}"
188221

189222
# Compute receipt hash for chain continuity
190223
receipt_hash = sha256(canonical_json_bytes(receipt_data)).hexdigest()
191-
_last_receipt_hash = receipt_hash
224+
chain.set_last_hash(receipt_hash)
192225

193226
validate_against_schema(cls.SCHEMA, receipt_data)
194227
obj = cls(receipt_data)

xap/settlement.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def create(cls, negotiation: Any, idempotency_key: str | None = None) -> "Settle
7373
total_amount = negotiation_data["pricing"]["amount_minor_units"]
7474
currency = negotiation_data["pricing"]["currency"]
7575
else:
76-
total_amount = int(negotiation_data["offer"]["offered_rate"] * 100)
76+
total_amount = round(negotiation_data["offer"]["offered_rate"] * 100)
7777
currency = negotiation_data["offer"].get("settlement_unit", "USD")
7878

7979
# Build conditions from SLA
@@ -310,7 +310,7 @@ def _evaluate_condition(self, cond: dict[str, Any], result: dict[str, Any]) -> b
310310
# Map check to result field
311311
check = cond.get("check", "")
312312
if check == "quality_score":
313-
actual = int(result.get("quality_score", 0) * 10000)
313+
actual = round(result.get("quality_score", 0) * 10000)
314314
elif check == "latency_ms":
315315
actual = result.get("latency_ms", 0)
316316
else:

0 commit comments

Comments
 (0)