dsh-nsfc-form-check 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # Changelog
2
+
3
+ ## 0.2.1
4
+
5
+ - Ship `CHANGELOG.md` and `SECURITY.md` inside the package. `files` is an
6
+ allowlist and neither was on it, so no release note had ever reached anyone
7
+ who installed this package, and npm had no changelog section to show.
8
+ ## 0.2.0
9
+
10
+ - Release infrastructure brought to the family standard: `verify:self-contained`,
11
+ `check:lockfile`, `check:readmes` and `check:citations` gates, a `prepublishOnly` that
12
+ re-runs the whole chain, SECURITY.md, dependabot, and the OpenSSF Scorecard workflow.
13
+ - `check:citations` enforces the rule this pack's own header states: every `excerpt`
14
+ must be a verbatim quotation, findable in `rules/evidence/`. Rules that are not
15
+ traceable yet are listed in `rules/citations-baseline.json`, and that file can only
16
+ shrink - anything new has to be sourced before it can land.
17
+ - The README install command now names the published package instead of a local tarball.
18
+ - Five-language READMEs hold the same section count and the same configuration keys.
19
+ - Rule pack: 15 rules across NF-001..NF-015.
20
+ - Licensed Apache-2.0.
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
package/README-es.md ADDED
@@ -0,0 +1,94 @@
1
+ # dsh-nsfc-form-check
2
+
3
+ **Boundary:** this plugin checks an **NSFC proposal's text and electronic document properties** and
4
+ reports the identity clues it finds, alongside the length and form-review items a deployment
5
+ configures. It is not `dsh-rulefile-check` (which audits the legality of a regulatory document), not
6
+ `dsh-policy-brief-draft` (which drafts a policy explainer), and not `dsh-review-reply-check` (which
7
+ tracks reviewer comments). It reads one proposal and reports literal matches against cited clauses.
8
+
9
+ > ### ⚠️ Read this before using the anonymity checks
10
+ >
11
+ > **There is no current national provision that forbids an applicant's name, institution or advisor
12
+ > from appearing in an NSFC proposal.** Three independent texts were read in full for this plugin —
13
+ > 《国家自然科学基金条例》(国务院令第796号), the 2026 年度《项目指南·申请规定》, and the 2019 年度
14
+ > guidelines — and none contains 匿名, 盲审, 活页, or any "must not appear" wording about identity.
15
+ > The regulation does not contain the word 匿名 at all. Its **avoidance** clause (第二十条) requires a
16
+ > reviewer from the applicant's own legal entity to recuse, which presupposes that the system knows
17
+ > the applicant's institution — the opposite of an anonymous process.
18
+ >
19
+ > So every finding this plugin makes about a name or an institution is a **technical observation for
20
+ > a human to weigh**, labelled 身份线索词 and never phrased as a violation. The plugin is useful for a
21
+ > research-office self-check, and it is **wrong to use it as a compliance gate**. Two rules go further
22
+ > and cite the guidance verbatim: `NF-014` checks that the advisor's name **is** filled in, because the
23
+ > yearly guidelines require it, and `NF-015` checks 涉密与敏感信息, which is the one "must not appear"
24
+ > requirement that really exists — and its object is classified information, not identity.
25
+
26
+ ## Compatibility
27
+
28
+ | Superficie | Estado |
29
+ |---|---|
30
+ | Harness | Rango de peers `>=0.1.2-rc.1 <0.2.0 \|\| >=0.2.0-0 <0.3.0` — verificado para aceptar tanto `0.2.0-rc.2` como `0.2.1-alpha.1`. **No se declara `engines.dsh`**: no tiene lector y no puede rechazar ningún host |
31
+ | Node | `^22.19.0 || >=24.0.0` |
32
+ | Plataformas | Todas (ESM puro; sin código nativo, sin red, sin llamada al modelo) |
33
+ | Modo de herramienta | Funciona en `native`, `ptc` y `both`; para un directorio completo use `ptc` |
34
+
35
+ ## What it does
36
+
37
+ La tabla de reglas, los campos y el comportamiento detallado están en [README.md](README.md#what-it-does) (versión principal en inglés). El plugin sólo enumera divergencias literales frente a las cláusulas citadas e indica en `skipped` cada comprobación que no pudo ejecutarse.
38
+
39
+ ## Install
40
+
41
+ ```sh
42
+ dsh plugin --profile <name> add dsh-nsfc-form-check
43
+ dsh --profile <name> --dump-config | grep 'dsh-nsfc-form-check'
44
+ ```
45
+
46
+ ## Configuration
47
+
48
+ Todos los parámetros ajustables viven en el esquema Schemastery de `src/config.ts`, por lo que se cambian desde `cordis.yml` sin tocar el código; los umbrales por regla están en el paquete de reglas bajo `rules/`.
49
+
50
+ | Clave | Tipo | Predeterminado | Descripción |
51
+ |---|---|---|---|
52
+ | `rulesFile` | string | `rules/nsfc-form-check.yaml` | Ruta del paquete de reglas, relativa a la raíz del paquete |
53
+ | `disabledRules` | string[] | `[]` | Ids de reglas que se dejan de ejecutar; cada una aparece en `skipped` |
54
+ | `onlyRules` | string[] | `[]` | Ejecutar solo estas reglas; vacío ejecuta todas |
55
+ | `skipNotes` | string | `""` | Nota añadida a cada motivo de `skipped` |
56
+ | `authorNames` | string[] | `[]` | Nombres de solicitantes que se buscan |
57
+ | `institutionNames` | string[] | `[]` | Nombres de instituciones y unidades que se buscan |
58
+ | `advisorNames` | string[] | `[]` | Nombres de asesores que se buscan |
59
+ | `allowList` | string[] | `[]` | Términos que se ignoran aunque coincidan |
60
+ | `timeoutMs` | number | `120000` | Presupuesto de tiempo de espera cooperativo de la herramienta |
61
+
62
+ ## Material format
63
+
64
+ Acepta JSON o YAML. El ejemplo completo de campos está en [README.md](README.md#material-format) (versión principal en inglés). Los campos son opcionales en la capa de lectura y los valida el motor, de modo que una exportación parcial produce hallazgos sobre lo que falta en lugar de un fallo.
65
+
66
+ ## Rule sources
67
+
68
+ Los datos de las reglas están separados del código: cada regla lleva documento, número, cláusula en la numeración propia de la fuente, extracto literal y URL de origen. El cargador impone que el extracto sea una cita real de al menos ocho caracteres y que una comprobación basada sólo en un principio general (`kind: derived-from-principle`, tope `warn`) o en una política local (`kind: institutional-configuration`, tope `info`) nunca se declare `error`.
69
+
70
+ Los límites verificados y las conclusiones deliberadamente **no** afirmadas están en [README.md](README.md#rule-sources) (versión principal en inglés) y en `rules/evidence/`.
71
+
72
+ ## Troubleshooting
73
+
74
+ - **El plugin se instala pero la herramienta no aparece**: compruebe que `main` resuelve a `lib/index.mjs` y que `pnpm run build` lo generó.
75
+ - **`dsh plugin add` rechaza el paquete**: la faixa de peers cubre `0.1.x` y `0.2.x`; fuera de ella, conceda una exención explícita con `dsh plugin --profile <name> allow-version <pkg@ver> --dsh-version <runtime> --accept-risk`.
76
+ - **Una regla no se ejecutó**: lea el arreglo `skipped`.
77
+ - **`check` informa `manifest-peers` como fallo**: es un problema conocido de `dsh-plugin-dev`; el runtime aplica la compatibilidad al instalar.
78
+ - **Los horarios parecen desplazados**: toda la aritmética es de hora local sobre las cadenas entregadas.
79
+
80
+ ## Development
81
+
82
+ ```sh
83
+ pnpm install
84
+ pnpm run typecheck
85
+ pnpm test
86
+ pnpm run build
87
+ node ../scripts/sync-shared.mjs dsh-nsfc-form-check
88
+ ```
89
+
90
+ El último comando copia el kit compartido de `../_shared` a `src/shared/`; vuelva a ejecutarlo tras cada cambio compartido.
91
+
92
+ ## License
93
+
94
+ [Apache License 2.0](LICENSE) © 2026 dsh-nsfc-form-check contributors.
package/README-hi.md ADDED
@@ -0,0 +1,94 @@
1
+ # dsh-nsfc-form-check
2
+
3
+ **Boundary:** this plugin checks an **NSFC proposal's text and electronic document properties** and
4
+ reports the identity clues it finds, alongside the length and form-review items a deployment
5
+ configures. It is not `dsh-rulefile-check` (which audits the legality of a regulatory document), not
6
+ `dsh-policy-brief-draft` (which drafts a policy explainer), and not `dsh-review-reply-check` (which
7
+ tracks reviewer comments). It reads one proposal and reports literal matches against cited clauses.
8
+
9
+ > ### ⚠️ Read this before using the anonymity checks
10
+ >
11
+ > **There is no current national provision that forbids an applicant's name, institution or advisor
12
+ > from appearing in an NSFC proposal.** Three independent texts were read in full for this plugin —
13
+ > 《国家自然科学基金条例》(国务院令第796号), the 2026 年度《项目指南·申请规定》, and the 2019 年度
14
+ > guidelines — and none contains 匿名, 盲审, 活页, or any "must not appear" wording about identity.
15
+ > The regulation does not contain the word 匿名 at all. Its **avoidance** clause (第二十条) requires a
16
+ > reviewer from the applicant's own legal entity to recuse, which presupposes that the system knows
17
+ > the applicant's institution — the opposite of an anonymous process.
18
+ >
19
+ > So every finding this plugin makes about a name or an institution is a **technical observation for
20
+ > a human to weigh**, labelled 身份线索词 and never phrased as a violation. The plugin is useful for a
21
+ > research-office self-check, and it is **wrong to use it as a compliance gate**. Two rules go further
22
+ > and cite the guidance verbatim: `NF-014` checks that the advisor's name **is** filled in, because the
23
+ > yearly guidelines require it, and `NF-015` checks 涉密与敏感信息, which is the one "must not appear"
24
+ > requirement that really exists — and its object is classified information, not identity.
25
+
26
+ ## Compatibility
27
+
28
+ | सतह | स्थिति |
29
+ |---|---|
30
+ | Harness | peer रेंज `>=0.1.2-rc.1 <0.2.0 \|\| >=0.2.0-0 <0.3.0` — `0.2.0-rc.2` और `0.2.1-alpha.1` दोनों को स्वीकार करने के लिए सत्यापित। **`engines.dsh` जानबूझकर घोषित नहीं**: इसका कोई पाठक नहीं और यह किसी होस्ट को अस्वीकार नहीं कर सकता |
31
+ | Node | `^22.19.0 || >=24.0.0` |
32
+ | प्लेटफ़ॉर्म | सभी (शुद्ध ESM; कोई नेटिव कोड नहीं, कोई नेटवर्क नहीं, कोई मॉडल कॉल नहीं) |
33
+ | टूल मोड | `native`, `ptc` और `both` में काम करता है; पूरे फ़ोल्डर के लिए `ptc` चुनें |
34
+
35
+ ## What it does
36
+
37
+ नियम-सूची, फ़ील्ड और विस्तृत व्यवहार [README.md](README.md#what-it-does) (अंग्रेज़ी मुख्य संस्करण) में हैं। यह प्लगइन केवल उद्धृत धाराओं के सामने शाब्दिक अंतर सूचीबद्ध करता है और हर न चल पाई जाँच को `skipped` में बताता है।
38
+
39
+ ## Install
40
+
41
+ ```sh
42
+ dsh plugin --profile <name> add dsh-nsfc-form-check
43
+ dsh --profile <name> --dump-config | grep 'dsh-nsfc-form-check'
44
+ ```
45
+
46
+ ## Configuration
47
+
48
+ सभी समायोज्य पैरामीटर `src/config.ts` की Schemastery स्कीमा में हैं, इसलिए कोड बदले बिना `cordis.yml` से बदले जा सकते हैं; प्रति-नियम सीमाएँ `rules/` के नियम-पैक में हैं।
49
+
50
+ | कुंजी | प्रकार | डिफ़ॉल्ट | विवरण |
51
+ |---|---|---|---|
52
+ | `rulesFile` | string | `rules/nsfc-form-check.yaml` | नियम-पैक का पथ, पैकेज रूट के सापेक्ष |
53
+ | `disabledRules` | string[] | `[]` | बंद करने वाले नियम id; प्रत्येक `skipped` में दिखता है |
54
+ | `onlyRules` | string[] | `[]` | केवल ये नियम चलाएँ; खाली होने पर सभी नियम चलते हैं |
55
+ | `skipNotes` | string | `""` | हर `skipped` कारण के आगे जोड़ी जाने वाली टिप्पणी |
56
+ | `authorNames` | string[] | `[]` | खोजे जाने वाले आवेदक नाम |
57
+ | `institutionNames` | string[] | `[]` | खोजे जाने वाले संस्थान और इकाई नाम |
58
+ | `advisorNames` | string[] | `[]` | खोजे जाने वाले सलाहकार नाम |
59
+ | `allowList` | string[] | `[]` | मेल खाने पर भी अनदेखा किए जाने वाले शब्द |
60
+ | `timeoutMs` | number | `120000` | उपकरण का सहकारी समय-सीमा बजट |
61
+
62
+ ## Material format
63
+
64
+ JSON या YAML स्वीकार्य है। पूरा फ़ील्ड उदाहरण [README.md](README.md#material-format) (अंग्रेज़ी मुख्य संस्करण) में है। पढ़ने की परत में फ़ील्ड वैकल्पिक हैं और जाँच इंजन उन्हें सत्यापित करता है, इसलिए आंशिक निर्यात पर क्रैश के बजाय "अनुपस्थित" श्रेणी के निष्कर्ष मिलते हैं।
65
+
66
+ ## Rule sources
67
+
68
+ नियम-डेटा कोड से अलग है: प्रत्येक नियम में दस्तावेज़, संख्या, स्रोत की अपनी क्रमांकन-प्रणाली के अनुसार धारा, शब्दशः उद्धरण और स्रोत URL होता है। लोडर लागू करता है कि उद्धरण कम से कम आठ अक्षरों का वास्तविक उद्धरण हो, और जिस जाँच का आधार केवल सामान्य सिद्धांत (`kind: derived-from-principle`, अधिकतम `warn`) या स्थानीय नीति (`kind: institutional-configuration`, अधिकतम `info`) हो, उसे कभी `error` घोषित न किया जाए।
69
+
70
+ सत्यापित सीमाएँ और जान-बूझकर **न** कहे गए निष्कर्ष [README.md](README.md#rule-sources) (अंग्रेज़ी मुख्य संस्करण) और `rules/evidence/` में हैं।
71
+
72
+ ## Troubleshooting
73
+
74
+ - **प्लगइन इंस्टॉल हो गया पर टूल दिखता नहीं**: जाँचें कि `main` `lib/index.mjs` पर जाता है और `pnpm run build` ने उसे बनाया है।
75
+ - **`dsh plugin add` असंगत बताकर मना करता है**: peer range `0.1.x` और `0.2.x` दोनों को कवर करती है; बाहर होने पर स्पष्ट छूट दें: `dsh plugin --profile <name> allow-version <pkg@ver> --dsh-version <runtime> --accept-risk`।
76
+ - **कोई नियम नहीं चला**: `skipped` सरणी देखें।
77
+ - **`check` में `manifest-peers` विफल दिखता है**: यह `dsh-plugin-dev` की ज्ञात अपस्ट्रीम समस्या है; रनटाइम इंस्टॉल के समय अनुकूलता लागू करता है।
78
+ - **समय खिसका हुआ लगता है**: सारी गणना दिए गए स्ट्रिंग पर वॉल-क्लॉक है।
79
+
80
+ ## Development
81
+
82
+ ```sh
83
+ pnpm install
84
+ pnpm run typecheck
85
+ pnpm test
86
+ pnpm run build
87
+ node ../scripts/sync-shared.mjs dsh-nsfc-form-check
88
+ ```
89
+
90
+ अंतिम कमांड `../_shared` का साझा किट `src/shared/` में कॉपी करता है; हर साझा बदलाव के बाद इसे दोबारा चलाएँ।
91
+
92
+ ## License
93
+
94
+ [Apache License 2.0](LICENSE) © 2026 dsh-nsfc-form-check contributors.
package/README-pt.md ADDED
@@ -0,0 +1,94 @@
1
+ # dsh-nsfc-form-check
2
+
3
+ **Boundary:** this plugin checks an **NSFC proposal's text and electronic document properties** and
4
+ reports the identity clues it finds, alongside the length and form-review items a deployment
5
+ configures. It is not `dsh-rulefile-check` (which audits the legality of a regulatory document), not
6
+ `dsh-policy-brief-draft` (which drafts a policy explainer), and not `dsh-review-reply-check` (which
7
+ tracks reviewer comments). It reads one proposal and reports literal matches against cited clauses.
8
+
9
+ > ### ⚠️ Read this before using the anonymity checks
10
+ >
11
+ > **There is no current national provision that forbids an applicant's name, institution or advisor
12
+ > from appearing in an NSFC proposal.** Three independent texts were read in full for this plugin —
13
+ > 《国家自然科学基金条例》(国务院令第796号), the 2026 年度《项目指南·申请规定》, and the 2019 年度
14
+ > guidelines — and none contains 匿名, 盲审, 活页, or any "must not appear" wording about identity.
15
+ > The regulation does not contain the word 匿名 at all. Its **avoidance** clause (第二十条) requires a
16
+ > reviewer from the applicant's own legal entity to recuse, which presupposes that the system knows
17
+ > the applicant's institution — the opposite of an anonymous process.
18
+ >
19
+ > So every finding this plugin makes about a name or an institution is a **technical observation for
20
+ > a human to weigh**, labelled 身份线索词 and never phrased as a violation. The plugin is useful for a
21
+ > research-office self-check, and it is **wrong to use it as a compliance gate**. Two rules go further
22
+ > and cite the guidance verbatim: `NF-014` checks that the advisor's name **is** filled in, because the
23
+ > yearly guidelines require it, and `NF-015` checks 涉密与敏感信息, which is the one "must not appear"
24
+ > requirement that really exists — and its object is classified information, not identity.
25
+
26
+ ## Compatibility
27
+
28
+ | Superfície | Estado |
29
+ |---|---|
30
+ | Harness | Faixa de peers `>=0.1.2-rc.1 <0.2.0 \|\| >=0.2.0-0 <0.3.0` — verificada para aceitar tanto `0.2.0-rc.2` quanto `0.2.1-alpha.1`. **`engines.dsh` não é declarado**: não tem leitor e não pode recusar nenhum host |
31
+ | Node | `^22.19.0 || >=24.0.0` |
32
+ | Plataformas | Todas (ESM puro; sem código nativo, sem rede, sem chamada ao modelo) |
33
+ | Modo de ferramenta | Funciona em `native`, `ptc` e `both`; para um diretório inteiro use `ptc` |
34
+
35
+ ## What it does
36
+
37
+ A tabela de regras, os campos e o comportamento detalhado estão em [README.md](README.md#what-it-does) (versão principal em inglês). O plugin apenas lista divergências literais frente às cláusulas citadas e indica em `skipped` cada verificação que não pôde ser executada.
38
+
39
+ ## Install
40
+
41
+ ```sh
42
+ dsh plugin --profile <name> add dsh-nsfc-form-check
43
+ dsh --profile <name> --dump-config | grep 'dsh-nsfc-form-check'
44
+ ```
45
+
46
+ ## Configuration
47
+
48
+ Todos os parâmetros ajustáveis ficam no esquema Schemastery de `src/config.ts`, portanto mudam pelo `cordis.yml` sem editar código; os limites por regra ficam no pacote de regras sob `rules/`.
49
+
50
+ | Chave | Tipo | Padrão | Descrição |
51
+ |---|---|---|---|
52
+ | `rulesFile` | string | `rules/nsfc-form-check.yaml` | Caminho do pacote de regras, relativo à raiz do pacote |
53
+ | `disabledRules` | string[] | `[]` | Ids de regras a desativar; cada uma aparece em `skipped` |
54
+ | `onlyRules` | string[] | `[]` | Executar apenas estas regras; vazio executa todas |
55
+ | `skipNotes` | string | `""` | Nota acrescentada a cada motivo de `skipped` |
56
+ | `authorNames` | string[] | `[]` | Nomes de solicitantes a procurar |
57
+ | `institutionNames` | string[] | `[]` | Nomes de instituições e unidades a procurar |
58
+ | `advisorNames` | string[] | `[]` | Nomes de orientadores a procurar |
59
+ | `allowList` | string[] | `[]` | Termos a ignorar mesmo quando coincidem |
60
+ | `timeoutMs` | number | `120000` | Orçamento de tempo limite cooperativo da ferramenta |
61
+
62
+ ## Material format
63
+
64
+ Aceita JSON ou YAML. O exemplo completo de campos está em [README.md](README.md#material-format) (versão principal em inglês). Os campos são opcionais na camada de leitura e validados pelo motor, de modo que uma exportação parcial gera achados sobre o que falta em vez de falhar.
65
+
66
+ ## Rule sources
67
+
68
+ Os dados das regras ficam separados do código: cada regra traz documento, número, cláusula na numeração própria da fonte, trecho literal e URL de origem. O carregador impõe que o trecho seja citação real de pelo menos oito caracteres e que uma verificação baseada apenas em princípio geral (`kind: derived-from-principle`, teto `warn`) ou em política local (`kind: institutional-configuration`, teto `info`) nunca seja declarada `error`.
69
+
70
+ Os limites verificados e as conclusões deliberadamente **não** afirmadas estão em [README.md](README.md#rule-sources) (versão principal em inglês) e em `rules/evidence/`.
71
+
72
+ ## Troubleshooting
73
+
74
+ - **O plugin instala mas a ferramenta não aparece**: confirme que `main` resolve para `lib/index.mjs` e que `pnpm run build` o gerou.
75
+ - **`dsh plugin add` recusa o pacote**: a faixa de peers cobre `0.1.x` e `0.2.x`; fora dela, conceda isenção explícita com `dsh plugin --profile <name> allow-version <pkg@ver> --dsh-version <runtime> --accept-risk`.
76
+ - **Uma regra não executou**: leia o arranjo `skipped`.
77
+ - **`check` informa `manifest-peers` como falha**: problema conhecido do `dsh-plugin-dev`; o runtime aplica a compatibilidade na instalação.
78
+ - **Os horários parecem deslocados**: toda a aritmética é de hora local sobre as cadeias fornecidas.
79
+
80
+ ## Development
81
+
82
+ ```sh
83
+ pnpm install
84
+ pnpm run typecheck
85
+ pnpm test
86
+ pnpm run build
87
+ node ../scripts/sync-shared.mjs dsh-nsfc-form-check
88
+ ```
89
+
90
+ O último comando copia o kit compartilhado de `../_shared` para `src/shared/`; execute-o novamente após cada alteração compartilhada.
91
+
92
+ ## License
93
+
94
+ [Apache License 2.0](LICENSE) © 2026 dsh-nsfc-form-check contributors.
package/README-zh.md ADDED
@@ -0,0 +1,94 @@
1
+ # dsh-nsfc-form-check
2
+
3
+ **Boundary:** this plugin checks an **NSFC proposal's text and electronic document properties** and
4
+ reports the identity clues it finds, alongside the length and form-review items a deployment
5
+ configures. It is not `dsh-rulefile-check` (which audits the legality of a regulatory document), not
6
+ `dsh-policy-brief-draft` (which drafts a policy explainer), and not `dsh-review-reply-check` (which
7
+ tracks reviewer comments). It reads one proposal and reports literal matches against cited clauses.
8
+
9
+ > ### ⚠️ Read this before using the anonymity checks
10
+ >
11
+ > **There is no current national provision that forbids an applicant's name, institution or advisor
12
+ > from appearing in an NSFC proposal.** Three independent texts were read in full for this plugin —
13
+ > 《国家自然科学基金条例》(国务院令第796号), the 2026 年度《项目指南·申请规定》, and the 2019 年度
14
+ > guidelines — and none contains 匿名, 盲审, 活页, or any "must not appear" wording about identity.
15
+ > The regulation does not contain the word 匿名 at all. Its **avoidance** clause (第二十条) requires a
16
+ > reviewer from the applicant's own legal entity to recuse, which presupposes that the system knows
17
+ > the applicant's institution — the opposite of an anonymous process.
18
+ >
19
+ > So every finding this plugin makes about a name or an institution is a **technical observation for
20
+ > a human to weigh**, labelled 身份线索词 and never phrased as a violation. The plugin is useful for a
21
+ > research-office self-check, and it is **wrong to use it as a compliance gate**. Two rules go further
22
+ > and cite the guidance verbatim: `NF-014` checks that the advisor's name **is** filled in, because the
23
+ > yearly guidelines require it, and `NF-015` checks 涉密与敏感信息, which is the one "must not appear"
24
+ > requirement that really exists — and its object is classified information, not identity.
25
+
26
+ ## Compatibility
27
+
28
+ | 项目 | 状态 |
29
+ |---|---|
30
+ | Harness | 对等版本范围 `>=0.1.2-rc.1 <0.2.0 \|\| >=0.2.0-0 <0.3.0` —— 已实测同时接受 `0.2.0-rc.2` 与 `0.2.1-alpha.1`。**刻意不声明 `engines.dsh`**:它没有任何读取者,也无法拒装任何宿主 |
31
+ | Node | `^22.19.0 || >=24.0.0` |
32
+ | 平台 | 全平台(纯 ESM;无原生代码、无联网、不调用模型) |
33
+ | 工具模式 | `native` / `ptc` / `both` 均可;批量校验整个目录时建议 `ptc`,schema 成本只付一次 |
34
+
35
+ ## What it does
36
+
37
+ 规则表、字段说明与行为细节见 [README.md](README.md#what-it-does)(英文主版本)。本插件只列出材料与所引条款之间的字面差异,并对无法执行的检查在 `skipped` 中逐项说明。
38
+
39
+ ## Install
40
+
41
+ ```sh
42
+ dsh plugin --profile <name> add dsh-nsfc-form-check
43
+ dsh --profile <name> --dump-config | grep 'dsh-nsfc-form-check'
44
+ ```
45
+
46
+ ## Configuration
47
+
48
+ 全部可调参数都在 `src/config.ts` 的 Schemastery schema 中,只改 `cordis.yml` 即可生效,无需改代码;逐条阈值在 `rules/` 下的规则库文件里。
49
+
50
+ | 键 | 类型 | 默认值 | 说明 |
51
+ |---|---|---|---|
52
+ | `rulesFile` | string | `rules/nsfc-form-check.yaml` | 规则库文件路径,相对插件包根目录 |
53
+ | `disabledRules` | string[] | `[]` | 要停用的规则 id 列表;每条都会出现在 `skipped` 中 |
54
+ | `onlyRules` | string[] | `[]` | 只执行这些规则 id;留空表示执行全部规则 |
55
+ | `skipNotes` | string | `""` | 附加到每条 `skipped` 说明后的备注 |
56
+ | `authorNames` | string[] | `[]` | 要检索的申请人姓名 |
57
+ | `institutionNames` | string[] | `[]` | 要检索的依托单位与院系名称 |
58
+ | `advisorNames` | string[] | `[]` | 要检索的导师姓名 |
59
+ | `allowList` | string[] | `[]` | 即使命中也要忽略的词语 |
60
+ | `timeoutMs` | number | `120000` | 工具协作式超时预算(毫秒) |
61
+
62
+ ## Material format
63
+
64
+ 支持 JSON 与 YAML。完整字段示例见 [README.md](README.md#material-format)(英文主版本)。字段在读取层是可选的,由检查引擎校验,因此部分导出的材料会产生"缺项"类差异,而不是让程序崩溃。
65
+
66
+ ## Rule sources
67
+
68
+ 规则数据与代码分离,每条规则都带文件名、文号、按原文自身编号体系的条款号、逐字摘录与来源地址。加载期强制:摘录必须是真实引文且不少于八个字符;依据仅为原则性条款(`kind: derived-from-principle`,严重级上限 `warn`)或本机构配置(`kind: institutional-configuration`,上限 `info`)的检查不得标为 `error`。夸大依据的规则库会在加载期失败,而不会产出一份看起来很有底气的报告。
69
+
70
+ 核验中确认的边界与"刻意没有作出的结论"见 [README.md](README.md#rule-sources)(英文主版本)与随包的 `rules/evidence/` 目录。
71
+
72
+ ## Troubleshooting
73
+
74
+ - **插件装上了但工具不出现**:确认 `main` 指向 `lib/index.mjs` 且 `pnpm run build` 已生成该文件;`main` 写错会让加载器静默跳过该条目。
75
+ - **`dsh plugin add` 报版本不兼容**:peer 范围覆盖 `0.1.x` 与 `0.2.x`;若运行时在其之外,可显式豁免:`dsh plugin --profile <name> allow-version <包名@版本> --dsh-version <runtime> --accept-risk`
76
+ - **某条规则没有执行**:查看 `skipped` 数组,其中写明了规则 id 与原因。
77
+ - **`check` 报 `manifest-peers` 失败**:静态检查器比对的是一份早于 0.2 世代的硬编码 peer 范围;安装期的 peer 校验以运行时为准。这是 `dsh-plugin-dev` 的已知上游问题。
78
+ - **时间看起来偏移**:全部计算都是对输入字符串做墙上时钟运算,不做时区换算。
79
+
80
+ ## Development
81
+
82
+ ```sh
83
+ pnpm install
84
+ pnpm run typecheck
85
+ pnpm test
86
+ pnpm run build
87
+ node ../scripts/sync-shared.mjs dsh-nsfc-form-check
88
+ ```
89
+
90
+ 第 4 项把 `../_shared` 的共享件同步进 `src/shared/`;每次改动共享件后都要重跑。
91
+
92
+ ## License
93
+
94
+ [Apache License 2.0](LICENSE) © 2026 dsh-nsfc-form-check contributors.
package/README.md CHANGED
@@ -1,3 +1,177 @@
1
- # Temporary Holding Version
1
+ # dsh-nsfc-form-check
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Boundary:** this plugin checks an **NSFC proposal's text and electronic document properties** and
4
+ reports the identity clues it finds, alongside the length and form-review items a deployment
5
+ configures. It is not `dsh-rulefile-check` (which audits the legality of a regulatory document), not
6
+ `dsh-policy-brief-draft` (which drafts a policy explainer), and not `dsh-review-reply-check` (which
7
+ tracks reviewer comments). It reads one proposal and reports literal matches against cited clauses.
8
+
9
+ > ### ⚠️ Read this before using the anonymity checks
10
+ >
11
+ > **There is no current national provision that forbids an applicant's name, institution or advisor
12
+ > from appearing in an NSFC proposal.** Three independent texts were read in full for this plugin —
13
+ > 《国家自然科学基金条例》(国务院令第796号), the 2026 年度《项目指南·申请规定》, and the 2019 年度
14
+ > guidelines — and none contains 匿名, 盲审, 活页, or any "must not appear" wording about identity.
15
+ > The regulation does not contain the word 匿名 at all. Its **avoidance** clause (第二十条) requires a
16
+ > reviewer from the applicant's own legal entity to recuse, which presupposes that the system knows
17
+ > the applicant's institution — the opposite of an anonymous process.
18
+ >
19
+ > So every finding this plugin makes about a name or an institution is a **technical observation for
20
+ > a human to weigh**, labelled 身份线索词 and never phrased as a violation. The plugin is useful for a
21
+ > research-office self-check, and it is **wrong to use it as a compliance gate**. Two rules go further
22
+ > and cite the guidance verbatim: `NF-014` checks that the advisor's name **is** filled in, because the
23
+ > yearly guidelines require it, and `NF-015` checks 涉密与敏感信息, which is the one "must not appear"
24
+ > requirement that really exists — and its object is classified information, not identity.
25
+
26
+ ## Compatibility
27
+
28
+ | Surface | Status |
29
+ |---|---|
30
+ | Harness | Peer range `>=0.1.2-rc.1 <0.2.0 \|\| >=0.2.0-0 <0.3.0` — verified to accept both `0.2.0-rc.2` and `0.2.1-alpha.1`. `engines.dsh` is deliberately not declared: it has no reader and cannot reject a host |
31
+ | Node | `^22.19.0 || >=24.0.0` |
32
+ | Platforms | All (plain ESM; no native code, no network, no model call) |
33
+ | Tool mode | Works in `native`, `ptc` and `both`; for a batch of proposals use `ptc` |
34
+
35
+ ## What it does
36
+
37
+ Registers the `nsfc_form_check` tool. It reads one proposal — body text, document properties, and the
38
+ institution's own roster of identity terms — applies a versioned rule pack, and returns a report in
39
+ which every finding names the clause it came from and states whether that clause is a direct
40
+ requirement, a principle, or a local configuration.
41
+
42
+ | Rule | Check | Severity | Basis kind |
43
+ |---|---|---|---|
44
+ | `NF-001` | a roster is configured, otherwise the identity checks cannot run | warn | principle |
45
+ | `NF-002` | the body mentions an applicant name | warn | principle |
46
+ | `NF-003` | the body mentions an institution name | warn | principle |
47
+ | `NF-004` | the body mentions an advisor name | warn | principle |
48
+ | `NF-005` | the document title property contains an identity clue | info | principle |
49
+ | `NF-006` | the author property is non-empty or names the applicant | info | principle |
50
+ | `NF-007` | the company property names the institution | info | principle |
51
+ | `NF-008` | the last-modified-by property names the applicant | info | principle |
52
+ | `NF-009` | identity clues inside a configured line range | info | principle |
53
+ | `NF-010` | the material fits the configured length limit | info | local |
54
+ | `NF-011` | the material contains the configured form-review items | info | local |
55
+ | `NF-012` | a repeat or follow-on proposal carries the configured disclosure | info | local |
56
+ | `NF-013` | identity clues inside a configured scope | info | principle |
57
+ | `NF-014` | the advisor's name **is** present, as the yearly guidelines require | info | local |
58
+ | `NF-015` | the material contains no configured classified term | info | local |
59
+
60
+ Rules whose thresholds are yearly or per-project-type ship **empty** on purpose. They report
61
+ themselves in `skipped` until configured, so an unfilled threshold can never read as "nothing wrong".
62
+
63
+ ## Install
64
+
65
+ ```sh
66
+ dsh plugin --profile <name> add dsh-nsfc-form-check
67
+ dsh --profile <name> --dump-config | grep 'dsh-nsfc-form-check'
68
+ ```
69
+
70
+ ## Configuration
71
+
72
+ Every tunable lives in the Schemastery schema in `src/config.ts`. The roster is the one thing this
73
+ plugin cannot work without: an identity check needs a list of the terms to look for, and that list is
74
+ institution-specific, so it stays in the deployment's own configuration rather than travelling with
75
+ the package.
76
+
77
+ | Key | Type | Default | Description |
78
+ |---|---|---|---|
79
+ | `rulesFile` | string | `rules/nsfc-form-check.yaml` | Rule-pack path, relative to the package root |
80
+ | `disabledRules` | string[] | `[]` | Rule ids to stop running; each appears in `skipped` |
81
+ | `onlyRules` | string[] | `[]` | Run only these rule ids; empty runs every rule |
82
+ | `skipNotes` | string | `""` | Note appended to every `skipped` reason |
83
+ | `authorNames` | string[] | `[]` | Applicant names to look for |
84
+ | `institutionNames` | string[] | `[]` | Institution and unit names to look for |
85
+ | `advisorNames` | string[] | `[]` | Advisor names to look for |
86
+ | `allowList` | string[] | `[]` | Terms to ignore even when they match |
87
+ | `timeoutMs` | number | `120000` | Cooperative tool timeout budget |
88
+
89
+ Rule-level parameters worth knowing:
90
+
91
+ - `NF-009` `coverLines` — how many leading lines count as the cover page. Empty means the plugin does
92
+ not assume any institution's template.
93
+ - `NF-010` `maxLines` / `maxChars` — the length limit **for the year and project type you are filing
94
+ under**. `maxLines` counts extracted paragraphs, which is not the same as typeset pages.
95
+ - `NF-011` `require` — your form-review item list, as `[{ label, match }]` pairs.
96
+ - `NF-012` `markerPatterns` / `requiredDisclosure` — how a repeat proposal is recognised in your
97
+ template, and what it must disclose.
98
+ - `NF-014` `requireMention` + `advisorNames` — the inverse check, off until you enable it for the year.
99
+ - `NF-015` `secretTerms` — your confidentiality office's term list. The plugin ships none, because what
100
+ counts as classified is defined by state secrecy rules, not by this plugin.
101
+
102
+ ## Material format
103
+
104
+ Point the tool at a `.docx` and it reads the body text and the document properties itself. A `.pdf` is
105
+ **not** parsed: page-accurate extraction is a different problem with a different failure mode, and a
106
+ wrong page number is worse than none, so pass the text you extracted with your own tool. `.txt`, `.md`
107
+ and `.json` are accepted too; the JSON form carries both parts explicitly.
108
+
109
+ ```json
110
+ {
111
+ "lines": ["申请代码:H0301", "一、立项依据与研究内容", "……"],
112
+ "metadata": { "title": "某信号通路机制研究", "author": "", "company": "", "lastModifiedBy": "" }
113
+ }
114
+ ```
115
+
116
+ Matching is literal and line-oriented, so every finding carries a line number you can jump to. A term
117
+ shorter than two characters is ignored, and anything in `allowList` is ignored, because a common
118
+ surname would otherwise flood the report.
119
+
120
+ ## Rule sources
121
+
122
+ Rule data lives in `rules/nsfc-form-check.yaml`. Every rule carries a document, a document number, a
123
+ clause in the source's own numbering, a verbatim excerpt and the URL the excerpt was read from. The
124
+ loader enforces that an excerpt is a real quotation of at least eight characters, and that a check
125
+ resting only on a general principle or a local policy can never be declared `error`.
126
+
127
+ Four findings shaped this pack:
128
+
129
+ 1. **The regulation was revised.** 《国家自然科学基金条例》 is now **国务院令第796号** (revised
130
+ 2024-11-08, in force 2025-01-01), superseding 国务院令第487号 (2007). Clause numbers moved: the
131
+ review-confidentiality duty is now 第二十一条, not the old 第二十四条. This pack cites only the
132
+ current revision, and a test asserts that the superseded order number never appears.
133
+ 2. **Anonymity has no legal basis**, as set out at the top of this file. The rules state this in their
134
+ own notes so a reader of the report sees it without consulting the README.
135
+ 3. **The advisor's name is *required*, not forbidden.** The yearly guidelines instruct applicants to
136
+ fill in their graduate and postdoctoral advisor names truthfully. `NF-014` therefore checks the
137
+ opposite of `NF-004`, and the pack explains why the two coexist.
138
+ 4. **Yearly policy is configuration, not code.** Page and word limits, the itemised limits on how many
139
+ proposals may be filed, which project types use a lump-sum budget, and the yearly list of
140
+ non-acceptance situations all change between years; every one of them ships empty.
141
+
142
+ The full clause-verification report, including the sources that were checked and rejected, is in
143
+ `rules/evidence/clause-verification.md`.
144
+
145
+ ## Troubleshooting
146
+
147
+ - **The plugin installs but the tool never appears.** Check that `main` resolves to `lib/index.mjs`
148
+ and that `pnpm run build` produced it; a wrong `main` makes the loader skip the entry silently.
149
+ - **Every identity check is skipped.** No roster is configured. Fill in `authorNames`,
150
+ `institutionNames` and `advisorNames`, and `NF-001` will stop reporting.
151
+ - **The report is noisy with a common surname.** Add the term to `allowList`; the plugin refuses to
152
+ guess which matches are meaningful.
153
+ - **A `.pdf` is refused.** By design. Extract the text with a tool you trust and pass it through
154
+ `text`, or convert the proposal to `.docx`.
155
+ - **`dsh plugin add` refuses the package as incompatible.** The peer range covers `0.1.x` and `0.2.x`;
156
+ if your runtime sits outside it, grant an explicit exemption:
157
+ `dsh plugin --profile <name> allow-version dsh-nsfc-form-check@0.1.0 --dsh-version <runtime> --accept-risk`
158
+ - **`check` reports `manifest-peers` as failed.** The static checker compares against a hard-coded peer
159
+ range that predates the 0.2 line. The runtime enforces peer compatibility at install time, so the
160
+ declared range is the correct one; this is a known upstream issue in `dsh-plugin-dev`.
161
+
162
+ ## Development
163
+
164
+ ```sh
165
+ pnpm install
166
+ pnpm run typecheck # tsc --noEmit
167
+ pnpm test # vitest, paired fixtures per rule, plus a real .docx round trip
168
+ pnpm run build # tsdown -> lib/index.mjs + lib/index.d.mts
169
+ node ../scripts/sync-shared.mjs dsh-nsfc-form-check # refresh src/shared from ../_shared
170
+ ```
171
+
172
+ `tests/docx-fixture.ts` builds real `.docx` packages in memory (a genuine ZIP with deflated entries),
173
+ so the docx path is exercised end to end while the fixtures stay reviewable JSON.
174
+
175
+ ## License
176
+
177
+ [Apache License 2.0](LICENSE) © 2026 dsh-nsfc-form-check contributors.
package/SECURITY.md ADDED
@@ -0,0 +1,39 @@
1
+ # Security Policy
2
+
3
+ ## Reporting a vulnerability
4
+
5
+ Please report security vulnerabilities **privately** through GitHub's private
6
+ vulnerability reporting:
7
+
8
+ **Security → Report a vulnerability** at
9
+ https://github.com/PerryLink/dsh-nsfc-form-check/security/advisories/new
10
+
11
+ Do **not** open a public issue for security findings.
12
+
13
+ Before reporting, **sanitize everything you paste**: remove tokens, API keys,
14
+ credentials, authorization headers, session contents, and personal data. Logs
15
+ without secrets only.
16
+
17
+ ## Scope note
18
+
19
+ This plugin is an offline checker. It reads the material you hand it and
20
+ compares it against a versioned rule pack; it makes no network calls and no
21
+ model calls, and it stores nothing.
22
+
23
+ ## What to include
24
+
25
+ - affected package version (and DeepSeek Harness runtime version)
26
+ - a minimal reproduction
27
+ - your impact assessment
28
+
29
+ ## Response expectations
30
+
31
+ - acknowledgement within **7 days**
32
+ - status updates at least every **14 days** until resolution
33
+ - coordinated disclosure: the fix and advisory are prepared first; the finding
34
+ is disclosed publicly after the fix ships
35
+
36
+ ## Credits and disclosure
37
+
38
+ - reporters are credited in the advisory and CHANGELOG unless they ask to stay anonymous
39
+ - advisories follow GitHub's advisory workflow and are published together with the fix
@@ -0,0 +1,8 @@
1
+ # dsh-nsfc-form-check bundle layer: register this plugin as a bundle row.
2
+ # This file is the `dsh.bundle.patch` layer (see package.json#dsh.bundle.patch).
3
+ # Layer semantics: a YAML array of row verbs; `insert` adds plugin rows to the
4
+ # composed config. A row's `id` must be unique within the layer stack; `name`
5
+ # resolves the plugin module through the installed package (package.json#main).
6
+ - insert:
7
+ - id: dsh-nsfc-form-check
8
+ name: dsh-nsfc-form-check