@@ -10,6 +10,8 @@ const {
1010 detectIssueKind,
1111 validateIssue,
1212 shouldReopen,
13+ shouldEnforceClosure,
14+ labelForKind,
1315} = require ( "./issue-quality.cjs" ) ;
1416
1517// ---------------------------------------------------------------------------
@@ -517,3 +519,234 @@ describe("shouldReopen", () => {
517519 assert . equal ( shouldReopen ( baseBotState , issue , false ) , true ) ;
518520 } ) ;
519521} ) ;
522+
523+ describe ( "shouldEnforceClosure" , ( ) => {
524+ it ( "enforces when there is no bot state yet" , ( ) => {
525+ assert . equal ( shouldEnforceClosure ( null ) , true ) ;
526+ } ) ;
527+
528+ it ( "enforces while the bot still owns an active closure" , ( ) => {
529+ assert . equal (
530+ shouldEnforceClosure ( {
531+ version : 2 ,
532+ active : true ,
533+ kind : "feature" ,
534+ closedAt : "2026-07-20T10:00:00Z" ,
535+ stateReason : "not_planned" ,
536+ } ) ,
537+ true ,
538+ ) ;
539+ } ) ;
540+
541+ it ( "does not enforce after a maintainer override" , ( ) => {
542+ assert . equal (
543+ shouldEnforceClosure ( {
544+ version : 2 ,
545+ active : false ,
546+ kind : "feature" ,
547+ closedAt : "2026-07-20T10:00:00Z" ,
548+ stateReason : "not_planned" ,
549+ maintainerOverride : true ,
550+ } ) ,
551+ false ,
552+ ) ;
553+ } ) ;
554+
555+ it ( "still enforces after a normal active:false without maintainer override" , ( ) => {
556+ assert . equal (
557+ shouldEnforceClosure ( {
558+ version : 2 ,
559+ active : false ,
560+ kind : "feature" ,
561+ closedAt : "2026-07-20T10:00:00Z" ,
562+ stateReason : "not_planned" ,
563+ } ) ,
564+ true ,
565+ ) ;
566+ } ) ;
567+ } ) ;
568+
569+ // ---------------------------------------------------------------------------
570+ // Translated / soft-pass / labels
571+ // ---------------------------------------------------------------------------
572+
573+ describe ( "translated feature headings and soft-pass" , ( ) => {
574+ it ( "accepts Goal / Problem + Expected behaviour as a valid feature" , ( ) => {
575+ const body = [
576+ "### Goal / Problem" ,
577+ "Codex App rejects image paste for noVisionModels before the vision sidecar can run." ,
578+ "### Expected behaviour" ,
579+ "Catalog should advertise image input when the vision sidecar covers the model." ,
580+ "### Environment" ,
581+ "opencodex 2.7.36 on macOS with Codex App." ,
582+ ] . join ( "\n" ) ;
583+ const result = validateIssue ( {
584+ title : "[Feature]: Auto-advertise image inputModalities for noVisionModels" ,
585+ body,
586+ labels : [ ] ,
587+ } ) ;
588+ assert . equal ( result . kind , "feature" ) ;
589+ assert . equal ( result . valid , true , `Expected valid but got: ${ result . reasons . join ( "; " ) } ` ) ;
590+ assert . equal ( result . softPass , false ) ;
591+ } ) ;
592+
593+ it ( "soft-passes [Feature]: with rich custom headings outside the alias map" , ( ) => {
594+ const body = [
595+ "### Concrete user workflow that fails" ,
596+ "User pastes an image in Codex App while a text-only routed model is selected and the App blocks upload." ,
597+ "### Why this matters" ,
598+ "Vision sidecar is advertised but never reached from the App client path." ,
599+ "### Verification" ,
600+ "Same proxy config works end-to-end in Claude Code with the sidecar describing the image." ,
601+ ] . join ( "\n" ) ;
602+ const result = validateIssue ( {
603+ title : "[Feature]: Vision sidecar unusable from Codex App" ,
604+ body,
605+ labels : [ ] ,
606+ } ) ;
607+ assert . equal ( result . kind , "feature" ) ;
608+ assert . equal ( result . softPass , true ) ;
609+ assert . equal ( result . valid , false ) ;
610+ } ) ;
611+
612+ it ( "still rejects empty [Feature]: bodies" , ( ) => {
613+ const result = validateIssue ( {
614+ title : "[Feature]: do something cool" ,
615+ body : "please add this" ,
616+ labels : [ ] ,
617+ } ) ;
618+ assert . equal ( result . kind , "feature" ) ;
619+ assert . equal ( result . valid , false ) ;
620+ assert . equal ( result . softPass , false ) ;
621+ } ) ;
622+
623+ it ( "does not treat a title containing problem as a bug" , ( ) => {
624+ assert . equal (
625+ detectIssueKind ( {
626+ title : "Problem with documentation wording" ,
627+ body : "The docs are confusing about install." ,
628+ labels : [ ] ,
629+ } ) ,
630+ null ,
631+ ) ;
632+ } ) ;
633+
634+ it ( "does not soft-pass long unstructured bodies without headings" , ( ) => {
635+ const result = validateIssue ( {
636+ title : "[Feature]: please add thing" ,
637+ body : "x" . repeat ( 250 ) ,
638+ labels : [ ] ,
639+ } ) ;
640+ assert . equal ( result . kind , "feature" ) ;
641+ assert . equal ( result . softPass , false ) ;
642+ assert . equal ( result . valid , false ) ;
643+ } ) ;
644+
645+ it ( "does not classify Expected behaviour + Example as feature without a feature hint" , ( ) => {
646+ assert . equal (
647+ detectIssueKind ( {
648+ title : "Something broke in the proxy" ,
649+ body : [
650+ "### Expected behaviour" ,
651+ "Proxy should return 200." ,
652+ "### Example" ,
653+ "curl localhost:10100/v1/responses" ,
654+ ] . join ( "\n" ) ,
655+ labels : [ ] ,
656+ } ) ,
657+ null ,
658+ ) ;
659+ } ) ;
660+
661+ it ( "classifies alias headings as feature when a goal heading is present" , ( ) => {
662+ assert . equal (
663+ detectIssueKind ( {
664+ title : "Advertise image input for sidecar models" ,
665+ body : [
666+ "### Goal / Problem" ,
667+ "App blocks images before the sidecar runs." ,
668+ "### Expected behaviour" ,
669+ "Catalog should advertise image input." ,
670+ ] . join ( "\n" ) ,
671+ labels : [ ] ,
672+ } ) ,
673+ "feature" ,
674+ ) ;
675+ } ) ;
676+
677+ it ( "lets a strong bug form override a stale stored feature kind" , ( ) => {
678+ const result = validateIssue ( {
679+ title : "Crash on start" ,
680+ body : [
681+ "### Client or integration" ,
682+ "Codex CLI" ,
683+ "### Summary" ,
684+ "Proxy segfaults on ARM64 when streaming is enabled." ,
685+ "### Reproduction" ,
686+ "ocx start on Raspberry Pi 4, send any streaming request." ,
687+ "### Version" ,
688+ "2.7.36" ,
689+ "### Operating system" ,
690+ "Linux" ,
691+ ] . join ( "\n" ) ,
692+ labels : [ "bug" ] ,
693+ storedKind : "feature" ,
694+ } ) ;
695+ assert . equal ( result . kind , "bug" ) ;
696+ assert . equal ( result . valid , true , `Expected valid bug but got: ${ result . reasons . join ( "; " ) } ` ) ;
697+ } ) ;
698+
699+ it ( "accepts US spelling Expected behavior as a behaviour alias" , ( ) => {
700+ const result = validateIssue ( {
701+ title : "[Feature]: Auto-advertise image inputModalities" ,
702+ body : [
703+ "### Goal / Problem" ,
704+ "App blocks images before the vision sidecar can run." ,
705+ "### Expected behavior" ,
706+ "Catalog should advertise image input when the sidecar covers the model." ,
707+ ] . join ( "\n" ) ,
708+ labels : [ ] ,
709+ } ) ;
710+ assert . equal ( result . kind , "feature" ) ;
711+ assert . equal ( result . valid , true , `Expected valid but got: ${ result . reasons . join ( "; " ) } ` ) ;
712+ } ) ;
713+
714+ it ( "does not treat enhancement + non-goal aliases as a feature detect hit" , ( ) => {
715+ assert . equal (
716+ detectIssueKind ( {
717+ title : "Something odd in the proxy" ,
718+ body : [
719+ "### Current limitation" ,
720+ "No fallback provider today for upstream 5xx responses." ,
721+ "### Expected behaviour" ,
722+ "Auto failover to a backup provider." ,
723+ ] . join ( "\n" ) ,
724+ labels : [ "enhancement" ] ,
725+ } ) ,
726+ null ,
727+ ) ;
728+ } ) ;
729+
730+ it ( "does not let a weak title-prefix detection override stored documentation kind" , ( ) => {
731+ assert . equal (
732+ detectIssueKind ( {
733+ title : "[Feature]: rewrite the docs" ,
734+ body : "Still working on the write-up." ,
735+ labels : [ ] ,
736+ storedKind : "documentation" ,
737+ } ) ,
738+ "documentation" ,
739+ ) ;
740+ } ) ;
741+ } ) ;
742+
743+ describe ( "labelForKind" , ( ) => {
744+ it ( "maps kinds to triage labels" , ( ) => {
745+ assert . equal ( labelForKind ( "bug" ) , "bug" ) ;
746+ assert . equal ( labelForKind ( "feature" ) , "enhancement" ) ;
747+ assert . equal ( labelForKind ( "documentation" ) , "documentation" ) ;
748+ assert . equal ( labelForKind ( "provider-compatibility" ) , "provider-compatibility" ) ;
749+ assert . equal ( labelForKind ( null ) , null ) ;
750+ assert . equal ( labelForKind ( "unknown" ) , null ) ;
751+ } ) ;
752+ } ) ;
0 commit comments