HOMELAB-1727: LD4.10 — skill & lore health scan (obsolescence probes + sampled observer) #600
No reviewers
Labels
No labels
operator-review
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Homelab/infra-core!600
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "plane/HOMELAB-1727-skillhealth"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes HOMELAB-1727 (LD4.10, epic LD4, slot 6).
Design brief:
.rpiv/artifacts/epic-ld4/design-LD4.10-skillhealth.md. Adjudication applied: corpus is a BUNDLE (files in / files out, no PVC coupling), the observer proposing its own rate is refused, retirement = empty-blob pin + roster shadow.What lands
Leg A — deterministic probes (0 tokens, seconds),
dispatcher/skillhealth/Resolverseam; a missing token thatgit log --diff-filter=Dsays a commit deleted is obsolescence (the candidate cites the sha), a never-existent token is a typo, and out of any repo a vanished parent directory is obsolescence (the livefleet-ops→~/.pi/workflows/model-tiers.jsonhit)last_verifiedvsgit log --sinceper claimed path; no baseline = ano-baselineFAULT, never a silent passEach candidate is a derivation that names the command which re-derives it (P-L7-011). Probes never file proposals.
Leg B —
routines/turns.go(the episode leg) + the zero-keys observer contractrole: user+ first blocktext+ text opening<skill name="…" location="…">); the 25 assistant records and 8toolResultrecords that also contain<skillare excluded structurally. Table test builds exactly those shapes.custom/custom_messageinjected context is not a turn; aparentSession(subagent brief) is not a correction; the generation cutoff is a required parameter; a firing whoselocationno longer resolves is routed to P-A1.probe-candidates.json,episodes.jsonl,skill-inventory.jsonwith bodies,lore-index.json,budget.json,manifest.json), drafts out as files (proposals.jsonl,episodes-verdicts.jsonl).ObserverSpec.Validaterefuses keys, a service account, Plane/NATS/Outline egress, a non-ephemeral sandbox, an inactive role.ReadProposalDraftsrefuses any edge butproposedand any actor butroutine:R-26— the observer can neither accept, apply nor revert, and the package imports no NATS client (asserted by a source-property test).The rate controller measures (observer bill vs harvested corrections,
unitValuewhen it exists,H × C_sotherwise) and recommends:CheckRateAuthorityrefuses anroutine:R-26-authored change to the registry file carrying its ownsampling.rate;RateChangeTransitionis only authorable byagent:architect; low signal never widens.The digest (
RenderDigest) refuses to render without the run row's owncostUsdand prints the mandatoryobserver cost: $… · N tokens · M episodes · $/episode · harvested H · budget $Bline.The teardown drain (the 4.6 handoff): idempotent by byte digest, carrying the spool to
fleet.ledger.friction.line,proposals.jsonlthrough the dispatcher'sLedgerWriter, and the verdict file back as evidence.Registry: R-26 activates its reserved shape,
status: pausedwithpausedReason: "dep:routine-executor, dep:LD4.10-observer-bundle"(the Executor is not in this ticket's scope — R-25/R-28/R-30's posture),sampling.rate: 0.25as the ceiling,observerCostVisible: true.Policy book:
P-L7-001…014land inroutines/policy.mdand inBook(), soTestPolicyBookMatchesDocholds them likeP-ROUT-*; the wiring table gains the L7 rows (and the P-ROUT-006 row now cites a file that exists — the probe found a real pre-existing drift hit).4.6 handoff items closed: teardown drain (generalised, above) ·
roles.jsoninclude: ["friction"]for both pi-running roles · theP-L7-001…014block · the reverted-binding defect (Bindings.Validatenow acceptssample: 0iff a verdict closed it — a persisted reverted document is loadable).The three Verify lines, mechanized
TestVerifyLine1…): a real git repo with a real deletion and a skill citing it → exactly oneproposedtransition,baseRev= the HEAD blob pin,candidateRev= the empty blob, evidence namingprobe:P-A1and the deleting commit, a roster shadow that validates against the landedcontext-bindings/v1schema; a second identical run files nothing and prints the deferral.TestVerifyLine2…): the fixture session (firing + operator turn, with an assistant quote, a toolResult and acustom_messageas false fires) samples to one episode, the zero-keys spec validates, and the observer's draft lands as oneproposedskill-class proposal quoting the correction and citing both lines.TestVerifyLine3…): the digest's cost line equals the cost re-derived independently from the session file byReadSessionFile; a run with no measured spend refuses to render a digest.Evidence / real-corpus integration
TestLiveFleetOpsHitIsObsolescenceEvidence).<skilloccurrences vs 0 detector firings — the exclusion is not theoretical. The full corpus yields 3 firings, matching the brief's measurement.routines/policy.mdparses with every P-L7 row wired and the probe reports nothing.go test -race ./...andgo vet ./...green inlab-director/dispatcher(skillhealth 106s under race; the real-corpus census is bounded to a 150-file slice and skipped in-short).Not in this PR (stated, not implied)
The
routines.Executor; the sandbox launcher behindObserverRunner; a declaration-strict symbol index for P-A4 (bag-of-words today, recorded inBuildSymbolIndex); the architect's ratification of a rate change. The/frictionactor defect (pi-frictionstampsactor.kind: agenton the human command path) lives inpi-extensions, so it lands as a second PR againstmainthere, not here.Review —
b631233(HOMELAB-1727 · LD4.10)verdict: blocker — the code review is clean; the PR cannot land because it no longer merges.
Blocker: content conflict against the current
epic/LD4tipbase.shais now43ae78e(LD4.11 / R-27, PR #598 merged after this branch was cut from7496e07), andgit merge-treereports a content conflict in exactly three files:lab-director/dispatcher/routines/policy.golab-director/dispatcher/routines/policy.mdlab-director/dispatcher/routines/registry.yamlAll three are adjacent-insert clashes, not semantic ones:
policy.go— ours inserts theP-L7-001…014block into the const block,citedLineIDs, andBook(); #598 insertsLineFitnessNeverAGate = "P-ROUT-013"at the same three anchors. Resolution = keep both.policy.md— the L7 wiring rows vs. theP-ROUT-013wiring row, same insertion point. Keep both.registry.yaml— both rewrite theRESERVED IDSparagraph and append a tail entry (ours R-26, theirs R-27). Merge the paragraph, keep both entries.Not resolvable by API (
mergeable: false); needs a merge/rebase commit → new head → re-CI and a re-check of the delta. Flagging to the orchestrator rather than hand-merging so the review verdict stays attached to a reviewed sha.Focus items 1–9 — all pass on the reviewed sha
IsFiringrequires the full conjunction (typemessage∧ roleuser∧content[0].type == text∧ text opens<skill name="…" location="…");turns_test.gobuilds exactly 25 assistant + 8toolResultfalse-fire records and asserts one firing. PASS.TestVerifyLine1uses a real temp git repo with a realgit rm+commit; asserts oneproposedtransition,candidateRev == EmptyBlobPin,baseRev== the re-derived HEAD blob pin, evidence namingprobe:P-A1andgit:<sha>, a roster-shadow binding that validates against the landedcontext-bindings/v1schema, and a second identical run filing nothing (printed asalready-proposed). PASS.RenderDigestreturnsErrNoObserverCostwhenRun.CostUSD == nil, andRun()refuses outright on!Spend.Measured; V3 re-derives the cost independently viaReadSessionFile. PASS.ParseProposalLinesrefuses any edge butproposedand any actor butroutine:R-26;go listshows no NATS import (only a transitivenetvia deps, no socket call sites);TestNoRateWriteNoPauseNoSelfAcceptInThisPackagebansnats.Connectand every forbidden state edge as a source property. PASS.RecommendRatereturns aRateRecommendation; theHarvested == 0branch never widens (hold, or halve bounded by floor);CheckRateAuthorityrefuses an R-26-authored registry change and is wired atrun.go:320;RateChangeTransitionrefuses any author butagent:architect. PASS.RosterBindingsetsVariantPin = EmptyBlobPin,RosterExclude), restore path proven by the landedRevertAndProvebyte-for-byte test. PASS on mechanism (see concern below).proposals.jsonlthrough the dispatcher's writer;roles.jsonaddsfrictionto both pi-running roles (builder-core,light);P-L7-001…014land inBook()andpolicy.mdwith the parity test;/frictionoperator-line test lands here (actor defect itself is the pi-extensions PR #63). The P-ROUT-006 wiring row now cites a file that exists. PASS.status: paused,pausedReason: "dep:routine-executor, dep:LD4.10-observer-bundle",sampling.rate: 0.25as ceiling,observerCostVisible: true,runsAs: architect(an active v1 role); "Active today (5)" is unchanged, which is correct for a paused routine. PASS.go vetclean andgo test -race -count=1green locally: skillhealth 106.8s, routines/friction/teardown green. CI run #1817 green onb631233. PASS.Concerns (non-blocking, posted per the landing contract)
friction/shadow.go'sBindings.Validatewas loosened to admitsample == 0iffVerdictAt != nil, butfriction/shadow_test.gois untouched: the table still has only the negativesample_zerocase, and no test persists+reloads a reverted binding. The handoff item "a persisted reverted document is loadable" is implemented but not asserted. One positive case (Sample = 0+VerdictAtset →Validate()nil) would close it.SessionTurns.Episodeskeeps a firing whoseAtis zero (!f.At.IsZero() && …Before(cutoff)), whileCensorSkillsdrops it (f.At.IsZero() || …Before(since)). Undated firings can therefore enter leg B's episode population despite the required generation scope. Low likelihood on the real corpus, but the two legs disagree on the same input.CI verified green at head
b631233(run #1817: dispatcher-test 2m17s, alert-receiver-test, helm-validate, receiver-test all pass). Merge not attempted successfully — blocker above.b631233d3a7db2def425Integration note — rebase onto the new epic tip
The base went stale when LD4.11 / R-27 (PR #598) landed beneath this branch. Rebased and re-pushed.
43ae78e(epic/LD4 tip, the #598 merge), 11 commits replayed cleanly on top.b631233→7db2def(new head).lab-director/dispatcher/routines/policy.go— kept theP-ROUT-013/LineFitnessNeverAGateconst, theCitedLineIDsentry and theBook()entry and the wholeP-L7-001…014block in all three places (const block, cited-id list, book text). Book order staysP-ROUT-001…013thenP-L7-001…014.lab-director/dispatcher/routines/policy.md— kept theP-ROUT-013wiring row and the eightP-L7-*wiring rows. Also carried this branch's intentional reword of theP-L7-001/002row and theP-ROUT-006→routines/sessions.gocorrection from0ebd810.lab-director/dispatcher/routines/registry.yaml— header paragraph rebuilt to the true state: R-23 is the only remaining reserved id; R-26 landed (HOMELAB-1727, LD4.10, PAUSEDdep:routine-executor) and R-27 landed (HOMELAB-1731, LD4.11, weekly/ACTIVE/script) with the 2026-09-28 operator ruling retained.lab-director/dispatcher):go build ./...OK ·go vet ./...OK ·go test -race ./...all packages ok (routines 3.3s, skillhealth 106s, fitness 3.7s) — no flakes, no-p 1needed.The prior CLEAN code review (all 9 focus items) carries over unchanged — this is a pure rebase, no semantic change beyond the union resolutions above. Integration worker does not merge; handing back to the orchestrator.
CI on the rebased head
7db2def— run #1819: pass (dispatcher-test 2m17s, receiver-test 6s, alert-receiver-test 21s, helm-validate 6s). Head is green for the landing lock. Note for the merge: the CLEAN review verdict predates this head sha, so re-post it against7db2defbefore merging.Landing verdict — head
7db2def(carried review, after-head posting)The CLEAN code review (all 9 focus items PASS — episode-detector conjunction with the 25/8 false-fire table, planted-obsolete-skill retirement path with empty-blob pin + roster shadow, digest cost refusal, sole-ledger-writer/no-NATS as a source property, recommend-only rate controller with self-rate-change refused, 4.6 handoff items closed, R-26 paused entry, no sockets, race green) was posted at
b631233. This head7db2defdiffers fromb631233ONLY by the union rebase documented in the integration note above (policy.go/policy.md/registry.yaml keep-both resolutions; no semantic change). CI run #1819 green on7db2def. Verdict: APPROVE, no blockers, carried to this head.Resolved B1 — the "content conflict against the current epic/LD4 tip" blocker (stale base after #598/R-27 landed) is resolved: union rebase pushed (
b631233→7db2def, integration note above), conflicts in policy.go / policy.md / registry.yaml resolved keep-both,go build/vet/go test -race ./...green, CI run #1819 green on7db2def, and the CLEAN review carried to the new head in the verdict comment above. No semantic change beyond the union resolutions.7db2def425a561d23b5bSecond union rebase onto
a3d3b5a(#599/R-23): new heada561d23— same keep-both resolutions (P-L7 + P-ROUT-013 inpolicy.go/policy.md; registry header now reads the true state, 30 registered: R-23…R-30 all landed, reserved list empty),go build/vet/test -racegreen; prior CLEAN review carries.Head now
a561d23(second union rebase ontoa3d3b5aafter #599/R-23 landed — registry header to 30 registered, reserved list empty; policy files kept both blocks). The carried CLEAN review + the APPRove verdict above stand; this head differs only by that rebase. CI pending ona561d23— merge follows when green.Review —
a561d23verdict: pass — the CLEAN review (all 9 focus items, posted in full at
b631233and carried through both union rebases) stands for this head.a561d23differs from the reviewed tree only by the two documented keep-both rebases (registry header to 30 registered; policy blocks intact). CI: commit status success (4/4) ona561d23. No blockers.