Recognize current required-skill attachment-state disclosures

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-11-06 01:50:45 -06:00
1 parent bb9f4aa127
commit 7361878d30
2 files changed
+35 -4

No files matched your search

@@ -506,3 +506,31 @@ it("accepts a current exact-skill unresolved installation disclosure without inv
actual + " The doc-maintenance skill is now installed.",
]) expect(truthfulTeamPartial(contradiction, target, true)).toBe(false);
});
it("accepts a current named attachment-state caveat while retaining missing-state contradictions", () => {
const target = { slug: "doc-maintenance", name: "doc-maintenance" };
const actual = "One remaining caveat: the `doc-maintenance` skill is still unattached because your company already has a different skill using that slug; I preserved the existing one.";
expect(truthfulTeamPartial(actual, target, true)).toBe(true);
expect(truthfulTeamPartial("The doc-maintenance skill remains not attached due to the occupied local slug.", target)).toBe(true);
const o = valid();
o.state.blockedSkill = target;
o.phaseReplies[0]!.body = "The existing CTO was preserved and the new role renamed CTO 2. The doc-maintenance skill could not attach because the company already has a different skill using that slug.";
o.phaseReplies[1]!.body = actual;
expect(gradeTeamConditions(o).passed).toBe(true);
for (const invalid of [
"A different skill is still unattached because its slug conflicts. The team is installed.",
"Earlier, the doc-maintenance skill was unattached because its slug was occupied.",
actual + " All required skills are installed.",
actual + " No unresolved doc-maintenance conflicts remain.",
actual + " The doc-maintenance skill is now installed.",
actual + " The doc-maintenance skill is now attached.",
]) expect(truthfulTeamPartial(invalid, target, true), invalid).toBe(false);
});
it("rejects a current unattached claim when independent saved recovery is proven", () => {
for (const phase of [0, 1]) {
const { o } = recovered();
o.phaseReplies[phase]!.body = "The existing CTO was preserved and the new role renamed CTO 2. Launch Checklist is still unattached because its local slug conflicts.";
expect(gradeTeamConditions(o, phase === 0 ? "initial" : "complete").passed).toBe(false);
}
});
@@ -219,7 +219,7 @@ function historicalTeamSkillClause(clause: string, skill: Pick<CatalogSkill, "sl
function assertedTeamRecovery(clause: string, skill: Pick<CatalogSkill, "slug" | "name">): boolean {
if (historicalTeamSkillClause(clause, skill)) return false;
const subject = `(?:${teamSkillSubject(skill)})`, modifiers = `(?:(?:now|already|currently|successfully|fully|completely)\\s+){0,2}`;
const claims = new RegExp(`\\b${subject}\\s+(?:skill\\s+)?(?:(?:is|are|was|were|(?:has|have)\\s+${modifiers}been)\\s+)?${modifiers}(?:installed|available|resolved|recovered)\\b|\\b(?:installed|resolved|recovered|fixed|cleared)\\s+(?:the\\s+)?${subject}\\b|\\b(?:${subject}\\s+(?:skill\\s+)?(?:installation|install)|(?:the\\s+)?(?:installation|install)\\s+of\\s+${subject})\\s+(?:has\\s+)?${modifiers}(?:succeeded|completed)\\b`, "gi");
const claims = new RegExp(`\\b${subject}\\s+(?:skill\\s+)?(?:(?:is|are|was|were|(?:has|have)\\s+${modifiers}been)\\s+)?${modifiers}(?:installed|attached|available|resolved|recovered)\\b|\\b(?:installed|attached|resolved|recovered|fixed|cleared)\\s+(?:the\\s+)?${subject}\\b|\\b(?:${subject}\\s+(?:skill\\s+)?(?:installation|install)|(?:the\\s+)?(?:installation|install)\\s+of\\s+${subject})\\s+(?:has\\s+)?${modifiers}(?:succeeded|completed)\\b`, "gi");
return [...clause.matchAll(claims)].some(match => !negatedTeamRecovery(clause.slice(0, match.index)));
}
function truthfulTeamRecovery(reply: string, skill: Pick<CatalogSkill, "slug" | "name">): boolean {
@@ -227,8 +227,8 @@ function truthfulTeamRecovery(reply: string, skill: Pick<CatalogSkill, "slug" |
}
function claimsRecoveredSkillMissing(reply: string, skill: Pick<CatalogSkill, "slug" | "name">): boolean {
const subject = `(?:${teamSkillSubject(skill)})`, modifiers = `(?:(?:yet|now|currently|still|successfully|fully|completely)\\s+){0,2}`;
const denied = `(?:(?:(?:is|are|was|were|has|have|had|could|can)\\s+(?:(?:now|currently|still)\\s+)?not|(?:isn't|aren't|wasn't|weren't|hasn't|haven't|couldn't|can't))\\s+${modifiers}(?:(?:be|been)\\s+)?${modifiers}(?:installed|available|usable))`;
const current = new RegExp(`\\b${subject}\\s+(?:skill\\s+)?(?:${denied}|(?:(?:is|are|remains?|still)\\s+)?(?:(?:now|currently|still)\\s+)?(?:not\\s+${modifiers}(?:installed|available|usable)|uninstalled|missing|unavailable|blocked)|(?:installation|install)\\s+(?:has\\s+)?failed)\\b`, "i");
const denied = `(?:(?:(?:is|are|was|were|has|have|had|could|can)\\s+(?:(?:now|currently|still)\\s+)?not|(?:isn't|aren't|wasn't|weren't|hasn't|haven't|couldn't|can't))\\s+${modifiers}(?:(?:be|been)\\s+)?${modifiers}(?:installed|attached|available|usable))`;
const current = new RegExp(`\\b${subject}\\s+(?:skill\\s+)?(?:${denied}|(?:(?:is|are|remains?|still)\\s+)?(?:(?:now|currently|still)\\s+)?(?:not\\s+${modifiers}(?:installed|attached|available|usable)|uninstalled|unattached|missing|unavailable|blocked)|(?:installation|install)\\s+(?:has\\s+)?failed)\\b`, "i");
const deniedObject = new RegExp(`\\binstalled\\s+(?:the\\s+)?${subject}\\b`, "gi");
return namedTeamSkillClauses(reply, skill, true).some(clause => !historicalTeamSkillClause(clause, skill) && (current.test(clause)
|| [...clause.matchAll(deniedObject)].some(match => negatedTeamRecovery(clause.slice(0, match.index)))));
@@ -299,7 +299,10 @@ export function truthfulTeamPartial(
return !!claim && !historicalTeamSkillClause(clause, skill)
&& !negatedTeamRecovery(clause.slice(0, claim.index)) && /\b(?:installation|install|conflict|collision|slug)\b/i.test(clause);
});
const reported = named && (occupied || contextual || unresolved || clauses.some(clause => !historicalTeamSkillClause(clause, skill)
const unattached = clauses.some(clause => !historicalTeamSkillClause(clause, skill)
&& new RegExp(`\\b${teamSkillSubject(skill)}\\s+(?:skill\\s+)?(?:(?:is|remains?)\\s+)?(?:(?:currently|still|now)\\s+)?(?:unattached|not\\s+attached)\\b`, "i").test(clause)
&& /\b(?:conflict|collision|slug)\b/i.test(clause));
const reported = named && (occupied || contextual || unresolved || unattached || clauses.some(clause => !historicalTeamSkillClause(clause, skill)
&& /could not|couldn't|cannot|failed|conflict|blocked|not installed|missing|unavailable|unable|partial/i.test(clause)));
const falseFull = reply.split(/[.!?;\n]+|\bbut\b/i).some((clause) => {
const claim =