From fe25e606b886f920bb0a04229931e557fc1ed9bb Mon Sep 17 00:00:00 2001 From: GRayHook Date: Wed, 17 Jun 2026 17:28:32 +0700 Subject: [PATCH] =?UTF-8?q?Phase=2014:=20split=20kept-window=20filter=20fr?= =?UTF-8?q?om=20registry=20gate=20=E2=80=94=20defer=20planning=20stage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit filterSkillsNeedingReinjectByKept tracks absent skills without registeredNames; immediate path keeps the combined filter. Co-authored-by: Cursor --- src/kept.ts | 24 +++++++++++++++++------- test/kept-window.test.ts | 18 ++++++++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/src/kept.ts b/src/kept.ts index 6855872..be72a9f 100644 --- a/src/kept.ts +++ b/src/kept.ts @@ -56,17 +56,27 @@ export function skillsPresentInKeptWindow( return present; } +/** Tracked skills absent from kept window — defer planning stage without registry filter (Phase 14 / B-002). */ +export function filterSkillsNeedingReinjectByKept( + tracked: readonly TrackedSkill[], + keptPresent: ReadonlySet, +): string[] { + const needing: string[] = []; + for (const skill of tracked) { + if (!keptPresent.has(skill.name)) { + needing.push(skill.name); + } + } + return needing; +} + /** Tracked skills missing from kept window but still registered (SPEC §5.2, §6.4). */ export function filterSkillsNeedingReinject( tracked: readonly TrackedSkill[], keptPresent: ReadonlySet, registeredNames: ReadonlySet, ): string[] { - const needing: string[] = []; - for (const skill of tracked) { - if (registeredNames.has(skill.name) && !keptPresent.has(skill.name)) { - needing.push(skill.name); - } - } - return needing; + return filterSkillsNeedingReinjectByKept(tracked, keptPresent).filter((name) => + registeredNames.has(name), + ); } diff --git a/test/kept-window.test.ts b/test/kept-window.test.ts index 72633dd..431e05b 100644 --- a/test/kept-window.test.ts +++ b/test/kept-window.test.ts @@ -2,6 +2,7 @@ import type { SessionEntry } from "@earendil-works/pi-coding-agent"; import { describe, expect, it } from "vitest"; import { filterSkillsNeedingReinject, + filterSkillsNeedingReinjectByKept, getKeptEntries, skillsPresentInKeptWindow, } from "../src/kept"; @@ -95,6 +96,23 @@ describe("skillsPresentInKeptWindow", () => { }); }); +describe("filterSkillsNeedingReinjectByKept", () => { + it("returns tracked skills absent from kept window regardless of registration", () => { + const keptPresent = new Set(["alpha"]); + expect(filterSkillsNeedingReinjectByKept(trackedSkills, keptPresent)).toEqual(["beta"]); + }); + + it("preserves tracked order including unregistered names", () => { + const keptPresent = new Set(); + expect(filterSkillsNeedingReinjectByKept(trackedSkills, keptPresent)).toEqual(["alpha", "beta"]); + }); + + it("returns empty when all tracked skills are in kept window", () => { + const keptPresent = new Set(["alpha", "beta"]); + expect(filterSkillsNeedingReinjectByKept(trackedSkills, keptPresent)).toEqual([]); + }); +}); + describe("filterSkillsNeedingReinject", () => { it("returns registered tracked skills absent from kept window", () => { const keptPresent = new Set(["alpha"]);