strata-agent-memory 0.2.1__tar.gz → 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. {strata_agent_memory-0.2.1/strata_agent_memory.egg-info → strata_agent_memory-0.2.2}/PKG-INFO +8 -4
  2. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/README.md +7 -3
  3. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/__init__.py +1 -1
  4. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/audit.py +7 -4
  5. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/ledger.py +2 -2
  6. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/trust.py +37 -18
  7. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/pyproject.toml +1 -1
  8. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2/strata_agent_memory.egg-info}/PKG-INFO +8 -4
  9. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_audit.py +3 -1
  10. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_ledger.py +10 -8
  11. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_trust.py +41 -3
  12. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/LICENSE +0 -0
  13. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/__main__.py +0 -0
  14. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/cli.py +0 -0
  15. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/compact.py +0 -0
  16. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/compiler.py +0 -0
  17. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/contracts.py +0 -0
  18. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/evidence.py +0 -0
  19. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/export.py +0 -0
  20. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/hooks.py +0 -0
  21. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/identity.py +0 -0
  22. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/mcp_server.py +0 -0
  23. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/memorizer.py +0 -0
  24. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/pathmap.py +0 -0
  25. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/recheck.py +0 -0
  26. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/records.py +0 -0
  27. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/relay/__init__.py +0 -0
  28. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/relay/adapters.py +0 -0
  29. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/relay/cli.py +0 -0
  30. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/relay/client.py +0 -0
  31. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/relay/core.py +0 -0
  32. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/reliability.py +0 -0
  33. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/remote.py +0 -0
  34. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/render.py +0 -0
  35. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/serve.py +0 -0
  36. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/signing.py +0 -0
  37. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/agent_memory/stats.py +0 -0
  38. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/setup.cfg +0 -0
  39. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/strata_agent_memory.egg-info/SOURCES.txt +0 -0
  40. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/strata_agent_memory.egg-info/dependency_links.txt +0 -0
  41. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/strata_agent_memory.egg-info/entry_points.txt +0 -0
  42. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/strata_agent_memory.egg-info/requires.txt +0 -0
  43. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/strata_agent_memory.egg-info/top_level.txt +0 -0
  44. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_cli.py +0 -0
  45. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_compact.py +0 -0
  46. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_compiler_render.py +0 -0
  47. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_contracts.py +0 -0
  48. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_deploy_relay.py +0 -0
  49. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_deploy_replica.py +0 -0
  50. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_deploy_tokens.py +0 -0
  51. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_evidence.py +0 -0
  52. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_hooks.py +0 -0
  53. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_identity.py +0 -0
  54. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_issue_bridge.py +0 -0
  55. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_ledger_index.py +0 -0
  56. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_memorizer.py +0 -0
  57. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_packaging.py +0 -0
  58. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_pathmap.py +0 -0
  59. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_phase2.py +0 -0
  60. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_recheck.py +0 -0
  61. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_records.py +0 -0
  62. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_redaction.py +0 -0
  63. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_relay_client.py +0 -0
  64. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_relay_codex.py +0 -0
  65. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_relay_core.py +0 -0
  66. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_reliability.py +0 -0
  67. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_remote.py +0 -0
  68. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_remote_signing.py +0 -0
  69. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_review2_fixes.py +0 -0
  70. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_review3_fixes.py +0 -0
  71. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_review_fixes.py +0 -0
  72. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_scale.py +0 -0
  73. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_serve.py +0 -0
  74. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_signing.py +0 -0
  75. {strata_agent_memory-0.2.1 → strata_agent_memory-0.2.2}/tests/test_stats.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: strata-agent-memory
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Portable, auditable, cross-vendor memory for LLM agents: evidence-bound claims, trust tiers, and compiled context views.
5
5
  Author: Strata Intelligence
6
6
  License-Expression: Apache-2.0
@@ -45,9 +45,13 @@ service, no dependencies, Python 3.10+.
45
45
  commit/file hash. The claim is rejected otherwise. A receipt is the
46
46
  filer's record; the proof is the re-run, which `verify` and the nightly
47
47
  lane perform, and the audit lists what nobody has re-run.
48
- - **Same-vendor review is not verification.** A Claude reviewer approving a
49
- Claude coder yields `self-verified`. Only a human or a different vendor
50
- lineage (Codex, Gemini, a local Qwen) promotes a claim to `verified`.
48
+ - **Self-review is not verification.** A claim reaches `verified` when a
49
+ person, or an agent that is not its author, re-runs the proof; the author's
50
+ own re-run stays `self-verified`. A fleet on one vendor gets the same rigor
51
+ as a mixed one. A ledger that wants the stronger rule sets
52
+ `cross_vendor_required`, and then only a person or another vendor's agent
53
+ (Codex, Gemini, a local Qwen) promotes; either way the audit says which
54
+ lineage verified what.
51
55
  - **Refuting a claim taints everything built on it.** Retractions walk the
52
56
  provenance graph; nothing is deleted; the audit prints the chain.
53
57
  - **Verifiers are isolated, workers fork.** A reviewer never sees the
@@ -21,9 +21,13 @@ service, no dependencies, Python 3.10+.
21
21
  commit/file hash. The claim is rejected otherwise. A receipt is the
22
22
  filer's record; the proof is the re-run, which `verify` and the nightly
23
23
  lane perform, and the audit lists what nobody has re-run.
24
- - **Same-vendor review is not verification.** A Claude reviewer approving a
25
- Claude coder yields `self-verified`. Only a human or a different vendor
26
- lineage (Codex, Gemini, a local Qwen) promotes a claim to `verified`.
24
+ - **Self-review is not verification.** A claim reaches `verified` when a
25
+ person, or an agent that is not its author, re-runs the proof; the author's
26
+ own re-run stays `self-verified`. A fleet on one vendor gets the same rigor
27
+ as a mixed one. A ledger that wants the stronger rule sets
28
+ `cross_vendor_required`, and then only a person or another vendor's agent
29
+ (Codex, Gemini, a local Qwen) promotes; either way the audit says which
30
+ lineage verified what.
27
31
  - **Refuting a claim taints everything built on it.** Retractions walk the
28
32
  provenance graph; nothing is deleted; the audit prints the chain.
29
33
  - **Verifiers are isolated, workers fork.** A reviewer never sees the
@@ -9,4 +9,4 @@ see, what did it claim, and can the proof be re-run.
9
9
  The core is dependency-free (Python 3.10+ standard library only).
10
10
  """
11
11
 
12
- __version__ = "0.2.1"
12
+ __version__ = "0.2.2"
@@ -195,8 +195,11 @@ def report(ledger, recheck: bool = False, cwd: Optional[str] = None, timeout: Op
195
195
  if backed in ("static", "attested"):
196
196
  # a clean static gate is not evidence that the thing runs
197
197
  rep["static_only_outcomes"].append({**entry, "assurance": backed})
198
- if info["tier"] == "self-verified":
199
- rep["same_vendor_only"].append({**entry, "verified_by": info["verified_by"]})
198
+ # a measure, not a tier: every claim whose only verifiers are agents of its own
199
+ # vendor, whether the ledger's policy lets that reach `verified` or not
200
+ if info["verified_by"] and not info.get("cross_vendor") and not info.get("self_verified_by_author") \
201
+ and info["tier"] in ("verified", "self-verified"):
202
+ rep["same_vendor_only"].append({**entry, "verified_by": info["verified_by"], "tier": info["tier"]})
200
203
  if info.get("self_verified_by_author"):
201
204
  rep["self_verified_by_author"].append({**entry, "verified_by": info["verified_by"]})
202
205
  if info["tier"] == "refuted":
@@ -406,8 +409,8 @@ def format_report(rep: dict) -> str:
406
409
  section("Receipts whose assurance was declared above the computed default", rep["assurance_overridden"],
407
410
  lambda r: f"{r['id']} [{r['by']}] declared {r['declared']}, computed {r['computed']}: {r['what']}"
408
411
  + (f" (cited by {', '.join(r['claims'])})" if r["claims"] else " (cited by no claim)"))
409
- section("Verified only by the same vendor (collusion risk)", rep["same_vendor_only"],
410
- lambda r: f"{r['id']} [{r['vendor']} verified by {', '.join(r['verified_by'])}] {r['statement']}")
412
+ section("Verified only by the same vendor (no person or other vendor has re-run it)", rep["same_vendor_only"],
413
+ lambda r: f"{r['id']} [{r['vendor']} verified by {', '.join(r['verified_by'])}; {r.get('tier','')}] {r['statement']}")
411
414
  section("Verified only by the claim's own author", rep["self_verified_by_author"],
412
415
  lambda r: f"{r['id']} [{r['by']}] {r['statement']}")
413
416
  section("Verified from the claim's own session under another harness (collusion risk)",
@@ -43,7 +43,7 @@ DEFAULT_CONFIG = {
43
43
  "project_aliases": [],
44
44
  "org_path": None,
45
45
  "user": None,
46
- "cross_vendor_required": True,
46
+ "cross_vendor_required": False,
47
47
  "org_include_other_projects": True,
48
48
  "budget_tokens": 6000,
49
49
  "vendor_map": {},
@@ -109,7 +109,7 @@ UNTRUSTED_POLICY = {
109
109
  "redact": [], # an attacker-supplied regex can hang capture (catastrophic backtracking)
110
110
  "path_map": [], # where a re-check runs
111
111
  "org_path": None, # what it reads and writes outside the checkout
112
- "cross_vendor_required": True, # whether same-vendor review counts as verification
112
+ "cross_vendor_required": False, # off: any agent other than the author verifies; on: another vendor or a person
113
113
  "vendor_map": {}, # which lineage a harness belongs to, which is the same question
114
114
  "signers": {}, # whose signature this machine believes
115
115
  # A tier is a preference until it is a number: stale_after_days moves claims between
@@ -2,8 +2,11 @@
2
2
 
3
3
  Tiers (highest first):
4
4
  constraint human-filed hard rule
5
- verified independently verified (human, or a different vendor lineage)
6
- self-verified verified only by the same vendor lineage as the author
5
+ verified re-run by an independent verifier: a person, or an agent that is not the
6
+ claim's author (a different identity; of another vendor when the ledger
7
+ requires that)
8
+ self-verified re-run only by the claim's own author, or -- when the ledger requires a
9
+ verifier of another vendor -- only by agents of the author's vendor
7
10
  evidenced carries receipts/bindings but nobody has re-run them
8
11
  asserted no evidence (only allowed for non-outcome claim types)
9
12
  stale TTL expired, or a re-check found the world moved (including a changed
@@ -11,11 +14,13 @@ Tiers (highest first):
11
14
  refuted retracted, or a verification refuted it
12
15
  tainted derived (transitively) from a refuted claim
13
16
 
14
- The cross-vendor rule is the structural answer to collusive validation: a
15
- Claude reviewer approving a Claude coder's claim yields self-verified, not
16
- verified, when ``cross_vendor_required`` is on. A verification filed by the
17
- claim's own author (same harness, user and session, or the same person) never
18
- counts as independent, whatever the lineage rule says.
17
+ Independence is a different identity, not a different vendor. A verification
18
+ filed by the claim's own author (same harness, user and session, or the same
19
+ person) never counts as independent, whatever the ledger's policy says. A ledger
20
+ that wants the stronger rule sets ``cross_vendor_required`` (off by default since
21
+ 0.2.2): then a same-vendor verifier yields self-verified and only a person or
22
+ another vendor's agent reaches verified. Either way the audit reports which
23
+ lineage verified what, so a single-vendor fleet sees its own mix.
19
24
  """
20
25
  from __future__ import annotations
21
26
 
@@ -56,6 +61,17 @@ def lineage_differs(a: dict, b: dict) -> bool:
56
61
  return (a.get("vendor") or "unknown") != (b.get("vendor") or "unknown")
57
62
 
58
63
 
64
+ def same_identity(a: dict, b: dict) -> bool:
65
+ """True when two author blocks are the same identity: same kind, harness and user.
66
+
67
+ The session is not part of it. A session is a label the client declares (a header on
68
+ the served path, a fresh id per initialize on the tool-server path), so an identity
69
+ that verified its own claim from a second session is still the claim's own author;
70
+ were the session counted, a coder could reach `verified` alone (review of PR #57).
71
+ """
72
+ return (a.get("kind"), a.get("harness"), a.get("user")) == (b.get("kind"), b.get("harness"), b.get("user"))
73
+
74
+
59
75
  def same_author(a: dict, b: dict) -> bool:
60
76
  """True when two author blocks are the same identity: same kind, harness and user,
61
77
  and the same session whenever both sessions are known."""
@@ -227,7 +243,7 @@ def assess(ledger, config: Optional[dict] = None, now: Optional[str] = None,
227
243
  Tiers are never changed by a statistic: the boundary moves, the rules do not.
228
244
  """
229
245
  config = config or ledger.config
230
- cross_required = bool(config.get("cross_vendor_required", True))
246
+ cross_required = bool(config.get("cross_vendor_required", False))
231
247
  # a number config.json proposes and this code adds to a timestamp: read it through the
232
248
  # one accessor that bounds it and names the file when it is malformed (review PE-11)
233
249
  from .ledger import stale_after_days as _stale_after_days # local: ledger imports trust's callers
@@ -279,21 +295,24 @@ def assess(ledger, config: Optional[dict] = None, now: Optional[str] = None,
279
295
  info["verdict"] = "verified"
280
296
  verifiers = [v for v in verifications if _body(v).get("verdict") == "verified"]
281
297
  info["verified_by"] = sorted({f"{v['author'].get('harness')}:{v['author'].get('vendor')}" for v in verifiers})
282
- # a claim's own author never counts as an independent check of it
283
- independent = any(lineage_differs(author, v["author"]) and not same_author(author, v["author"])
284
- for v in verifiers)
285
- own_author_only = bool(verifiers) and all(same_author(author, v["author"]) for v in verifiers)
286
- info["cross_vendor"] = independent
298
+ # a claim's own identity never counts as an independent check of it, from any
299
+ # session and whatever the policy says; the policy decides whether another
300
+ # identity of the same vendor does
301
+ others = [v for v in verifiers if not same_identity(author, v["author"])]
302
+ cross_vendor = any(lineage_differs(author, v["author"]) for v in others)
303
+ own_author_only = bool(verifiers) and not others
304
+ independent = cross_vendor or (bool(others) and not cross_required)
305
+ info["cross_vendor"] = cross_vendor
287
306
  info["self_verified_by_author"] = own_author_only
288
- info["tier"] = "verified" if (independent or not cross_required) else "self-verified"
307
+ info["tier"] = "verified" if independent else "self-verified"
289
308
  if own_author_only:
290
309
  info["reasons"].append("verified by the claim's own author")
310
+ elif cross_vendor:
311
+ info["reasons"].append("independently verified (another lineage)")
291
312
  elif independent:
292
- info["reasons"].append("independently verified")
293
- elif not cross_required:
294
- info["reasons"].append("verified (cross-vendor rule off)")
313
+ info["reasons"].append("independently verified (another agent of the same vendor)")
295
314
  else:
296
- info["reasons"].append("verified only by the same vendor lineage as the author")
315
+ info["reasons"].append("verified only by the same vendor lineage as the author; this ledger requires another vendor or a person")
297
316
  elif body.get("type") == "constraint" and author.get("kind") == "human":
298
317
  info["tier"] = "constraint"
299
318
  info["reasons"].append("human-filed constraint")
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "strata-agent-memory"
7
- version = "0.2.1"
7
+ version = "0.2.2"
8
8
  description = "Portable, auditable, cross-vendor memory for LLM agents: evidence-bound claims, trust tiers, and compiled context views."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: strata-agent-memory
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Portable, auditable, cross-vendor memory for LLM agents: evidence-bound claims, trust tiers, and compiled context views.
5
5
  Author: Strata Intelligence
6
6
  License-Expression: Apache-2.0
@@ -45,9 +45,13 @@ service, no dependencies, Python 3.10+.
45
45
  commit/file hash. The claim is rejected otherwise. A receipt is the
46
46
  filer's record; the proof is the re-run, which `verify` and the nightly
47
47
  lane perform, and the audit lists what nobody has re-run.
48
- - **Same-vendor review is not verification.** A Claude reviewer approving a
49
- Claude coder yields `self-verified`. Only a human or a different vendor
50
- lineage (Codex, Gemini, a local Qwen) promotes a claim to `verified`.
48
+ - **Self-review is not verification.** A claim reaches `verified` when a
49
+ person, or an agent that is not its author, re-runs the proof; the author's
50
+ own re-run stays `self-verified`. A fleet on one vendor gets the same rigor
51
+ as a mixed one. A ledger that wants the stronger rule sets
52
+ `cross_vendor_required`, and then only a person or another vendor's agent
53
+ (Codex, Gemini, a local Qwen) promotes; either way the audit says which
54
+ lineage verified what.
51
55
  - **Refuting a claim taints everything built on it.** Retractions walk the
52
56
  provenance graph; nothing is deleted; the audit prints the chain.
53
57
  - **Verifiers are isolated, workers fork.** A reviewer never sees the
@@ -28,7 +28,8 @@ def test_report_unrechecked_outcomes(ledger, claude_agent, file_evidence):
28
28
 
29
29
 
30
30
  def test_report_same_vendor_only(ledger, claude_agent, codex_agent):
31
- """report() lists self-verified claims."""
31
+ """report() lists claims verified only by the author's own vendor, whatever tier the
32
+ ledger's policy gave them (a measure, not a tier: `verified` under the default)."""
32
33
  scope = make_scope()
33
34
 
34
35
  body = {"type": "fact", "statement": "test", "source": "test"}
@@ -50,6 +51,7 @@ def test_report_same_vendor_only(ledger, claude_agent, codex_agent):
50
51
 
51
52
  assert len(rep["same_vendor_only"]) == 1
52
53
  assert rep["same_vendor_only"][0]["id"] == claim["id"]
54
+ assert rep["same_vendor_only"][0]["tier"] == "verified"
53
55
 
54
56
 
55
57
  def test_report_taint_chains(ledger, claude_agent):
@@ -782,11 +782,13 @@ def test_a_cloned_config_does_not_choose_what_this_machine_runs(tmp_path, claude
782
782
  assert marker.exists() # trusted: the lane re-runs it
783
783
 
784
784
 
785
- def test_a_cloned_config_cannot_switch_off_the_cross_vendor_rule(tmp_path, claude_agent,
786
- machine_home, capsys):
787
- """F-6: one line in a committed file turned same-vendor review into `verified` for
788
- everyone who cloned, and the audit section written to catch that reported zero."""
789
- clone = _cloned_repo(tmp_path, {"cross_vendor_required": False})
785
+ def test_a_cloned_config_cannot_switch_the_cross_vendor_rule(tmp_path, claude_agent,
786
+ machine_home, capsys):
787
+ """F-6: one line in a committed file changed what `verified` meant for everyone who
788
+ cloned. The policy key is a proposal until the repository is trusted: with the
789
+ stronger rule committed, an untrusted clone still applies the default (off) and a
790
+ same-vendor verifier reaches `verified`; trusting the clone makes it `self-verified`."""
791
+ clone = _cloned_repo(tmp_path, {"cross_vendor_required": True})
790
792
  ledger = Ledger.open(clone)
791
793
  claim = ledger.append(new_record("claim", claude_agent, make_scope(),
792
794
  {"type": "fact", "statement": "the toolchain bootstrap succeeded",
@@ -797,8 +799,8 @@ def test_a_cloned_config_cannot_switch_off_the_cross_vendor_rule(tmp_path, claud
797
799
  "results": [{"outcome": "match"}]}))
798
800
 
799
801
  info = assess(ledger)[claim["id"]]
800
- assert info["tier"] == "self-verified"
801
- assert "verified only by the same vendor lineage as the author" in info["reasons"]
802
+ assert info["tier"] == "verified" # the committed `true` is a proposal: default applies
803
+ assert "another agent of the same vendor" in " ".join(info["reasons"])
802
804
  rep = audit_report(ledger)
803
805
  assert rep["trusted"] is False
804
806
  assert [r["id"] for r in rep["same_vendor_only"]] == [claim["id"]]
@@ -806,7 +808,7 @@ def test_a_cloned_config_cannot_switch_off_the_cross_vendor_rule(tmp_path, claud
806
808
 
807
809
  ledger_mod.trust_repo(clone)
808
810
  trusted = Ledger.open(clone)
809
- assert assess(trusted)[claim["id"]]["tier"] == "verified"
811
+ assert assess(trusted)[claim["id"]]["tier"] == "self-verified" # now the stronger rule holds
810
812
  assert audit_report(trusted)["trusted"] is True
811
813
 
812
814
 
@@ -37,8 +37,9 @@ def test_asserted_claim_tier_asserted(ledger, claude_agent):
37
37
  assert assessment[rec["id"]]["tier"] == "asserted"
38
38
 
39
39
 
40
- def test_verification_by_same_vendor_self_verified(ledger, claude_agent, codex_agent):
41
- """Verification by same vendor yields 'self-verified'."""
40
+ def test_verification_by_another_agent_of_the_same_vendor_is_verified_by_default(ledger, claude_agent, codex_agent):
41
+ """Independence is a different identity, not a different vendor (the operator's
42
+ decision, 2026-09-24): by default another agent of the author's own vendor verifies."""
42
43
  scope = make_scope()
43
44
 
44
45
  # Create claim by Claude
@@ -59,7 +60,44 @@ def test_verification_by_same_vendor_self_verified(ledger, claude_agent, codex_a
59
60
  ledger.append(ver_rec)
60
61
 
61
62
  assessment = assess(ledger)
62
- assert assessment[claim["id"]]["tier"] == "self-verified"
63
+ assert assessment[claim["id"]]["tier"] == "verified"
64
+ assert assessment[claim["id"]]["cross_vendor"] is False
65
+ assert "another agent of the same vendor" in " ".join(assessment[claim["id"]]["reasons"])
66
+ # the stronger policy, per ledger: only a person or another vendor reaches verified
67
+ strict = assess(ledger, config={"cross_vendor_required": True})
68
+ assert strict[claim["id"]]["tier"] == "self-verified"
69
+ assert "requires another vendor or a person" in " ".join(strict[claim["id"]]["reasons"])
70
+
71
+
72
+ def test_the_same_identity_from_another_session_is_still_the_own_author(ledger, claude_agent):
73
+ """A session is client-declared; a coder re-launched, or a second header value, is the
74
+ same identity and never an independent verifier (review of PR #57, 2026-09-24)."""
75
+ scope = make_scope()
76
+ claim = ledger.append(new_record("claim", claude_agent, scope,
77
+ {"type": "fact", "statement": "test", "source": "test"}))
78
+ second_session = dict(claude_agent, session="a-second-visit")
79
+ ledger.append(new_record("verification", second_session, scope,
80
+ {"claim": claim["id"], "verdict": "verified", "method": "rerun",
81
+ "results": [{"outcome": "match"}]}))
82
+ for config in ({"cross_vendor_required": False}, {"cross_vendor_required": True}):
83
+ info = assess(ledger, config=config)[claim["id"]]
84
+ assert info["tier"] == "self-verified", config
85
+ assert info["self_verified_by_author"] is True
86
+
87
+
88
+ def test_the_claims_own_author_never_verifies_it_whatever_the_policy(ledger, claude_agent):
89
+ """With the rule off the own author's re-run used to reach `verified`; it never does."""
90
+ scope = make_scope()
91
+ claim = ledger.append(new_record("claim", claude_agent, scope,
92
+ {"type": "fact", "statement": "test", "source": "test"}))
93
+ ledger.append(new_record("verification", claude_agent, scope,
94
+ {"claim": claim["id"], "verdict": "verified", "method": "rerun",
95
+ "results": [{"outcome": "match"}]}))
96
+ for config in ({"cross_vendor_required": False}, {"cross_vendor_required": True}, None):
97
+ info = assess(ledger, config=config)[claim["id"]]
98
+ assert info["tier"] == "self-verified", config
99
+ assert info["self_verified_by_author"] is True
100
+ assert "own author" in " ".join(info["reasons"])
63
101
 
64
102
 
65
103
  def test_verification_by_different_vendor_verified(ledger, claude_agent, codex_agent):