NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist · #5982 most downloaded on Packagist
Reviewable finding, proposal, redaction, and decision-history tooling for coding-agent learning loops.
Last release today
07 Oct 2026
Ships on a steady schedule
a new release about every 8 days
Nearly every release is documented
notes for 60 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
4 months old
71 releases · first in 2026
One column per month.
A validated finding whose proposals all reached a terminal decision (applied, rejected, acknowledged, retired) no longer counts as open work. Learning
LearningCatalog::overview() leaves it out of findingAttentionIds, so backlog and the SessionStart hint stop counting decided findings. On a mature root, 19 of 56 backlog findings were already decided and only looked pending, because the last bookkeeping step was manual and easy to forget. A finding with no proposal, or one a candidate/approved proposal still waits on, stays in the backlog.ProposalTransitionManager::reject(), acknowledge() and apply() now move the proposal's still-validated source findings to consolidated (only when no other proposal still waits on them), through the new FindingConsolidationService. The proposal transition is not undone if that step fails; the error names the repair (finding-reconcile).FindingTransitionManager no longer overwrites validated_by and validated_at when consolidating a finding; they still record who validated it and when. Validating a candidate behaves as before.backlog looks findings up with one findings() call instead of finding() per id, which revalidated the whole root each time.agent-learning finding-reconcile --by ACTOR [--dry-run]: consolidates validated findings whose proposals are all terminal, for roots decided before the transitions did it themselves. backlog points at it when such findings exist.agent-learning finding-queue [--probe PATH]... [--format text|json] and FindingReviewQueue: a read-only list of backlog findings with deterministic facts (proposals citing the finding, scope paths that no longer resolve, how much of the conclusion already exists in probe files, the transitions the lifecycle accepts). Like proposal-queue, it never recommends a bucket, owner or transition; that judgement stays with a named human.WordingOverlap, the exact/4-gram overlap measure, now shared by proposal-queue and finding-queue.composer ci (444 tests, 1670 assertions, PHPStan clean). New FindingConsolidationTest (7 tests); disabling the transition hook fails two of them. LearningCatalogTest now expects a finding cited only by a rejected proposal to be bookkeeping, not attention. Against the IT-Portal root, finding-reconcile --dry-run listed exactly the 19 findings decided by hand and finding-queue listed the 37 real backlog findings.agent-learning proposal-queue [--probe PATH]... [--format text|json] and ProposalReviewQueue: a read-only list of the proposals still waiting for a hu
agent-learning proposal-queue [--probe PATH]... [--format text|json] and ProposalReviewQueue: a read-only list of the proposals still waiting for a human decision (candidate) or for application (approved). Each row carries only deterministic facts: age, source-finding count, the transitions the lifecycle accepts for that status and action, lineage (corrects, corrected_by, supersedes, conflicts_with), other proposals on the same target with their status, scope and target paths that no longer resolve, and how much of the proposed wording (share of its word 4-grams, or an exact whitespace-normalised match) already exists in the target skill file and in any --probe file. It never recommends approve, reject or acknowledge; that judgement stays with a named human. Motivation: a reviewer judging a queue by hand called two proposals duplicates from memory instead of from the repository, and missed that three approved memory proposals were already present in MEMORY.md.LearningCatalog::finding(), proposal() and guidance() that every call validates the whole Learning root again. A host that looked up 56 attention findings one by one paid about 12 seconds per session start on a mature root; findings() and proposals() validate once and are the batch path. No behavior change: the catalog still caches nothing, so a long-lived host never reads a stale snapshot.LearningLineageService::lineage() and precedentsForTask() take a trailing bool $repairProjection = true. With false, an absent, empty or stale derived
LearningLineageService::lineage() and precedentsForTask() take a trailing bool $repairProjection = true. With false, an absent, empty or stale derived graph throws the existing LearningLineageProjectionUnavailable instead of being rebuilt, and the database is opened through GraphStore::openReadOnly(), so nothing under the Learning root is created or modified - including a zero-byte or half-built file at the projection path, which GraphStore's ordinary constructor would have completed with a full schema just by being opened. A non-empty database whose schema this reader cannot use is refused with a RuntimeException (fail closed), not repaired. The default is unchanged: command-line consumers keep the self-healing read introduced in 0.18.6. This is for consumers that answer an HTTP GET, which must not write into Learning's derived cache or rescan every source record per page view, and that would rather show "projection stale" than repair it (#144).voku/agent-graph ^0.2.3 (was ^0.2.0), the release that added GraphStore::openReadOnly().verifyCurrent() still reports staleness.LearningLineageObservingReadTest (11 tests): an absent and a stale projection are reported for both lineage() and precedentsForTask(), and a before/after snapshot of every path under the root (hash, mtime, size, directories) is identical; a current projection answers identically in both modes and is not touched; a root with no records still answers empty with no .derived directory; the default still repairs; a corrupt database still raises PDOException; a zero-byte file stays zero bytes and a database with only graph_meta is not completed (both failed against the writable open, which grew the latter from 12,288 to 45,056 bytes). Seven mutants (repair always on, either method dropping the flag, default flipped, writable open, no empty-file guard, observing never read-only) each fail the suite.composer ci (428 tests, 1590 assertions) clean.Daily event sequences may grow past 999. recall-selection.*, guidance-outcome.* and the read-compatibility outcome.* ids now accept a zero-padded sequ
recall-selection.*, guidance-outcome.* and the read-compatibility outcome.* ids now accept a zero-padded sequence of at least three digits (.1000, .1001, …) instead of exactly three. The writers (voku/agent-recall-compiler EventHistoryWriter::nextEventId() / OutcomeLogger) already parse the previous maximum numerically and format with %03d, so after .999 they emit .1000 - which RecallSelectionEventParser then rejected, and agent-loop verify failed for the rest of the day once a busy learning root crossed 999 selections (observed: 1023 selections on 2026-09-26 in one consuming project). No id is rewritten; every existing three-digit id stays valid.RecallSelectionEventRepository and GuidanceOutcomeEventRepository order events with strnatcmp instead of strcmp, so .1000 sorts after .999. Three-digit ids keep their order..002, .999, .1000 numerically. Against the previous source the new tests fail (3 errors); with the new parser but the old strcmp the ordering test fails on its own.composer test (417 tests, 1559 assertions) and composer phpstan clean.ProposalProjection exposes the lifecycle moments ProposalTransitionManager already writes into the proposal record: $acknowledgedAt/$acknowledgedBy, $
ProposalProjection exposes the lifecycle moments ProposalTransitionManager already writes into the proposal record: $acknowledgedAt/$acknowledgedBy, $appliedAt/$appliedBy and $retiredAt/$retiredBy. Each pair is null exactly when that transition has not happened. Until now the projection stopped at $approvedAt, so a consumer that wanted to date anything after approval had to read $proposal->raw - which this package's own AppliedGuidanceTargetValidator, DreamingEvaluator and CorpusAnalyticsService do, and which a presentation-layer consumer must not (#140).GuidanceProjection::$appliedAt: when the guidance became durable, taken from its source proposal's applied_at. null while the proposal is approved but not yet applied.No stored field is added or changed: these values are already in every record written by the corresponding transition, and a record that predates it projects null. All new constructor parameters are trailing and optional, so positional construction against the earlier shape keeps compiling.
Not in scope, noted for a follow-up: reject() records no time at all, so there is no rejectedAt to project, although DreamingEvaluator already reads a rejected_at that nothing writes.
acknowledge, apply, retire), equal to the value the writer persisted, not to a hand-written fixture. Six mutations - each mapping nulled, one pointed at the wrong key, and the guidance mapping nulled - are each killed.composer ci: 413 tests, 1554 assertions, PHPStan clean; exact-head PR CI is the release gate.DreamService::run(DreamRequest): DreamOutcome, the typed entry point for a Dream run. It owns the validator → evolution → suppression → evaluation com
DreamService::run(DreamRequest): DreamOutcome, the typed entry point for a Dream run. It owns the validator → evolution → suppression → evaluation composition that previously lived only inside agent-learning dream, so embedding owners (agent-loop, and through it agent-ui) can run Dream and read its DreamRunResult, history projection and written candidate ids without rebuilding that sequence or parsing CLI output. A run is read-only unless writeCandidates is set, and even then only writes candidate Proposals; nothing becomes active guidance without the existing human review path.agent-learning dream delegates to DreamService; its text and JSON reports are unchanged.composer ci: 413 tests passed; exact-head PR CI is the release gate.RunLearningDecision::$reason is optional (non-empty-string|null); RunLearningDecisionStore::record() accepts no reason, and a blank one is stored as n
RunLearningDecision::$reason is optional (non-empty-string|null); RunLearningDecisionStore::record() accepts no reason, and a blank one is stored as null. The decision, Finding ids, and follow-up reference are the evidence. Existing records with a reason stay readable. Typed consumers must accept a null reason.outcome_missing for selected-but-unjudged guidance: an unjudged selection is neutral. The judged/selected metric remains and is now labelled "Outcome coverage".composer ci passed (409 tests, PHPStan clean); exact-head PR CI is the release gate.Record optional decision-time attribution on guidance outcome events (seen_before_decision, also_prescribed_by) and expose unconfounded helpful outcom
attribution on guidance outcome events (seen_before_decision, also_prescribed_by) and expose unconfounded helpful outcomes as GuidanceUsageSummary::$attributableHelpfulEventIds, the candidate set for a causal behavioral-value audit (#137).composer ci passed with 407 tests, 1,505 assertions, and PHPStan clean; PR #137 exact-head CI is the release gate.Add the Learning-owned agent-skill-promotion consumer skill for shaping bounded UPDATE_SKILL and CREATE_SKILL candidates through the existing prepare
agent-skill-promotion consumer skill for shaping bounded UPDATE_SKILL and CREATE_SKILL candidates through the existing prepare and proposal-import owner paths while preserving human Proposal authority (#135).composer ci passed with 394 tests, 1,478 assertions, and PHPStan clean.Add a typed, read-only human Finding intake projection that reports current status, validation state, classification, and owner-supported available_ac
available_actions after capture, review transitions, and classification (#131, #132).next_action, keeping validation and classification under explicit human/owner semantics (#131, #132).finding-classify unless the Finding is already validated, preventing classification from bypassing the released human-review boundary (#131, #132).FindingLifecycle::allowedTransitions() the single transition-policy source used by mutation and intake projection (#131, #132).Add agent-learning finding-capture for developers, product owners, and testers to record plain evidence-backed reports as unverified candidate Finding
agent-learning finding-capture for developers, product owners, and testers to record plain evidence-backed reports as unverified candidate Findings without a Session or caller-shaped evidence JSON (#128).validated, recording the validator and timestamp while retaining the original report lineage (#128).composer ci (388 tests, 1,430 assertions; PHPStan clean).Make Learning attention/backlog semantics LearningNote-aware: validated Findings already represented by a current LearningNote remain validated for fu
learn backlog through the typed LearningCatalog::overview() attention projection while preserving the historical validated-only backlog gate.Add typed, read-only LearningNote repository-evidence review through LearningNoteService::reviewEvidence() and agent-learning-note review, exposing ex
LearningNoteService::reviewEvidence() and agent-learning-note review, exposing exact recorded/current hashes and per-source evidence state without mutating or re-anchoring the note (#121, #122).composer ci passed with 382 tests, 1391 assertions, and PHPStan clean on PHP 8.4; the exact PR head also passed PHP 8.3 and 8.5 plus the installed-skill consumer and lineage rebuild benchmark.Discover configured learning_root from .agent-loop/init.json in LearningRootResolver::discover() and resolveExplicit(). When a consumer project config
learning_root from .agent-loop/init.json in LearningRootResolver::discover() and resolveExplicit(). When a consumer project configures a custom learning root in .agent-loop/init.json (such as infra/doc/agent-learning), CLI commands without an explicit --root automatically resolve that location instead of falling back to default .agent-loop/learning.composer ci passed with 381 tests, 1362 assertions, and PHPStan clean level 8.composer ci passed with 380 tests, 1360 assertions, and PHPStan clean level max.
composer ci passed with 380 tests, 1360 assertions, and PHPStan clean level max.proposal-reanchor repair a shared target whose proofs block each other: --supersede <drifted-id>=<replacement-id> retires a semantically drifted applied proof (with superseded_by, actor, reason and a retirement history record) in the same transaction that re-pins the remaining proofs, and validates the root once. The replacement must exist, be active and name the same target; it is not approved or applied. Previously a drifted proof and a stale proof on one file made every transition roll back.Report null / N/A for cohort latencies when no terminal transitions exist, rather than emitting a misleading 0.0d (#115).
null / N/A for cohort latencies when no terminal transitions exist, rather than emitting a misleading 0.0d (#115).composer ci passed with 374 tests, 1332 assertions, and PHPStan clean level max.Distinguish semantic guidance drift from stale shared-target proof drift for applied memory/skill guidance: validate the reviewed ADD/REPLACE/DELETE e
ADD/REPLACE/DELETE effect before treating a whole-file hash mismatch as repairable, and surface the existing target-scoped proposal-reanchor <target> --by ACTOR --reason TEXT recovery only when the guidance still matches (#118, #119).agent-learning-consumer skill and applied-guidance proof documentation with that owner recovery, while keeping re-anchoring explicit, attributed, transactional, and fail-closed when semantic guidance is missing or changed.main CI passed on the merged commit.Restore 0.18.8 changelog entry so release marker validation passes in release-tag workflow.
0.18.8 changelog entry so release marker validation passes in release-tag workflow.CorpusAnalyticsService with strict owner-evidence (active constraint manifests in constraints/active/, structural superseded_by, canonical phrases) and expose explicit RATIONALE_CORRECTED, OTHER_AUDITED_REASON, and UNKNOWN_LEGACY_REASON buckets (#116).HISTORICAL_ONLY/CURRENT_EVALUATION classification from consolidation analysis (#117).composer ci passed with 374 tests, 1330 assertions, and PHPStan clean level max.feat: add CorpusAnalyticsService, corpus-analyze CLI, and LearningCat…
feat: add CorpusAnalyticsService, corpus-analyze CLI, and LearningCat…
CorpusAnalyticsService and bin/agent-learning corpus-analyze command to compute workflow evolution by monthly cohorts (#115), proposal terminal lifecycle breakdown (#116), and consolidation distributions (#117).corpusAnalytics(): CorpusAnalysisResult on LearningCatalog for typed, read-only UI and agent integration.composer ci passed with 374 tests, 1330 assertions, and PHPStan clean level max.Add LearningClassification::NO_DURABLE_LEARNING to preserve pattern key and validation case lineage for validated Findings without creating promotion-
LearningClassification::NO_DURABLE_LEARNING to preserve pattern key and validation case lineage for validated Findings without creating promotion-ready soft guidance or Dream memory candidates when deterministic enforcement already owns the pattern (#112).agent-learning-consumer) and guidance tests with the NO_DURABLE_LEARNING classification (#114).composer ci passed with 372 tests, 1303 assertions, and PHPStan clean level max.Constraint generation now falls back to a bounded, engine-specific package precedent bundle only when a consuming project has no readable local exampl
composer ci passed with regressions proving PHPStan and PHP-CS-Fixer fallback
exports, local-example preservation, and the typed package resource boundary.Merge branch 'feature/scoped-package-resources'
Merge branch 'feature/scoped-package-resources'
PackageResources: consumerSkills() exposes consumer-facing skills (agent-learning-consumer, agent-learning-note, agent-hard-constraint-author, agent-learning-ctx-evidence), maintainerSkills() exposes agent-learning-maintainer, and consumerInstructionFragment() provides a concise AGENTS.md instruction fragment for consumer repositories.PackageResources: consumerSkills() exposes consumer-facing skills (agent-learning-consumer, agent-learning-note, agent-hard-constraint-author, agent-learning-ctx-evidence), maintainerSkills() exposes agent-learning-maintainer, and consumerInstructionFragment() provides a concise AGENTS.md instruction fragment for consumer repositories.proposal-retire --superseded-by <proposal-id> can retire an approved
memory/skill guidance record only when the named replacement is an applied
constraint with an active manifest and covers every source Finding from the
guidance it replaces. The retirement retains both approvals and adds exact
supersession lineage, so mechanically enforced rules can leave Recall's
prompt pool without an unreviewed deletion or a fabricated legacy proof.examples/constraints/
covering real-world PHPStan rules (custom rules, dynamic return type extensions,
type specifying extensions) and PHP-CS-Fixer fixers, complete with configuration
examples, test cases, and fixture structures (valid, invalid, boundary, and false-positive).examples.json few-shot usage and structural precedent guidance to
ConstraintGenerationPackageExporter: constraint generation packages now
instruct models to inspect structural precedents (rule/fixer interfaces, error builder
usage, AST traversal, type resolving, false-positive avoidance, registration, and fixture
isolation) tailored to the target constraint engine.project-phpstan-precision skill and update agent-hard-constraint-author to
guide deterministic AST/type analysis and robust rule authoring.ConstraintPromotionValidator now uses case-insensitive path segment matching for
sniffs and fixer target rule paths.composer ci passed with regression coverage for successful constrained
supersession, refusal without the required active applied constraint, generation
package prompt export with precedent guidance, and path segment validation.proposal-retire --superseded-by can retire an approved memory/skill guidance record only when the named replacement is an applied constraint with an a
proposal-retire --superseded-by <proposal-id> can retire an approved
memory/skill guidance record only when the named replacement is an applied
constraint with an active manifest and covers every source Finding from the
guidance it replaces. The retirement retains both approvals and adds exact
supersession lineage, so mechanically enforced rules can leave Recall's
prompt pool without an unreviewed deletion or a fabricated legacy proof.examples/constraints/
covering real-world PHPStan rules (custom rules, dynamic return type extensions,
type specifying extensions) and PHP-CS-Fixer fixers, complete with configuration
examples, test cases, and fixture structures (valid, invalid, boundary, and false-positive).examples.json few-shot usage and structural precedent guidance to
ConstraintGenerationPackageExporter: constraint generation packages now
instruct models to inspect structural precedents (rule/fixer interfaces, error builder
usage, AST traversal, type resolving, false-positive avoidance, registration, and fixture
isolation) tailored to the target constraint engine.project-phpstan-precision skill and update agent-hard-constraint-author to
guide deterministic AST/type analysis and robust rule authoring.ConstraintPromotionValidator now uses case-insensitive path segment matching for
sniffs and fixer target rule paths.composer ci passed with regression coverage for successful constrained
supersession, refusal without the required active applied constraint, generation
package prompt export with precedent guidance, and path segment validation.Add explicit post-capture Finding classification through FindingClassifier and the finding-classify CLI command. Raw observation capture remains separ
FindingClassifier and the finding-classify CLI command. Raw observation capture remains separate from reusable-learning triage, while supported tooling can now provide classification, pattern identity, and a validation case so LearningNoteService::promotionReadiness() can become satisfiable without schema edits or private-file mutation.LearningLineageService::precedentsForTask() can now accept task files and tags and applies that coarse relevance to active-note top-up candidates before the bounded capacity cut. Exact lineage precedents remain intact, legacy callers without task context keep their existing behavior, and expensive owner projection remains limited to selected candidates. This fixes the scale failure from #87 where lexicographically earlier unrelated note IDs consumed the entire top-up window and Recall never saw cross-task precedents that actually matched the task.composer ci plus the installed-skill consumer for the supported Finding classification path.composer ci, PHPStan, the bounded file/tag relevance regression, and the installed-skill consumer.lineage() and precedentsForTask() reconstruct a missing or stale derived lineage graph instead of refusing. The graph is a Learning-owned projection o
lineage() and precedentsForTask() reconstruct a missing or stale derived lineage graph instead of refusing. The graph is a Learning-owned projection of findings, proposals and active notes and holds no fact that cannot be recomputed, so refusing a read asked the caller to repair this package's own cache. The refusal also surfaced far from its cause: FindingCreator::createValidated() publishes a Finding and returns, so the command that failed was the next unrelated one that happened to consume lineage - in voku/agent-loop an edit-replay test that reports only Failed asserting that 1 is identical to 0 and never mentions lineage. LearningNoteService::publish() and retire() already rebuilt after changing durable state; this closes the same loop for the writers that do not. Repair is confined to the two recomputable states, named by LearningLineageProjectionUnavailable: an absent database, and one whose stored revision no longer matches durable state. Unreadable records, invalid durable data, a corrupt projection file and Learning state moving mid-rebuild all still fail closed, and verifyCurrent() deliberately keeps refusing so the diagnostic that exists to detect staleness can still detect it. Reported as voku/agent-loop#425.voku/agent-loop wrote a durable Finding and immediately ran the previously failing edit-replay and enter paths without a manual lineage rebuild.LearningLineageResult::toArray() now exposes lossless identity_depths entries with the identity carried as a string value instead of only as a PHP arr
LearningLineageResult::toArray() now exposes lossless identity_depths entries with the identity carried as a string value instead of only as a PHP array key. Numeric-looking task identities such as 403 therefore remain strings across the public owner boundary instead of being coerced to integer keys by PHP. The existing depth_by_identity_id projection remains temporarily for released-consumer compatibility while consumers move to the lossless form.composer ci, the numeric-task-id regression through LearningLineageService::precedentsForTask(), and the clean installed-skill consumer.LearningNoteService::promotionReadiness() and candidatePromotionReadiness() answer, read-only, whether a validated Finding could become a LearningNote
LearningNoteService::promotionReadiness() and candidatePromotionReadiness() answer, read-only, whether a validated Finding could become a LearningNote and which required inputs are absent. prepare() already enforced the rule, but only by throwing on the first thing it disliked, which tells a caller that has decided to promote what to fix and tells a caller asking "is any of this reusable yet" nothing at all. A Learning root can accumulate validated Findings for months while every one of them is unpromotable; without a read-only answer that store looks like durable knowledge and behaves like a write-only log. Blockers are reported together rather than one at a time, because a Finding missing three fields is a different amount of work from one missing a classification. prepare() now derives its refusals from the same evaluator, so the two cannot drift, and its exception messages are unchanged.LearningLineageService::precedentsForTask() now bounds active-note top-up work before projection: it deterministically enumerates active note identiti
LearningLineageService::precedentsForTask() now bounds active-note top-up work before projection: it deterministically enumerates active note identities, excludes lineage-selected notes, and decodes/evidence-checks only the remaining capacity instead of projecting the complete active-note corpus and discarding the excess afterwards.LearningTaskPrecedentResult now exposes precedents_truncated separately from lineage traversal truncation, so a capped active-note top-up cannot be mistaken for a complete owner observation when lineage.truncated is false.composer ci plus the clean installed-skill consumer.LearningLineageService::precedentsForTask() now returns active notes as candidate precedents up to maximumRelatedIdentities when a task has no direct
LearningLineageService::precedentsForTask() now returns active notes as candidate precedents up to maximumRelatedIdentities when a task has no direct findings or has remaining capacity, ensuring that new tasks receive relevant repository precedents without transient session memory.LearningLineageService::precedentsForTask() returns a bounded empty result when the given Learning root directory does not exist yet.LearningNoteRepository::loadActive() now reads only from the notes/active storage directory instead of scanning all statuses, preventing unreferenced or malformed retired notes from breaking active note loading.LearningNoteService::publish() and retire() now automatically rebuild the derived lineage graph so the lineage database remains current with published notes.bin/agent-learning and bin/agent-learning-note now resolve and require Composer's vendor/autoload.php before CLI execution to ensure external dependencies (such as voku/agent-graph) are properly autoloaded.lineage-rebuild CLI command to agent-learning for explicitly rebuilding the derived lineage graph.LearningLineageService::precedentsForTask() now treats a genuinely empty Learning root as an explicit bounded empty result when no derived lineage dat
LearningLineageService::precedentsForTask() now treats a genuinely empty Learning root as an explicit bounded empty result when no derived lineage database exists. Hosts that install agent-learning therefore do not become broken merely because they have no Learning records yet.voku/agent-recall-compiler#159.agent-learning#73 real-hundreds scale gate remains open and is not claimed by this patch release.Add the exact persisted finding_from_task lineage relation and LearningLineageService::precedentsForTask() so consumers can seed bounded precedent loo
finding_from_task lineage relation and LearningLineageService::precedentsForTask() so consumers can seed bounded precedent lookup from one task id without scanning the full Learning corpus. Task anchors are traversed only by the task-precedent API; ordinary lineage() stays record-focused and does not fan out through sibling Findings from the same task.LearningTaskPrecedentResult, which keeps the bounded lineage identities, relations, limits, and truncation state visible alongside selected active LearningNote projections.LearningNoteRepository::findActive() as a true point read for notes/active/<id>.json; task-precedent lookup decodes only LearningNote ids returned by bounded lineage and re-checks the Learning source generation after those reads.2 in the derived graph revision and fingerprint identity. Graphs built before the task-anchor relation therefore fail stale after upgrade instead of silently returning an incomplete task-precedent result.composer ci plus the clean installed-skill consumer. Regression coverage proves exact task anchoring, preserves ordinary record-lineage boundaries, surfaces truncation, and demonstrates that a malformed unrelated retired LearningNote is not decoded by the task-precedent path.agent-learning#73 hundreds-of-real-records acceptance remains open; this release adds the bounded owner API required for Recall dogfood and does not claim that external scale evidence.Add the Learning-owned lineage projection over released voku/agent-graph:^0.2.0, limited to six mechanically encoded relation kinds across Findings, P
voku/agent-graph:^0.2.0, limited to six mechanically encoded relation kinds across Findings, Proposals, and active LearningNotes. Projection is deterministic and deduplicated; no inferred similarity or generic graph mutation surface is introduced.LearningLineageService as the bounded typed owner API for rebuilding and querying the private derived lineage graph. Ordinary lineage() reads use a cheap Learning-source generation revision and expose only Learning identities, depths, and relations, while verifyCurrent() performs the deeper source-byte fingerprint and SQLite integrity verification.agent-graph 0.2.x dependency.composer ci plus the clean installed-skill consumer for deterministic rebuild/query parity, stale-generation rejection, cyclic boundedness/truncation, and explicit deep verification.agent-loop Learning root currently contains 72 projected durable records (53 Findings and 19 Proposals), so the separate agent-learning#73 hundreds-of-real-records scale acceptance remains open and is not claimed by this release.Allow FindingCreator::createValidated() to publish optional typed classification, pattern_key, and validation_case metadata through the Learning owner
FindingCreator::createValidated() to publish optional typed classification, pattern_key, and validation_case metadata through the Learning owner boundary, so hosts can create an ADD_LEARNING_NOTE Finding that is immediately consumable by LearningNoteService::prepare() without hand-writing Learning-private storage.Move package-owned skills from root skills/ to resources/skills/ following the shared repository layout convention. Introduce PackageResources as the
skills/ to resources/skills/ following the shared repository layout convention. Introduce PackageResources as the single owner of package-shipped asset paths.approve, reject, retire, acknowledge, apply and reanchor each allocated their next id by scanning the log they were about to append to, so two runs that both read before either appended agreed on the same sequence number and wrote duplicate audit ids - into immutable history, the one place this package cannot repair. The lock file is history/.transition.lock, beside the logs it guards, so a project that already ignores history/ ignores it too.proposal-reanchor repairs the applied memory/skill proofs pinned to one target file after it legitimately changed. applied_validation.target_content_h
proposal-reanchor <target> repairs the applied memory/skill proofs pinned to one target file after it legitimately changed. applied_validation.target_content_hash pins the whole target, so a shared guidance home such as MEMORY.md could not be edited again - not even to repair an evidence path a directory move invalidated - without every applied record on that file reporting drift it did not cause; retiring answered a curation question nobody asked and re-applying is closed to an applied record. The repair is scoped to the target rather than one proposal because the drift belongs to the file: repairing one of several proofs would leave the root invalid and could never commit. It re-pins the hash in one transaction and only while each proposal's own guidance wording is still present, keeps approval/application/validation evidence unchanged, and requires an explicit actor and reason recorded per proposal in history/reanchored-proposals.jsonl.history/reanchored-proposals.jsonl is validated like the other transition logs: every record must name a known proposal, a reason, who re-anchored it and when, plus the target it re-pinned and that target's sha256 - the proof is the whole content of the record, so an entry missing it cannot be checked against the repository it describes.proposal-reanchor matches proofs by the file they resolve to rather than by how they spell it. AppliedGuidanceTargetValidator accepts MEMORY.md and ./MEMORY.md as the same in-root target, so a root whose proofs used both forms repaired only one subset and then rolled the whole transaction back citing a proposal the caller had no way to name.release: prepare 0.14.2
release: prepare 0.14.2
TODO@... references remain valid; ambiguous two-segment and malformed IDs remain rejected.Expose complete read-only Finding and Proposal lists through LearningCatalog, with optional owner-status filtering and deterministic newest-first orde
LearningCatalog, with optional owner-status filtering and deterministic newest-first ordering, so consumers can browse Learning state without private storage scans.Add first-class evidence-backed LearningNote precedents for ADD_LEARNING_NOTE, with a typed Learning-owned prepare/publish/read/status/retire boundary
LearningNote precedents for ADD_LEARNING_NOTE, with a typed Learning-owned prepare/publish/read/status/retire boundary, stable pattern_key ownership, exact Finding/Proposal lineage, redaction, source-drift states, and compact read projections. LearningNotes are durable solved-case precedent, not active guidance or mutation authority.agent-learning-note authoring skill and standalone CLI. The flow grounds current facts separately from validated historical Findings and synthesis, preserves human promotion authority, and ships with clean copied Composer consumer proof plus real bug-shaped and architecture/workflow-shaped dogfood.finding-create failures report the accepted evidence types and missing requirements together so a caller can repair an invalid Finding in one round trip.outcome_withheld_reason through the typed RecallSelectionEvent owner API so downstream consumers do not need to inspect raw Learning JSONL history.agent-learning-note binary resolve Composer/package autoloading through the same supported path as the primary CLI instead of assuming a package-local vendor/autoload.php.Keep inactive and historical Learning records discoverable through the typed bounded overview by exposing deterministic recent finding and proposal ID
Add the typed read-only LearningCatalog query boundary with bounded overview, finding, proposal, durable-guidance and task-scoped projections. The cat
LearningCatalog query boundary with bounded overview, finding, proposal, durable-guidance and task-scoped projections. The catalog preserves owner lifecycle states, finding/proposal/task lineage, guidance usage outcomes, rejected/history visibility and explicit attention candidates without exposing Learning's directory layout to consumers.[*]: update the changelog
[*]: update the changelog
file targets and proof-less pre-policy memory/skill records remain readable, while new applications continue to require a modern memory, skill, or constraint target and current physical proof.Make Constraint generation-package export adoption-aware. When the configured target rule and every registration file already exist, export now record
mode: adopt_existing, keeps the validation commands and lineage evidence, omits synthetic PHP fixture expectations, and emits an adoption prompt instead of instructing the caller to generate duplicate enforcement.mode: generate: a missing target rule or any missing registration file preserves the existing generation package and four fixture expectations.composer ci on PHP 8.3 and 8.4 on the exact final head. Focused regressions cover existing enforcement adoption, missing-target fallback, and missing-registration fallback; CodeRabbit's only review finding requested the missing-registration regression and was addressed before merge.Match a PHPStan constraint's target_rule_path per path segment, case-insensitively and with backslashes normalised, instead of requiring the literal s
target_rule_path per path segment, case-insensitively and with backslashes normalised, instead of requiring the literal substring /PHPStan/. The previous check compared a filesystem path against a namespace-shaped fragment and rejected two legitimate layouts: a lowercase rule directory such as phpstan/Rules/, and any path whose first segment is the rule directory, which has no leading slash. Paths that merely contain the word, such as rules/phpstanish/, are still rejected.constraint as a durable target in the consolidation prompt, which previously offered only skill and learning-note outcomes while the result schema, promotion validator, generation exporter, activator and constraint-* commands all supported constraints. The prompt now requires deterministic enforceability, asks for the cheapest reliable owner, and states that proposing a constraint neither approves nor activates anything.Add FindingCreator::createValidated() as the typed owner-level operation for creating one schema-valid validated Finding, including optional ID alloca
FindingCreator::createValidated() as the typed owner-level operation for creating one schema-valid validated Finding, including optional ID allocation, target-directory creation, duplicate protection, validation-before-publication, and atomic publication.finding-create support over the same owner behavior for human and script callers.FindingCreator as the PHP lifecycle-host boundary so orchestrators do not need finding-id plus hand-written JSON or knowledge of the findings/validated storage layout.Findings can optionally identify an external Composer package with target_package and the tested package identity with tested_ref, allowing host repos
target_package and the tested package identity with tested_ref, allowing
host repositories to keep dogfood friction in the normal validated Finding
lifecycle instead of a parallel feedback document.FindingExporter emits deterministic, read-only JSON for one exact target
package. It preserves the raw Finding record and projects current source Run
IDs through the existing RunLearningDecision.finding_ids relation rather
than copying run_id onto Findings.src and tests.Breaking: a governed Run Learning decision may bind to the exact Contract revision, implementation snapshot, validation evidence digest, and review ev
agent-learning; repository snapshot semantics stay owned by the workflow
orchestrator.dream text output now prints selected-versus-judged guidance outcome completeness and warns with the exact selections still missing a judgement. Place
dream text output now prints selected-versus-judged guidance outcome
completeness and warns with the exact selections still missing a judgement.
Placeholder unknown rows therefore cannot make a human-facing report look
complete merely because every selected item has a draft row.RecordIdGenerator and agent-learning finding-id: record IDs are now allocated rather than guessed. Findings never had an allocator at all, and Proposa
RecordIdGenerator and agent-learning finding-id: record IDs are now
allocated rather than guessed. Findings never had an allocator at all, and
ProposalIdGenerator derived the next number by scanning the local
directory for the highest one - unique only for a writer who can see every
other writer. In a system built for several agents on several branches, two
branches each saw .004, each allocated .005, and each passed its own
validation because neither could load the other's file. The duplicate only
appeared at the merge, where renumbering is most expensive.ProposalIdGenerator is replaced by RecordIdGenerator.
Allocated IDs now carry a random suffix (proposal.2026-08-14.a3f2c1)
instead of a per-day sequence. The date prefix is unchanged, so a record set
still reads as a timeline.GuidanceCandidateProposalWriter now refuses to write over an existing
candidate. The free filename used to be guaranteed by the sequential
allocator's construction; an allocated suffix does not provide it, so the
guarantee is asserted rather than assumed.learn backlog stays clear.random_bytes rather than this package.RunLearningDecision, RunLearningDecisionStatus and RunLearningDecisionStore: agent-learning now owns the durable Learning close-out for a governed Run
RunLearningDecision, RunLearningDecisionStatus and
RunLearningDecisionStore: agent-learning now owns the durable Learning
close-out for a governed Run, keyed by run_id. Previously this lived in
agent-session, where pruning working memory destroyed the record that
explained why a Run had been allowed to close. A decision recorded here
outlives the Session it was reached in..agent-loop/learning, and
historical discovery of older locations is removed. A repository that kept
learning state elsewhere must migrate it or configure the root explicitly;
nothing is copied, symlinked or dual-read. See UPGRADING.md.Breaking: APPLIED Memory/Skill guidance must now prove that the reviewed mutation physically exists in a concrete canonical target under the configure
APPLIED Memory/Skill guidance must now prove that the reviewed
mutation physically exists in a concrete canonical target under the configured
project root. applied_validation requires target_source_ref and a SHA-256
target_content_hash; repository validation checks the real file and the
action postcondition before accepting the applied state.ADD requires the new wording to be present, REPLACE requires the new wording
to be present and the old wording absent, and DELETE requires the old wording
to be absent. A semantic proposal target alone is no longer sufficient proof
that application happened.ProposalTransitionManager::apply() now fails repository re-validation and
rolls back to approved when Memory/Skill application evidence is missing,
points outside the configured project root, references a missing file, carries
the wrong content hash, or does not satisfy the proposed mutation.docs/applied-guidance-proof.md describing the physical-proof contract,
the distinction between semantic target identity and repository source
evidence, and why canonical-source activation/retirement remain separate.[*]: update changelog for 0.8.12 release and refactor unit tests to u…
[*]: update changelog for 0.8.12 release and refactor unit tests to u…
Add explicit history-rebuild and history-status commands. They produce deterministic active-guidance.snapshot.json, chronicle.jsonl, and a source-dige
history-rebuild and history-status commands. They produce
deterministic active-guidance.snapshot.json, chronicle.jsonl, and a
source-digest manifest from immutable learning evidence, and fail clearly
when a compact projection is missing, corrupted, or stale.(compilation_id, guidance_id) identities, report no rate for an empty
selection history, count producer decision overlap before deduplication, and
use the reviewable decision population for the stale-candidate rate.Extend Agent Dreaming replacement detection with explicit newer-finding lineage, harmful-outcome corrected successors, and scope narrowing; extend con
All notable changes to voku/agent-learning will be documented in this file.
The format follows Keep a Changelog, and this project uses semantic versioning where practical.
Include the Dreaming value objects, evaluator, evidence-quality auditor, and replacement/conflict policies in the package archive. Version 0.8.8 wired
agent-loop learn dream to fail at runtime with a missing-class error.dream: a deterministic, human-reviewed guidance-maintenance command. It rebuilds its result from immutable recall-selection and guidance-outcome histo
dream: a deterministic, human-reviewed guidance-maintenance command. It
rebuilds its result from immutable recall-selection and guidance-outcome
histories; audits missing versus unknown outcomes, unresolved file evidence,
and aged findings; reports compact machine-readable or text output; and
writes no durable guidance unless --write-candidates is explicitly passed.
Reports deliberately omit generated timestamps so equal inputs produce
byte-stable output.REPLACEMENT_CANDIDATE production for exact, explicit applied
guidance successors, plus report-only CONFLICT records. A conflict needs
explicit conflicts_with lineage; unrelated wording under one pattern key is
not guessed to be contradictory.guidance-evaluate --write-candidates regenerated a proposal a reviewer had already declined. GuidanceCandidateProposalWriter::findExistingCandidate()
guidance-evaluate --write-candidates regenerated a proposal a reviewer had
already declined. GuidanceCandidateProposalWriter::findExistingCandidate()
only scanned proposals/candidate/, so once a candidate was rejected (and
therefore moved to proposals/rejected/) the next run no longer saw it and
wrote an identical decision under a fresh proposal ID. Rejecting five
auto-generated candidates and re-running the command immediately recreated
all five. The lookup now also scans proposals/rejected/ and
proposals/acknowledged/, so a terminal human decision suppresses
regeneration of the same guidance_id + decision_type + source_tier +
target_tier combination. approved/applied are deliberately not included:
those describe a change that landed, not a decision to stop proposing.ConstraintEngine::PHPCS (phpcs). Repositories that enforce constraints with PHP_CodeSniffer sniffs previously had no honest way to express that: the e
ConstraintEngine::PHPCS (phpcs). Repositories that enforce constraints with
PHP_CodeSniffer sniffs previously had no honest way to express that: the enum
offered only phpstan, php_cs_fixer, test, and ci, and php_cs_fixer
is a different tool (php-cs-fixer) with a different rule location and command,
so reusing it would have mislabeled the constraint. Discovered downstream in
a downstream repository, where a *_UnitCest.php constraint had to be a phpcs
sniff because phpstan.neon excludes *Cest.php from analysis entirely,
making an equivalent PHPStan rule unable to ever fire. ConstraintPromotionValidator
now applies the matching symmetry checks for the new engine: a phpcs
constraint requires a phpcs/codesniffer validation command and a
target_rule_path pointing at a /Sniffs/ location.SkillStalenessPolicy, MemoryToSkillPromotionPolicy, and SkillToConstraintPromotionPolicy did not propagate an approved proposal's scope_justification
SkillStalenessPolicy, MemoryToSkillPromotionPolicy, and
SkillToConstraintPromotionPolicy did not propagate an approved proposal's
scope_justification into the generated candidate's proposalExtras, unlike
MemoryStalenessPolicy (fixed in 0.8.4/d436428 for the memory tier only).
A guidance-evaluate --write-candidates run for a skill-tier STALE_CANDIDATE
or a memory-to-skill / skill-to-constraint PROMOTION_CANDIDATE whose source
proposal's scope was legitimately broader than any single cited finding
(and had a scope_justification explaining why) would hit
ProposalValidator's "proposal scope is broader than source finding
evidence without justification" check and abort the entire
write-candidates batch, including every other pending candidate. All three
policies now build proposalExtras['scope_justification'] from the source
proposal the same way MemoryStalenessPolicy does.guidance-evaluate now warns when history/outcomes.jsonl contains records in the older "outcome.*" session-summary shape (helpful/irrelevant/harmful as
guidance-evaluate now warns when history/outcomes.jsonl contains records
in the older "outcome." session-summary shape (helpful/irrelevant/harmful
as proposal-ID arrays, predating the per-guidance-item "guidance-outcome."
shape). OutcomeRepository::loadAll() already validated both shapes without
error, but GuidanceOutcomeEventRepository::load() -- and therefore every
GuidanceUsageProjector statistic and promotion/staleness decision -- has
always silently skipped anything that is not guidance-outcome.*. A
guidance item whose only recorded usage was in the older shape was
indistinguishable from one that was never used at all, with no signal that
older data was excluded. New GuidanceOutcomeEventRepository::countLegacyRecords()
makes that count explicit; guidance-evaluate now prints it before its
usual summary/decision output when the count is non-zero. This does not
migrate old records into the new shape (that would require fabricating
matching recall-selection linkage that never existed for them) -- it only
makes the existing exclusion visible instead of silent.Preserve an approved memory proposal's non-empty scope_justification when guidance-evaluate --write-candidates creates a stale-review candidate. This
scope_justification when
guidance-evaluate --write-candidates creates a stale-review candidate. This
keeps a valid broader scope reviewable instead of failing candidate validation
after the justification was dropped during projection.Your coding agent can read these notes before it upgrades. Set up the MCP server →