project-tiny-context-harness 0.2.53 → 0.2.54
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/README.md +39 -9
- package/assets/README.md +36 -8
- package/assets/github/harness.yml +13 -9
- package/assets/make/sdlc-harness.mk +13 -8
- package/assets/skills/context_development_engineer/SKILL.md +3 -3
- package/dist/commands/check-modularity.js +13 -5
- package/dist/commands/index.js +7 -4
- package/dist/lib/config.js +5 -0
- package/dist/lib/modularity.d.ts +9 -0
- package/dist/lib/modularity.js +132 -8
- package/dist/lib/types.d.ts +12 -0
- package/dist/lib/validators.js +35 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -115,7 +115,7 @@ npm ci
|
|
|
115
115
|
npm run smoke:quickstart
|
|
116
116
|
npm run preview:pack
|
|
117
117
|
cd /path/to/your/test-repo
|
|
118
|
-
npm install -D /path/to/project-tiny-context-harness/tmp/sdlc/source-preview/package/project-tiny-context-harness-0.2.
|
|
118
|
+
npm install -D /path/to/project-tiny-context-harness/tmp/sdlc/source-preview/package/project-tiny-context-harness-0.2.54.tgz
|
|
119
119
|
npx --no-install sdlc-harness init --adopt
|
|
120
120
|
make validate-context
|
|
121
121
|
```
|
|
@@ -184,7 +184,7 @@ For existing projects:
|
|
|
184
184
|
npx --yes --package project-tiny-context-harness@latest sdlc-harness init --adopt
|
|
185
185
|
```
|
|
186
186
|
|
|
187
|
-
`init` creates `project_context/context.toml`, `project_context/global.md`, `project_context/architecture.md`, `project_context/areas/main.md`, `project_context/areas/main/verification.md`, agent guidance, Context authoring Skills, a full-project export Skill, a Harness upgrade Skill, managed templates/tools, a Makefile include and `.github/workflows/harness.yml`. The generated workflow runs only the selected Harness gate
|
|
187
|
+
`init` creates `project_context/context.toml`, `project_context/global.md`, `project_context/architecture.md`, `project_context/areas/main.md`, `project_context/areas/main/verification.md`, agent guidance, Context authoring Skills, a full-project export Skill, a Harness upgrade Skill, managed templates/tools, a Makefile include and `.github/workflows/harness.yml`. The generated workflow runs only the selected Harness gate: `validate-context`, `validate-code-modularity` or the composite `validate-harness`; maintainer-only package tests and source-drift checks are intentionally kept out of consumer projects. It does not create stage work-product trees, lifecycle state or stage skills by default.
|
|
188
188
|
|
|
189
189
|
## FAQ
|
|
190
190
|
|
|
@@ -235,14 +235,43 @@ Use `npx --no-install sdlc-harness ...` only when you explicitly want the alread
|
|
|
235
235
|
| Combined project export | `npx --yes --package project-tiny-context-harness@latest sdlc-harness export-context --all [--check]` | Creates both default temporary exports under `tmp/sdlc/context-exports/**`. |
|
|
236
236
|
| Project Context export | `npx --yes --package project-tiny-context-harness@latest sdlc-harness export-context --full [--output tmp/sdlc/context-exports/name.md] [--check]` | Creates a temporary Context summary artifact. It is not Context and must not be registered in `project_context/context.toml`. |
|
|
237
237
|
| Code implementation export | `npx --yes --package project-tiny-context-harness@latest sdlc-harness export-context --code [--output tmp/sdlc/context-exports/name.md] [--check]` | Creates a temporary single-file code implementation artifact. It is not Context and must not be registered in `project_context/context.toml`. |
|
|
238
|
-
| Modularity check | `npx --yes --package project-tiny-context-harness@latest sdlc-harness check-modularity --touched [--limit 300] [--fail-on-warning]` |
|
|
238
|
+
| Modularity check | `npx --yes --package project-tiny-context-harness@latest sdlc-harness check-modularity --touched [--limit 300] [--fail-on-warning]` | Reports selected handwritten source files over the physical line-count limit; `--file <path>` and `--base <ref>` select explicit files or branch changes, and config waivers are reported distinctly. |
|
|
239
|
+
| Code modularity validation | `make validate-code-modularity` | Hard gate for touched handwritten source modularity; CI can set `SDLC_MODULARITY_BASE=<ref>` to audit PR/base changes. |
|
|
240
|
+
| Harness validation | `make validate-harness` | Composite gate for `validate-context` and `validate-code-modularity`. |
|
|
239
241
|
| Context validation | `npx --yes --package project-tiny-context-harness@latest sdlc-harness validate-context`, `make validate-context` | Checks required project recovery fields, Context graph metadata, declared paths/roles and fake test-execution claims. |
|
|
240
242
|
| Diagnostics | `make sdlc-doctor` or `npx --yes --package project-tiny-context-harness@latest sdlc-harness doctor` | Reports Harness root, package version, schema version and required Minimal Context paths. |
|
|
241
243
|
| Package source checks | `sdlc-harness package sync-source`, `sdlc-harness package check-source` | Maintainer-only commands for keeping package canonical assets aligned with the source workspace. |
|
|
242
244
|
|
|
243
245
|
For product, UI/UX and engineering tasks that touch design intent, API/Schema, state/runtime behavior, architecture boundaries or verification design, the default Skills compile a short current-task contract before implementation. The contract starts with `Context Delta: none|required`; `required` preserves context-first behavior, while `none` means the task can proceed against existing Context. For engineering, RFC and implementation work, the existing Task Contract also includes `Modularity Check: none|required|exception` so oversized touched files trigger split-or-exception reasoning. For module design work, the development engineer Skill also compiles `Applicable Module Design`: the relevant principles, minimal design logic and durable rationale that control the current implementation or verification choice. The task contract and Contract Conformance are handoff evidence, not new PRD, tech-plan, validator or gate surfaces.
|
|
244
246
|
|
|
245
|
-
`sdlc-harness check-modularity` supports that field by auditing selected handwritten source files for physical line-count risk. It is warning-only by default and is not `validate-context
|
|
247
|
+
`sdlc-harness check-modularity` supports that field by auditing selected handwritten source files for physical line-count risk. It is warning-only by default as a report command, while `validate-code-modularity` and `validate-harness` run it as a hard gate. The gate is not `validate-context`: `validate-context` remains pure Context recoverability. When `policy` is `scoped_waivers`, over-limit exceptions must be backed by `<harnessRoot>/config.yaml` `modularity.waivers` entries with `path`, narrow `category`, `reason` and `future_split_boundary`; handoff prose alone is not a machine waiver.
|
|
248
|
+
|
|
249
|
+
### Modularity Policy
|
|
250
|
+
|
|
251
|
+
Newly generated Harness configs default to `strict_except_generated`, which enforces the touched/PR handwritten source limit without legacy waivers:
|
|
252
|
+
|
|
253
|
+
```yaml
|
|
254
|
+
modularity:
|
|
255
|
+
limit: 300
|
|
256
|
+
policy: strict_except_generated
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Generated and non-source files are still auto-skipped when they match existing lock/build/dist/path exclusions or generated-file headers such as `@generated` / `Code generated ... DO NOT EDIT`. `strict_except_generated` does not allow `modularity.waivers`; any configured waiver fails the modularity gate.
|
|
260
|
+
|
|
261
|
+
Use `scoped_waivers` when a small number of legacy exceptions must be explicit and time-bounded:
|
|
262
|
+
|
|
263
|
+
```yaml
|
|
264
|
+
modularity:
|
|
265
|
+
limit: 300
|
|
266
|
+
policy: scoped_waivers
|
|
267
|
+
waivers:
|
|
268
|
+
- path: src/legacy/big-file.ts
|
|
269
|
+
category: legacy_migration
|
|
270
|
+
reason: "Existing legacy module exceeds the hard source size bound."
|
|
271
|
+
future_split_boundary: "Extract provider adapters and retry policy."
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Omitting `policy` behaves the same as `scoped_waivers` for compatibility with existing projects. Allowed waiver categories are `generated`, `third_party_reference`, `legacy_migration`, `aggregate_styles` and `fixture_snapshot`.
|
|
246
275
|
|
|
247
276
|
Multilingual trigger phrases are compatibility details. Public README, npm and launch copy stay English-first, and public/package-managed surfaces must remain English-complete; literal non-English examples are documented only where they explain generated Skill matching and must not be the sole activation path.
|
|
248
277
|
|
|
@@ -430,11 +459,12 @@ npx --yes --package project-tiny-context-harness@latest sdlc-harness upgrade --c
|
|
|
430
459
|
npx --yes --package project-tiny-context-harness@latest sdlc-harness validate-context
|
|
431
460
|
npx --yes --package project-tiny-context-harness@latest sdlc-harness doctor
|
|
432
461
|
make sdlc-doctor
|
|
433
|
-
make validate-context
|
|
434
|
-
make validate-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
462
|
+
make validate-context
|
|
463
|
+
make validate-code-modularity
|
|
464
|
+
make validate-harness
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
`make validate-harness` runs `validate-context` and the hard touched-source modularity gate.
|
|
438
468
|
|
|
439
469
|
## Current Boundary
|
|
440
470
|
|
package/assets/README.md
CHANGED
|
@@ -94,7 +94,7 @@ That smoke packs the local workspace, installs it into a disposable repo, runs `
|
|
|
94
94
|
```sh
|
|
95
95
|
npm run preview:pack
|
|
96
96
|
cd /path/to/your/test-repo
|
|
97
|
-
npm install -D /path/to/project-tiny-context-harness/tmp/sdlc/source-preview/package/project-tiny-context-harness-0.2.
|
|
97
|
+
npm install -D /path/to/project-tiny-context-harness/tmp/sdlc/source-preview/package/project-tiny-context-harness-0.2.54.tgz
|
|
98
98
|
npx --no-install sdlc-harness init --adopt
|
|
99
99
|
make validate-context
|
|
100
100
|
```
|
|
@@ -236,7 +236,7 @@ npx --yes --package project-tiny-context-harness@latest sdlc-harness init --adop
|
|
|
236
236
|
- a root `Makefile` include block
|
|
237
237
|
- `.github/workflows/harness.yml`
|
|
238
238
|
|
|
239
|
-
The generated workflow runs only the selected Harness gate
|
|
239
|
+
The generated workflow runs only the selected Harness gate: `validate-context`, `validate-code-modularity` or the composite `validate-harness`. Maintainer-only package tests and source-drift checks are intentionally kept out of consumer projects.
|
|
240
240
|
|
|
241
241
|
`init` does not create lifecycle state, plan state, stage skills or stage work-product trees by default.
|
|
242
242
|
|
|
@@ -270,9 +270,36 @@ Multilingual trigger phrases are compatibility details. Public README, npm and l
|
|
|
270
270
|
|
|
271
271
|
For product, UI/UX and engineering tasks that touch design intent, API/Schema, state/runtime behavior, architecture boundaries or verification design, the default Skills compile a short current-task contract before implementation. The contract starts with `Context Delta: none|required`; `required` preserves context-first behavior, while `none` means the task can proceed against existing Context. For engineering, RFC and implementation work, the existing Task Contract also includes `Modularity Check: none|required|exception` so oversized touched files trigger split-or-exception reasoning. For module design work, the development engineer Skill also compiles `Applicable Module Design`: the relevant principles, minimal design logic and durable rationale that control the current implementation or verification choice. The task contract and Contract Conformance are handoff evidence, not new PRD, tech-plan, validator or gate surfaces.
|
|
272
272
|
|
|
273
|
-
`sdlc-harness check-modularity` supports that field by auditing selected handwritten source files for physical line-count risk. It is warning-only by default and is not `validate-context
|
|
274
|
-
|
|
275
|
-
|
|
273
|
+
`sdlc-harness check-modularity` supports that field by auditing selected handwritten source files for physical line-count risk. It is warning-only by default as a report command, while `validate-code-modularity` and `validate-harness` run it as a hard gate. The gate is not `validate-context`: `validate-context` remains pure Context recoverability. When `policy` is `scoped_waivers`, over-limit exceptions must be backed by `<harnessRoot>/config.yaml` `modularity.waivers` entries with `path`, narrow `category`, `reason` and `future_split_boundary`; handoff prose alone is not a machine waiver.
|
|
274
|
+
|
|
275
|
+
### Modularity Policy
|
|
276
|
+
|
|
277
|
+
Newly generated Harness configs default to `strict_except_generated`, which enforces the touched/PR handwritten source limit without legacy waivers:
|
|
278
|
+
|
|
279
|
+
```yaml
|
|
280
|
+
modularity:
|
|
281
|
+
limit: 300
|
|
282
|
+
policy: strict_except_generated
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Generated and non-source files are still auto-skipped when they match existing lock/build/dist/path exclusions or generated-file headers such as `@generated` / `Code generated ... DO NOT EDIT`. `strict_except_generated` does not allow `modularity.waivers`; any configured waiver fails the modularity gate.
|
|
286
|
+
|
|
287
|
+
Use `scoped_waivers` when a small number of legacy exceptions must be explicit and time-bounded:
|
|
288
|
+
|
|
289
|
+
```yaml
|
|
290
|
+
modularity:
|
|
291
|
+
limit: 300
|
|
292
|
+
policy: scoped_waivers
|
|
293
|
+
waivers:
|
|
294
|
+
- path: src/legacy/big-file.ts
|
|
295
|
+
category: legacy_migration
|
|
296
|
+
reason: "Existing legacy module exceeds the hard source size bound."
|
|
297
|
+
future_split_boundary: "Extract provider adapters and retry policy."
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Omitting `policy` behaves the same as `scoped_waivers` for compatibility with existing projects. Allowed waiver categories are `generated`, `third_party_reference`, `legacy_migration`, `aggregate_styles` and `fixture_snapshot`.
|
|
301
|
+
|
|
302
|
+
For complex task-contract work, agents may use `plan.md` or an equivalent temporary plan surface as scratch space for `Context Delta`, `Task Contract`, implementation steps and Conformance notes. It is execution cache only: durable facts must be extracted into `project_context/**` or `DESIGN.md`, and temporary plans are not Context, not registered in `context.toml` and not default project assets.
|
|
276
303
|
|
|
277
304
|
For Web pages, frontend layout, UI/UX, product module boundaries or decisions about where information belongs, agents should run a lightweight page product-positioning check before deciding whether the change is context-first. The check asks what judgment the user needs to make on the page, what information/actions/feedback the product must provide, what should not be persistent, what belongs in downstream consumption, ops, detail or another page, and whether layout and information density match the page task. If ownership is unclear, inspect the relevant pages and Context first. The check is input to change classification: it does not by itself require a Context update, new document chain or validator gate.
|
|
278
305
|
|
|
@@ -299,10 +326,11 @@ Use `npx --no-install sdlc-harness ...` only when you explicitly want the alread
|
|
|
299
326
|
| `npx --yes --package project-tiny-context-harness@latest sdlc-harness export-context --all [--check]` | Creates both default temporary exports under `tmp/sdlc/context-exports/**`. |
|
|
300
327
|
| `npx --yes --package project-tiny-context-harness@latest sdlc-harness export-context --full [--output tmp/sdlc/context-exports/name.md] [--check]` | Creates a temporary project Context summary Markdown artifact. |
|
|
301
328
|
| `npx --yes --package project-tiny-context-harness@latest sdlc-harness export-context --code [--output tmp/sdlc/context-exports/name.md] [--check]` | Creates a temporary single-file code implementation Markdown artifact. |
|
|
302
|
-
| `npx --yes --package project-tiny-context-harness@latest sdlc-harness check-modularity --touched [--limit 300] [--fail-on-warning]` |
|
|
329
|
+
| `npx --yes --package project-tiny-context-harness@latest sdlc-harness check-modularity --touched [--limit 300] [--fail-on-warning]` | Reports selected handwritten source files over the physical line-count limit; `--file <path>` and `--base <ref>` select explicit files or branch changes, and config waivers are reported distinctly. |
|
|
303
330
|
| `npx --yes --package project-tiny-context-harness@latest sdlc-harness validate-context` | Checks minimum project recovery fields, Context graph metadata, declared paths/roles and fake test-execution claims. |
|
|
304
|
-
| `make validate-context` | Makefile wrapper for `validate-context`. |
|
|
305
|
-
| `make validate-
|
|
331
|
+
| `make validate-context` | Makefile wrapper for `validate-context`. |
|
|
332
|
+
| `make validate-code-modularity` | Hard gate for touched handwritten source modularity; CI can set `SDLC_MODULARITY_BASE=<ref>` to audit PR/base changes. |
|
|
333
|
+
| `make validate-harness` | Composite gate for `validate-context` and `validate-code-modularity`. |
|
|
306
334
|
| `sdlc-harness package sync-source` | Maintainer-only command to sync source workspace assets into `packages/sdlc-harness/assets/**`. |
|
|
307
335
|
| `sdlc-harness package check-source` | Maintainer-only drift check for package canonical assets. |
|
|
308
336
|
|
|
@@ -14,14 +14,17 @@ on:
|
|
|
14
14
|
default: "validate-context"
|
|
15
15
|
type: choice
|
|
16
16
|
options:
|
|
17
|
-
- validate-context
|
|
18
|
-
- validate-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
- validate-context
|
|
18
|
+
- validate-code-modularity
|
|
19
|
+
- validate-harness
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
harness:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
24
25
|
- uses: actions/checkout@v6
|
|
26
|
+
with:
|
|
27
|
+
fetch-depth: 0
|
|
25
28
|
- uses: actions/setup-node@v6
|
|
26
29
|
with:
|
|
27
30
|
node-version: "24"
|
|
@@ -33,5 +36,6 @@ jobs:
|
|
|
33
36
|
- name: Run harness gate
|
|
34
37
|
run: make "${HARNESS_GATE}"
|
|
35
38
|
env:
|
|
36
|
-
HARNESS_GATE: ${{ github.event.inputs.gate || 'validate-
|
|
37
|
-
|
|
39
|
+
HARNESS_GATE: ${{ github.event.inputs.gate || 'validate-harness' }}
|
|
40
|
+
SDLC_MODULARITY_BASE: ${{ github.event_name == 'pull_request' && format('origin/{0}', github.base_ref) || github.event.before || '' }}
|
|
41
|
+
# pjsdlc:sdlc-harness:github-workflow:end
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
PYTHON ?= python3
|
|
2
|
-
SDLC_HARNESS ?= $(if $(wildcard packages/sdlc-harness/dist/cli.js),node packages/sdlc-harness/dist/cli.js,npx --yes --package project-tiny-context-harness@latest sdlc-harness)
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
SDLC_HARNESS ?= $(if $(wildcard packages/sdlc-harness/dist/cli.js),node packages/sdlc-harness/dist/cli.js,npx --yes --package project-tiny-context-harness@latest sdlc-harness)
|
|
3
|
+
SDLC_MODULARITY_SCOPE = $(if $(SDLC_MODULARITY_BASE),--base $(SDLC_MODULARITY_BASE),--touched)
|
|
4
|
+
|
|
5
|
+
.PHONY: help sdlc-doctor sdlc-sync sdlc-upgrade sdlc-check-modularity validate-context validate-code-modularity validate-harness lint test-current-domain test-all build
|
|
6
|
+
|
|
6
7
|
help:
|
|
7
8
|
@echo "Minimal Context Harness commands"
|
|
8
9
|
@echo " make sdlc-doctor Diagnose Harness root, core package and schema version"
|
|
@@ -10,7 +11,8 @@ help:
|
|
|
10
11
|
@echo " make sdlc-upgrade Run safe upgrade migrations, sync managed assets and doctor"
|
|
11
12
|
@echo " make sdlc-check-modularity Warn on oversized touched handwritten source files"
|
|
12
13
|
@echo " make validate-context Check whether project_context/** supports context recovery"
|
|
13
|
-
@echo " make validate-
|
|
14
|
+
@echo " make validate-code-modularity Fail on oversized touched handwritten source files"
|
|
15
|
+
@echo " make validate-harness Run validate-context and validate-code-modularity"
|
|
14
16
|
@echo " make test-all Run the project regression suite after replacing this placeholder"
|
|
15
17
|
|
|
16
18
|
sdlc-doctor:
|
|
@@ -23,12 +25,15 @@ sdlc-upgrade:
|
|
|
23
25
|
$(SDLC_HARNESS) upgrade
|
|
24
26
|
|
|
25
27
|
sdlc-check-modularity:
|
|
26
|
-
$(SDLC_HARNESS) check-modularity
|
|
28
|
+
$(SDLC_HARNESS) check-modularity $(SDLC_MODULARITY_SCOPE)
|
|
27
29
|
|
|
28
30
|
validate-context:
|
|
29
31
|
$(SDLC_HARNESS) validate-context
|
|
30
|
-
|
|
31
|
-
validate-
|
|
32
|
+
|
|
33
|
+
validate-code-modularity:
|
|
34
|
+
$(SDLC_HARNESS) check-modularity $(SDLC_MODULARITY_SCOPE) --fail-on-warning
|
|
35
|
+
|
|
36
|
+
validate-harness: validate-context validate-code-modularity
|
|
32
37
|
|
|
33
38
|
lint:
|
|
34
39
|
@echo "No project lint command configured yet. Replace this target with your stack-specific lint command."
|
|
@@ -33,9 +33,9 @@ Project-specific engineering rules belong in a separate project-local Skill unde
|
|
|
33
33
|
10. 实现时保持精准修改,优先遵循仓库现有框架、接口、测试和代码风格。
|
|
34
34
|
11. 当用户明确要求 / 允许“多开agent”或使用 subagent,且当前会话存在可用 subagent 工具时,积极把可并行的探索、审查或实现拆分交给 subagent;使用前先复用已有相关 agent,没有合适 agent 或并行度不足时再新开。`wait_agent` 只表示取得结果,不释放资源;subagent 完成、空闲或不再需要时必须调用 `close_agent`,收尾前清理已完成 / 空闲 / 不再需要的 subagent,避免占满后续资源。
|
|
35
35
|
12. 当任务涉及新实现、重构、重复逻辑、模块边界或影响面控制时,先做轻量 abstraction / decomposition scan:
|
|
36
|
-
- 工程 / RFC / 实现类 Task Contract 包含 `Modularity Check: none|required|exception`;可用 `sdlc-harness check-modularity --file <path> --limit 300` 审计计划编辑文件,用 `sdlc-harness check-modularity --touched --limit 300`
|
|
36
|
+
- 工程 / RFC / 实现类 Task Contract 包含 `Modularity Check: none|required|exception`;可用 `sdlc-harness check-modularity --file <path> --limit 300` 审计计划编辑文件,用 `make validate-code-modularity` 或 `sdlc-harness check-modularity --touched --limit 300 --fail-on-warning` 做交付前硬审计。若项目本地 Skill 定义了不同 limit,使用项目本地值。
|
|
37
37
|
- 发现超限 touched file 后,不只记录行数;判断本次是否在该文件加入新职责,并回到本节拆分原则选择产品面、hook、model、adapter、component、service / facade 或 verification helper 等边界。避免只按行数机械拆分、但耦合和职责仍留在原处。
|
|
38
|
-
- 如果本次不拆,`Modularity Check` 取 `exception
|
|
38
|
+
- 如果本次不拆,`Modularity Check` 取 `exception`,必须有 `<harnessRoot>/config.yaml` 的 `modularity.waivers` 记录(`path`、收窄 `category`、`reason`、`future_split_boundary`);交付说明只能补充说明,不是机器豁免。若项目设置 `modularity.policy: strict_except_generated`,legacy waiver 不可用,超限手写源码必须拆分或停止触碰。已豁免历史债也不得继续塞新职责,除非本次任务就是拆分 / 迁移。
|
|
39
39
|
- 查找相似实现、重复逻辑、紧耦合模块或影响面异常扩散点。
|
|
40
40
|
- 当一个业务对象、能力或接口的变更需要跨多个 Context、产品域或实现层同步调整时,将该影响范围视为模块边界复核信号;优先评估是否应通过独立模块、服务、facade 或稳定接口收敛依赖,避免通过手工 manifest 长期复制实现暴露面。
|
|
41
41
|
- 将候选项分为局部重构与长期边界变化,后者按既有 Context-first 规则处理。
|
|
@@ -69,7 +69,7 @@ Project-specific engineering rules belong in a separate project-local Skill unde
|
|
|
69
69
|
- 工程 / RFC / 实现类任务的 `Task Contract` 必须包含 `Modularity Check: none|required|exception`:
|
|
70
70
|
- `none`:没有超限计划 / touched 手写源码文件,或本次没有向超限文件增加新职责。
|
|
71
71
|
- `required`:拆分是本次验收条件,应按 abstraction / decomposition scan 的职责边界完成。
|
|
72
|
-
- `exception
|
|
72
|
+
- `exception`:本次触碰超限文件但暂不拆;只有默认 `modularity.policy: scoped_waivers` 允许此路径,且必须已有或同步新增 `<harnessRoot>/config.yaml` `modularity.waivers` 记录文件、收窄分类、原因和后续拆分边界。若项目设置 `modularity.policy: strict_except_generated`,不得用 legacy waiver 绕过超限手写源码,交付说明只记录本次是否新增职责以及为什么没有拆。
|
|
73
73
|
- `Applicable Module Design` 是高风险任务的前置字段:列出命中的 Context / Skill 来源、适用的 Principles、Design Logic 和 Design Rationale,以及它们控制的当前实现或验证选择。
|
|
74
74
|
- `Principle Decision Gate` 要写明首选执行路径、fallback / degraded path 的进入条件,以及什么证据不能证明本次目标。涉及 capability、metric 或 acceptance claim 时,先声明要证明的 claim,再选择命令或 probe。
|
|
75
75
|
- 对长任务、多模块、多 agent、容易发生 `Context Delta` 调头或多轮验证的任务,可以用 `plan.md` 或等价临时计划面暂存 `Context Delta`、`Task Contract`、`Implementation Steps` 和 `Contract Conformance`;它只是临时执行缓存。
|
|
@@ -23,21 +23,27 @@ export async function checkModularity(args) {
|
|
|
23
23
|
files: parsed.files,
|
|
24
24
|
limit: parsed.limit
|
|
25
25
|
});
|
|
26
|
-
console.log(`check-modularity audited=${report.files.length} warning=${report.warnings.length} limit=${report.limit}`);
|
|
26
|
+
console.log(`check-modularity audited=${report.files.length} warning=${report.warnings.length} limit=${report.limit} waived=${report.waivedWarnings.length}`);
|
|
27
27
|
if (report.files.length === 0) {
|
|
28
28
|
console.log("No handwritten source files matched the selected scope.");
|
|
29
29
|
}
|
|
30
30
|
for (const file of report.files) {
|
|
31
|
-
const prefix = file.overLimit ? "over-limit" : "ok";
|
|
31
|
+
const prefix = file.overLimit && file.waived ? "waived" : file.overLimit ? "over-limit" : "ok";
|
|
32
32
|
console.log(`${prefix}: ${file.relativePath} ${file.lines} lines`);
|
|
33
33
|
}
|
|
34
|
+
for (const error of report.errors) {
|
|
35
|
+
console.error(`error: ${error}`);
|
|
36
|
+
}
|
|
34
37
|
for (const warning of report.warnings) {
|
|
35
38
|
console.warn(`warning: ${warning}`);
|
|
36
39
|
}
|
|
40
|
+
for (const waiver of report.waivedWarnings) {
|
|
41
|
+
console.warn(`waived: ${waiver}`);
|
|
42
|
+
}
|
|
37
43
|
if (report.warnings.length > 0) {
|
|
38
|
-
console.warn("warning: over-limit touched files need a split
|
|
44
|
+
console.warn("warning: over-limit touched files need a split or, when modularity.policy is scoped_waivers, a valid <harnessRoot>/config.yaml modularity waiver with file, reason and future split boundary.");
|
|
39
45
|
}
|
|
40
|
-
if (report.warnings.length > 0 && parsed.failOnWarning) {
|
|
46
|
+
if (report.errors.length > 0 || (report.warnings.length > 0 && parsed.failOnWarning)) {
|
|
41
47
|
process.exitCode = 1;
|
|
42
48
|
}
|
|
43
49
|
}
|
|
@@ -133,5 +139,7 @@ function helpText() {
|
|
|
133
139
|
check-modularity --base <ref> [--limit 300] [--fail-on-warning]
|
|
134
140
|
|
|
135
141
|
Audits selected handwritten source files for physical line-count risk.
|
|
136
|
-
The default is warning-only; --fail-on-warning lets projects opt into CI enforcement
|
|
142
|
+
The default is warning-only; --fail-on-warning lets projects opt into CI enforcement.
|
|
143
|
+
Generated configs default to modularity.policy: strict_except_generated; omitted policy is treated as scoped_waivers for compatibility.
|
|
144
|
+
Over-limit files can be waived only through <harnessRoot>/config.yaml modularity.waivers when policy is scoped_waivers.`;
|
|
137
145
|
}
|
package/dist/commands/index.js
CHANGED
|
@@ -16,6 +16,7 @@ export const commands = {
|
|
|
16
16
|
"export-context": exportContext,
|
|
17
17
|
validate,
|
|
18
18
|
"validate-context": (args) => validate(["validate-context", ...args]),
|
|
19
|
+
"validate-code-modularity": (args) => validate(["validate-code-modularity", ...args]),
|
|
19
20
|
"validate-harness": (args) => validate(["validate-harness", ...args]),
|
|
20
21
|
package: packageSource
|
|
21
22
|
};
|
|
@@ -30,9 +31,11 @@ export function help() {
|
|
|
30
31
|
check-modularity --touched|--file <path>|--base <ref> [--limit 300] [--fail-on-warning]
|
|
31
32
|
Warn when selected handwritten source files exceed a line-count limit
|
|
32
33
|
export-context --full|--code|--all [--output <path>] [--check]
|
|
33
|
-
Export a temporary Context summary or code implementation Markdown artifact
|
|
34
|
-
validate <gate> Run a Harness validation gate
|
|
35
|
-
validate-context Validate Minimal Context fact-source recoverability
|
|
36
|
-
validate-
|
|
34
|
+
Export a temporary Context summary or code implementation Markdown artifact
|
|
35
|
+
validate <gate> Run a Harness validation gate
|
|
36
|
+
validate-context Validate Minimal Context fact-source recoverability
|
|
37
|
+
validate-code-modularity
|
|
38
|
+
Enforce touched handwritten source file modularity
|
|
39
|
+
validate-harness Run validate-context and validate-code-modularity
|
|
37
40
|
package <subcommand> Maintain package canonical source`);
|
|
38
41
|
}
|
package/dist/lib/config.js
CHANGED
|
@@ -9,6 +9,10 @@ export function defaultConfig(root) {
|
|
|
9
9
|
package: CANONICAL_CORE_PACKAGE,
|
|
10
10
|
schema_version: CURRENT_SCHEMA_VERSION
|
|
11
11
|
},
|
|
12
|
+
modularity: {
|
|
13
|
+
limit: 300,
|
|
14
|
+
policy: "strict_except_generated"
|
|
15
|
+
},
|
|
12
16
|
managed_files: [
|
|
13
17
|
{ path: "AGENTS.md", strategy: "merge-block" },
|
|
14
18
|
{ path: "Makefile", strategy: "merge-block" },
|
|
@@ -46,6 +50,7 @@ export function normalizeConfig(value, root = ".agent") {
|
|
|
46
50
|
package: value.core?.package ?? fallback.core.package,
|
|
47
51
|
schema_version: value.core?.schema_version ?? fallback.core.schema_version
|
|
48
52
|
},
|
|
53
|
+
modularity: value.modularity,
|
|
49
54
|
managed_files: value.managed_files ?? fallback.managed_files,
|
|
50
55
|
never_overwrite: value.never_overwrite ?? fallback.never_overwrite
|
|
51
56
|
};
|
package/dist/lib/modularity.d.ts
CHANGED
|
@@ -8,11 +8,20 @@ export interface ModularityFileReport {
|
|
|
8
8
|
relativePath: string;
|
|
9
9
|
lines: number;
|
|
10
10
|
overLimit: boolean;
|
|
11
|
+
waived?: ModularityWaiver;
|
|
11
12
|
}
|
|
12
13
|
export interface ModularityCheckReport {
|
|
13
14
|
limit: number;
|
|
14
15
|
files: ModularityFileReport[];
|
|
15
16
|
warnings: string[];
|
|
17
|
+
waivedWarnings: string[];
|
|
18
|
+
errors: string[];
|
|
19
|
+
}
|
|
20
|
+
export interface ModularityWaiver {
|
|
21
|
+
relativePath: string;
|
|
22
|
+
category: string;
|
|
23
|
+
reason: string;
|
|
24
|
+
futureSplitBoundary: string;
|
|
16
25
|
}
|
|
17
26
|
export declare function runModularityCheck(projectRoot: string, options: ModularityCheckOptions): Promise<ModularityCheckReport>;
|
|
18
27
|
export declare function countPhysicalLines(content: string): number;
|
package/dist/lib/modularity.js
CHANGED
|
@@ -2,9 +2,13 @@ import { execFile } from "node:child_process";
|
|
|
2
2
|
import { promises as fs } from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
|
+
import { readConfig } from "./config.js";
|
|
5
6
|
import { shouldIncludeCodeFile, toPosix } from "./source-files.js";
|
|
6
7
|
const execFileAsync = promisify(execFile);
|
|
7
8
|
const GIT_MAX_BUFFER = 16 * 1024 * 1024;
|
|
9
|
+
const DEFAULT_LINE_LIMIT = 300;
|
|
10
|
+
const DEFAULT_MODULARITY_POLICY = "scoped_waivers";
|
|
11
|
+
const MODULARITY_POLICIES = new Set([DEFAULT_MODULARITY_POLICY, "strict_except_generated"]);
|
|
8
12
|
const GENERATED_FILE_PATTERNS = [
|
|
9
13
|
/^\s*(?:\/\/|#|--|;)\s*@generated\b/im,
|
|
10
14
|
/^\s*(?:\/\/|#|--|;)\s*code generated .*do not edit\.?\s*$/im,
|
|
@@ -13,8 +17,18 @@ const GENERATED_FILE_PATTERNS = [
|
|
|
13
17
|
/^\s*(?:\/\/|#|--|;)\s*this file was generated\b/im,
|
|
14
18
|
/^\s*(?:\/\/|#|--|;)\s*generated by\b/im
|
|
15
19
|
];
|
|
20
|
+
const WAIVER_CATEGORIES = new Set([
|
|
21
|
+
"generated",
|
|
22
|
+
"third_party_reference",
|
|
23
|
+
"legacy_migration",
|
|
24
|
+
"aggregate_styles",
|
|
25
|
+
"fixture_snapshot"
|
|
26
|
+
]);
|
|
16
27
|
export async function runModularityCheck(projectRoot, options) {
|
|
17
|
-
const
|
|
28
|
+
const config = await readConfig(projectRoot);
|
|
29
|
+
const { limit: configuredLimit, waiverValues, errors: configErrors } = validateModularityConfig(config.modularity);
|
|
30
|
+
const { waivers, errors: waiverErrors } = validateWaivers(projectRoot, waiverValues);
|
|
31
|
+
const limit = options.limit ?? configuredLimit ?? DEFAULT_LINE_LIMIT;
|
|
18
32
|
const candidates = new Set();
|
|
19
33
|
for (const file of options.files ?? []) {
|
|
20
34
|
candidates.add(normalizeExplicitPath(projectRoot, file));
|
|
@@ -39,12 +53,16 @@ export async function runModularityCheck(projectRoot, options) {
|
|
|
39
53
|
continue;
|
|
40
54
|
}
|
|
41
55
|
const lines = countPhysicalLines(await fs.readFile(absolutePath, "utf8"));
|
|
42
|
-
|
|
56
|
+
const waiver = waivers.get(relativePath);
|
|
57
|
+
files.push({ relativePath, lines, overLimit: lines > limit, waived: waiver });
|
|
43
58
|
}
|
|
44
59
|
const warnings = files
|
|
45
|
-
.filter((file) => file.overLimit)
|
|
60
|
+
.filter((file) => file.overLimit && !file.waived)
|
|
46
61
|
.map((file) => `${file.relativePath}: ${file.lines} physical lines exceeds limit ${limit}`);
|
|
47
|
-
|
|
62
|
+
const waivedWarnings = files
|
|
63
|
+
.filter((file) => file.overLimit && file.waived)
|
|
64
|
+
.map((file) => `${file.relativePath}: ${file.lines} physical lines exceeds limit ${limit} but is waived as ${file.waived?.category}`);
|
|
65
|
+
return { limit, files, warnings, waivedWarnings, errors: [...configErrors, ...waiverErrors] };
|
|
48
66
|
}
|
|
49
67
|
export function countPhysicalLines(content) {
|
|
50
68
|
if (content.length === 0) {
|
|
@@ -57,10 +75,19 @@ export function countPhysicalLines(content) {
|
|
|
57
75
|
return lines.length;
|
|
58
76
|
}
|
|
59
77
|
async function gitTouchedFiles(projectRoot) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
78
|
+
let result;
|
|
79
|
+
try {
|
|
80
|
+
result = await execFileAsync("git", ["-C", projectRoot, "status", "--porcelain", "-z"], {
|
|
81
|
+
encoding: "utf8",
|
|
82
|
+
maxBuffer: GIT_MAX_BUFFER
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
if (isNotGitRepositoryError(error)) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
64
91
|
const records = result.stdout.split("\0").filter(Boolean);
|
|
65
92
|
const files = [];
|
|
66
93
|
for (let index = 0; index < records.length; index += 1) {
|
|
@@ -83,6 +110,13 @@ async function gitDiffFiles(projectRoot, base) {
|
|
|
83
110
|
});
|
|
84
111
|
return result.stdout.split("\0").filter(Boolean);
|
|
85
112
|
}
|
|
113
|
+
function isNotGitRepositoryError(error) {
|
|
114
|
+
if (!error || typeof error !== "object") {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
const stderr = "stderr" in error && typeof error.stderr === "string" ? error.stderr : "";
|
|
118
|
+
return /not a git repository/i.test(stderr);
|
|
119
|
+
}
|
|
86
120
|
function normalizeExplicitPath(projectRoot, value) {
|
|
87
121
|
const absolute = path.isAbsolute(value) ? path.resolve(value) : path.resolve(projectRoot, value);
|
|
88
122
|
const relative = toPosix(path.relative(projectRoot, absolute));
|
|
@@ -94,6 +128,96 @@ function normalizeExplicitPath(projectRoot, value) {
|
|
|
94
128
|
function normalizeGitPath(value) {
|
|
95
129
|
return toPosix(value).replace(/^\.\//, "");
|
|
96
130
|
}
|
|
131
|
+
function validateModularityConfig(modularity) {
|
|
132
|
+
const errors = [];
|
|
133
|
+
if (modularity === undefined) {
|
|
134
|
+
return { errors };
|
|
135
|
+
}
|
|
136
|
+
if (!modularity || typeof modularity !== "object" || Array.isArray(modularity)) {
|
|
137
|
+
errors.push("<harnessRoot>/config.yaml modularity must be an object");
|
|
138
|
+
return { errors };
|
|
139
|
+
}
|
|
140
|
+
const value = modularity;
|
|
141
|
+
let limit;
|
|
142
|
+
let policy = DEFAULT_MODULARITY_POLICY;
|
|
143
|
+
if (value.limit !== undefined) {
|
|
144
|
+
if (!Number.isInteger(value.limit) || Number(value.limit) <= 0) {
|
|
145
|
+
errors.push("<harnessRoot>/config.yaml modularity.limit must be a positive integer");
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
limit = Number(value.limit);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (value.policy !== undefined) {
|
|
152
|
+
if (typeof value.policy !== "string" || !MODULARITY_POLICIES.has(value.policy)) {
|
|
153
|
+
errors.push("<harnessRoot>/config.yaml modularity.policy must be one of scoped_waivers, strict_except_generated");
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
policy = value.policy;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (policy === "strict_except_generated" && value.waivers !== undefined) {
|
|
160
|
+
errors.push("<harnessRoot>/config.yaml modularity.waivers is not allowed when modularity.policy is strict_except_generated");
|
|
161
|
+
}
|
|
162
|
+
return { limit, waiverValues: policy === "scoped_waivers" ? value.waivers : undefined, errors };
|
|
163
|
+
}
|
|
164
|
+
function validateWaivers(projectRoot, waiverValues) {
|
|
165
|
+
const waivers = new Map();
|
|
166
|
+
const errors = [];
|
|
167
|
+
if (waiverValues === undefined) {
|
|
168
|
+
return { waivers, errors };
|
|
169
|
+
}
|
|
170
|
+
if (!Array.isArray(waiverValues)) {
|
|
171
|
+
errors.push("<harnessRoot>/config.yaml modularity.waivers must be an array");
|
|
172
|
+
return { waivers, errors };
|
|
173
|
+
}
|
|
174
|
+
for (const [index, waiver] of waiverValues.entries()) {
|
|
175
|
+
const label = `<harnessRoot>/config.yaml modularity.waivers[${index}]`;
|
|
176
|
+
if (!waiver || typeof waiver !== "object") {
|
|
177
|
+
errors.push(`${label} must be an object`);
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
const relativePath = requiredWaiverString(waiver, "path", label, errors);
|
|
181
|
+
const category = requiredWaiverString(waiver, "category", label, errors);
|
|
182
|
+
const reason = requiredWaiverString(waiver, "reason", label, errors);
|
|
183
|
+
const futureSplitBoundary = requiredWaiverString(waiver, "future_split_boundary", label, errors);
|
|
184
|
+
if (!relativePath || !category || !reason || !futureSplitBoundary) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (!WAIVER_CATEGORIES.has(category)) {
|
|
188
|
+
errors.push(`${label}.category must be one of generated, third_party_reference, legacy_migration, aggregate_styles, fixture_snapshot`);
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
let normalized;
|
|
192
|
+
try {
|
|
193
|
+
normalized = normalizeExplicitPath(projectRoot, relativePath);
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
errors.push(`${label}.path must stay inside the project root: ${relativePath}`);
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const existing = waivers.get(normalized);
|
|
200
|
+
if (existing) {
|
|
201
|
+
errors.push(`${label}.path duplicates an existing modularity waiver for ${normalized}`);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
waivers.set(normalized, {
|
|
205
|
+
relativePath: normalized,
|
|
206
|
+
category,
|
|
207
|
+
reason,
|
|
208
|
+
futureSplitBoundary
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return { waivers, errors };
|
|
212
|
+
}
|
|
213
|
+
function requiredWaiverString(waiver, field, label, errors) {
|
|
214
|
+
const value = waiver[field];
|
|
215
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
216
|
+
errors.push(`${label}.${field} must be a non-empty string`);
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
return value.trim();
|
|
220
|
+
}
|
|
97
221
|
async function isRegularFile(target) {
|
|
98
222
|
try {
|
|
99
223
|
return (await fs.stat(target)).isFile();
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -3,9 +3,21 @@ export interface HarnessConfig {
|
|
|
3
3
|
package: string;
|
|
4
4
|
schema_version: string;
|
|
5
5
|
};
|
|
6
|
+
modularity?: HarnessModularityConfig;
|
|
6
7
|
managed_files: ManagedFile[];
|
|
7
8
|
never_overwrite: string[];
|
|
8
9
|
}
|
|
10
|
+
export interface HarnessModularityConfig {
|
|
11
|
+
limit?: number;
|
|
12
|
+
policy?: "scoped_waivers" | "strict_except_generated";
|
|
13
|
+
waivers?: ModularityWaiverConfig[];
|
|
14
|
+
}
|
|
15
|
+
export interface ModularityWaiverConfig {
|
|
16
|
+
path?: string;
|
|
17
|
+
category?: string;
|
|
18
|
+
reason?: string;
|
|
19
|
+
future_split_boundary?: string;
|
|
20
|
+
}
|
|
9
21
|
export interface ManagedFile {
|
|
10
22
|
path: string;
|
|
11
23
|
strategy: "merge-block" | "generated" | "generated-compat" | "managed" | "merge-with-local" | "create-if-missing";
|
package/dist/lib/validators.js
CHANGED
|
@@ -2,10 +2,12 @@ import path from "node:path";
|
|
|
2
2
|
import { readConfig } from "./config.js";
|
|
3
3
|
import { harnessPath, harnessRoot } from "./harness-root.js";
|
|
4
4
|
import { listFiles, pathExists, readText } from "./fs.js";
|
|
5
|
+
import { runModularityCheck } from "./modularity.js";
|
|
5
6
|
import { unsupportedSchemaMessage } from "./schema-guard.js";
|
|
6
7
|
const VALIDATORS = {
|
|
7
8
|
"validate-context": validateContext,
|
|
8
|
-
"validate-
|
|
9
|
+
"validate-code-modularity": validateCodeModularity,
|
|
10
|
+
"validate-harness": validateHarness
|
|
9
11
|
};
|
|
10
12
|
const GLOBAL_REQUIRED_SECTIONS = [
|
|
11
13
|
...sectionSpecs([
|
|
@@ -71,12 +73,43 @@ export async function runValidator(projectRoot, gate) {
|
|
|
71
73
|
return {
|
|
72
74
|
info: [],
|
|
73
75
|
errors: [
|
|
74
|
-
`unknown validator: ${gate}. Minimal Context Harness supports validate-context and validate-harness only.`
|
|
76
|
+
`unknown validator: ${gate}. Minimal Context Harness supports validate-context, validate-code-modularity and validate-harness only.`
|
|
75
77
|
]
|
|
76
78
|
};
|
|
77
79
|
}
|
|
78
80
|
return validator(projectRoot);
|
|
79
81
|
}
|
|
82
|
+
async function validateHarness(projectRoot) {
|
|
83
|
+
const contextReport = await validateContext(projectRoot);
|
|
84
|
+
const modularityReport = await validateCodeModularity(projectRoot);
|
|
85
|
+
return {
|
|
86
|
+
info: [...contextReport.info, ...modularityReport.info],
|
|
87
|
+
errors: [...contextReport.errors, ...modularityReport.errors]
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async function validateCodeModularity(projectRoot) {
|
|
91
|
+
const report = await runModularityCheck(projectRoot, { touched: true });
|
|
92
|
+
const info = [
|
|
93
|
+
`code modularity audited=${report.files.length} warning=${report.warnings.length} waived=${report.waivedWarnings.length} limit=${report.limit}`
|
|
94
|
+
];
|
|
95
|
+
if (report.files.length === 0) {
|
|
96
|
+
info.push("No handwritten source files matched the selected scope.");
|
|
97
|
+
}
|
|
98
|
+
for (const file of report.files) {
|
|
99
|
+
const prefix = file.overLimit && file.waived ? "waived" : file.overLimit ? "over-limit" : "ok";
|
|
100
|
+
info.push(`${prefix}: ${file.relativePath} ${file.lines} lines`);
|
|
101
|
+
}
|
|
102
|
+
for (const waiver of report.waivedWarnings) {
|
|
103
|
+
info.push(`waived: ${waiver}`);
|
|
104
|
+
}
|
|
105
|
+
if (report.errors.length === 0 && report.warnings.length === 0) {
|
|
106
|
+
info.push("Code modularity validation passed");
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
info,
|
|
110
|
+
errors: [...report.errors, ...report.warnings]
|
|
111
|
+
};
|
|
112
|
+
}
|
|
80
113
|
async function validateContext(projectRoot) {
|
|
81
114
|
const info = [];
|
|
82
115
|
const errors = [];
|