strata-agent-memory 0.2.6__tar.gz → 0.2.7__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.
- {strata_agent_memory-0.2.6/strata_agent_memory.egg-info → strata_agent_memory-0.2.7}/PKG-INFO +1 -1
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/__init__.py +1 -1
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/cli.py +34 -10
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/contracts.py +150 -6
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/evidence.py +10 -2
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/lane.py +255 -37
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/ledger.py +5 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/mcp_server.py +3 -2
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/recheck.py +88 -5
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/records.py +20 -2
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/remote.py +27 -1
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/serve.py +212 -1
- strata_agent_memory-0.2.7/deploy/lib/served-ledger.sh +58 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/README.md +6 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/seed-board.sh +3 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/seed-topics.sh +3 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/upgrade-driver.sh +11 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/pyproject.toml +5 -2
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7/strata_agent_memory.egg-info}/PKG-INFO +1 -1
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/strata_agent_memory.egg-info/SOURCES.txt +10 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_assessment_67b26eae.py +11 -0
- strata_agent_memory-0.2.7/tests/test_contract_amend_649e0b45.py +354 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_deploy_mac.py +3 -1
- strata_agent_memory-0.2.7/tests/test_install_lane_cb430a30.py +335 -0
- strata_agent_memory-0.2.7/tests/test_lane_derived_roots_817ab446.py +263 -0
- strata_agent_memory-0.2.7/tests/test_lane_nightly_84b22b63.py +203 -0
- strata_agent_memory-0.2.7/tests/test_lane_stop_63b7cf8b.py +159 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_ledger.py +1 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_packaging.py +3 -1
- strata_agent_memory-0.2.7/tests/test_recheck_away_207af145.py +276 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_recheck_sandbox_543e48a7.py +16 -4
- strata_agent_memory-0.2.7/tests/test_recheck_sandbox_nits_543e48a7.py +73 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_relay_release_gate_b3b3e487.py +2 -2
- strata_agent_memory-0.2.7/tests/test_rollback_78710ebf.py +510 -0
- strata_agent_memory-0.2.7/tests/test_served_ledger_6e8e49f3.py +261 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/LICENSE +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/README.md +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/__main__.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/approvals.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/audit.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/compact.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/compiler.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/export.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/hooks.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/identity.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/instances.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/kit.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/measure.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/memorizer.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/pathmap.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/relay/__init__.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/relay/adapters.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/relay/cli.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/relay/client.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/relay/core.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/reliability.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/render.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/roster.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/signing.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/stats.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/supersession.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/trust.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/agent_memory/wake.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/agent-keys.sh +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/agent-session.sh +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/backup-tokens.sh +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/install-hooks-mac.sh +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/install-relay-driver.sh +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/install-replica-driver.sh +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/install-runner-driver.sh +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/instances.example.json +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/onboard-agent.sh +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/relay-agent.sh +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/mac/seed-mail-context.sh +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/.claude-plugin/marketplace.json +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/README.md +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/claude-code/.claude-plugin/plugin.json +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/claude-code/bin/agent-memory-approvals +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/claude-code/bin/agent-memory-wake +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/claude-code/hooks/hooks.json +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/claude-code/monitors/monitors.json +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/claude-code/rules/approvals.md +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/claude-code/skills/approvals/SKILL.md +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/claude-code/skills/approve/SKILL.md +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/claude-code/skills/inbox/SKILL.md +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/claude-code/skills/reject/SKILL.md +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/codex/skills/agent-memory-answer/SKILL.md +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/codex/skills/agent-memory-answer/agents/openai.yaml +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/codex/skills/agent-memory-approvals/SKILL.md +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/codex/skills/agent-memory-approvals/agents/openai.yaml +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/codex/skills/agent-memory-approvals/rules/approvals.md +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/plugins/codex/skills/agent-memory-approvals/scripts/agent-memory-approvals +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/relay/README.md +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/relay/agent-memory-relay@.service +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/relay/install-relay-mac.sh +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/relay/install-relay.sh +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/relay/macos/com.strata.agent-memory.relay.plist.template +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/deploy/relay/relay-launch.sh +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/setup.cfg +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/strata_agent_memory.egg-info/dependency_links.txt +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/strata_agent_memory.egg-info/entry_points.txt +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/strata_agent_memory.egg-info/requires.txt +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/strata_agent_memory.egg-info/top_level.txt +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_audit.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_audit_lane_numbers_ea89ba26.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_audit_measurement_13190a20.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_backlog_check.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_changelog_fragments.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_changelog_guard_shallow_57a2f78e.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_ci_docs_only_353ce010.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_cli.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_compact.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_compiler.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_compiler_render.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_config.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_contracts.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_deploy_relay.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_deploy_replica.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_deploy_tokens.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_evidence.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_find_root_b4b5019b.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_hooks.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_identity.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_issue_bridge.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_lane_63b7cf8b.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_lane_priority_779d51ba.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_ledger_index.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_live_wake_c217c336.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_local_lanes_sibling_1411a2b2.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_memorizer.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_outcome_commit_binding_b1de564d.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_outcome_needs_evidence_b1de564d.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_pathmap.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_phase2.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_plugins.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_receipt_reports_274e6199.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_recheck.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_recheck_remote_8185d2d3.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_records.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_redaction.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_relay_adapters.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_relay_client.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_relay_codex.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_relay_core.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_relay_instance_2a2e3887.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_relay_release_526f5261.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_reliability.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_remote.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_remote_signing.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_review2_fixes.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_review3_fixes.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_review_fixes.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_scale.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_serve.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_signing.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_sit_fleet_1b24b0b1.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_sit_fleet_ci_0d9b76a2.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_sit_var_bindings_e802ca84.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_stats.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_supersedes_61416280.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_trust.py +0 -0
- {strata_agent_memory-0.2.6 → strata_agent_memory-0.2.7}/tests/test_uv_lock_env_274e6199.py +0 -0
{strata_agent_memory-0.2.6/strata_agent_memory.egg-info → strata_agent_memory-0.2.7}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: strata-agent-memory
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
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
|
|
@@ -606,9 +606,10 @@ def _private_contract_refusal(args) -> Optional[int]:
|
|
|
606
606
|
|
|
607
607
|
|
|
608
608
|
def _acceptance_checks(args) -> List[dict]:
|
|
609
|
-
"""The acceptance checks a `contract create` asks for,
|
|
610
|
-
|
|
611
|
-
|
|
609
|
+
"""The acceptance checks a `contract create` asks for, or a `contract update` amends the
|
|
610
|
+
list to (ctr_649e0b459a758929), in the order the flags read them. The remote lane files
|
|
611
|
+
the same record as the local one, so both build the list here: a check that got lost on
|
|
612
|
+
the way is a contract nobody can prove."""
|
|
612
613
|
acceptance = [{"kind": "command", "cmd": c, "expect_exit": 0} for c in args.check or []]
|
|
613
614
|
acceptance += [{"kind": "human", "statement": s} for s in args.human_check or []]
|
|
614
615
|
acceptance += [{"kind": "file", "path": f} for f in args.file_check or []]
|
|
@@ -637,8 +638,13 @@ def cmd_contract(args) -> int:
|
|
|
637
638
|
_print(out)
|
|
638
639
|
return 0 if signed else 3
|
|
639
640
|
if args.action == "update":
|
|
640
|
-
|
|
641
|
-
|
|
641
|
+
try:
|
|
642
|
+
rec = contracts.update(ledger, author, scope, args.id, args.status, note=args.note, claims=args.claims or (),
|
|
643
|
+
assigned_to=getattr(args, "assign", None),
|
|
644
|
+
acceptance_checks=_acceptance_checks(args) or None)
|
|
645
|
+
except ValidationError as exc:
|
|
646
|
+
print(f"rejected: {exc}", file=sys.stderr)
|
|
647
|
+
return 3
|
|
642
648
|
out = {"id": rec["id"], "status": args.status}
|
|
643
649
|
signed = _sign_after(args, ledger, author, scope, rec, out)
|
|
644
650
|
_print(out)
|
|
@@ -1479,7 +1485,10 @@ def _remote_recheck(args, client) -> int:
|
|
|
1479
1485
|
max=args.max, cwd=root, timeout=args.timeout, path_map=path_map,
|
|
1480
1486
|
dry_run=args.dry_run, min_age_days=args.min_age_days, force=args.force,
|
|
1481
1487
|
assessment=view.assessment, file=file,
|
|
1482
|
-
policy=_sandbox_policy(args, _local_sandbox(view.config, root))
|
|
1488
|
+
policy=_sandbox_policy(args, _local_sandbox(view.config, root)),
|
|
1489
|
+
# a re-run away from the outcome's bound commit refutes nothing
|
|
1490
|
+
# (the lane's rule, ctr_207af145a59463ed)
|
|
1491
|
+
away_is_error=True)
|
|
1483
1492
|
except ValueError as exc:
|
|
1484
1493
|
print(f"recheck: {exc}", file=sys.stderr)
|
|
1485
1494
|
return 2
|
|
@@ -1679,7 +1688,8 @@ def _remote_contract_update(args, client) -> int:
|
|
|
1679
1688
|
return 2
|
|
1680
1689
|
claims = list(args.claims or [])
|
|
1681
1690
|
body = contracts.update_body(args.id, args.status, note=args.note, claims=claims,
|
|
1682
|
-
assigned_to=getattr(args, "assign", None)
|
|
1691
|
+
assigned_to=getattr(args, "assign", None),
|
|
1692
|
+
acceptance_checks=_acceptance_checks(args) or None)
|
|
1683
1693
|
payload = {"kind": "contract_update", "body": body, "derived_from": [args.id, *claims]}
|
|
1684
1694
|
rec, _ = client.post_record(_scoped_payload(payload, _remote_scope(args, client)))
|
|
1685
1695
|
out = {"id": rec["id"], "status": rec["body"]["status"]}
|
|
@@ -1704,9 +1714,16 @@ def _remote_contract_accept(args, client) -> int:
|
|
|
1704
1714
|
if local is None:
|
|
1705
1715
|
return 2
|
|
1706
1716
|
served = _ServedLedger(client, local)
|
|
1717
|
+
# read once, before anything runs: the checks the replica says the contract answers to
|
|
1718
|
+
# now, an amendment's included (the served view holds no contract_updates to find one
|
|
1719
|
+
# in, ctr_649e0b459a758929), and the status the run reports it started under, which
|
|
1720
|
+
# ``contracts.run_acceptance`` reads before its checks run too (PROTOCOL 4.1)
|
|
1721
|
+
started = client.contract_status(args.id)
|
|
1722
|
+
checks = started.get("acceptance")
|
|
1707
1723
|
results = contracts.run_acceptance(served, args.id, cwd=args.cwd or str(local.root),
|
|
1708
1724
|
timeout=args.timeout, file=False,
|
|
1709
|
-
policy=_sandbox_policy(args, served.config)
|
|
1725
|
+
policy=_sandbox_policy(args, served.config),
|
|
1726
|
+
checks=checks if isinstance(checks, list) else None)
|
|
1710
1727
|
# asked once and used for the receipts and the run alike: a --scope this client typed
|
|
1711
1728
|
# has to reach the server the way `claim` and `retract` send it, and a project scope
|
|
1712
1729
|
# is still the server's own to stamp
|
|
@@ -1716,7 +1733,7 @@ def _remote_contract_accept(args, client) -> int:
|
|
|
1716
1733
|
if body and body.get("kind") in ("command", "test"):
|
|
1717
1734
|
rec, _ = client.post_record(_scoped_payload({"kind": "evidence", "body": body}, scope))
|
|
1718
1735
|
r["receipt"] = rec["id"]
|
|
1719
|
-
status =
|
|
1736
|
+
status = started["status"] # an acceptance run reports, it does not promote
|
|
1720
1737
|
client.post_record(_scoped_payload({"kind": "contract_update", "body": {
|
|
1721
1738
|
"contract": args.id, "status": status,
|
|
1722
1739
|
"acceptance": [{"check": r["check"], "outcome": r["outcome"], "detail": r["detail"],
|
|
@@ -2349,6 +2366,12 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2349
2366
|
c.add_argument("--note")
|
|
2350
2367
|
c.add_argument("--claims", nargs="*")
|
|
2351
2368
|
c.add_argument("--assign", help="hand the contract to this agent (the board follows the latest assignee)")
|
|
2369
|
+
# an amendment of the acceptance checks: the whole list, as `contract create` takes it
|
|
2370
|
+
# (ctr_649e0b459a758929); refused under the contract's assignee's own key
|
|
2371
|
+
c.add_argument("--check", action="append", help="amend the acceptance: a command expecting exit 0 (repeatable)")
|
|
2372
|
+
c.add_argument("--human-check", action="append", help="amend the acceptance: a human sign-off (repeatable)")
|
|
2373
|
+
c.add_argument("--file-check", action="append", help="amend the acceptance: a file that must exist (repeatable)")
|
|
2374
|
+
c.add_argument("--acceptance-json", help="amend the acceptance: a JSON list of checks, replacing the contract's")
|
|
2352
2375
|
_add_identity(c)
|
|
2353
2376
|
_add_signing(c)
|
|
2354
2377
|
c = cs.add_parser("from-issue", help="create a contract from a GitHub issue (needs the gh CLI)")
|
|
@@ -2419,7 +2442,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
2419
2442
|
"verifications under the running identity; with --remote, the "
|
|
2420
2443
|
"replica's claims, re-run here under the replica's re-check policy")
|
|
2421
2444
|
s.add_argument("--only", choices=list(recheck_mod.ONLY_CHOICES), default="unverified",
|
|
2422
|
-
help="unverified (tier evidenced
|
|
2445
|
+
help="unverified (tier evidenced or self-verified, whatever inconclusives it carries; the "
|
|
2446
|
+
"default), stale, or all (everything but refuted/tainted)")
|
|
2423
2447
|
s.add_argument("--type", action="append",
|
|
2424
2448
|
help="restrict to these claim types, comma-separated (e.g. outcome,fact)")
|
|
2425
2449
|
s.add_argument("--max", type=int, help="stop after this many claims")
|
|
@@ -181,6 +181,116 @@ def _linked_claims(ledger, contract_id: str, extra: Iterable[str] = ()) -> List[
|
|
|
181
181
|
return out
|
|
182
182
|
|
|
183
183
|
|
|
184
|
+
def _assignee_of(body: dict) -> Optional[str]:
|
|
185
|
+
value = (body or {}).get("assigned_to")
|
|
186
|
+
return value.strip().lower() if isinstance(value, str) and value.strip() else None
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def assignee_now(ledger, contract_id: str) -> Optional[str]:
|
|
190
|
+
"""Who holds the contract: the latest update that names an assignee, else the one it
|
|
191
|
+
was filed with (what ``status_of`` reports as ``assigned_to``)."""
|
|
192
|
+
rec = ledger.get(contract_id)
|
|
193
|
+
held = _assignee_of((rec or {}).get("body") or {})
|
|
194
|
+
for u in sorted(ledger.contract_updates(contract_id), key=sort_key):
|
|
195
|
+
held = _assignee_of(u["body"]) or held
|
|
196
|
+
return held
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _may_amend(config: dict, author: dict) -> bool:
|
|
200
|
+
"""A person, or the ledger's PM (config.pm): an agent's standing to amend a bar would
|
|
201
|
+
rest on an assignment any token can file, so no other agent has it."""
|
|
202
|
+
return author.get("kind") == "human" or is_pm(config, author)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def _names_of(author: dict) -> set:
|
|
206
|
+
"""The names that mean ``author`` when a contract is assigned: its user id and the local
|
|
207
|
+
part the relay addresses it by, as a reader is matched (``compiler._reader_names``,
|
|
208
|
+
PROTOCOL section 11). Every holder test here goes through it (the review of #148)."""
|
|
209
|
+
from .compiler import _reader_names # local: compiler imports this module
|
|
210
|
+
return _reader_names(author)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _has_note(body: dict) -> bool:
|
|
214
|
+
note = body.get("note")
|
|
215
|
+
return isinstance(note, str) and bool(note.strip())
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def amendment_refusal(ledger, author: dict, contract_id: str, assigned_to: Optional[str] = None,
|
|
219
|
+
note: Optional[str] = None) -> Optional[str]:
|
|
220
|
+
"""Why ``author`` may not amend the contract's acceptance checks, or None
|
|
221
|
+
(ctr_649e0b459a758929). The worker never writes their own bar, so only a person or
|
|
222
|
+
the PM amends one. Reassignment is open to any token, so "anybody but the assignee"
|
|
223
|
+
would let the assignee reassign the contract away and amend it in the next update,
|
|
224
|
+
and would leave an unassigned contract to whoever wants it (the review of this
|
|
225
|
+
story). The PM does not amend a contract it holds, or one the same update hands to
|
|
226
|
+
it; a person may, as a person may reopen a closed one: the bar is the person's to set
|
|
227
|
+
(the PE's ruling). An amendment is a decision on the record, so it carries a note.
|
|
228
|
+
A contract names its holder by user id or by local part, and both mean the author."""
|
|
229
|
+
names = _names_of(author)
|
|
230
|
+
if not _may_amend(getattr(ledger, "config", None) or {}, author):
|
|
231
|
+
return (f"{author.get('user') or 'this agent'} may not amend contract {contract_id}'s acceptance checks: "
|
|
232
|
+
f"an amendment is a person's or the PM's (config.pm), since the worker never writes their own bar")
|
|
233
|
+
if not (note or "").strip():
|
|
234
|
+
return (f"an amendment of contract {contract_id}'s acceptance checks is a decision on the record: "
|
|
235
|
+
f"say why in --note")
|
|
236
|
+
if author.get("kind") == "human":
|
|
237
|
+
return None
|
|
238
|
+
holder = assignee_now(ledger, contract_id)
|
|
239
|
+
if holder and holder in names:
|
|
240
|
+
return (f"contract {contract_id} is assigned to {holder}, and the assignee never amends its own "
|
|
241
|
+
f"acceptance checks: the PM or somebody else does")
|
|
242
|
+
handed = _assignee_of({"assigned_to": assigned_to})
|
|
243
|
+
if handed and handed in names:
|
|
244
|
+
return (f"an amendment of contract {contract_id}'s acceptance checks may not assign the contract to its "
|
|
245
|
+
f"own author ({handed})")
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def amendments(ledger, contract_id: str) -> List[dict]:
|
|
250
|
+
"""The contract_updates that amend the contract's acceptance checks, oldest first.
|
|
251
|
+
|
|
252
|
+
Refused where it is filed, and not counted where it is read, by the same rules: an
|
|
253
|
+
amendment by anybody but a person or the PM, or with no note, or whose agent author
|
|
254
|
+
held the contract when it was filed (by user id or local part), or which handed it to
|
|
255
|
+
that author, changes nothing (one written by hand, or through a replica that did not
|
|
256
|
+
know the key). The history is folded in ``sort_key`` order, so every machine counts
|
|
257
|
+
the same ones."""
|
|
258
|
+
config = getattr(ledger, "config", None) or {}
|
|
259
|
+
rec = ledger.get(contract_id)
|
|
260
|
+
held = _assignee_of((rec or {}).get("body") or {})
|
|
261
|
+
counted = []
|
|
262
|
+
for u in sorted(ledger.contract_updates(contract_id), key=sort_key):
|
|
263
|
+
body = u.get("body") or {}
|
|
264
|
+
if "acceptance_checks" in body and "acceptance" not in body:
|
|
265
|
+
author = u.get("author") or {}
|
|
266
|
+
names = _names_of(author)
|
|
267
|
+
handed = _assignee_of(body)
|
|
268
|
+
held_by_author = author.get("kind") != "human" and bool(
|
|
269
|
+
(held and held in names) or (handed and handed in names))
|
|
270
|
+
if _may_amend(config, author) and _has_note(body) and not held_by_author:
|
|
271
|
+
counted.append(u)
|
|
272
|
+
held = _assignee_of(body) or held
|
|
273
|
+
return counted
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def acceptance_of(ledger, contract_id: str) -> tuple:
|
|
277
|
+
"""(the acceptance checks the contract answers to, the amendment that set them or
|
|
278
|
+
None): the latest counted amendment's list, else the one the contract was filed with.
|
|
279
|
+
Every reader and runner of the checks goes through here (ctr_649e0b459a758929)."""
|
|
280
|
+
rec = ledger.get(contract_id)
|
|
281
|
+
if rec is None or rec.get("kind") != "contract":
|
|
282
|
+
raise KeyError(f"no contract {contract_id}")
|
|
283
|
+
if not hasattr(ledger, "contract_updates"):
|
|
284
|
+
# a view that holds no contract_updates cannot see an amendment, and running the list
|
|
285
|
+
# the contract was filed with would be the very slip this reads past: a caller with
|
|
286
|
+
# such a view passes the checks the replica reports (``run_acceptance(checks=)``)
|
|
287
|
+
raise TypeError(f"the acceptance of {contract_id} needs a ledger that holds its contract_updates")
|
|
288
|
+
latest = amendments(ledger, contract_id)
|
|
289
|
+
if latest:
|
|
290
|
+
return latest[-1]["body"]["acceptance_checks"], latest[-1]["id"]
|
|
291
|
+
return rec["body"]["acceptance"], None
|
|
292
|
+
|
|
293
|
+
|
|
184
294
|
def acceptance_runs(ledger, contract_id: str) -> List[dict]:
|
|
185
295
|
"""Acceptance-run contract_updates for a contract, oldest first."""
|
|
186
296
|
runs = [u for u in ledger.contract_updates(contract_id) if u["body"].get("acceptance")]
|
|
@@ -270,6 +380,13 @@ def verification_basis(ledger, contract_id: str, assessment=None) -> dict:
|
|
|
270
380
|
return out
|
|
271
381
|
latest = runs[-1]
|
|
272
382
|
out["run"] = latest["id"]
|
|
383
|
+
amended = amendments(ledger, contract_id)
|
|
384
|
+
if amended and sort_key(amended[-1]) > sort_key(latest):
|
|
385
|
+
# the run asked the checks the contract answered to then, not the ones it answers to
|
|
386
|
+
# now (ctr_649e0b459a758929)
|
|
387
|
+
out["reason"] = (f"the acceptance was amended by {amended[-1]['id']} after the latest run; "
|
|
388
|
+
f"run `agent-memory contract accept {contract_id}`")
|
|
389
|
+
return out
|
|
273
390
|
if not _run_ok(latest):
|
|
274
391
|
failing = [r.get("check", {}).get("cmd") or r.get("check", {}).get("path") or r.get("check", {}).get("kind")
|
|
275
392
|
for r in latest["body"]["acceptance"] if r.get("outcome") not in ("match", "skipped")]
|
|
@@ -452,6 +569,11 @@ def transition_refusal(ledger, rec: dict, assessment=None) -> Optional[str]:
|
|
|
452
569
|
note=note if isinstance(note, str) else None, assessment=assessment)
|
|
453
570
|
except ValidationError as exc:
|
|
454
571
|
return str(exc)
|
|
572
|
+
if "acceptance_checks" in body:
|
|
573
|
+
assigned = body.get("assigned_to")
|
|
574
|
+
return amendment_refusal(ledger, rec.get("author") or {}, contract_id,
|
|
575
|
+
assigned if isinstance(assigned, str) else None,
|
|
576
|
+
note=note if isinstance(note, str) else None)
|
|
455
577
|
return None
|
|
456
578
|
|
|
457
579
|
|
|
@@ -461,6 +583,7 @@ def update_body(
|
|
|
461
583
|
note: Optional[str] = None,
|
|
462
584
|
claims: Iterable[str] = (),
|
|
463
585
|
assigned_to: Optional[str] = None,
|
|
586
|
+
acceptance_checks: Optional[List[dict]] = None,
|
|
464
587
|
) -> dict:
|
|
465
588
|
"""The body of a contract_update that asks for a move, without filing it.
|
|
466
589
|
|
|
@@ -473,6 +596,8 @@ def update_body(
|
|
|
473
596
|
body["note"] = note
|
|
474
597
|
if assigned_to:
|
|
475
598
|
body["assigned_to"] = assigned_to
|
|
599
|
+
if acceptance_checks:
|
|
600
|
+
body["acceptance_checks"] = list(acceptance_checks)
|
|
476
601
|
return body
|
|
477
602
|
|
|
478
603
|
|
|
@@ -486,13 +611,21 @@ def update(
|
|
|
486
611
|
claims: Iterable[str] = (),
|
|
487
612
|
tags: Iterable[str] = (),
|
|
488
613
|
assigned_to: Optional[str] = None,
|
|
614
|
+
acceptance_checks: Optional[List[dict]] = None,
|
|
489
615
|
) -> dict:
|
|
490
616
|
"""File a status transition, enforcing the rules above. ``assigned_to`` hands the
|
|
491
617
|
contract to another agent: the latest update that names one is who holds it, which
|
|
492
|
-
is what the compiled board shows (a PM reassigns with `contract update --assign`).
|
|
618
|
+
is what the compiled board shows (a PM reassigns with `contract update --assign`).
|
|
619
|
+
``acceptance_checks`` replaces the checks the contract answers to from here on
|
|
620
|
+
(``acceptance_of``); the assignee may not file one (``amendment_refusal``)."""
|
|
493
621
|
_check_transition(ledger, author, contract_id, status, claims, note=note)
|
|
622
|
+
if acceptance_checks:
|
|
623
|
+
refusal = amendment_refusal(ledger, author, contract_id, assigned_to, note=note)
|
|
624
|
+
if refusal:
|
|
625
|
+
raise ValidationError(refusal)
|
|
494
626
|
rec = new_record("contract_update", author, scope,
|
|
495
|
-
update_body(contract_id, status, note=note, claims=claims, assigned_to=assigned_to
|
|
627
|
+
update_body(contract_id, status, note=note, claims=claims, assigned_to=assigned_to,
|
|
628
|
+
acceptance_checks=acceptance_checks),
|
|
496
629
|
derived_from=[contract_id, *claims], tags=tags)
|
|
497
630
|
return ledger.append(rec)
|
|
498
631
|
|
|
@@ -522,6 +655,8 @@ def status_of(ledger, contract_id: str, assessment: Optional[dict] = None) -> di
|
|
|
522
655
|
for u in updates:
|
|
523
656
|
linked_ids.update(u["body"].get("claims", []))
|
|
524
657
|
claims = [ledger.get(i) for i in sorted(linked_ids) if ledger.get(i)]
|
|
658
|
+
checks, amended_by = acceptance_of(ledger, contract_id)
|
|
659
|
+
amended_note = next((u["body"].get("note") for u in updates if u["id"] == amended_by), None)
|
|
525
660
|
out = {
|
|
526
661
|
"id": contract_id,
|
|
527
662
|
"title": rec["body"]["title"],
|
|
@@ -537,7 +672,11 @@ def status_of(ledger, contract_id: str, assessment: Optional[dict] = None) -> di
|
|
|
537
672
|
# thread besides the assignee's (ctr_b3b3e487832b5c94): a relay asks the replica
|
|
538
673
|
"pm": pm_users(getattr(ledger, "config", None) or {}),
|
|
539
674
|
"issue": rec["body"].get("issue"),
|
|
540
|
-
"acceptance":
|
|
675
|
+
"acceptance": checks,
|
|
676
|
+
# the amendment the checks come from, or None for the list the contract was filed
|
|
677
|
+
# with, and the note that says why (an amendment is a decision on the record)
|
|
678
|
+
"acceptance_amended_by": amended_by,
|
|
679
|
+
"acceptance_amended_note": amended_note,
|
|
541
680
|
"updates": [{"ts": u["ts"], "status": u["body"]["status"], "by": u["author"].get("harness"), "note": u["body"].get("note")} for u in updates],
|
|
542
681
|
"acceptance_runs": [],
|
|
543
682
|
"last_acceptance_ok": False,
|
|
@@ -560,7 +699,7 @@ def status_of(ledger, contract_id: str, assessment: Optional[dict] = None) -> di
|
|
|
560
699
|
|
|
561
700
|
def run_acceptance(ledger, contract_id: str, cwd: Optional[str] = None, timeout: Optional[float] = 600,
|
|
562
701
|
author: Optional[dict] = None, scope: Optional[dict] = None, file: bool = True,
|
|
563
|
-
policy: Optional[dict] = None) -> List[dict]:
|
|
702
|
+
policy: Optional[dict] = None, checks: Optional[List[dict]] = None) -> List[dict]:
|
|
564
703
|
"""Run each acceptance check and return [{check, outcome, detail, receipt_body, receipt}].
|
|
565
704
|
|
|
566
705
|
With ``file=True`` and an author and scope, the command/test receipts and a
|
|
@@ -588,7 +727,12 @@ def run_acceptance(ledger, contract_id: str, cwd: Optional[str] = None, timeout:
|
|
|
588
727
|
except ev.SandboxError as exc: # asked for and malformed: no check runs at all
|
|
589
728
|
sandbox, unsandboxable = None, str(exc)
|
|
590
729
|
results = []
|
|
591
|
-
|
|
730
|
+
# what this runner saw when it started (PROTOCOL 4.1: the status its runner read before
|
|
731
|
+
# the checks ran), as the remote lane reads it, never a status a close filed mid-run
|
|
732
|
+
started = status_now(ledger, contract_id) if file and author and scope else None
|
|
733
|
+
# the checks it answers to now (``acceptance_of``); a caller whose ledger holds no
|
|
734
|
+
# updates (the remote lane's served view) passes the list the replica reports
|
|
735
|
+
for check in (checks if checks is not None else acceptance_of(ledger, contract_id)[0]):
|
|
592
736
|
kind = check.get("kind")
|
|
593
737
|
if kind == "command":
|
|
594
738
|
where = ev.resolved_directory(base / check["cwd"] if check.get("cwd") else base,
|
|
@@ -646,7 +790,7 @@ def run_acceptance(ledger, contract_id: str, cwd: Optional[str] = None, timeout:
|
|
|
646
790
|
body = r.get("receipt_body")
|
|
647
791
|
if body and body.get("kind") in ("command", "test"):
|
|
648
792
|
r["receipt"] = ledger.append(new_record("evidence", author, scope, body))["id"]
|
|
649
|
-
status =
|
|
793
|
+
status = started # what this runner saw when it started: a run reports, it never promotes
|
|
650
794
|
body = {
|
|
651
795
|
"contract": contract_id,
|
|
652
796
|
"status": status,
|
|
@@ -2150,6 +2150,11 @@ def _check_binding(body: dict, cwd: Optional[str] = None, strict: bool = False,
|
|
|
2150
2150
|
f"without it): bind it, even at 0, with `agent-memory run --var "
|
|
2151
2151
|
f"{gate} -- ...`")
|
|
2152
2152
|
cmd, run_env, variables = body["cmd"], None, bound
|
|
2153
|
+
if unsets and not body.get("shell"):
|
|
2154
|
+
# what the receipt's `env -u` takes away is not in the environment the command
|
|
2155
|
+
# runs in, so not on a sandbox's podman line either (LANG, TZ); a shell line's
|
|
2156
|
+
# prefix is the shell's guess, and it unsets for its first command only
|
|
2157
|
+
run_env = {k: v for k, v in os.environ.items() if k not in unsets}
|
|
2153
2158
|
if bound is not None or assigned:
|
|
2154
2159
|
# exactly the receipt's SIT_* switches, however it bound them, and none of
|
|
2155
2160
|
# the verifier's own; never its place variables (SIT_PLACE_VARS)
|
|
@@ -2365,8 +2370,11 @@ def recheck_claim(ledger, claim: dict, cwd: Optional[str] = None, strict: bool =
|
|
|
2365
2370
|
role = binding_role(body)
|
|
2366
2371
|
if role != "proof":
|
|
2367
2372
|
entry["role"] = role
|
|
2368
|
-
|
|
2369
|
-
|
|
2373
|
+
# a file's path rides on its result whatever its role: a reader deciding whether that
|
|
2374
|
+
# file was re-checked in a given tree needs the path, not the claim's other bindings
|
|
2375
|
+
# (recheck.away_from_bound; the review of #146)
|
|
2376
|
+
if body.get("path") and (role != "proof" or body.get("kind") == "file"):
|
|
2377
|
+
entry["path"] = body["path"]
|
|
2370
2378
|
if rid:
|
|
2371
2379
|
answered.add(rid)
|
|
2372
2380
|
results.append(entry)
|