11'use strict'
22
33/**
4- * Discipline cluster probe (VL-053~061 family) — lightweight classifiers for
5- * unauthorized push, preference menus after convergence, and CI-regression stop-without-self-fix.
4+ * Discipline cluster probe (VL-053~061 + PF-172 + control-plane absorb PF-138/139/140 family).
5+ * Lightweight classifiers for unauthorized push, preference menus after convergence,
6+ * CI-regression stop-without-self-fix, completion free-text next-step "A 或 B" forks,
7+ * CP artifact-before-confirm, CodeTruth matrix at CP, control-plane digest binding,
8+ * and author-self-review vs independent review boundary.
69 * Extends existing CP/release/expert gates; does not invent a parallel Skill.
710 */
811
@@ -35,6 +38,76 @@ function classifyPreferenceMenuAfterConvergenceSample(sample) {
3538 return 'ok'
3639}
3740
41+ /**
42+ * CP confirm request after convergence must point at on-disk artifact (PF-138 / GR-045).
43+ * @param {string } sample
44+ * @returns {'ok'|'missing-cp-artifact'|'not-cp-confirm' }
45+ */
46+ function classifyCpArtifactBeforeConfirmSample ( sample ) {
47+ const text = String ( sample || '' )
48+ const asksConfirm = / 确 认 \s * C P [ 1 2 3 ] | ⏸ \s * C P [ 1 2 3 ] | 等 待 确 认 \s * C P | 请 确 认 \s * ( 需 求 | 方 案 | 计 划 ) | C P 1 \s * 确 认 | C P 2 \s * 确 认 / i. test ( text )
49+ if ( ! asksConfirm ) return 'not-cp-confirm'
50+ const hasArtifact =
51+ / 0 1 - 需 求 确 认 \. m d | 0 1 - 产 品 需 求 \. m d | 0 1 - 问 题 确 认 \. m d | 0 2 - 技 术 方 案 \. m d | 0 4 - 实 施 计 划 \. m d | 0 0 - 需 求 概 况 \. m d | 0 0 - 问 题 概 况 \. m d | a r t i f a c t P a t h | a r t i f a c t S h a 2 5 6 | C p A r t i f a c t B e f o r e C o n f i r m G a t e / i. test (
52+ text
53+ )
54+ if ( ! hasArtifact ) return 'missing-cp-artifact'
55+ return 'ok'
56+ }
57+
58+ /**
59+ * Control-plane / optimization CP draft must carry CodeTruth matrix evidence (PF-139 / GR-046).
60+ * @param {string } sample
61+ * @returns {'ok'|'missing-code-truth-matrix'|'not-control-plane-cp' }
62+ */
63+ function classifyCodeTruthMatrixAtCpSample ( sample ) {
64+ const text = String ( sample || '' )
65+ const controlCp =
66+ / 控 制 面 | M C P | H o o k | C L I | v a l i d a t e \. j s | 分 发 | 部 署 副 本 | w o r k f l o w - c a p a b i l i t i e s | C o d e T r u t h | C P 2 \s * 技 术 方 案 | 技 术 方 案 确 认 / i. test (
67+ text
68+ ) && / C P [ 1 2 ] | 方 案 | 需 求 确 认 | 可 实 施 | 定 稿 / i. test ( text )
69+ if ( ! controlCp ) return 'not-control-plane-cp'
70+ const hasMatrix =
71+ / C o d e T r u t h E v i d e n c e M a t r i x | C o d e T r u t h E v i d e n c e M a t r i x G a t e | r e p o P a t h | c u r r e n t B e h a v i o r | n e g a t i v e P r o b e | 证 据 矩 阵 / i. test ( text )
72+ const claimsReady = / 可 确 认 \s * C P | 可 实 施 | 方 案 已 定 稿 | P R - 1 \s * 通 过 | 已 验 证 .* 方 案 / i. test ( text )
73+ if ( claimsReady && ! hasMatrix ) return 'missing-code-truth-matrix'
74+ if ( ! hasMatrix && / 控 制 面 | M C P | H o o k | C L I / . test ( text ) && / 定 稿 | 最 终 方 案 | 推 荐 方 案 \s * = / i. test ( text ) ) {
75+ return 'missing-code-truth-matrix'
76+ }
77+ return 'ok'
78+ }
79+
80+ /**
81+ * Control-plane CP confirmation must bind digest (PF-140 / ConfirmBindingGate residual).
82+ * @param {string } sample
83+ * @returns {'ok'|'missing-digest'|'not-control-plane-confirm' }
84+ */
85+ function classifyControlPlaneDigestSample ( sample ) {
86+ const text = String ( sample || '' )
87+ const controlConfirm =
88+ / 控 制 面 | H o o k | M C P | C L I | 分 发 | 部 署 / i. test ( text ) && / 确 认 \s * C P | C P [ 1 2 3 ] \s * [ ✅ | 已 确 认 ] | m e m o r y _ c p _ c o n f i r m / i. test ( text )
89+ if ( ! controlConfirm ) return 'not-control-plane-confirm'
90+ const hasDigest = / a r t i f a c t S h a 2 5 6 | s h a 2 5 6 | d i g e s t | C o n f i r m B i n d i n g G a t e | a r t i f a c t P a t h / i. test ( text )
91+ if ( ! hasDigest ) return 'missing-digest'
92+ return 'ok'
93+ }
94+
95+ /**
96+ * Author self-check must not be labeled independent design review (PF-140 / VL-057 residual).
97+ * @param {string } sample
98+ * @returns {'ok'|'author-self-review-as-independent'|'not-review-claim' }
99+ */
100+ function classifyAuthorSelfReviewBoundarySample ( sample ) {
101+ const text = String ( sample || '' )
102+ const claimsIndependent = / 独 立 审 查 | 独 立 设 计 审 查 | i n d e p e n d e n t \s * r e v i e w | 外 部 审 查 已 通 过 | 作 者 自 审 代 替 / i. test ( text )
103+ if ( ! claimsIndependent ) return 'not-review-claim'
104+ const onlySelf = / 作 者 自 审 | s e l f - r e v i e w | 自 检 通 过 | 本 人 复 审 | 方 案 作 者 .* 审 查 / i. test ( text )
105+ const hasIndependentEvidence = / 独 立 审 查 员 | 第 二 审 查 | C o d e x .* 审 查 | G r o k .* 审 查 | a u d i t \s * s e s s i o n | 外 部 A g e n t / i. test ( text )
106+ if ( onlySelf && ! hasIndependentEvidence ) return 'author-self-review-as-independent'
107+ if ( / 作 者 自 审 代 替 独 立 | 自 审 冒 充 独 立 / i. test ( text ) ) return 'author-self-review-as-independent'
108+ return 'ok'
109+ }
110+
38111/**
39112 * @param {string } sample
40113 * @returns {'ok'|'stop-without-self-fix'|'not-ci-regression' }
@@ -49,18 +122,90 @@ function classifyOwnIntroducedRegressionSample(sample) {
49122 return 'ok'
50123}
51124
125+ /**
126+ * Strip quoted / meta discussion of the word 或 so rules about forbidding "或"
127+ * do not false-positive as dual-action forks.
128+ * @param {string } text
129+ * @returns {string }
130+ */
131+ function stripOrMetaNoise ( text ) {
132+ return String ( text || '' )
133+ . replace ( / 「 [ ^ 」 ] * 」 / g, ' ' )
134+ . replace ( / 『 [ ^ 』 ] * 』 / g, ' ' )
135+ . replace ( / " [ ^ " ] * " / g, ' ' )
136+ . replace ( / ' [ ^ ' ] * ' / g, ' ' )
137+ . replace ( / 禁 止 [ ^ \n 。 ; ; ] { 0 , 40 } (?: 或 | 或 者 ) / g, ' ' )
138+ . replace ( / 不 得 [ ^ \n 。 ; ; ] { 0 , 40 } (?: 或 | 或 者 ) / g, ' ' )
139+ . replace ( / 勿 [ ^ \n 。 ; ; ] { 0 , 40 } (?: 或 | 或 者 ) / g, ' ' )
140+ . replace ( / f o r b i d [ ^ \n . ] { 0 , 40 } \b o r \b / gi, ' ' )
141+ . replace ( / d o \s + n o t [ ^ \n . ] { 0 , 40 } \b o r \b / gi, ' ' )
142+ }
143+
144+ /**
145+ * Primary recommendation surface only — alternatives under 不推荐 are allowed.
146+ * @param {string } text
147+ * @returns {string }
148+ */
149+ function primaryRecommendationSurface ( text ) {
150+ return String ( text || '' ) . split ( / (?: ^ | \n ) \s * (?: 不 推 荐 | 备 选 \s * [ ( ( ] 明 确 劣 于 | 非 推 荐 路 径 | 明 确 劣 于 推 荐 ) / ) [ 0 ]
151+ }
152+
153+ /**
154+ * Completion / free-text next-step must be a single primary action (PF-172 / PI-151 / VL-077).
155+ * @param {string } sample
156+ * @returns {'ok'|'or-fork'|'not-next-step' }
157+ */
158+ function classifyNextStepOrForkSample ( sample ) {
159+ const text = String ( sample || '' )
160+ if ( / 推 荐 \s * [ : : ] \s * 无 后 续 动 作 / . test ( text ) ) return 'ok'
161+ const isNext = / 下 一 步 | 后 续 建 议 | 推 荐 下 一 步 | 推 荐 结 论 | 推 荐 方 案 | # # \s * 后 续 | # # \s * 推 荐 | 推 荐 \s * [ : : ] / i. test ( text )
162+ if ( ! isNext ) return 'not-next-step'
163+
164+ const primary = stripOrMetaNoise ( primaryRecommendationSurface ( text ) )
165+
166+ if ( / (?: 下 一 步 | 后 续 建 议 | 推 荐 下 一 步 | 推 荐 结 论 | 推 荐 方 案 ) [ ^ \n ] * \n \s * [ - * • ] \s * .+ \n \s * [ - * • ] \s * .+ / i. test ( primary ) ) {
167+ return 'or-fork'
168+ }
169+ if ( / (?: 下 一 步 | 推 荐 下 一 步 | 后 续 建 议 | 推 荐 结 论 | 推 荐 方 案 ) [ ^ \n ] { 0 , 160 } ?(?: 或 | 或 者 ) / . test ( primary ) ) {
170+ return 'or-fork'
171+ }
172+ if ( / 推 荐 \s * [ : : ] [ ^ \n ] { 0 , 120 } ?(?: 或 | 或 者 ) / . test ( primary ) ) {
173+ return 'or-fork'
174+ }
175+ if (
176+ / (?: 做 | 对 齐 | 实 施 | 处 理 | 先 | 挑 | 选 | 跑 | 修 ) [ ^ \n ] { 0 , 48 } (?: 或 | 或 者 ) [ ^ \n ] { 0 , 48 } (?: 做 | 对 齐 | 实 施 | 处 理 | 挑 | 选 | 跑 | 修 ) / . test ( primary ) &&
177+ / (?: 下 一 步 | 后 续 | 推 荐 ) / . test ( primary )
178+ ) {
179+ return 'or-fork'
180+ }
181+
182+ return 'ok'
183+ }
184+
52185function buildDisciplineProbeReceipt ( sample ) {
53186 return {
54187 schemaVersion : 'DisciplineExecutionProbeReceiptV1' ,
55188 push : classifyPushAuthorizationSample ( sample ) ,
56189 preferenceMenu : classifyPreferenceMenuAfterConvergenceSample ( sample ) ,
57- ciRegression : classifyOwnIntroducedRegressionSample ( sample )
190+ ciRegression : classifyOwnIntroducedRegressionSample ( sample ) ,
191+ nextStepOrFork : classifyNextStepOrForkSample ( sample ) ,
192+ cpArtifactBeforeConfirm : classifyCpArtifactBeforeConfirmSample ( sample ) ,
193+ codeTruthAtCp : classifyCodeTruthMatrixAtCpSample ( sample ) ,
194+ controlPlaneDigest : classifyControlPlaneDigestSample ( sample ) ,
195+ authorSelfReviewBoundary : classifyAuthorSelfReviewBoundarySample ( sample )
58196 }
59197}
60198
61199module . exports = {
62200 classifyPushAuthorizationSample,
63201 classifyPreferenceMenuAfterConvergenceSample,
202+ classifyCpArtifactBeforeConfirmSample,
203+ classifyCodeTruthMatrixAtCpSample,
204+ classifyControlPlaneDigestSample,
205+ classifyAuthorSelfReviewBoundarySample,
64206 classifyOwnIntroducedRegressionSample,
207+ classifyNextStepOrForkSample,
208+ stripOrMetaNoise,
209+ primaryRecommendationSurface,
65210 buildDisciplineProbeReceipt
66211}
0 commit comments