voku/agent-learning

Reviewable finding, proposal, redaction, and decision-history tooling for coding-agent learning loops.

Maintainers

Package info

github.com/voku/agent-learning

pkg:composer/voku/agent-learning

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 2

0.0.2 2026-06-11 08:43 UTC

This package is auto-updated.

Last update: 2026-06-11 08:44:04 UTC


README

Reviewable finding, proposal, redaction, and decision-history tooling for coding-agent learning loops.

This library provides core domain logic and validation classes to support structured post-session learning for coding agents. It separates raw experiences (Findings) from potential guideline changes (Proposals), keeping the agent's knowledge extraction workflow structured, secure, and fully auditable.

Key Concepts

Findings

A Finding represents a single raw experience or observation captured from a task session. It stores:

  • An observation and a hypothetical rule or pattern.
  • A confidence level.
  • Explicit validation metadata (unverified, validated, invalidated).
  • A validated conclusion detailing why the pattern was verified or rejected.

Proposals

A Proposal defines a potential durable mutation to the repository's guidelines or instructions (e.g., in MEMORY.md or dedicated agent skills).

  • Can represent actions like ADD, DELETE, REPLACE, REJECT, or NO_DURABLE_LEARNING.
  • References one or more validated source findings that back it up.
  • Contains metadata about target type, scope, proposed boundary, validation checklist, status, and approval.

Evidence

Findings must be backed by concrete, verifiable evidence. Supported types include:

  • file_reference: References to specific files and line numbers.
  • commit: Reference to a specific git commit.
  • test_result / phpstan_result: Command execution command and summary.
  • review_comment: Pull/merge request comments or reviews.
  • issue_reference: Bounded issue or ticket tracker reference.
  • Others (e.g., schema_reference, runtime_observation, manual_verification).

Decision History

A persistent record of approved or rejected proposals stored in JSON Lines (.jsonl) format.

  • decisions.jsonl logs approved and applied mutations.
  • rejected-proposals.jsonl logs rejected candidate proposals with detailed reasons.

Core Classes & APIs

The package codebase is organized under the voku\AgentLearning namespace in the following structure:

Value Objects & Enums

  • Finding: Read-only entity representing a captured session finding.
  • FindingStatus: Enum defining finding lifecycles (candidate, validated, invalidated, rejected, superseded, consolidated, archived).
  • Proposal: Read-only entity representing a proposed modification to guidelines.
  • ProposalStatus: Enum defining proposal states (candidate, approved, rejected, applied).
  • Action: Enum representing actions (NO_DURABLE_LEARNING, ADD, DELETE, REPLACE, REJECT).

Parsers & Repositories

Validators

  • FindingValidator: Enforces structure, format, and lifecycle consistency for findings.
  • ProposalValidator: Validates proposal mutations, targets, actions, and references.
  • EvidenceValidator: Inspects list of evidence objects to ensure required fields for each type exist.
  • JsonlValidator: Parses and validates JSON Lines log formats.
  • RedactionGuard: Scans all content for credentials, secrets, or sensitive configuration keys to prevent accidental leaks.
  • DecisionHistoryValidator: Validates log consistency of the decision history.

Utilities & Infrastructure

  • ConsolidationPromptBuilder: Assembles validated findings and rejected proposals history into a structured LLM consolidation prompt.
  • RecordAccess: Utility helper to extract strongly typed fields from raw array data.
  • Json: Helper for decoding files safely.
  • ValidationException: Custom runtime exception with file name, line numbers, and record IDs context.

Validation Specifications

Finding Validation

  1. Finding ID: Must match finding.YYYY-MM-DD.NNN.
  2. Created At: Must be a valid ISO 8601/Atom timestamp string.
  3. Task ID: Must match the configured task ID pattern (passed via $taskIdPattern to the FindingValidator constructor; defaults to '/^(?:[A-Z][A-Z0-9_-]*-\d+|TODO@[\w:\/.-]+)$/').
  4. Observation/Hypothesis Separation: Both must be non-empty strings and cannot be identical.
  5. Confidence: Must be one of low, medium, or high.
  6. Validation Status: Must be one of unverified, validated, or invalidated.
  7. Lifecycle Enforcements:
    • candidate requires validation_status=unverified.
    • validated and consolidated require validation_status=validated.
    • invalidated requires validation_status=invalidated.
    • superseded and rejected require validation_status=validated or validation_status=invalidated.
    • archived preserves the prior validation state and may use any supported validation_status.
    • A validation_status=validated finding requires a non-empty validated_conclusion.
    • The validated_conclusion must not be identical to the hypothesis.

Proposal Validation

  1. Proposal ID: Must match proposal.YYYY-MM-DD.NNN.
  2. Created At: Must be a valid ISO 8601/Atom timestamp string.
  3. Mutations Constraint: Fields mutations, changes, or targets must contain at most 1 item to prevent overly broad proposals.
  4. Source Findings: Must have at least 1 referenced source finding.
  5. Action-Specific Constraints:
    • If not a NO_DURABLE_LEARNING action: requires target_type, target, scope (non-empty list), boundary (non-empty), and validation checklist.
    • ADD action requires new wording.
    • DELETE action requires old wording.
    • REPLACE action requires both old and new wording.
  6. Status Constraints:
    • Proposal action describes the requested durable change (ADD, DELETE, REPLACE, REJECT, NO_DURABLE_LEARNING).
    • Proposal status describes the human lifecycle decision (candidate, approved, rejected, applied).
    • Durable actions (ADD, DELETE, REPLACE) may be candidate, approved, rejected, or applied.
    • REJECT and NO_DURABLE_LEARNING may only be candidate or rejected.
    • APPROVED or APPLIED proposal requires approved_by and approved_at timestamp.
    • REJECTED proposal or a REJECT action requires a non-empty reason.
  7. Lifecycle Directory Check: Proposal files under proposals/<status>/ must embed the same status value.
  8. Scope Broader Check: If proposal scope includes entries not present in the referenced findings, a scope_justification must be provided.

Redaction Constraints

All keys and values are checked using RedactionGuard against secret assignment patterns. Any matches of standard credential assignments (e.g. password, token, api_key, ms-Mcs-AdmPwd patterns) throw a validation exception.

JSON Structure Formats

Example Finding

{
  "id": "finding.2026-06-08.001",
  "task_id": "PROJECT-1234",
  "session": "session_abc123",
  "created_at": "2026-06-08T10:00:00+00:00",
  "created_by": "agent_alpha",
  "scope": [
    "lib/framework/forms"
  ],
  "observation": "FormElement validation fails when checking numeric bounds if string decimals are passed.",
  "evidence": [
    {
      "type": "file_reference",
      "path": "lib/framework/forms/FormElement.php",
      "line": 42
    },
    {
      "type": "test_result",
      "command": "make test_unit_file FILE=tests/FormElement_UnitCest.php",
      "summary": "Failed asserting that false is true on DecimalBound test"
    }
  ],
  "hypothesis": "String decimal inputs should be normalized to float/int before calling range checks in FormElement.",
  "validated_conclusion": "Normalizing value to float in range validation resolves bounds failures without side-effects.",
  "confidence": "high",
  "validation_status": "validated",
  "status": "validated",
  "sensitivity": "public"
}

Example Proposal

{
  "id": "proposal.2026-06-08.001",
  "created_at": "2026-06-08T11:30:00+00:00",
  "action": "REPLACE",
  "target_type": "skill",
  "target": "form-validation",
  "scope": [
    "lib/framework/forms"
  ],
  "source_findings": [
    "finding.2026-06-08.001"
  ],
  "old": "Validate range bounds directly using the raw inputs.",
  "new": "Ensure numeric inputs are cast/normalized to numeric values before validating range bounds.",
  "reason": "Prevents float/string type comparisons from failing bounds checks.",
  "boundary": "Only run numeric bounds normalization on Decimal and Float FormElement subclasses.",
  "validation": [
    "Ensure unit tests verify decimal string normalization."
  ],
  "status": "candidate",
  "proposed_by": "agent_alpha",
  "approved_by": null,
  "approved_at": null
}

Development & Testing

Running Tests

To run unit and integration tests for this package:

composer test

Or use the local Makefile:

make test

Static Analysis

To run PHPStan checks on the package:

composer phpstan

Or use the local Makefile:

make phpstan

CLI

The Composer binary exposes the package workflow without requiring consuming-project classes:

vendor/bin/agent-learning validate --root infra/doc/agent-learning
vendor/bin/agent-learning prepare --root infra/doc/agent-learning --task PROJECT-1234 --task GH-158
vendor/bin/agent-learning prepare --root infra/doc/agent-learning --finding finding.2026-06-08.001 --scope src/Auth --since 2026-06-01
vendor/bin/agent-learning proposal-validate --root infra/doc/agent-learning --proposal proposal.2026-06-08.001.json

prepare prints the selected finding IDs before writing the prompt. Empty selections fail unless --allow-empty is passed. If templates/consolidation-prompt.md exists under the learning root, its content is appended to the generated consolidation input as a project-specific prompt addendum.

--root may point either to the learning root itself or to a project root containing one of these directories:

  • infra/doc/agent-learning
  • .agent-learning
  • docs/agent-learning
  • agent-learning

Zero-byte .json files are treated as extraction placeholders and skipped. Non-empty finding, proposal, and history records are validated strictly.