Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions .github/workflows/agent-learning-proposal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: agent-learning-proposal

on:
pull_request:
paths:
- "schemas/agent-learning-proposal.schema.json"
- "examples/agent-learning/proposal.example.json"
- "scripts/create_agent_learning_proposal.py"
- "scripts/validate_agent_learning_proposal.py"
- "scripts/validate_agent_learning_proposal_generator.py"
- ".github/workflows/agent-learning-proposal.yml"
- "README.md"
- "Makefile"
push:
branches:
- main
paths:
- "schemas/agent-learning-proposal.schema.json"
- "examples/agent-learning/proposal.example.json"
- "scripts/create_agent_learning_proposal.py"
- "scripts/validate_agent_learning_proposal.py"
- "scripts/validate_agent_learning_proposal_generator.py"
- ".github/workflows/agent-learning-proposal.yml"
- "README.md"
- "Makefile"

jobs:
validate-agent-learning-proposal:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install validation dependency
run: python -m pip install jsonschema
- name: Validate AgentLearningProposal example and generator
run: make validate-agent-learning-proposal
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
PYTHON ?= python

.PHONY: validate-upstreams validate-python validate-deploy-assets validate local-preflight local-up local-smoke local-debug local-down
.PHONY: validate-upstreams validate-python validate-deploy-assets validate-agent-learning-proposal validate local-preflight local-up local-smoke local-debug local-down

validate-upstreams:
$(PYTHON) scripts/validate_upstreams.py third_party/upstreams.lock.yaml
Expand All @@ -13,6 +13,10 @@ validate-python:
validate-deploy-assets:
$(PYTHON) scripts/validate_deploy_assets.py

validate-agent-learning-proposal:
$(PYTHON) scripts/validate_agent_learning_proposal.py
$(PYTHON) scripts/validate_agent_learning_proposal_generator.py

validate: validate-upstreams validate-python validate-deploy-assets

local-preflight:
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,37 @@ This repository currently includes:
- importer and validation scripts so upstream resolution happens in one controlled place instead of at runtime;
- local M2 Mac Podman and Google Cloud review deployment scaffolding.

## Governed agent learning proposals

Memory Mesh now carries the first review-only proposal contract for durable agent learnings discovered during guarded AgentPlane sessions.

This is the bridge from agent execution evidence to durable repo/project memory without silent writeback:

- AgentPlane remains the source of guarded workcell, invocation, and stop-gate evidence.
- guardrail-fabric remains the source of policy-decision evidence.
- Policy Fabric remains the source of break-glass approval evidence.
- Memory Mesh receives reviewable learning proposals.
- Durable writeback remains disabled unless explicitly approved by a later governance flow.

The contract, example, validator, and generator live at:

- `schemas/agent-learning-proposal.schema.json`
- `examples/agent-learning/proposal.example.json`
- `scripts/validate_agent_learning_proposal.py`
- `scripts/create_agent_learning_proposal.py`
- `scripts/validate_agent_learning_proposal_generator.py`

Validate locally:

```bash
python -m pip install jsonschema
make validate-agent-learning-proposal
```

The workflow `.github/workflows/agent-learning-proposal.yml` runs this validation when the proposal schema, example, generator, validator, workflow, README, or Makefile changes.

The example enforces review-only proposal mode, pending human review, no raw sensitive payload storage, evidence references, policy decision references, repo-local operating-contract destinations, and disabled durable writeback.

## Lampstand adapter-record promotion packets

Memory Mesh now carries a review-only promotion-packet contract for Lampstand governed adapter records.
Expand Down
61 changes: 61 additions & 0 deletions examples/agent-learning/proposal.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"apiVersion": "memory.mesh.agent-learning/v1",
"kind": "AgentLearningProposal",
"proposalId": "urn:srcos:memory-proposal:agent-learning-example",
"createdAt": "2026-05-06T00:00:00Z",
"sourceSession": {
"sessionRef": "urn:srcos:session:agent-learning-example",
"taskRef": "urn:srcos:task:agent-learning-example",
"repo": "SocioProphet/agentplane",
"agentRef": "agentplane:guarded-command-invocation",
"workcellArtifactRef": "urn:srcos:artifact:guarded-workcell:example",
"invocationArtifactRef": "urn:srcos:artifact:guarded-invocation:example",
"stopGateArtifactRef": "urn:srcos:artifact:stop-gate:example"
},
"destination": {
"scope": "repo",
"targetRef": "SocioProphet/agentplane",
"path": "AGENTS.md",
"memoryNamespace": "repo:SocioProphet/agentplane"
},
"proposalMode": "review_only",
"learningType": "playbook-update",
"learning": {
"title": "Document stop-gate failure remediation in repo playbook",
"summary": "When AgentPlane stop-gate evaluation fails because the branch has no upstream, the agent should push the branch or explicitly request a no-PR/no-push exception rather than claiming completion.",
"rationale": "The stop-gate evaluator now treats missing upstream evidence as a hard failure. Capturing the remediation in the repo playbook reduces repeat false-done attempts.",
"proposedDiff": {
"format": "markdown-section",
"content": "### Agent completion gate remediation\n\nIf the stop gate reports `branch-pushed` failure, do not claim completion. Push the branch, open or update the PR, then rerun `tools/evaluate_stop_gate.py`. If branch push is intentionally disallowed, request a scoped PolicyFabric BreakGlassOverride and reference it in the stop-gate artifact.\n"
},
"conflictsWith": [],
"confidence": 0.91,
"retention": "repo-durable"
},
"review": {
"required": true,
"status": "pending",
"reviewerRefs": [
"human:repo-maintainer"
],
"approvalRef": null,
"reviewNotes": "Review before modifying AGENTS.md."
},
"evidenceRefs": [
"urn:srcos:artifact:stop-gate:example",
"urn:srcos:artifact:guarded-invocation:example"
],
"policyDecisionRefs": [
"sourceos/core/baseline-allow",
"agentplane/stop-gate/branch-pushed"
],
"redaction": {
"rawSensitivePayloadStored": false,
"redactionSummary": "No raw session transcript or secret-bearing payload is stored in this proposal."
},
"writeback": {
"enabled": false,
"performed": false,
"writebackRef": null
}
}
109 changes: 109 additions & 0 deletions schemas/agent-learning-proposal.schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://schemas.socioprophet.org/memory-mesh/agent-learning-proposal.schema.json",
"title": "Memory Mesh Agent Learning Proposal",
"type": "object",
"required": [
"apiVersion",
"kind",
"proposalId",
"createdAt",
"sourceSession",
"destination",
"proposalMode",
"learningType",
"learning",
"review",
"evidenceRefs",
"policyDecisionRefs"
],
"properties": {
"apiVersion": { "const": "memory.mesh.agent-learning/v1" },
"kind": { "const": "AgentLearningProposal" },
"proposalId": { "type": "string", "minLength": 1 },
"createdAt": { "type": "string", "format": "date-time" },
"sourceSession": {
"type": "object",
"required": ["sessionRef", "taskRef", "repo", "agentRef"],
"properties": {
"sessionRef": { "type": "string", "minLength": 1 },
"taskRef": { "type": "string", "minLength": 1 },
"repo": { "type": "string", "minLength": 1 },
"agentRef": { "type": "string", "minLength": 1 },
"workcellArtifactRef": { "type": ["string", "null"] },
"invocationArtifactRef": { "type": ["string", "null"] },
"stopGateArtifactRef": { "type": ["string", "null"] }
},
"additionalProperties": false
},
"destination": {
"type": "object",
"required": ["scope", "targetRef", "path"],
"properties": {
"scope": { "enum": ["repo", "project", "user", "organization", "enterprise"] },
"targetRef": { "type": "string", "minLength": 1 },
"path": { "type": "string", "minLength": 1 },
"memoryNamespace": { "type": ["string", "null"] }
},
"additionalProperties": false
},
"proposalMode": { "enum": ["review_only", "approved_writeback"] },
"learningType": { "enum": ["operational-fact", "playbook-update", "policy-note", "tooling-note", "risk-note", "architecture-note"] },
"learning": {
"type": "object",
"required": ["title", "summary", "rationale", "proposedDiff"],
"properties": {
"title": { "type": "string", "minLength": 1 },
"summary": { "type": "string", "minLength": 1 },
"rationale": { "type": "string", "minLength": 1 },
"proposedDiff": {
"type": "object",
"required": ["format", "content"],
"properties": {
"format": { "enum": ["unified-diff", "markdown-section", "json-patch", "none"] },
"content": { "type": "string" }
},
"additionalProperties": false
},
"conflictsWith": { "type": "array", "items": { "type": "string" } },
"confidence": { "type": "number", "minimum": 0, "maximum": 1 },
"retention": { "enum": ["session-only", "repo-durable", "project-durable", "org-durable", "enterprise-durable"] }
},
"additionalProperties": false
},
"review": {
"type": "object",
"required": ["required", "status", "reviewerRefs", "approvalRef"],
"properties": {
"required": { "type": "boolean" },
"status": { "enum": ["pending", "approved", "rejected", "superseded"] },
"reviewerRefs": { "type": "array", "items": { "type": "string" } },
"approvalRef": { "type": ["string", "null"] },
"reviewNotes": { "type": ["string", "null"] }
},
"additionalProperties": false
},
"evidenceRefs": { "type": "array", "minItems": 1, "items": { "type": "string" } },
"policyDecisionRefs": { "type": "array", "minItems": 1, "items": { "type": "string" } },
"redaction": {
"type": "object",
"required": ["rawSensitivePayloadStored", "redactionSummary"],
"properties": {
"rawSensitivePayloadStored": { "type": "boolean" },
"redactionSummary": { "type": "string" }
},
"additionalProperties": false
},
"writeback": {
"type": "object",
"required": ["enabled", "performed", "writebackRef"],
"properties": {
"enabled": { "type": "boolean" },
"performed": { "type": "boolean" },
"writebackRef": { "type": ["string", "null"] }
},
"additionalProperties": false
}
},
"additionalProperties": false
}
125 changes: 125 additions & 0 deletions scripts/create_agent_learning_proposal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env python3
"""Create review-only AgentLearningProposal artifacts.

This script never writes durable memory. It produces a reviewable JSON proposal
that can later be approved, rejected, or superseded by a human/governance flow.
"""

from __future__ import annotations

import argparse
import json
from datetime import datetime, timezone
from pathlib import Path


def utc_now() -> str:
return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")


def build_proposal(args: argparse.Namespace) -> dict:
proposal_id = args.proposal_id or f"urn:srcos:memory-proposal:{args.session_ref.split(':')[-1]}"
evidence_refs = args.evidence_ref or []
policy_refs = args.policy_decision_ref or []
return {
"apiVersion": "memory.mesh.agent-learning/v1",
"kind": "AgentLearningProposal",
"proposalId": proposal_id,
"createdAt": args.created_at or utc_now(),
"sourceSession": {
"sessionRef": args.session_ref,
"taskRef": args.task_ref,
"repo": args.repo,
"agentRef": args.agent_ref,
"workcellArtifactRef": args.workcell_artifact_ref,
"invocationArtifactRef": args.invocation_artifact_ref,
"stopGateArtifactRef": args.stop_gate_artifact_ref,
},
"destination": {
"scope": args.scope,
"targetRef": args.target_ref,
"path": args.path,
"memoryNamespace": args.memory_namespace,
},
"proposalMode": "review_only",
"learningType": args.learning_type,
"learning": {
"title": args.title,
"summary": args.summary,
"rationale": args.rationale,
"proposedDiff": {
"format": args.diff_format,
"content": args.diff_content,
},
"conflictsWith": args.conflicts_with or [],
"confidence": args.confidence,
"retention": args.retention,
},
"review": {
"required": True,
"status": "pending",
"reviewerRefs": args.reviewer_ref or ["human:repo-maintainer"],
"approvalRef": None,
"reviewNotes": args.review_notes,
},
"evidenceRefs": evidence_refs,
"policyDecisionRefs": policy_refs,
"redaction": {
"rawSensitivePayloadStored": False,
"redactionSummary": args.redaction_summary,
},
"writeback": {
"enabled": False,
"performed": False,
"writebackRef": None,
},
}


def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Create a review-only Memory Mesh AgentLearningProposal artifact.")
parser.add_argument("--proposal-id")
parser.add_argument("--created-at")
parser.add_argument("--session-ref", required=True)
parser.add_argument("--task-ref", required=True)
parser.add_argument("--repo", required=True)
parser.add_argument("--agent-ref", required=True)
parser.add_argument("--workcell-artifact-ref")
parser.add_argument("--invocation-artifact-ref")
parser.add_argument("--stop-gate-artifact-ref")
parser.add_argument("--scope", choices=["repo", "project", "user", "organization", "enterprise"], default="repo")
parser.add_argument("--target-ref", required=True)
parser.add_argument("--path", required=True)
parser.add_argument("--memory-namespace")
parser.add_argument("--learning-type", choices=["operational-fact", "playbook-update", "policy-note", "tooling-note", "risk-note", "architecture-note"], default="playbook-update")
parser.add_argument("--title", required=True)
parser.add_argument("--summary", required=True)
parser.add_argument("--rationale", required=True)
parser.add_argument("--diff-format", choices=["unified-diff", "markdown-section", "json-patch", "none"], default="markdown-section")
parser.add_argument("--diff-content", required=True)
parser.add_argument("--conflicts-with", action="append")
parser.add_argument("--confidence", type=float, default=0.75)
parser.add_argument("--retention", choices=["session-only", "repo-durable", "project-durable", "org-durable", "enterprise-durable"], default="repo-durable")
parser.add_argument("--reviewer-ref", action="append")
parser.add_argument("--review-notes")
parser.add_argument("--evidence-ref", action="append", required=True)
parser.add_argument("--policy-decision-ref", action="append", required=True)
parser.add_argument("--redaction-summary", default="No raw sensitive payload stored in the proposal.")
parser.add_argument("--out", required=True)
return parser


def main(argv: list[str] | None = None) -> int:
args = build_parser().parse_args(argv)
if not 0 <= args.confidence <= 1:
raise SystemExit("--confidence must be between 0 and 1")
proposal = build_proposal(args)
out = Path(args.out)
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(proposal, indent=2, sort_keys=True) + "\n", encoding="utf-8")
print(json.dumps(proposal, indent=2, sort_keys=True))
return 0


if __name__ == "__main__":
raise SystemExit(main())
Loading
Loading